minecodex 2.0.32 → 2.0.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -417,20 +417,43 @@ function applyTheme() {
417
417
  document.documentElement.dataset.theme = dark ? "dark" : "light";
418
418
  elements.themeToggle.setAttribute("aria-label", locale === "zh-CN" ? "外观" : "Appearance");
419
419
  for (const button of elements.themeButtons) {
420
- button.setAttribute("aria-label", button.dataset.themeChoice === "light" ? copy.lightAppearance : copy.darkAppearance);
421
- button.setAttribute("aria-pressed", String(button.dataset.themeChoice === (dark ? "dark" : "light")));
422
- button.setAttribute("data-minecodex-tooltip", button.getAttribute("aria-label"));
420
+ button.dataset.themeChoice = dark ? "dark" : "light";
421
+ button.querySelector(".ui-icon").className = `ui-icon ${dark ? "moon" : "sun"}-icon`;
422
+ const current = dark ? copy.darkAppearance : copy.lightAppearance;
423
+ const next = dark ? copy.lightAppearance : copy.darkAppearance;
424
+ const label = locale === "zh-CN" ? `${current} · 切换为${next}` : `${current} · Switch to ${next.toLowerCase()}`;
425
+ button.setAttribute("aria-label", label);
426
+ button.setAttribute("data-minecodex-tooltip", label);
423
427
  }
424
428
  }
425
- elements.themeToggle.addEventListener("click", (event) => {
426
- const button = event.target.closest("[data-theme-choice]");
427
- if (!button) return;
429
+ let themeTransition = null;
430
+ async function toggleTheme(button) {
431
+ if (themeTransition) return;
428
432
  previews.close();
429
- selectedTheme = button.dataset.themeChoice;
433
+ selectedTheme = document.documentElement.dataset.theme === "dark" ? "light" : "dark";
430
434
  if (!window.minecodexMcpApp) {
431
435
  try { localStorage.setItem("minecodex-appearance", selectedTheme); } catch { /* 保留内存中的选择。 */ }
432
436
  }
433
- applyTheme();
437
+ if (!document.startViewTransition || matchMedia("(prefers-reduced-motion: reduce)").matches) {
438
+ applyTheme(); return;
439
+ }
440
+ const rect = button.getBoundingClientRect();
441
+ const x = rect.left + rect.width / 2, y = rect.top + rect.height / 2;
442
+ const radius = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y));
443
+ const transition = document.startViewTransition(applyTheme);
444
+ themeTransition = transition;
445
+ try {
446
+ await transition.ready;
447
+ document.documentElement.animate({clipPath:[`circle(0px at ${x}px ${y}px)`, `circle(${radius}px at ${x}px ${y}px)`]}, {
448
+ duration:420, easing:"cubic-bezier(0.23, 1, 0.32, 1)", pseudoElement:"::view-transition-new(root)",
449
+ });
450
+ await transition.finished;
451
+ } catch { transition.skipTransition(); }
452
+ finally { themeTransition = null; }
453
+ }
454
+ elements.themeToggle.addEventListener("click", event => {
455
+ const button = event.target.closest("[data-theme-choice]");
456
+ if (button) void toggleTheme(button);
434
457
  });
