diff --git a/release/UNIVERSAL_SpineViewer/viewer/spine.css b/release/UNIVERSAL_SpineViewer/viewer/spine.css
index bb4c8bc..8afc975 100644
--- a/release/UNIVERSAL_SpineViewer/viewer/spine.css
+++ b/release/UNIVERSAL_SpineViewer/viewer/spine.css
@@ -77,6 +77,21 @@
button.active { background: var(--accent); color: #fff; border-color: var(--accent); }
button.primary { background: var(--accent); color: #fff; border-color: var(--accent); }
button:disabled { cursor: default; opacity: .55; }
+ #groupThumbBtn { position: relative; background: #2e7d32; color: #fff; border-color: #2e7d32; }
+ #groupThumbBtn:hover { background: #256628; }
+ #thumbBtn { position: relative; background: #1976d2; color: #fff; border-color: #1976d2; }
+ #thumbBtn:hover { background: #155fa8; }
+ #groupThumbBtn::after, #thumbBtn::after {
+ content: attr(data-tooltip); position: absolute; top: calc(100% + 8px); z-index: 30;
+ width: max-content; max-width: min(620px, calc(100vw - 24px)); padding: 6px 10px;
+ color: var(--text); font-size: 12px; font-weight: 500; line-height: 1.4; white-space: normal;
+ background: var(--panel); border: 1px solid var(--border); border-radius: 4px;
+ box-shadow: 0 4px 14px rgba(0, 0, 0, .35); pointer-events: none;
+ opacity: 0; visibility: hidden;
+ }
+ #groupThumbBtn::after { left: 0; background: #256628; border-color: #256628; color: #fff; }
+ #thumbBtn::after { right: 0; background: #155fa8; border-color: #155fa8; color: #fff; }
+ #groupThumbBtn:hover::after, #thumbBtn:hover::after { opacity: 1; visibility: visible; }
.thumb-dialog {
position: absolute; inset: 0; z-index: 40; display: flex;
align-items: center; justify-content: center; padding: 24px;
diff --git a/release/UNIVERSAL_SpineViewer/viewer/spine.html b/release/UNIVERSAL_SpineViewer/viewer/spine.html
index 1fb42a0..6efd566 100644
--- a/release/UNIVERSAL_SpineViewer/viewer/spine.html
+++ b/release/UNIVERSAL_SpineViewer/viewer/spine.html
@@ -29,8 +29,8 @@
尾缀
-
-
+
+
@@ -241,7 +241,11 @@
function applyStaticI18n(root) {
const scope = root || document;
for (const el of scope.querySelectorAll('[data-i18n]')) el.textContent = t(el.dataset.i18n);
- for (const el of scope.querySelectorAll('[data-i18n-title]')) el.title = t(el.dataset.i18nTitle);
+ for (const el of scope.querySelectorAll('[data-i18n-title]')) {
+ const text = t(el.dataset.i18nTitle);
+ if (el.hasAttribute('data-instant-tooltip')) el.dataset.tooltip = text;
+ else el.title = text;
+ }
for (const el of scope.querySelectorAll('[data-i18n-aria-label]')) el.setAttribute('aria-label', t(el.dataset.i18nAriaLabel));
}
applyStaticI18n();
diff --git a/tests/viewer-i18n.test.js b/tests/viewer-i18n.test.js
index f646edb..9e49cf8 100644
--- a/tests/viewer-i18n.test.js
+++ b/tests/viewer-i18n.test.js
@@ -7,8 +7,21 @@ const { createRequire } = require('node:module');
const roots = ['.', 'release/UNIVERSAL_SpineViewer'];
for (const root of roots) {
const html = fs.readFileSync(path.join(root, 'viewer/spine.html'), 'utf8');
+ const css = fs.readFileSync(path.join(root, 'viewer/spine.css'), 'utf8');
const script = html.match(/