Skip to content

Instantly share code, notes, and snippets.

@Moyf
Last active December 20, 2025 03:12
Show Gist options
  • Select an option

  • Save Moyf/9ada3264e34368453ac806662212f859 to your computer and use it in GitHub Desktop.

Select an option

Save Moyf/9ada3264e34368453ac806662212f859 to your computer and use it in GitHub Desktop.
An interactive nyan can progress

How to use

Enable datacore plugin.
Put the jsx and css files together in your vault.

Then put this codeblock in your note. Remember to replace the scriptPath path.

```datacorejsx
const scriptPath = "PATH/TO/dc-nyanCatProgress-draggable.jsx";  // ⬅️ replace it with your jsx file path!
const target = dc.fileLink(scriptPath);
const result = await dc.require(target);
const view = result?.renderedView ?? result?.View ?? result;  
const Func = result?.Func ?? null;

return function View() {
    const currentFile = dc.useCurrentFile();
    if (Func) {
        return Func({ currentFile, scriptPath });
    }
    return view ?? <p>Failed to render</p>;
}
```

Note

It is recommended to use the Virtual Content plugin to dynamically insert this progress bar into any of your notes that have a progress property.

Version

Current version: v0.1

Discord

If you have any questions you can communicate in This Thread in Obsidian's Channel

Acknowledgments

The original author of NyanCat Progress CSS is: AnubisNekhet
For details, please refer to the License section at the top of the CSS file.