435
458
  elements.languageToggle.addEventListener("click", (event) => {
436
459
  const button = event.target.closest("[data-language-choice]");
@@ -22,9 +22,7 @@
22
22
  </div>
23
23
  </div>
24
24
  <div class="theme-toggle" role="group" aria-label="Appearance">
25
- <span class="theme-selection" aria-hidden="true"></span>
26
- <button data-theme-choice="light" type="button" aria-label="Light appearance" aria-pressed="false"><span class="ui-icon sun-icon" aria-hidden="true"></span></button>
27
- <button data-theme-choice="dark" type="button" aria-label="Dark appearance" aria-pressed="false"><span class="ui-icon moon-icon" aria-hidden="true"></span></button>
25
+ <button id="theme-button" data-theme-choice="light" type="button" aria-label="Light appearance"><span class="ui-icon sun-icon" aria-hidden="true"></span></button>
28
26
  </div>
29
27
  </div>
30
28
  <header class="page-header">
@@ -17,7 +17,7 @@
17
17
  --shadow: 0 4px 16px -6px #00000012;
18
18
  --focus: 0 0 0 3px #3a83f766;
19
19
  --card-radius: 20px;
20
- --grid-gap: 16px;
20
+ --grid-gap: 24px;
21
21
  --card-padding: 24px;
22
22
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
23
23
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
@@ -102,11 +102,12 @@ h1 { display: flex; flex-direction: column; align-items: center; gap: 16px; font
102
102
  .refresh-icon { --icon: url("/assets/refresh-cw.svg"); }
103
103
  .info-icon { --icon: url("/assets/info.svg"); }
104
104
  .alert-icon { --icon: url("/assets/triangle-alert.svg"); }
105
- .theme-toggle { position: relative; display: flex; gap: 4px; padding: 4px; border-radius: 999px; background: var(--surface-muted); box-shadow: inset 0 0 0 1px var(--border); }
106
- .theme-toggle button { position: relative; display: grid; place-items: center; width: 32px; height: 32px; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--ink-secondary); cursor: pointer; }
107
- .theme-toggle button[aria-pressed="true"] { color: var(--ink); }
108
- .theme-selection { position: absolute; inset: 4px auto auto 4px; width: 32px; height: 32px; border-radius: 50%; background: var(--surface-elevated); box-shadow: 0 1px 3px #0002; transition: transform 180ms var(--ease-out); }
109
- :root[data-theme="dark"] .theme-selection { transform: translateX(36px); }
105
+ .theme-toggle { display:flex; }
106
+ .theme-toggle button, .language-button { display:grid; place-items:center; width:40px; height:40px; padding:0; border:0; border-radius:50%; background:transparent; color:var(--ink-secondary); cursor:pointer; }
107
+ .theme-toggle button:hover, .language-button:hover { color:var(--ink); background:color-mix(in srgb,var(--ink) 5%,transparent); }
108
+ ::view-transition-image-pair(root) { isolation:auto; }
109
+ ::view-transition-old(root), ::view-transition-new(root) { animation:none; mix-blend-mode:normal; }
110
+ ::view-transition-new(root) { z-index:1; }
110
111
 
111
112
  h2 { font-size: 14px; font-weight: 620; }
112
113
 
@@ -117,8 +118,7 @@ h2 { font-size: 14px; font-weight: 620; }
117
118
  }
118
119
 
119
120
  .header-actions { display: flex; justify-content: flex-end; align-items: center; gap: 12px; flex-shrink: 0; }
120
- .language-toggle { display: flex; padding: 4px; border-radius: 999px; background: var(--surface-muted); box-shadow: inset 0 0 0 1px var(--border); }
121
- .language-button { display:grid; place-items:center; width:32px; height:32px; padding:0; border:0; border-radius:50%; background:transparent; color:var(--ink-secondary); cursor:pointer; }
121
+ .language-toggle { display:flex; }
122
122
  .language-icon { --icon: url("/assets/languages.svg"); }
123
123
  .language-menu { position: fixed; inset: auto; margin:0; padding:6px; min-width:140px; border:1px solid var(--border-heavy); border-radius:14px; background:var(--surface-elevated); color:var(--ink); box-shadow:var(--shadow); }
124
124
  .language-menu button { display:block; width:100%; padding:8px 12px; border:0; border-radius:8px; background:transparent; color:var(--ink-secondary); text-align:left; cursor:pointer; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minecodex",
3
- "version": "2.0.32",
3
+ "version": "2.0.33",
4
4
  "description": "Lightweight, local-first plugins for the Codex desktop app.",
5
5
  "license": "MIT",
6
6
  "type": "module",