显示调整
This commit is contained in:
1 parent
f91003bde2
commit
473a270510
20 files changed
+522
-524
No files matched your search
+1
-1
@@ -18,7 +18,7 @@
|
||||
"pmaTitle": "Für Texturen, die mit Premultiplied Alpha (PMA) exportiert wurden",
|
||||
"background": "Hintergrund",
|
||||
"backgroundTitle": "50×50-Schachbrett, das mit der Leinwand verschoben und skaliert wird und an Blend-Modi teilnimmt",
|
||||
"thumbnail": "🖼 Vorschaubild",
|
||||
"thumbnail": "Vorschaubild",
|
||||
"thumbnailTitle": "Aus dem aktuellen Frame ein 400×400-Vorschaubild erzeugen",
|
||||
"versionTitle": "Spine-Version",
|
||||
"reloadTitle": "Ordner neu laden",
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
"pmaTitle": "For textures exported with Premultiplied Alpha (PMA)",
|
||||
"background": "Background",
|
||||
"backgroundTitle": "50×50 checkerboard that pans and zooms with the canvas and participates in blend modes",
|
||||
"thumbnail": "🖼 Set thumbnail",
|
||||
"thumbnail": "Set thumbnail",
|
||||
"thumbnailTitle": "Generate a 400×400 thumbnail from the current frame",
|
||||
"versionTitle": "Spine version",
|
||||
"reloadTitle": "Reload folder",
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
"pmaTitle": "Para texturas exportadas con Premultiplied Alpha (PMA)",
|
||||
"background": "Fondo",
|
||||
"backgroundTitle": "Fondo de cuadros de 50×50 que se desplaza y escala con el lienzo y participa en los modos de fusión",
|
||||
"thumbnail": "🖼 Crear miniatura",
|
||||
"thumbnail": "Crear miniatura",
|
||||
"thumbnailTitle": "Crear una miniatura de 400×400 con el fotograma actual",
|
||||
"versionTitle": "Versión de Spine",
|
||||
"reloadTitle": "Recargar carpeta",
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
"pmaTitle": "Premultiplied Alpha(PMA)で書き出したテクスチャ用",
|
||||
"background": "背景",
|
||||
"backgroundTitle": "50×50 のチェッカー背景。キャンバスの移動・拡大に追従し、ブレンドモードにも参加します",
|
||||
"thumbnail": "🖼 サムネイル生成",
|
||||
"thumbnail": "サムネイル生成",
|
||||
"thumbnailTitle": "現在のフレームから 400×400 のサムネイルを生成",
|
||||
"versionTitle": "Spine バージョン",
|
||||
"reloadTitle": "フォルダーを再読み込み",
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
"pmaTitle": "Premultiplied Alpha(PMA)로 내보낸 텍스처용",
|
||||
"background": "배경",
|
||||
"backgroundTitle": "50×50 체커 배경. 캔버스 이동·확대를 따라가며 블렌드 모드에도 참여합니다",
|
||||
"thumbnail": "🖼 썸네일 생성",
|
||||
"thumbnail": "썸네일 생성",
|
||||
"thumbnailTitle": "현재 프레임으로 400×400 썸네일 생성",
|
||||
"versionTitle": "Spine 버전",
|
||||
"reloadTitle": "폴더 다시 불러오기",
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
"pmaTitle": "Для текстур, экспортированных с Premultiplied Alpha (PMA)",
|
||||
"background": "Фон",
|
||||
"backgroundTitle": "Шахматный фон 50×50: следует за сдвигом и масштабом холста и участвует в режимах смешивания",
|
||||
"thumbnail": "🖼 Создать миниатюру",
|
||||
"thumbnail": "Создать миниатюру",
|
||||
"thumbnailTitle": "Создать миниатюру 400×400 из текущего кадра",
|
||||
"versionTitle": "Версия Spine",
|
||||
"reloadTitle": "Перезагрузить папку",
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
"pmaTitle": "用于以 Premultiplied Alpha(PMA)方式导出的贴图",
|
||||
"background": "背景",
|
||||
"backgroundTitle": "50×50 方格背景,随画布移动和缩放,并参与混合模式",
|
||||
"thumbnail": "🖼 生成缩略图",
|
||||
"thumbnail": "生成缩略图",
|
||||
"thumbnailTitle": "使用当前帧生成 400×400 缩略图",
|
||||
"versionTitle": "Spine 版本",
|
||||
"reloadTitle": "重新加载目录",
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
"pmaTitle": "用於以 Premultiplied Alpha(PMA)方式匯出的貼圖",
|
||||
"background": "背景",
|
||||
"backgroundTitle": "50×50 方格背景,隨畫布移動和縮放,並參與混合模式",
|
||||
"thumbnail": "🖼 產生縮圖",
|
||||
"thumbnail": "產生縮圖",
|
||||
"thumbnailTitle": "使用目前影格產生 400×400 縮圖",
|
||||
"versionTitle": "Spine 版本",
|
||||
"reloadTitle": "重新載入目錄",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"pmaTitle": "Für Texturen, die mit Premultiplied Alpha (PMA) exportiert wurden",
|
||||
"background": "Hintergrund",
|
||||
"backgroundTitle": "50×50-Schachbrett, das mit der Leinwand verschoben und skaliert wird und an Blend-Modi teilnimmt",
|
||||
"thumbnail": "🖼 Vorschaubild",
|
||||
"thumbnail": "Vorschaubild",
|
||||
"thumbnailTitle": "Aus dem aktuellen Frame ein 400×400-Vorschaubild erzeugen",
|
||||
"versionTitle": "Spine-Version",
|
||||
"reloadTitle": "Ordner neu laden",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"pmaTitle": "For textures exported with Premultiplied Alpha (PMA)",
|
||||
"background": "Background",
|
||||
"backgroundTitle": "50×50 checkerboard that pans and zooms with the canvas and participates in blend modes",
|
||||
"thumbnail": "🖼 Set thumbnail",
|
||||
"thumbnail": "Set thumbnail",
|
||||
"thumbnailTitle": "Generate a 400×400 thumbnail from the current frame",
|
||||
"versionTitle": "Spine version",
|
||||
"reloadTitle": "Reload folder",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"pmaTitle": "Para texturas exportadas con Premultiplied Alpha (PMA)",
|
||||
"background": "Fondo",
|
||||
"backgroundTitle": "Fondo de cuadros de 50×50 que se desplaza y escala con el lienzo y participa en los modos de fusión",
|
||||
"thumbnail": "🖼 Crear miniatura",
|
||||
"thumbnail": "Crear miniatura",
|
||||
"thumbnailTitle": "Crear una miniatura de 400×400 con el fotograma actual",
|
||||
"versionTitle": "Versión de Spine",
|
||||
"reloadTitle": "Recargar carpeta",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"pmaTitle": "Premultiplied Alpha(PMA)で書き出したテクスチャ用",
|
||||
"background": "背景",
|
||||
"backgroundTitle": "50×50 のチェッカー背景。キャンバスの移動・拡大に追従し、ブレンドモードにも参加します",
|
||||
"thumbnail": "🖼 サムネイル生成",
|
||||
"thumbnail": "サムネイル生成",
|
||||
"thumbnailTitle": "現在のフレームから 400×400 のサムネイルを生成",
|
||||
"versionTitle": "Spine バージョン",
|
||||
"reloadTitle": "フォルダーを再読み込み",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"pmaTitle": "Premultiplied Alpha(PMA)로 내보낸 텍스처용",
|
||||
"background": "배경",
|
||||
"backgroundTitle": "50×50 체커 배경. 캔버스 이동·확대를 따라가며 블렌드 모드에도 참여합니다",
|
||||
"thumbnail": "🖼 썸네일 생성",
|
||||
"thumbnail": "썸네일 생성",
|
||||
"thumbnailTitle": "현재 프레임으로 400×400 썸네일 생성",
|
||||
"versionTitle": "Spine 버전",
|
||||
"reloadTitle": "폴더 다시 불러오기",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"pmaTitle": "Для текстур, экспортированных с Premultiplied Alpha (PMA)",
|
||||
"background": "Фон",
|
||||
"backgroundTitle": "Шахматный фон 50×50: следует за сдвигом и масштабом холста и участвует в режимах смешивания",
|
||||
"thumbnail": "🖼 Создать миниатюру",
|
||||
"thumbnail": "Создать миниатюру",
|
||||
"thumbnailTitle": "Создать миниатюру 400×400 из текущего кадра",
|
||||
"versionTitle": "Версия Spine",
|
||||
"reloadTitle": "Перезагрузить папку",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"pmaTitle": "用于以 Premultiplied Alpha(PMA)方式导出的贴图",
|
||||
"background": "背景",
|
||||
"backgroundTitle": "50×50 方格背景,随画布移动和缩放,并参与混合模式",
|
||||
"thumbnail": "🖼 生成缩略图",
|
||||
"thumbnail": "生成缩略图",
|
||||
"thumbnailTitle": "使用当前帧生成 400×400 缩略图",
|
||||
"versionTitle": "Spine 版本",
|
||||
"reloadTitle": "重新加载目录",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"pmaTitle": "用於以 Premultiplied Alpha(PMA)方式匯出的貼圖",
|
||||
"background": "背景",
|
||||
"backgroundTitle": "50×50 方格背景,隨畫布移動和縮放,並參與混合模式",
|
||||
"thumbnail": "🖼 產生縮圖",
|
||||
"thumbnail": "產生縮圖",
|
||||
"thumbnailTitle": "使用目前影格產生 400×400 縮圖",
|
||||
"versionTitle": "Spine 版本",
|
||||
"reloadTitle": "重新載入目錄",
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
:root {
|
||||
--bg: #1e1e1e;
|
||||
--panel: #252526;
|
||||
--panel-2: #2d2d30;
|
||||
--text: #e6e6e6;
|
||||
--text-dim: #9d9d9d;
|
||||
--accent: #4fc1ff;
|
||||
--border: #3c3c3c;
|
||||
--error: #f14c4c;
|
||||
/* 悬浮层填充与滚动条配色。Eagle 内置 Electron 22 / Chromium 108 不支持 color-mix(),
|
||||
这些必须写成 rgba(),否则整条颜色声明会被丢弃。 */
|
||||
--hover-fill: rgba(255, 255, 255, .07);
|
||||
--scroll-thumb: rgba(255, 255, 255, .34);
|
||||
--scroll-thumb-hover: rgba(255, 255, 255, .55);
|
||||
--scroll-thumb-active: rgba(255, 255, 255, .72);
|
||||
--scroll-track: rgba(255, 255, 255, .07);
|
||||
--panel-glass: rgba(37, 37, 38, .92); /* #toolbar / #animationPanel / #timelineBar 底色 */
|
||||
--active-item-fill: rgb(45, 68, 81); /* .animation-item.active 底色 */
|
||||
--list-thumb: rgba(157, 157, 157, .58); /* 动画列表滚动条,与预览区各自独立 */
|
||||
--list-thumb-hover: rgba(157, 157, 157, .82);
|
||||
}
|
||||
[data-theme="light"] {
|
||||
--bg: #f3f4f6;
|
||||
--panel: #ffffff;
|
||||
--panel-2: #f0f0f0;
|
||||
--text: #1f1f1f;
|
||||
--text-dim: #6b6b6b;
|
||||
--accent: #0078d4;
|
||||
--border: #ddd;
|
||||
--error: #d13438;
|
||||
--hover-fill: rgba(0, 0, 0, .06);
|
||||
--scroll-thumb: rgba(0, 0, 0, .30);
|
||||
--scroll-thumb-hover: rgba(0, 0, 0, .46);
|
||||
--scroll-thumb-active: rgba(0, 0, 0, .62);
|
||||
--scroll-track: rgba(0, 0, 0, .06);
|
||||
--panel-glass: rgba(255, 255, 255, .92);
|
||||
--active-item-fill: rgb(204, 228, 246);
|
||||
--list-thumb: rgba(107, 107, 107, .58);
|
||||
--list-thumb-hover: rgba(107, 107, 107, .82);
|
||||
}
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
html, body {
|
||||
margin: 0; padding: 0; height: 100%; overflow: hidden;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||
background: var(--bg); color: var(--text);
|
||||
font-size: 13px;
|
||||
}
|
||||
#app {
|
||||
position: relative; height: 100%;
|
||||
--toolbar-height: 46px; --timeline-height: 68px;
|
||||
}
|
||||
#toolbar {
|
||||
position: absolute; top: 12px; left: 12px; right: 12px;
|
||||
display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
|
||||
padding: 8px 12px;
|
||||
background: var(--panel-glass);
|
||||
border: 1px solid var(--border); border-radius: 8px;
|
||||
box-shadow: 0 5px 18px rgba(0, 0, 0, .25);
|
||||
backdrop-filter: blur(8px); z-index: 10;
|
||||
}
|
||||
#toolbar .spacer { flex: 1; }
|
||||
#toolbar label { color: var(--text-dim); display: flex; align-items: center; gap: 4px; }
|
||||
select, input[type="range"] {
|
||||
background: var(--panel-2); color: var(--text);
|
||||
border: 1px solid var(--border); border-radius: 4px; padding: 4px 6px;
|
||||
}
|
||||
button {
|
||||
background: var(--panel-2); color: var(--text);
|
||||
border: 1px solid var(--border); border-radius: 4px; padding: 5px 10px;
|
||||
cursor: pointer; white-space: nowrap;
|
||||
}
|
||||
button:hover { background: var(--border); }
|
||||
button.active { background: var(--accent); color: #fff; border-color: var(--accent); }
|
||||
#stage { position: absolute; inset: 0; display: flex; overflow: hidden; }
|
||||
#animationPanel {
|
||||
position: absolute;
|
||||
top: calc(22px + var(--toolbar-height)); left: 12px; z-index: 9;
|
||||
width: max-content; min-width: 128px; max-width: min(260px, 34vw);
|
||||
max-height: calc(100% - var(--toolbar-height) - var(--timeline-height) - 56px);
|
||||
display: none; flex-direction: column; overflow: hidden;
|
||||
background: var(--panel-glass);
|
||||
border: 1px solid var(--border); border-radius: 7px;
|
||||
box-shadow: 0 5px 18px rgba(0, 0, 0, .28);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
#animationPanelTitle {
|
||||
padding: 8px 10px; color: var(--text-dim); font-size: 12px;
|
||||
border-bottom: 1px solid var(--border); flex: none;
|
||||
}
|
||||
#animationList {
|
||||
width: max-content; min-width: 100%; max-width: 100%; min-height: 0;
|
||||
overflow: auto; padding: 5px;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--list-thumb) transparent;
|
||||
}
|
||||
#animationList::-webkit-scrollbar { width: 8px; height: 8px; }
|
||||
#animationList::-webkit-scrollbar-track { background: transparent; }
|
||||
#animationList::-webkit-scrollbar-thumb {
|
||||
min-height: 28px; border: 2px solid transparent; border-radius: 999px;
|
||||
background: var(--list-thumb);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
#animationList::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--list-thumb-hover);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
#animationList::-webkit-scrollbar-corner { background: transparent; }
|
||||
.animation-item {
|
||||
display: block; width: 100%; min-width: 116px; max-width: 248px;
|
||||
padding: 6px 8px; margin: 0 0 2px;
|
||||
border: 0; border-radius: 4px; background: transparent;
|
||||
color: var(--text); text-align: left; overflow: hidden;
|
||||
text-overflow: ellipsis; cursor: pointer;
|
||||
}
|
||||
.animation-item:hover { background: var(--panel-2); }
|
||||
.animation-item.active {
|
||||
background: var(--active-item-fill);
|
||||
color: var(--accent); box-shadow: inset 3px 0 0 var(--accent);
|
||||
}
|
||||
#viewport { position: relative; width: 100%; min-width: 0; flex: 1; overflow: hidden; }
|
||||
canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
|
||||
#status {
|
||||
position: absolute; bottom: calc(22px + var(--timeline-height)); left: 12px; color: var(--text-dim);
|
||||
font-size: 12px; pointer-events: none; text-shadow: 0 1px 2px rgba(0,0,0,.3);
|
||||
}
|
||||
#timelineBar {
|
||||
position: absolute; left: 12px; right: 12px; bottom: 12px; z-index: 10;
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 6px 12px;
|
||||
background: var(--panel-glass);
|
||||
border: 1px solid var(--border); border-radius: 8px;
|
||||
box-shadow: 0 -4px 18px rgba(0, 0, 0, .22);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
#timelineLabels {
|
||||
display: flex; flex-direction: column; justify-content: space-between;
|
||||
align-self: stretch; min-width: 136px; padding: 8px 0 1px;
|
||||
}
|
||||
#timelineBar .tl-label { color: var(--text-dim); font-size: 12px; white-space: nowrap; }
|
||||
#keyframeLegend {
|
||||
display: flex; align-items: center; gap: 6px; color: var(--text-dim);
|
||||
font-size: 11px; white-space: nowrap; cursor: help;
|
||||
}
|
||||
#keyframeLegend::before {
|
||||
content: ''; width: 10px; height: 7px; border-radius: 1px;
|
||||
background: var(--accent); opacity: .7;
|
||||
}
|
||||
#timelineBar .tl-wrap { position: relative; flex: 1; padding-top: 20px; }
|
||||
#timelineBar input[type="range"] {
|
||||
display: block; width: 100%; margin: 0; accent-color: var(--accent);
|
||||
outline: none; box-shadow: none; -webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
#timelineBar input[type="range"]:focus,
|
||||
#timelineBar input[type="range"]:focus-visible {
|
||||
outline: none; box-shadow: none; border-color: var(--border);
|
||||
}
|
||||
#timeRuler { position: absolute; top: 0; left: 7px; right: 7px; height: 19px; }
|
||||
#timeRuler .tick {
|
||||
position: absolute; bottom: 0; width: 1px; height: 5px;
|
||||
background: var(--border); transform: translateX(-.5px);
|
||||
}
|
||||
#timeRuler .tick.major { height: 9px; background: var(--text-dim); }
|
||||
#timeRuler .tick-label {
|
||||
position: absolute; bottom: 8px; color: var(--text-dim); font-size: 10px;
|
||||
font-variant-numeric: tabular-nums; transform: translateX(-50%); white-space: nowrap;
|
||||
}
|
||||
#timeRuler .tick-label.first { transform: none; }
|
||||
#timeRuler .tick-label.last { transform: translateX(-100%); }
|
||||
#keyframes { position: relative; height: 8px; margin-top: 2px; }
|
||||
#keyframes span {
|
||||
position: absolute; top: 0; width: 3px; height: 8px;
|
||||
background: var(--accent); opacity: .62; transform: translateX(-1.5px);
|
||||
cursor: help; transition: opacity .12s, transform .12s;
|
||||
}
|
||||
#keyframes span:hover { opacity: 1; transform: translateX(-1.5px) scaleY(1.35); }
|
||||
#timeLabel {
|
||||
color: var(--text-dim); font-size: 12px; min-width: 178px;
|
||||
text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap;
|
||||
}
|
||||
#overlay {
|
||||
position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
|
||||
background: var(--bg); z-index: 5; text-align: center;
|
||||
}
|
||||
#overlay .box { max-width: 520px; padding: 20px; }
|
||||
#overlay .title { font-size: 16px; margin-bottom: 8px; color: var(--error); }
|
||||
#overlay .msg { color: var(--text-dim); white-space: pre-wrap; word-break: break-all; }
|
||||
#overlay.text-preview { align-items: stretch; justify-content: stretch; text-align: left; }
|
||||
#overlay.text-preview .box { width: 100%; max-width: none; padding: 24px; overflow: auto; }
|
||||
#overlay.text-preview .title { color: var(--text); font-size: 13px; font-weight: 600; }
|
||||
#overlay.text-preview .msg {
|
||||
font: 12px/1.6 "SFMono-Regular", Consolas, "Liberation Mono", monospace;
|
||||
color: var(--text-dim); white-space: pre; word-break: normal;
|
||||
}
|
||||
/* 纯文本 / 普通 JSON 预览允许拖动选择与 Ctrl+C 复制;画布与查看器控件仍保持不可选中 */
|
||||
#overlay.text-preview .title,
|
||||
#overlay.text-preview .msg {
|
||||
-webkit-user-select: text; user-select: text;
|
||||
}
|
||||
#overlay.text-preview .msg { cursor: text; }
|
||||
/* 预览区滚动条:不使用原生样式。宽度 14px、滑块可见 8px,保证看得清。
|
||||
颜色走 --scroll-* 变量(rgba 写死),不能用 color-mix():Eagle 内置 Chromium 108
|
||||
不支持该函数,声明会整条失效,滑块就变成看不见。也不要写 scrollbar-width /
|
||||
scrollbar-color,那两个标准属性在 108 上无效且易与 ::-webkit-scrollbar 混淆。 */
|
||||
#overlay.text-preview .box::-webkit-scrollbar { width: 14px; height: 14px; }
|
||||
#overlay.text-preview .box::-webkit-scrollbar-track { background: var(--scroll-track); }
|
||||
#overlay.text-preview .box::-webkit-scrollbar-thumb {
|
||||
min-height: 36px;
|
||||
border: 3px solid transparent; border-radius: 999px;
|
||||
background: var(--scroll-thumb);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
#overlay.text-preview .box::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--scroll-thumb-hover);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
#overlay.text-preview .box::-webkit-scrollbar-thumb:active {
|
||||
background: var(--scroll-thumb-active);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
#overlay.text-preview .box::-webkit-scrollbar-corner { background: transparent; }
|
||||
/* JSON 字段树:折叠标记与计数只用符号 / 数字,无需新增界面文案 */
|
||||
#overlay.json-preview .msg { white-space: normal; }
|
||||
.jt-node > summary {
|
||||
list-style: none; cursor: pointer; border-radius: 4px;
|
||||
}
|
||||
.jt-node > summary::-webkit-details-marker { display: none; }
|
||||
.jt-node > summary:hover { background: var(--hover-fill); }
|
||||
.jt-node > summary::before {
|
||||
content: '▾'; display: inline-block; width: 14px; color: var(--text-dim);
|
||||
}
|
||||
.jt-node:not([open]) > summary::before { content: '▸'; }
|
||||
.jt-children { margin-left: 7px; padding-left: 11px; border-left: 1px solid var(--border); }
|
||||
.jt-leaf { padding-left: 14px; }
|
||||
.jt-key { color: var(--accent); }
|
||||
.jt-str { color: var(--text); }
|
||||
.jt-brace, .jt-count { color: var(--text-dim); }
|
||||
.jt-count { margin: 0 3px; font-size: 11px; }
|
||||
#overlay.hidden { display: none; }
|
||||
#loading { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; z-index: 4; background: var(--bg); }
|
||||
#loading.hidden { display: none; }
|
||||
.spinner {
|
||||
width: 34px; height: 34px; border: 3px solid var(--border);
|
||||
border-top-color: var(--accent); border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
#hideOthers { display: none; }
|
||||
@@ -4,259 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title data-i18n="viewer.title">Spine 预览</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #1e1e1e;
|
||||
--panel: #252526;
|
||||
--panel-2: #2d2d30;
|
||||
--text: #e6e6e6;
|
||||
--text-dim: #9d9d9d;
|
||||
--accent: #4fc1ff;
|
||||
--border: #3c3c3c;
|
||||
--error: #f14c4c;
|
||||
/* 悬浮层填充与滚动条配色。Eagle 内置 Electron 22 / Chromium 108 不支持 color-mix(),
|
||||
这些必须写成 rgba(),否则整条颜色声明会被丢弃。 */
|
||||
--hover-fill: rgba(255, 255, 255, .07);
|
||||
--scroll-thumb: rgba(255, 255, 255, .34);
|
||||
--scroll-thumb-hover: rgba(255, 255, 255, .55);
|
||||
--scroll-thumb-active: rgba(255, 255, 255, .72);
|
||||
--scroll-track: rgba(255, 255, 255, .07);
|
||||
--panel-glass: rgba(37, 37, 38, .92); /* #toolbar / #animationPanel / #timelineBar 底色 */
|
||||
--active-item-fill: rgb(45, 68, 81); /* .animation-item.active 底色 */
|
||||
--list-thumb: rgba(157, 157, 157, .58); /* 动画列表滚动条,与预览区各自独立 */
|
||||
--list-thumb-hover: rgba(157, 157, 157, .82);
|
||||
}
|
||||
[data-theme="light"] {
|
||||
--bg: #f3f4f6;
|
||||
--panel: #ffffff;
|
||||
--panel-2: #f0f0f0;
|
||||
--text: #1f1f1f;
|
||||
--text-dim: #6b6b6b;
|
||||
--accent: #0078d4;
|
||||
--border: #ddd;
|
||||
--error: #d13438;
|
||||
--hover-fill: rgba(0, 0, 0, .06);
|
||||
--scroll-thumb: rgba(0, 0, 0, .30);
|
||||
--scroll-thumb-hover: rgba(0, 0, 0, .46);
|
||||
--scroll-thumb-active: rgba(0, 0, 0, .62);
|
||||
--scroll-track: rgba(0, 0, 0, .06);
|
||||
--panel-glass: rgba(255, 255, 255, .92);
|
||||
--active-item-fill: rgb(204, 228, 246);
|
||||
--list-thumb: rgba(107, 107, 107, .58);
|
||||
--list-thumb-hover: rgba(107, 107, 107, .82);
|
||||
}
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
html, body {
|
||||
margin: 0; padding: 0; height: 100%; overflow: hidden;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||
background: var(--bg); color: var(--text);
|
||||
font-size: 13px;
|
||||
}
|
||||
#app {
|
||||
position: relative; height: 100%;
|
||||
--toolbar-height: 46px; --timeline-height: 68px;
|
||||
}
|
||||
#toolbar {
|
||||
position: absolute; top: 12px; left: 12px; right: 12px;
|
||||
display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
|
||||
padding: 8px 12px;
|
||||
background: var(--panel-glass);
|
||||
border: 1px solid var(--border); border-radius: 8px;
|
||||
box-shadow: 0 5px 18px rgba(0, 0, 0, .25);
|
||||
backdrop-filter: blur(8px); z-index: 10;
|
||||
}
|
||||
#toolbar .spacer { flex: 1; }
|
||||
#toolbar label { color: var(--text-dim); display: flex; align-items: center; gap: 4px; }
|
||||
select, input[type="range"] {
|
||||
background: var(--panel-2); color: var(--text);
|
||||
border: 1px solid var(--border); border-radius: 4px; padding: 4px 6px;
|
||||
}
|
||||
button {
|
||||
background: var(--panel-2); color: var(--text);
|
||||
border: 1px solid var(--border); border-radius: 4px; padding: 5px 10px;
|
||||
cursor: pointer; white-space: nowrap;
|
||||
}
|
||||
button:hover { background: var(--border); }
|
||||
button.active { background: var(--accent); color: #fff; border-color: var(--accent); }
|
||||
#stage { position: absolute; inset: 0; display: flex; overflow: hidden; }
|
||||
#animationPanel {
|
||||
position: absolute;
|
||||
top: calc(22px + var(--toolbar-height)); left: 12px; z-index: 9;
|
||||
width: max-content; min-width: 128px; max-width: min(260px, 34vw);
|
||||
max-height: calc(100% - var(--toolbar-height) - var(--timeline-height) - 56px);
|
||||
display: none; flex-direction: column; overflow: hidden;
|
||||
background: var(--panel-glass);
|
||||
border: 1px solid var(--border); border-radius: 7px;
|
||||
box-shadow: 0 5px 18px rgba(0, 0, 0, .28);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
#animationPanelTitle {
|
||||
padding: 8px 10px; color: var(--text-dim); font-size: 12px;
|
||||
border-bottom: 1px solid var(--border); flex: none;
|
||||
}
|
||||
#animationList {
|
||||
width: max-content; min-width: 100%; max-width: 100%; min-height: 0;
|
||||
overflow: auto; padding: 5px;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--list-thumb) transparent;
|
||||
}
|
||||
#animationList::-webkit-scrollbar { width: 8px; height: 8px; }
|
||||
#animationList::-webkit-scrollbar-track { background: transparent; }
|
||||
#animationList::-webkit-scrollbar-thumb {
|
||||
min-height: 28px; border: 2px solid transparent; border-radius: 999px;
|
||||
background: var(--list-thumb);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
#animationList::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--list-thumb-hover);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
#animationList::-webkit-scrollbar-corner { background: transparent; }
|
||||
.animation-item {
|
||||
display: block; width: 100%; min-width: 116px; max-width: 248px;
|
||||
padding: 6px 8px; margin: 0 0 2px;
|
||||
border: 0; border-radius: 4px; background: transparent;
|
||||
color: var(--text); text-align: left; overflow: hidden;
|
||||
text-overflow: ellipsis; cursor: pointer;
|
||||
}
|
||||
.animation-item:hover { background: var(--panel-2); }
|
||||
.animation-item.active {
|
||||
background: var(--active-item-fill);
|
||||
color: var(--accent); box-shadow: inset 3px 0 0 var(--accent);
|
||||
}
|
||||
#viewport { position: relative; width: 100%; min-width: 0; flex: 1; overflow: hidden; }
|
||||
canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
|
||||
#status {
|
||||
position: absolute; bottom: calc(22px + var(--timeline-height)); left: 12px; color: var(--text-dim);
|
||||
font-size: 12px; pointer-events: none; text-shadow: 0 1px 2px rgba(0,0,0,.3);
|
||||
}
|
||||
#timelineBar {
|
||||
position: absolute; left: 12px; right: 12px; bottom: 12px; z-index: 10;
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 6px 12px;
|
||||
background: var(--panel-glass);
|
||||
border: 1px solid var(--border); border-radius: 8px;
|
||||
box-shadow: 0 -4px 18px rgba(0, 0, 0, .22);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
#timelineLabels {
|
||||
display: flex; flex-direction: column; justify-content: space-between;
|
||||
align-self: stretch; min-width: 136px; padding: 8px 0 1px;
|
||||
}
|
||||
#timelineBar .tl-label { color: var(--text-dim); font-size: 12px; white-space: nowrap; }
|
||||
#keyframeLegend {
|
||||
display: flex; align-items: center; gap: 6px; color: var(--text-dim);
|
||||
font-size: 11px; white-space: nowrap; cursor: help;
|
||||
}
|
||||
#keyframeLegend::before {
|
||||
content: ''; width: 10px; height: 7px; border-radius: 1px;
|
||||
background: var(--accent); opacity: .7;
|
||||
}
|
||||
#timelineBar .tl-wrap { position: relative; flex: 1; padding-top: 20px; }
|
||||
#timelineBar input[type="range"] {
|
||||
display: block; width: 100%; margin: 0; accent-color: var(--accent);
|
||||
outline: none; box-shadow: none; -webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
#timelineBar input[type="range"]:focus,
|
||||
#timelineBar input[type="range"]:focus-visible {
|
||||
outline: none; box-shadow: none; border-color: var(--border);
|
||||
}
|
||||
#timeRuler { position: absolute; top: 0; left: 7px; right: 7px; height: 19px; }
|
||||
#timeRuler .tick {
|
||||
position: absolute; bottom: 0; width: 1px; height: 5px;
|
||||
background: var(--border); transform: translateX(-.5px);
|
||||
}
|
||||
#timeRuler .tick.major { height: 9px; background: var(--text-dim); }
|
||||
#timeRuler .tick-label {
|
||||
position: absolute; bottom: 8px; color: var(--text-dim); font-size: 10px;
|
||||
font-variant-numeric: tabular-nums; transform: translateX(-50%); white-space: nowrap;
|
||||
}
|
||||
#timeRuler .tick-label.first { transform: none; }
|
||||
#timeRuler .tick-label.last { transform: translateX(-100%); }
|
||||
#keyframes { position: relative; height: 8px; margin-top: 2px; }
|
||||
#keyframes span {
|
||||
position: absolute; top: 0; width: 3px; height: 8px;
|
||||
background: var(--accent); opacity: .62; transform: translateX(-1.5px);
|
||||
cursor: help; transition: opacity .12s, transform .12s;
|
||||
}
|
||||
#keyframes span:hover { opacity: 1; transform: translateX(-1.5px) scaleY(1.35); }
|
||||
#timeLabel {
|
||||
color: var(--text-dim); font-size: 12px; min-width: 178px;
|
||||
text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap;
|
||||
}
|
||||
#overlay {
|
||||
position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
|
||||
background: var(--bg); z-index: 5; text-align: center;
|
||||
}
|
||||
#overlay .box { max-width: 520px; padding: 20px; }
|
||||
#overlay .title { font-size: 16px; margin-bottom: 8px; color: var(--error); }
|
||||
#overlay .msg { color: var(--text-dim); white-space: pre-wrap; word-break: break-all; }
|
||||
#overlay.text-preview { align-items: stretch; justify-content: stretch; text-align: left; }
|
||||
#overlay.text-preview .box { width: 100%; max-width: none; padding: 24px; overflow: auto; }
|
||||
#overlay.text-preview .title { color: var(--text); font-size: 13px; font-weight: 600; }
|
||||
#overlay.text-preview .msg {
|
||||
font: 12px/1.6 "SFMono-Regular", Consolas, "Liberation Mono", monospace;
|
||||
color: var(--text-dim); white-space: pre; word-break: normal;
|
||||
}
|
||||
/* 纯文本 / 普通 JSON 预览允许拖动选择与 Ctrl+C 复制;画布与查看器控件仍保持不可选中 */
|
||||
#overlay.text-preview .title,
|
||||
#overlay.text-preview .msg {
|
||||
-webkit-user-select: text; user-select: text;
|
||||
}
|
||||
#overlay.text-preview .msg { cursor: text; }
|
||||
/* 预览区滚动条:不使用原生样式。宽度 14px、滑块可见 8px,保证看得清。
|
||||
颜色走 --scroll-* 变量(rgba 写死),不能用 color-mix():Eagle 内置 Chromium 108
|
||||
不支持该函数,声明会整条失效,滑块就变成看不见。也不要写 scrollbar-width /
|
||||
scrollbar-color,那两个标准属性在 108 上无效且易与 ::-webkit-scrollbar 混淆。 */
|
||||
#overlay.text-preview .box::-webkit-scrollbar { width: 14px; height: 14px; }
|
||||
#overlay.text-preview .box::-webkit-scrollbar-track { background: var(--scroll-track); }
|
||||
#overlay.text-preview .box::-webkit-scrollbar-thumb {
|
||||
min-height: 36px;
|
||||
border: 3px solid transparent; border-radius: 999px;
|
||||
background: var(--scroll-thumb);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
#overlay.text-preview .box::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--scroll-thumb-hover);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
#overlay.text-preview .box::-webkit-scrollbar-thumb:active {
|
||||
background: var(--scroll-thumb-active);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
#overlay.text-preview .box::-webkit-scrollbar-corner { background: transparent; }
|
||||
/* JSON 字段树:折叠标记与计数只用符号 / 数字,无需新增界面文案 */
|
||||
#overlay.json-preview .msg { white-space: normal; }
|
||||
.jt-node > summary {
|
||||
list-style: none; cursor: pointer; border-radius: 4px;
|
||||
}
|
||||
.jt-node > summary::-webkit-details-marker { display: none; }
|
||||
.jt-node > summary:hover { background: var(--hover-fill); }
|
||||
.jt-node > summary::before {
|
||||
content: '▾'; display: inline-block; width: 14px; color: var(--text-dim);
|
||||
}
|
||||
.jt-node:not([open]) > summary::before { content: '▸'; }
|
||||
.jt-children { margin-left: 7px; padding-left: 11px; border-left: 1px solid var(--border); }
|
||||
.jt-leaf { padding-left: 14px; }
|
||||
.jt-key { color: var(--accent); }
|
||||
.jt-str { color: var(--text); }
|
||||
.jt-brace, .jt-count { color: var(--text-dim); }
|
||||
.jt-count { margin: 0 3px; font-size: 11px; }
|
||||
#overlay.hidden { display: none; }
|
||||
#loading { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; z-index: 4; background: var(--bg); }
|
||||
#loading.hidden { display: none; }
|
||||
.spinner {
|
||||
width: 34px; height: 34px; border: 3px solid var(--border);
|
||||
border-top-color: var(--accent); border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
#hideOthers { display: none; }
|
||||
</style>
|
||||
<link rel="stylesheet" href="spine.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
@@ -277,7 +25,7 @@
|
||||
<input type="checkbox" id="backgroundCheck"> <span data-i18n="viewer.background">背景</span>
|
||||
</label>
|
||||
<div class="spacer"></div>
|
||||
<button id="thumbBtn" data-i18n="viewer.thumbnail" data-i18n-title="viewer.thumbnailTitle" title="使用当前帧生成 400×400 缩略图">🖼 生成缩略图</button>
|
||||
<button id="thumbBtn" data-i18n="viewer.thumbnail" data-i18n-title="viewer.thumbnailTitle" title="使用当前帧生成 400×400 缩略图">生成缩略图</button>
|
||||
<label id="verLabel" style="color: var(--text-dim);" data-i18n-title="viewer.versionTitle" title="Spine 版本"></label>
|
||||
<button id="reloadBtn" title="重新加载目录" data-i18n-title="viewer.reloadTitle">↻</button>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
:root {
|
||||
--bg: #1e1e1e;
|
||||
--panel: #252526;
|
||||
--panel-2: #2d2d30;
|
||||
--text: #e6e6e6;
|
||||
--text-dim: #9d9d9d;
|
||||
--accent: #4fc1ff;
|
||||
--border: #3c3c3c;
|
||||
--error: #f14c4c;
|
||||
/* 悬浮层填充与滚动条配色。Eagle 内置 Electron 22 / Chromium 108 不支持 color-mix(),
|
||||
这些必须写成 rgba(),否则整条颜色声明会被丢弃。 */
|
||||
--hover-fill: rgba(255, 255, 255, .07);
|
||||
--scroll-thumb: rgba(255, 255, 255, .34);
|
||||
--scroll-thumb-hover: rgba(255, 255, 255, .55);
|
||||
--scroll-thumb-active: rgba(255, 255, 255, .72);
|
||||
--scroll-track: rgba(255, 255, 255, .07);
|
||||
--panel-glass: rgba(37, 37, 38, .92); /* #toolbar / #animationPanel / #timelineBar 底色 */
|
||||
--active-item-fill: rgb(45, 68, 81); /* .animation-item.active 底色 */
|
||||
--list-thumb: rgba(157, 157, 157, .58); /* 动画列表滚动条,与预览区各自独立 */
|
||||
--list-thumb-hover: rgba(157, 157, 157, .82);
|
||||
}
|
||||
[data-theme="light"] {
|
||||
--bg: #f3f4f6;
|
||||
--panel: #ffffff;
|
||||
--panel-2: #f0f0f0;
|
||||
--text: #1f1f1f;
|
||||
--text-dim: #6b6b6b;
|
||||
--accent: #0078d4;
|
||||
--border: #ddd;
|
||||
--error: #d13438;
|
||||
--hover-fill: rgba(0, 0, 0, .06);
|
||||
--scroll-thumb: rgba(0, 0, 0, .30);
|
||||
--scroll-thumb-hover: rgba(0, 0, 0, .46);
|
||||
--scroll-thumb-active: rgba(0, 0, 0, .62);
|
||||
--scroll-track: rgba(0, 0, 0, .06);
|
||||
--panel-glass: rgba(255, 255, 255, .92);
|
||||
--active-item-fill: rgb(204, 228, 246);
|
||||
--list-thumb: rgba(107, 107, 107, .58);
|
||||
--list-thumb-hover: rgba(107, 107, 107, .82);
|
||||
}
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
html, body {
|
||||
margin: 0; padding: 0; height: 100%; overflow: hidden;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||
background: var(--bg); color: var(--text);
|
||||
font-size: 13px;
|
||||
}
|
||||
#app {
|
||||
position: relative; height: 100%;
|
||||
--toolbar-height: 46px; --timeline-height: 68px;
|
||||
}
|
||||
#toolbar {
|
||||
position: absolute; top: 12px; left: 12px; right: 12px;
|
||||
display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
|
||||
padding: 8px 12px;
|
||||
background: var(--panel-glass);
|
||||
border: 1px solid var(--border); border-radius: 8px;
|
||||
box-shadow: 0 5px 18px rgba(0, 0, 0, .25);
|
||||
backdrop-filter: blur(8px); z-index: 10;
|
||||
}
|
||||
#toolbar .spacer { flex: 1; }
|
||||
#toolbar label { color: var(--text-dim); display: flex; align-items: center; gap: 4px; }
|
||||
select, input[type="range"] {
|
||||
background: var(--panel-2); color: var(--text);
|
||||
border: 1px solid var(--border); border-radius: 4px; padding: 4px 6px;
|
||||
}
|
||||
button {
|
||||
background: var(--panel-2); color: var(--text);
|
||||
border: 1px solid var(--border); border-radius: 4px; padding: 5px 10px;
|
||||
cursor: pointer; white-space: nowrap;
|
||||
}
|
||||
button:hover { background: var(--border); }
|
||||
button.active { background: var(--accent); color: #fff; border-color: var(--accent); }
|
||||
#stage { position: absolute; inset: 0; display: flex; overflow: hidden; }
|
||||
#animationPanel {
|
||||
position: absolute;
|
||||
top: calc(22px + var(--toolbar-height)); left: 12px; z-index: 9;
|
||||
width: max-content; min-width: 128px; max-width: min(260px, 34vw);
|
||||
max-height: calc(100% - var(--toolbar-height) - var(--timeline-height) - 56px);
|
||||
display: none; flex-direction: column; overflow: hidden;
|
||||
background: var(--panel-glass);
|
||||
border: 1px solid var(--border); border-radius: 7px;
|
||||
box-shadow: 0 5px 18px rgba(0, 0, 0, .28);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
#animationPanelTitle {
|
||||
padding: 8px 10px; color: var(--text-dim); font-size: 12px;
|
||||
border-bottom: 1px solid var(--border); flex: none;
|
||||
}
|
||||
#animationList {
|
||||
width: max-content; min-width: 100%; max-width: 100%; min-height: 0;
|
||||
overflow: auto; padding: 5px;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--list-thumb) transparent;
|
||||
}
|
||||
#animationList::-webkit-scrollbar { width: 8px; height: 8px; }
|
||||
#animationList::-webkit-scrollbar-track { background: transparent; }
|
||||
#animationList::-webkit-scrollbar-thumb {
|
||||
min-height: 28px; border: 2px solid transparent; border-radius: 999px;
|
||||
background: var(--list-thumb);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
#animationList::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--list-thumb-hover);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
#animationList::-webkit-scrollbar-corner { background: transparent; }
|
||||
.animation-item {
|
||||
display: block; width: 100%; min-width: 116px; max-width: 248px;
|
||||
padding: 6px 8px; margin: 0 0 2px;
|
||||
border: 0; border-radius: 4px; background: transparent;
|
||||
color: var(--text); text-align: left; overflow: hidden;
|
||||
text-overflow: ellipsis; cursor: pointer;
|
||||
}
|
||||
.animation-item:hover { background: var(--panel-2); }
|
||||
.animation-item.active {
|
||||
background: var(--active-item-fill);
|
||||
color: var(--accent); box-shadow: inset 3px 0 0 var(--accent);
|
||||
}
|
||||
#viewport { position: relative; width: 100%; min-width: 0; flex: 1; overflow: hidden; }
|
||||
canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
|
||||
#status {
|
||||
position: absolute; bottom: calc(22px + var(--timeline-height)); left: 12px; color: var(--text-dim);
|
||||
font-size: 12px; pointer-events: none; text-shadow: 0 1px 2px rgba(0,0,0,.3);
|
||||
}
|
||||
#timelineBar {
|
||||
position: absolute; left: 12px; right: 12px; bottom: 12px; z-index: 10;
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 6px 12px;
|
||||
background: var(--panel-glass);
|
||||
border: 1px solid var(--border); border-radius: 8px;
|
||||
box-shadow: 0 -4px 18px rgba(0, 0, 0, .22);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
#timelineLabels {
|
||||
display: flex; flex-direction: column; justify-content: space-between;
|
||||
align-self: stretch; min-width: 136px; padding: 8px 0 1px;
|
||||
}
|
||||
#timelineBar .tl-label { color: var(--text-dim); font-size: 12px; white-space: nowrap; }
|
||||
#keyframeLegend {
|
||||
display: flex; align-items: center; gap: 6px; color: var(--text-dim);
|
||||
font-size: 11px; white-space: nowrap; cursor: help;
|
||||
}
|
||||
#keyframeLegend::before {
|
||||
content: ''; width: 10px; height: 7px; border-radius: 1px;
|
||||
background: var(--accent); opacity: .7;
|
||||
}
|
||||
#timelineBar .tl-wrap { position: relative; flex: 1; padding-top: 20px; }
|
||||
#timelineBar input[type="range"] {
|
||||
display: block; width: 100%; margin: 0; accent-color: var(--accent);
|
||||
outline: none; box-shadow: none; -webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
#timelineBar input[type="range"]:focus,
|
||||
#timelineBar input[type="range"]:focus-visible {
|
||||
outline: none; box-shadow: none; border-color: var(--border);
|
||||
}
|
||||
#timeRuler { position: absolute; top: 0; left: 7px; right: 7px; height: 19px; }
|
||||
#timeRuler .tick {
|
||||
position: absolute; bottom: 0; width: 1px; height: 5px;
|
||||
background: var(--border); transform: translateX(-.5px);
|
||||
}
|
||||
#timeRuler .tick.major { height: 9px; background: var(--text-dim); }
|
||||
#timeRuler .tick-label {
|
||||
position: absolute; bottom: 8px; color: var(--text-dim); font-size: 10px;
|
||||
font-variant-numeric: tabular-nums; transform: translateX(-50%); white-space: nowrap;
|
||||
}
|
||||
#timeRuler .tick-label.first { transform: none; }
|
||||
#timeRuler .tick-label.last { transform: translateX(-100%); }
|
||||
#keyframes { position: relative; height: 8px; margin-top: 2px; }
|
||||
#keyframes span {
|
||||
position: absolute; top: 0; width: 3px; height: 8px;
|
||||
background: var(--accent); opacity: .62; transform: translateX(-1.5px);
|
||||
cursor: help; transition: opacity .12s, transform .12s;
|
||||
}
|
||||
#keyframes span:hover { opacity: 1; transform: translateX(-1.5px) scaleY(1.35); }
|
||||
#timeLabel {
|
||||
color: var(--text-dim); font-size: 12px; min-width: 178px;
|
||||
text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap;
|
||||
}
|
||||
#overlay {
|
||||
position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
|
||||
background: var(--bg); z-index: 5; text-align: center;
|
||||
}
|
||||
#overlay .box { max-width: 520px; padding: 20px; }
|
||||
#overlay .title { font-size: 16px; margin-bottom: 8px; color: var(--error); }
|
||||
#overlay .msg { color: var(--text-dim); white-space: pre-wrap; word-break: break-all; }
|
||||
#overlay.text-preview { align-items: stretch; justify-content: stretch; text-align: left; }
|
||||
#overlay.text-preview .box { width: 100%; max-width: none; padding: 24px; overflow: auto; }
|
||||
#overlay.text-preview .title { color: var(--text); font-size: 13px; font-weight: 600; }
|
||||
#overlay.text-preview .msg {
|
||||
font: 12px/1.6 "SFMono-Regular", Consolas, "Liberation Mono", monospace;
|
||||
color: var(--text-dim); white-space: pre; word-break: normal;
|
||||
}
|
||||
/* 纯文本 / 普通 JSON 预览允许拖动选择与 Ctrl+C 复制;画布与查看器控件仍保持不可选中 */
|
||||
#overlay.text-preview .title,
|
||||
#overlay.text-preview .msg {
|
||||
-webkit-user-select: text; user-select: text;
|
||||
}
|
||||
#overlay.text-preview .msg { cursor: text; }
|
||||
/* 预览区滚动条:不使用原生样式。宽度 14px、滑块可见 8px,保证看得清。
|
||||
颜色走 --scroll-* 变量(rgba 写死),不能用 color-mix():Eagle 内置 Chromium 108
|
||||
不支持该函数,声明会整条失效,滑块就变成看不见。也不要写 scrollbar-width /
|
||||
scrollbar-color,那两个标准属性在 108 上无效且易与 ::-webkit-scrollbar 混淆。 */
|
||||
#overlay.text-preview .box::-webkit-scrollbar { width: 14px; height: 14px; }
|
||||
#overlay.text-preview .box::-webkit-scrollbar-track { background: var(--scroll-track); }
|
||||
#overlay.text-preview .box::-webkit-scrollbar-thumb {
|
||||
min-height: 36px;
|
||||
border: 3px solid transparent; border-radius: 999px;
|
||||
background: var(--scroll-thumb);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
#overlay.text-preview .box::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--scroll-thumb-hover);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
#overlay.text-preview .box::-webkit-scrollbar-thumb:active {
|
||||
background: var(--scroll-thumb-active);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
#overlay.text-preview .box::-webkit-scrollbar-corner { background: transparent; }
|
||||
/* JSON 字段树:折叠标记与计数只用符号 / 数字,无需新增界面文案 */
|
||||
#overlay.json-preview .msg { white-space: normal; }
|
||||
.jt-node > summary {
|
||||
list-style: none; cursor: pointer; border-radius: 4px;
|
||||
}
|
||||
.jt-node > summary::-webkit-details-marker { display: none; }
|
||||
.jt-node > summary:hover { background: var(--hover-fill); }
|
||||
.jt-node > summary::before {
|
||||
content: '▾'; display: inline-block; width: 14px; color: var(--text-dim);
|
||||
}
|
||||
.jt-node:not([open]) > summary::before { content: '▸'; }
|
||||
.jt-children { margin-left: 7px; padding-left: 11px; border-left: 1px solid var(--border); }
|
||||
.jt-leaf { padding-left: 14px; }
|
||||
.jt-key { color: var(--accent); }
|
||||
.jt-str { color: var(--text); }
|
||||
.jt-brace, .jt-count { color: var(--text-dim); }
|
||||
.jt-count { margin: 0 3px; font-size: 11px; }
|
||||
#overlay.hidden { display: none; }
|
||||
#loading { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; z-index: 4; background: var(--bg); }
|
||||
#loading.hidden { display: none; }
|
||||
.spinner {
|
||||
width: 34px; height: 34px; border: 3px solid var(--border);
|
||||
border-top-color: var(--accent); border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
#hideOthers { display: none; }
|
||||
+2
-254
@@ -4,259 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title data-i18n="viewer.title">Spine 预览</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #1e1e1e;
|
||||
--panel: #252526;
|
||||
--panel-2: #2d2d30;
|
||||
--text: #e6e6e6;
|
||||
--text-dim: #9d9d9d;
|
||||
--accent: #4fc1ff;
|
||||
--border: #3c3c3c;
|
||||
--error: #f14c4c;
|
||||
/* 悬浮层填充与滚动条配色。Eagle 内置 Electron 22 / Chromium 108 不支持 color-mix(),
|
||||
这些必须写成 rgba(),否则整条颜色声明会被丢弃。 */
|
||||
--hover-fill: rgba(255, 255, 255, .07);
|
||||
--scroll-thumb: rgba(255, 255, 255, .34);
|
||||
--scroll-thumb-hover: rgba(255, 255, 255, .55);
|
||||
--scroll-thumb-active: rgba(255, 255, 255, .72);
|
||||
--scroll-track: rgba(255, 255, 255, .07);
|
||||
--panel-glass: rgba(37, 37, 38, .92); /* #toolbar / #animationPanel / #timelineBar 底色 */
|
||||
--active-item-fill: rgb(45, 68, 81); /* .animation-item.active 底色 */
|
||||
--list-thumb: rgba(157, 157, 157, .58); /* 动画列表滚动条,与预览区各自独立 */
|
||||
--list-thumb-hover: rgba(157, 157, 157, .82);
|
||||
}
|
||||
[data-theme="light"] {
|
||||
--bg: #f3f4f6;
|
||||
--panel: #ffffff;
|
||||
--panel-2: #f0f0f0;
|
||||
--text: #1f1f1f;
|
||||
--text-dim: #6b6b6b;
|
||||
--accent: #0078d4;
|
||||
--border: #ddd;
|
||||
--error: #d13438;
|
||||
--hover-fill: rgba(0, 0, 0, .06);
|
||||
--scroll-thumb: rgba(0, 0, 0, .30);
|
||||
--scroll-thumb-hover: rgba(0, 0, 0, .46);
|
||||
--scroll-thumb-active: rgba(0, 0, 0, .62);
|
||||
--scroll-track: rgba(0, 0, 0, .06);
|
||||
--panel-glass: rgba(255, 255, 255, .92);
|
||||
--active-item-fill: rgb(204, 228, 246);
|
||||
--list-thumb: rgba(107, 107, 107, .58);
|
||||
--list-thumb-hover: rgba(107, 107, 107, .82);
|
||||
}
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
html, body {
|
||||
margin: 0; padding: 0; height: 100%; overflow: hidden;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||
background: var(--bg); color: var(--text);
|
||||
font-size: 13px;
|
||||
}
|
||||
#app {
|
||||
position: relative; height: 100%;
|
||||
--toolbar-height: 46px; --timeline-height: 68px;
|
||||
}
|
||||
#toolbar {
|
||||
position: absolute; top: 12px; left: 12px; right: 12px;
|
||||
display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
|
||||
padding: 8px 12px;
|
||||
background: var(--panel-glass);
|
||||
border: 1px solid var(--border); border-radius: 8px;
|
||||
box-shadow: 0 5px 18px rgba(0, 0, 0, .25);
|
||||
backdrop-filter: blur(8px); z-index: 10;
|
||||
}
|
||||
#toolbar .spacer { flex: 1; }
|
||||
#toolbar label { color: var(--text-dim); display: flex; align-items: center; gap: 4px; }
|
||||
select, input[type="range"] {
|
||||
background: var(--panel-2); color: var(--text);
|
||||
border: 1px solid var(--border); border-radius: 4px; padding: 4px 6px;
|
||||
}
|
||||
button {
|
||||
background: var(--panel-2); color: var(--text);
|
||||
border: 1px solid var(--border); border-radius: 4px; padding: 5px 10px;
|
||||
cursor: pointer; white-space: nowrap;
|
||||
}
|
||||
button:hover { background: var(--border); }
|
||||
button.active { background: var(--accent); color: #fff; border-color: var(--accent); }
|
||||
#stage { position: absolute; inset: 0; display: flex; overflow: hidden; }
|
||||
#animationPanel {
|
||||
position: absolute;
|
||||
top: calc(22px + var(--toolbar-height)); left: 12px; z-index: 9;
|
||||
width: max-content; min-width: 128px; max-width: min(260px, 34vw);
|
||||
max-height: calc(100% - var(--toolbar-height) - var(--timeline-height) - 56px);
|
||||
display: none; flex-direction: column; overflow: hidden;
|
||||
background: var(--panel-glass);
|
||||
border: 1px solid var(--border); border-radius: 7px;
|
||||
box-shadow: 0 5px 18px rgba(0, 0, 0, .28);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
#animationPanelTitle {
|
||||
padding: 8px 10px; color: var(--text-dim); font-size: 12px;
|
||||
border-bottom: 1px solid var(--border); flex: none;
|
||||
}
|
||||
#animationList {
|
||||
width: max-content; min-width: 100%; max-width: 100%; min-height: 0;
|
||||
overflow: auto; padding: 5px;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--list-thumb) transparent;
|
||||
}
|
||||
#animationList::-webkit-scrollbar { width: 8px; height: 8px; }
|
||||
#animationList::-webkit-scrollbar-track { background: transparent; }
|
||||
#animationList::-webkit-scrollbar-thumb {
|
||||
min-height: 28px; border: 2px solid transparent; border-radius: 999px;
|
||||
background: var(--list-thumb);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
#animationList::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--list-thumb-hover);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
#animationList::-webkit-scrollbar-corner { background: transparent; }
|
||||
.animation-item {
|
||||
display: block; width: 100%; min-width: 116px; max-width: 248px;
|
||||
padding: 6px 8px; margin: 0 0 2px;
|
||||
border: 0; border-radius: 4px; background: transparent;
|
||||
color: var(--text); text-align: left; overflow: hidden;
|
||||
text-overflow: ellipsis; cursor: pointer;
|
||||
}
|
||||
.animation-item:hover { background: var(--panel-2); }
|
||||
.animation-item.active {
|
||||
background: var(--active-item-fill);
|
||||
color: var(--accent); box-shadow: inset 3px 0 0 var(--accent);
|
||||
}
|
||||
#viewport { position: relative; width: 100%; min-width: 0; flex: 1; overflow: hidden; }
|
||||
canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
|
||||
#status {
|
||||
position: absolute; bottom: calc(22px + var(--timeline-height)); left: 12px; color: var(--text-dim);
|
||||
font-size: 12px; pointer-events: none; text-shadow: 0 1px 2px rgba(0,0,0,.3);
|
||||
}
|
||||
#timelineBar {
|
||||
position: absolute; left: 12px; right: 12px; bottom: 12px; z-index: 10;
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 6px 12px;
|
||||
background: var(--panel-glass);
|
||||
border: 1px solid var(--border); border-radius: 8px;
|
||||
box-shadow: 0 -4px 18px rgba(0, 0, 0, .22);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
#timelineLabels {
|
||||
display: flex; flex-direction: column; justify-content: space-between;
|
||||
align-self: stretch; min-width: 136px; padding: 8px 0 1px;
|
||||
}
|
||||
#timelineBar .tl-label { color: var(--text-dim); font-size: 12px; white-space: nowrap; }
|
||||
#keyframeLegend {
|
||||
display: flex; align-items: center; gap: 6px; color: var(--text-dim);
|
||||
font-size: 11px; white-space: nowrap; cursor: help;
|
||||
}
|
||||
#keyframeLegend::before {
|
||||
content: ''; width: 10px; height: 7px; border-radius: 1px;
|
||||
background: var(--accent); opacity: .7;
|
||||
}
|
||||
#timelineBar .tl-wrap { position: relative; flex: 1; padding-top: 20px; }
|
||||
#timelineBar input[type="range"] {
|
||||
display: block; width: 100%; margin: 0; accent-color: var(--accent);
|
||||
outline: none; box-shadow: none; -webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
#timelineBar input[type="range"]:focus,
|
||||
#timelineBar input[type="range"]:focus-visible {
|
||||
outline: none; box-shadow: none; border-color: var(--border);
|
||||
}
|
||||
#timeRuler { position: absolute; top: 0; left: 7px; right: 7px; height: 19px; }
|
||||
#timeRuler .tick {
|
||||
position: absolute; bottom: 0; width: 1px; height: 5px;
|
||||
background: var(--border); transform: translateX(-.5px);
|
||||
}
|
||||
#timeRuler .tick.major { height: 9px; background: var(--text-dim); }
|
||||
#timeRuler .tick-label {
|
||||
position: absolute; bottom: 8px; color: var(--text-dim); font-size: 10px;
|
||||
font-variant-numeric: tabular-nums; transform: translateX(-50%); white-space: nowrap;
|
||||
}
|
||||
#timeRuler .tick-label.first { transform: none; }
|
||||
#timeRuler .tick-label.last { transform: translateX(-100%); }
|
||||
#keyframes { position: relative; height: 8px; margin-top: 2px; }
|
||||
#keyframes span {
|
||||
position: absolute; top: 0; width: 3px; height: 8px;
|
||||
background: var(--accent); opacity: .62; transform: translateX(-1.5px);
|
||||
cursor: help; transition: opacity .12s, transform .12s;
|
||||
}
|
||||
#keyframes span:hover { opacity: 1; transform: translateX(-1.5px) scaleY(1.35); }
|
||||
#timeLabel {
|
||||
color: var(--text-dim); font-size: 12px; min-width: 178px;
|
||||
text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap;
|
||||
}
|
||||
#overlay {
|
||||
position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
|
||||
background: var(--bg); z-index: 5; text-align: center;
|
||||
}
|
||||
#overlay .box { max-width: 520px; padding: 20px; }
|
||||
#overlay .title { font-size: 16px; margin-bottom: 8px; color: var(--error); }
|
||||
#overlay .msg { color: var(--text-dim); white-space: pre-wrap; word-break: break-all; }
|
||||
#overlay.text-preview { align-items: stretch; justify-content: stretch; text-align: left; }
|
||||
#overlay.text-preview .box { width: 100%; max-width: none; padding: 24px; overflow: auto; }
|
||||
#overlay.text-preview .title { color: var(--text); font-size: 13px; font-weight: 600; }
|
||||
#overlay.text-preview .msg {
|
||||
font: 12px/1.6 "SFMono-Regular", Consolas, "Liberation Mono", monospace;
|
||||
color: var(--text-dim); white-space: pre; word-break: normal;
|
||||
}
|
||||
/* 纯文本 / 普通 JSON 预览允许拖动选择与 Ctrl+C 复制;画布与查看器控件仍保持不可选中 */
|
||||
#overlay.text-preview .title,
|
||||
#overlay.text-preview .msg {
|
||||
-webkit-user-select: text; user-select: text;
|
||||
}
|
||||
#overlay.text-preview .msg { cursor: text; }
|
||||
/* 预览区滚动条:不使用原生样式。宽度 14px、滑块可见 8px,保证看得清。
|
||||
颜色走 --scroll-* 变量(rgba 写死),不能用 color-mix():Eagle 内置 Chromium 108
|
||||
不支持该函数,声明会整条失效,滑块就变成看不见。也不要写 scrollbar-width /
|
||||
scrollbar-color,那两个标准属性在 108 上无效且易与 ::-webkit-scrollbar 混淆。 */
|
||||
#overlay.text-preview .box::-webkit-scrollbar { width: 14px; height: 14px; }
|
||||
#overlay.text-preview .box::-webkit-scrollbar-track { background: var(--scroll-track); }
|
||||
#overlay.text-preview .box::-webkit-scrollbar-thumb {
|
||||
min-height: 36px;
|
||||
border: 3px solid transparent; border-radius: 999px;
|
||||
background: var(--scroll-thumb);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
#overlay.text-preview .box::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--scroll-thumb-hover);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
#overlay.text-preview .box::-webkit-scrollbar-thumb:active {
|
||||
background: var(--scroll-thumb-active);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
#overlay.text-preview .box::-webkit-scrollbar-corner { background: transparent; }
|
||||
/* JSON 字段树:折叠标记与计数只用符号 / 数字,无需新增界面文案 */
|
||||
#overlay.json-preview .msg { white-space: normal; }
|
||||
.jt-node > summary {
|
||||
list-style: none; cursor: pointer; border-radius: 4px;
|
||||
}
|
||||
.jt-node > summary::-webkit-details-marker { display: none; }
|
||||
.jt-node > summary:hover { background: var(--hover-fill); }
|
||||
.jt-node > summary::before {
|
||||
content: '▾'; display: inline-block; width: 14px; color: var(--text-dim);
|
||||
}
|
||||
.jt-node:not([open]) > summary::before { content: '▸'; }
|
||||
.jt-children { margin-left: 7px; padding-left: 11px; border-left: 1px solid var(--border); }
|
||||
.jt-leaf { padding-left: 14px; }
|
||||
.jt-key { color: var(--accent); }
|
||||
.jt-str { color: var(--text); }
|
||||
.jt-brace, .jt-count { color: var(--text-dim); }
|
||||
.jt-count { margin: 0 3px; font-size: 11px; }
|
||||
#overlay.hidden { display: none; }
|
||||
#loading { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; z-index: 4; background: var(--bg); }
|
||||
#loading.hidden { display: none; }
|
||||
.spinner {
|
||||
width: 34px; height: 34px; border: 3px solid var(--border);
|
||||
border-top-color: var(--accent); border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
#hideOthers { display: none; }
|
||||
</style>
|
||||
<link rel="stylesheet" href="spine.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
@@ -277,7 +25,7 @@
|
||||
<input type="checkbox" id="backgroundCheck"> <span data-i18n="viewer.background">背景</span>
|
||||
</label>
|
||||
<div class="spacer"></div>
|
||||
<button id="thumbBtn" data-i18n="viewer.thumbnail" data-i18n-title="viewer.thumbnailTitle" title="使用当前帧生成 400×400 缩略图">🖼 生成缩略图</button>
|
||||
<button id="thumbBtn" data-i18n="viewer.thumbnail" data-i18n-title="viewer.thumbnailTitle" title="使用当前帧生成 400×400 缩略图">生成缩略图</button>
|
||||
<label id="verLabel" style="color: var(--text-dim);" data-i18n-title="viewer.versionTitle" title="Spine 版本"></label>
|
||||
<button id="reloadBtn" title="重新加载目录" data-i18n-title="viewer.reloadTitle">↻</button>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user