function Func() {
const current = dc.useCurrentFile();
let initialProgress = current.value("progress");
const task_progress = current.value("task_progress");
// i18n 支持
const getLanguage = () => {
try {
return app.vault.adapter.app.i18n?.language || 'en';
} catch {
return moment.locale() === 'zh-cn' ? 'zh' : 'en';
}
};
const texts = {
zh: {
updating: "正在更新...",
currentProgress: "当前进度:",
dragging: "拖动中",
clickOrDrag: "点击或拖动调整",
progressUpdated: "进度已更新为",
updateFailed: "更新进度失败!",
noActiveFile: "没有活动文件",
noProgressProperty: "progress 或 task_progress 属性不存在!",
percent: "%"
},
en: {
updating: "Updating...",
currentProgress: "Progress: ",
dragging: "Dragging",
clickOrDrag: "Click or drag to adjust",
progressUpdated: "Progress updated to",
updateFailed: "Failed to update progress!",
noActiveFile: "No active file",
noProgressProperty: "progress or task_progress property not found!",
percent: "%"
}
};
const lang = getLanguage();
const t = texts[lang] || texts.en;
// 状态管理
const [progress, setProgress] = dc.useState(0);
const [isDragging, setIsDragging] = dc.useState(false);
const [isUpdating, setIsUpdating] = dc.useState(false);
const [styles, setStyles] = dc.useState("");
const cssFile = app.metadataCache.getFirstLinkpathDest("dc-nyanCatProgress.css");
// 加载CSS样式
dc.useEffect(() => {
if (cssFile) {
app.vault.cachedRead(cssFile).then(setStyles);
}
}, []);
// 初始化进度值
dc.useEffect(() => {
if (initialProgress !== undefined && initialProgress !== null) {
setProgress(Math.min(Math.max(initialProgress, 0), 100));
} else if (task_progress) {
const split = task_progress.split("/");
if (split.length === 2) {
const done = parseFloat(split[0]);
const total = parseFloat(split[1]);
if (total > 0) {
setProgress(Math.round((done / total) * 100));
} else {
setProgress(0);
}
}
}
}, [initialProgress, task_progress]);
// 更新笔记中的progress属性
const updateNoteProgress = async (newProgress) => {
setIsUpdating(true);
try {
const activeFile = app.workspace.getActiveFile();
if (!activeFile) {
console.error(t.noActiveFile);
return;
}
await app.fileManager.processFrontMatter(activeFile, (frontmatter) => {
frontmatter.progress = newProgress;
});
new Notice(`${t.progressUpdated} ${newProgress}${t.percent}`);
} catch (error) {
console.error("更新进度失败:", error);
new Notice(t.updateFailed);
}
setIsUpdating(false);
};
// 用于存储进度条容器的引用
const progressContainerRef = dc.useRef(null);
const [finalProgress, setFinalProgress] = dc.useState(progress);
// 根据鼠标位置计算进度
const calculateProgressFromMouse = (e) => {
if (!progressContainerRef.current) return progress;
const rect = progressContainerRef.current.getBoundingClientRect();
const x = e.clientX - rect.left;
const percentage = Math.min(Math.max((x / rect.width) * 100, 0), 100);
return Math.round(percentage);
};
// 处理拖动开始
const handleMouseDown = (e) => {
e.preventDefault();
setIsDragging(true);
const newProgress = calculateProgressFromMouse(e);
setProgress(newProgress);
};
// 处理拖动中
const handleMouseMove = dc.useCallback((e) => {
if (!isDragging) return;
e.preventDefault();
const newProgress = calculateProgressFromMouse(e);
setProgress(newProgress);
}, [isDragging]);
// 处理拖动结束
const handleMouseUp = dc.useCallback((e) => {
if (!isDragging) return;
e.preventDefault();
const finalProgress = calculateProgressFromMouse(e);
setProgress(finalProgress);
setFinalProgress(finalProgress);
setIsDragging(false);
// 拖动结束后更新笔记
updateNoteProgress(finalProgress);
}, [isDragging]);
// 添加全局鼠标事件监听器
dc.useEffect(() => {
if (isDragging) {
const handleGlobalMouseMove = (e) => handleMouseMove(e);
const handleGlobalMouseUp = (e) => handleMouseUp(e);
document.addEventListener('mousemove', handleGlobalMouseMove);
document.addEventListener('mouseup', handleGlobalMouseUp);
document.addEventListener('mouseleave', handleGlobalMouseUp);
return () => {
document.removeEventListener('mousemove', handleGlobalMouseMove);
document.removeEventListener('mouseup', handleGlobalMouseUp);
document.removeEventListener('mouseleave', handleGlobalMouseUp);
};
}
}, [isDragging, handleMouseMove, handleMouseUp]);
// 如果没有初始进度值且没有task_progress,显示错误信息
if (initialProgress === undefined && initialProgress === null && !task_progress) {
return <p>{t.noProgressProperty}</p>;
}
const progressAdditionalCss = progress < 30 ? "progress-low" : progress < 60 ? "progress-medium" : "progress-high";
const catPosition = Math.min(Math.max(progress - 2, 0), 98); // 调整猫的位置让它不会超出边界,100%时可以到达98%位置
return (
<>
<style>{styles}</style>
<div className="progress-container nyan-cat" style={{marginTop: "0.5rem", marginBottom: "0.5rem"}}>
<div className="custom-progress-container" style={{display: "flex", alignItems: "center", justifyContent: "center", flexDirection: "column"}}>
<div className={`progress-fill ${progressAdditionalCss}`} style={{width: "80%"}}>
{/* 可拖动的进度条容器 */}
<div
ref={progressContainerRef}
className={`draggable-progress-container ${isUpdating ? 'progress-updating' : ''}`}
onMouseDown={handleMouseDown}
style={{
cursor: isDragging ? 'grabbing' : 'pointer',
userSelect: 'none'
}}
>
{/* 进度填充 */}
<div
className="draggable-progress-fill"
style={{ width: `${progress}%` }}
/>
{/* 可拖动的猫猫 */}
<div
className={`draggable-nyan-cat ${isDragging ? 'dragging' : ''}`}
style={{
left: `${catPosition}%`
}}
/>
</div>
</div>
<div style={{ textAlign: "center", marginTop: "0.5em" }}>
<span className="rainbow-hover progress-text-center" style={{
fontSize: "0.8em",
color: "var(--text-muted)"
}}>
{isUpdating ? t.updating : `${t.currentProgress}${progress}${t.percent}`}
</span>
<div style={{
fontSize: "0.6em",
color: "var(--text-muted)",
opacity: 0.6,
marginTop: "0.2em"
}}>
{!isUpdating && (isDragging ? t.dragging : t.clickOrDrag)}
</div>
</div>
</div>
</div>
</>
);
}
const renderedView = <Func />;
return { renderedView, Func };
function View() {
const currentFile = dc.useCurrentFile();
if (Func) {
return Func({ currentFile });
}
return <p>Nothing to render</p>;
}
/* Nyan Cat Progress Bars - Modified by Moy */
/* ! Original author information: */
/* AGPLv3 License
Nyan Cat Progress Bars
Author: AnubisNekhet
Note: If you decide to implement it in your theme or redistribute it, please keep this comment (Especially for *certain* individuals who may try to rebrand it as their own :))
Support me: https://buymeacoffee.com/AnubisNekhet
*/
/* 1. 进度条槽背景 */
.markdown-preview-view progress[class*=nyan-cat]::-webkit-progress-bar,
.markdown-rendered progress[class*=nyan-cat]::-webkit-progress-bar,
.markdown-source-view.is-live-preview progress[class*=nyan-cat]::-webkit-progress-bar {
background-color: var(--background-secondary);
box-shadow: none;
border-radius: 6px;
overflow: hidden;
border: none; /* 移除边框防止宽度计算误差 */
background: url("data:image/gif;base64,R0lGODlhMAAMAIAAAAxBd////yH/C05FVFNDQVBFMi4wAwEAAAAh+QQECgAAACwAAAAAMAAMAAACJYSPqcvtD6MKstpLr24Z9A2GYvJ544mhXQmxoesElIyCcB3dRgEAIfkEBAoAAAAsAQACAC0ACgAAAiGEj6nLHG0enNQdWbPefOHYhSLydVhJoSYXPO04qrAmJwUAIfkEBAoAAAAsBQABACkACwAAAiGEj6nLwQ8jcC5ViW3evHt1GaE0flxpphn6BNTEqvI8dQUAIfkEBAoAAAAsAQABACoACwAAAiGEj6nLwQ+jcU5VidPNvPtvad0GfmSJeicUUECbxnK0RgUAIfkEBAoAAAAsAAAAACcADAAAAiCEj6mbwQ+ji5QGd6t+c/v2hZzYiVpXmuoKIikLm6hXAAAh+QQECgAAACwAAAAALQAMAAACI4SPqQvBD6NysloTXL480g4uX0iW1Wg21oem7ismLUy/LFwAACH5BAQKAAAALAkAAAAkAAwAAAIghI8Joe0Po0yBWTaz3g/z7UXhMX7kYmplmo0rC8cyUgAAIfkEBAoAAAAsBQAAACUACgAAAh2Ejwmh7Q+jbIFZNrPeEXPudU74IVa5kSiYqOtRAAAh+QQECgAAACwEAAAAIgAKAAACHISPELfpD6OcqTGKs4bWRp+B36YFi0mGaVmtWQEAIfkEBAoAAAAsAAAAACMACgAAAh2EjxC36Q+jnK8xirOW1kavgd+2BYtJhmnpiGtUAAAh+QQECgAAACwAAAAALgALAAACIYSPqcvtD+MKicqLn82c7e6BIhZQ5jem6oVKbfdqQLzKBQAh+QQECgAAACwCAAIALAAJAAACHQx+hsvtD2OStDplKc68r2CEm0eW5uSN6aqe1lgAADs=");
}
/* 2. 进度条彩虹内容 */
.markdown-preview-view progress[class*=nyan-cat]::-webkit-progress-value,
.markdown-rendered progress[class*=nyan-cat]::-webkit-progress-value,
.markdown-source-view.is-live-preview progress[class*=nyan-cat]::-webkit-progress-value {
background: linear-gradient(to bottom, #FF0000 0%, #FF0000 16.5%, #FF9900 16.5%, #FF9900 33%, #FFFF00 33%, #FFFF00 50%, #33FF00 50%, #33FF00 66%, #0099FF 66%, #0099FF 83.5%, #6633ff 83.5%, #6633ff 100%) !important;
overflow: hidden;
transition: width 0.3s ease;
}
/* 3. 猫猫定位核心修正 */
.markdown-preview-view progress[class*=nyan-cat]::after,
.markdown-rendered progress[class*=nyan-cat]::after,
.markdown-source-view.is-live-preview progress[class*=nyan-cat]::after {
content: "";
left: calc(var(--nyan-pos, 0) * 1%);
/* top: 50%; 垂直居中 */
top: -5px; /* 微调猫的位置 */
z-index: 10;
/*
计算逻辑:
1. translateX(var(--nyan-pos) * 1%) -> 把猫移到进度的百分比位置(此时猫的左边缘对应进度结束点)。
2. - 15px -> 向左微调。猫宽30px,减去15px是为了让猫的尾巴正好盖在彩虹条末端,形成连接感。
3. - 50% -> 垂直居中修正
*/
transform: translate(-15px);
/* 基础样式 */
width: 34px;
height: 21px;
position: absolute;
image-rendering: pixelated;
background: url("data:image/gif;base64,R0lGODlhIgAVAKIHAL3/9/+Zmf8zmf/MmZmZmf+Z/wAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpDMkJBNjY5RTU1NEJFMzExOUM4QUM2MDAwNDQzRERBQyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCREIzOEIzMzRCN0IxMUUzODhEQjgwOTYzMTgyNTE0QiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCREIzOEIzMjRCN0IxMUUzODhEQjgwOTYzMTgyNTE0QiIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkM1QkE2NjlFNTU0QkUzMTE5QzhBQzYwMDA0NDNEREFDIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMyQkE2NjlFNTU0QkUzMTE5QzhBQzYwMDA0NDNEREFDIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8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") !important;
background-size: contain !important;
background-repeat: no-repeat !important;
/* 添加平滑动画 */
transition: left 0.3s ease-out, width 0.12s;
}
/* 4. 悬停放大效果 */
.markdown-preview-view progress[class*=nyan-cat]:hover::after,
.markdown-rendered progress[class*=nyan-cat]:hover::after,
.markdown-source-view.is-live-preview progress[class*=nyan-cat]:hover::after {
/* 放大时保持中心对齐 */
/* transform: translate(-20px, -50%) scale(1.3); */
}
/* 5. 容器基础样式 */
.markdown-preview-view progress[class*=nyan-cat],
.markdown-rendered progress[class*=nyan-cat],
.markdown-source-view.is-live-preview progress[class*=nyan-cat] {
-webkit-writing-mode: horizontal-tb;
appearance: none;
box-sizing: border-box; /* 确保 padding 不影响宽度计算 */
display: inline-block;
height: 14px;
margin-bottom: 4px;
/* 确保这里没有 padding */
padding: 0;
border: 0;
vertical-align: -0.2rem;
/* 确保伪元素定位参考该元素 */
position: relative;
overflow: visible !important;
}
/* 彩虹文字特效保持不变 */
.rainbow-neon {
background: linear-gradient(90deg, #ff0080, #ff8c00, #40e0d0, #20b2aa, #9370db, #ff0080);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
background-size: 300% 100%;
animation: neon-rainbow 3s ease-in-out infinite;
font-size: 48px;
font-weight: bold;
text-shadow: 0 0 10px rgba(255, 0, 128, 0.3);
}
.rainbow-flow {
background: linear-gradient(90deg, #ff0000, #ff7f00, #ffff00, #00ff00, #0000ff, #4b0082, #9400d3, #ff0000);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
background-size: 400% 100%;
animation: flow-rainbow 4s linear infinite;
font-size: 48px;
font-weight: bold;
}
@keyframes flow-rainbow {
0% { background-position: 0% 50%; }
100% { background-position: 400% 50%; }
}
.rainbow-hover {
color: #333;
transition: all 0.5s ease;
font-size: 48px;
font-weight: bold;
cursor: pointer;
}
.rainbow-hover:hover {
background: linear-gradient(90deg, #ff0000, #ff7f00, #ffff00, #00ff00, #0000ff, #4b0082, #9400d3);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
background-size: 200% 100%;
animation: hover-rainbow 2s linear infinite;
}
@keyframes hover-rainbow {
0% { background-position: 0% 50%; }
100% { background-position: 200% 50%; }
}
.rainbow-text {
background: linear-gradient(180deg, #ff0000, #ff7f00, #ffff00, #00ff00, #0000ff, #4b0082, #9400d3);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
background-size: 200% 100%;
font-size: 48px;
font-weight: bold;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
}
/* 居中文本样式 */
.progress-text-center {
text-align: center;
display: block;
width: 100%;
margin: 0 auto;
}
/* 进度容器样式调整 */
.custom-progress-container {
width: 100%;
}
.progress-fill {
width: 80%;
}
/* ______________ 拖动版本 _____________________________ */
/* 可拖动进度条样式 */
.draggable-progress-container {
position: relative;
height: 14px;
background-color: var(--background-secondary);
border-radius: 6px;
cursor: pointer;
margin: 8px 0;
overflow: visible;
background: url("data:image/gif;base64,R0lGODlhMAAMAIAAAAxBd////yH/C05FVFNDQVBFMi4wAwEAAAAh+QQECgAAACwAAAAAMAAMAAACJYSPqcvtD6MKstpLr24Z9A2GYvJ544mhXQmxoesElIyCcB3dRgEAIfkEBAoAAAAsAQACAC0ACgAAAiGEj6nLHG0enNQdWbPefOHYhSLydVhJoSYXPO04qrAmJwUAIfkEBAoAAAAsBQABACkACwAAAiGEj6nLwQ8jcC5ViW3evHt1GaE0flxpphn6BNTEqvI8dQUAIfkEBAoAAAAsAQABACoACwAAAiGEj6nLwQ+jcU5VidPNvPtvad0GfmSJeicUUECbxnK0RgUAIfkEBAoAAAAsAAAAACcADAAAAiCEj6mbwQ+ji5QGd6t+c/v2hZzYiVpXmuoKIikLm6hXAAAh+QQECgAAACwAAAAALQAMAAACI4SPqQvBD6NysloTXL480g4uX0iW1Wg21oem7ismLUy/LFwAACH5BAQKAAAALAkAAAAkAAwAAAIghI8Joe0Po0yBWTaz3g/z7UXhMX7kYmplmo0rC8cyUgAAIfkEBAoAAAAsBQAAACUACgAAAh2Ejwmh7Q+jbIFZNrPeEXPudU74IVa5kSiYqOtRAAAh+QQECgAAACwEAAAAIgAKAAACHISPELfpD6OcqTGKs4bWRp+B36YFi0mGaVmtWQEAIfkEBAoAAAAsAAAAACMACgAAAh2EjxC36Q+jnK8xirOW1kavgd+2BYtJhmnpiGtUAAAh+QQECgAAACwAAAAALgALAAACIYSPqcvtD+MKicqLn82c7e6BIhZQ5jem6oVKbfdqQLzKBQAh+QQECgAAACwCAAIALAAJAAACHQx+hsvtD2OStDplKc68r2CEm0eW5uSN6aqe1lgAADs=");
}
.draggable-progress-fill {
height: 100%;
background: linear-gradient(to bottom, #FF0000 0%, #FF0000 16.5%, #FF9900 16.5%, #FF9900 33%, #FFFF00 33%, #FFFF00 50%, #33FF00 50%, #33FF00 66%, #0099FF 66%, #0099FF 83.5%, #6633ff 83.5%, #6633ff 100%);
border-radius: 6px;
transition: width 0.1s ease;
position: relative;
}
.draggable-nyan-cat {
position: absolute;
top: -5px;
width: 34px;
height: 21px;
background: url("data:image/gif;base64,R0lGODlhIgAVAKIHAL3/9/+Zmf8zmf/MmZmZmf+Z/wAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpDMkJBNjY5RTU1NEJFMzExOUM4QUM2MDAwNDQzRERBQyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCREIzOEIzMzRCN0IxMUUzODhEQjgwOTYzMTgyNTE0QiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCREIzOEIzMjRCN0IxMUUzODhEQjgwOTYzMTgyNTE0QiIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkM1QkE2NjlFNTU0QkUzMTE5QzhBQzYwMDA0NDNEREFDIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMyQkE2NjlFNTU0QkUzMTE5QzhBQzYwMDA0NDNEREFDIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8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") !important;
background-size: contain !important;
background-repeat: no-repeat !important;
image-rendering: pixelated;
transform: translateX(-15px);
transition: left 0.1s ease;
z-index: 10;
pointer-events: none;
}
.draggable-nyan-cat.dragging {
transition: none;
}
.progress-updating {
opacity: 0.7;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment