minecodex 0.1.18 → 0.1.20

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.
@@ -22,6 +22,13 @@ distributed under MineCodex's MIT License.
22
22
  (`icons/xiaomi.svg`), licensed under CC0 1.0. The Xiaomi brand mark remains
23
23
  the property of Xiaomi Corporation.
24
24
 
25
+ ## Meta logo (provider mark)
26
+
27
+ `assets/providers/muse.svg` is the official Meta Platforms logo rendered in the
28
+ same 24×24 provider-mark style (Lobe Icons) as the other provider icons in this
29
+ folder. The Meta mark remains the property of Meta Platforms, Inc.; the SVG
30
+ wrapper is distributed under MineCodex's MIT License.
31
+
25
32
  ## Lucide
26
33
 
27
34
  Lucide icons distributed by MineCodex include the Model Slider UI icons, Notes action and
@@ -0,0 +1 @@
1
+ <svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Meta</title><path d="M6.897 4h-.024l-.031 2.615h.022c1.715 0 3.046 1.357 5.94 6.246l.175.297.012.02 1.62-2.438-.012-.019a48.763 48.763 0 00-1.098-1.716 28.01 28.01 0 00-1.175-1.629C10.413 4.932 8.812 4 6.896 4z" fill="url(#lobe-icons-meta-0-_R_0_)"></path><path d="M6.873 4C4.95 4.01 3.247 5.258 2.02 7.17a4.352 4.352 0 00-.01.017l2.254 1.231.011-.017c.718-1.083 1.61-1.774 2.568-1.785h.021L6.896 4h-.023z" fill="url(#lobe-icons-meta-1-_R_0_)"></path><path d="M2.019 7.17l-.011.017C1.2 8.447.598 9.995.274 11.664l-.005.022 2.534.6.004-.022c.27-1.467.786-2.828 1.456-3.845l.011-.017L2.02 7.17z" fill="url(#lobe-icons-meta-2-_R_0_)"></path><path d="M2.807 12.264l-2.533-.6-.005.022c-.177.918-.267 1.851-.269 2.786v.023l2.598.233v-.023a12.591 12.591 0 01.21-2.44z" fill="url(#lobe-icons-meta-3-_R_0_)"></path><path d="M2.677 15.537a5.462 5.462 0 01-.079-.813v-.022L0 14.468v.024a8.89 8.89 0 00.146 1.652l2.535-.585a4.106 4.106 0 01-.004-.022z" fill="url(#lobe-icons-meta-4-_R_0_)"></path><path d="M3.27 16.89c-.284-.31-.484-.756-.589-1.328l-.004-.021-2.535.585.004.021c.192 1.01.568 1.85 1.106 2.487l.014.017 2.018-1.745a2.106 2.106 0 01-.015-.016z" fill="url(#lobe-icons-meta-5-_R_0_)"></path><path d="M10.78 9.654c-1.528 2.35-2.454 3.825-2.454 3.825-2.035 3.2-2.739 3.917-3.871 3.917a1.545 1.545 0 01-1.186-.508l-2.017 1.744.014.017C2.01 19.518 3.058 20 4.356 20c1.963 0 3.374-.928 5.884-5.33l1.766-3.13a41.283 41.283 0 00-1.227-1.886z" fill="#0082FB"></path><path d="M13.502 5.946l-.016.016c-.4.43-.786.908-1.16 1.416.378.483.768 1.024 1.175 1.63.48-.743.928-1.345 1.367-1.807l.016-.016-1.382-1.24z" fill="url(#lobe-icons-meta-6-_R_0_)"></path><path d="M20.918 5.713C19.853 4.633 18.583 4 17.225 4c-1.432 0-2.637.787-3.723 1.944l-.016.016 1.382 1.24.016-.017c.715-.747 1.408-1.12 2.176-1.12.826 0 1.6.39 2.27 1.075l.015.016 1.589-1.425-.016-.016z" fill="#0082FB"></path><path d="M23.998 14.125c-.06-3.467-1.27-6.566-3.064-8.396l-.016-.016-1.588 1.424.015.016c1.35 1.392 2.277 3.98 2.361 6.971v.023h2.292v-.022z" fill="url(#lobe-icons-meta-7-_R_0_)"></path><path d="M23.998 14.15v-.023h-2.292v.022c.004.14.006.282.006.424 0 .815-.121 1.474-.368 1.95l-.011.022 1.708 1.782.013-.02c.62-.96.946-2.293.946-3.91 0-.083 0-.165-.002-.247z" fill="url(#lobe-icons-meta-8-_R_0_)"></path><path d="M21.344 16.52l-.011.02c-.214.402-.519.67-.917.787l.778 2.462a3.493 3.493 0 00.438-.182 3.558 3.558 0 001.366-1.218l.044-.065.012-.02-1.71-1.784z" fill="url(#lobe-icons-meta-9-_R_0_)"></path><path d="M19.92 17.393c-.262 0-.492-.039-.718-.14l-.798 2.522c.449.153.927.222 1.46.222.492 0 .943-.073 1.352-.215l-.78-2.462c-.167.05-.341.075-.517.073z" fill="url(#lobe-icons-meta-10-_R_0_)"></path><path d="M18.323 16.534l-.014-.017-1.836 1.914.016.017c.637.682 1.246 1.105 1.937 1.337l.797-2.52c-.291-.125-.573-.353-.9-.731z" fill="url(#lobe-icons-meta-11-_R_0_)"></path><path d="M18.309 16.515c-.55-.642-1.232-1.712-2.303-3.44l-1.396-2.336-.011-.02-1.62 2.438.012.02.989 1.668c.959 1.61 1.74 2.774 2.493 3.585l.016.016 1.834-1.914a2.353 2.353 0 01-.014-.017z" fill="url(#lobe-icons-meta-12-_R_0_)"></path><defs><linearGradient id="lobe-icons-meta-0-_R_0_" x1="75.897%" x2="26.312%" y1="89.199%" y2="12.194%"><stop offset=".06%" stop-color="#0867DF"></stop><stop offset="45.39%" stop-color="#0668E1"></stop><stop offset="85.91%" stop-color="#0064E0"></stop></linearGradient><linearGradient id="lobe-icons-meta-1-_R_0_" x1="21.67%" x2="97.068%" y1="75.874%" y2="23.985%"><stop offset="13.23%" stop-color="#0064DF"></stop><stop offset="99.88%" stop-color="#0064E0"></stop></linearGradient><linearGradient id="lobe-icons-meta-2-_R_0_" x1="38.263%" x2="60.895%" y1="89.127%" y2="16.131%"><stop offset="1.47%" stop-color="#0072EC"></stop><stop offset="68.81%" stop-color="#0064DF"></stop></linearGradient><linearGradient id="lobe-icons-meta-3-_R_0_" x1="47.032%" x2="52.15%" y1="90.19%" y2="15.745%"><stop offset="7.31%" stop-color="#007CF6"></stop><stop offset="99.43%" stop-color="#0072EC"></stop></linearGradient><linearGradient id="lobe-icons-meta-4-_R_0_" x1="52.155%" x2="47.591%" y1="58.301%" y2="37.004%"><stop offset="7.31%" stop-color="#007FF9"></stop><stop offset="100%" stop-color="#007CF6"></stop></linearGradient><linearGradient id="lobe-icons-meta-5-_R_0_" x1="37.689%" x2="61.961%" y1="12.502%" y2="63.624%"><stop offset="7.31%" stop-color="#007FF9"></stop><stop offset="100%" stop-color="#0082FB"></stop></linearGradient><linearGradient id="lobe-icons-meta-6-_R_0_" x1="34.808%" x2="62.313%" y1="68.859%" y2="23.174%"><stop offset="27.99%" stop-color="#007FF8"></stop><stop offset="91.41%" stop-color="#0082FB"></stop></linearGradient><linearGradient id="lobe-icons-meta-7-_R_0_" x1="43.762%" x2="57.602%" y1="6.235%" y2="98.514%"><stop offset="0%" stop-color="#0082FB"></stop><stop offset="99.95%" stop-color="#0081FA"></stop></linearGradient><linearGradient id="lobe-icons-meta-8-_R_0_" x1="60.055%" x2="39.88%" y1="4.661%" y2="69.077%"><stop offset="6.19%" stop-color="#0081FA"></stop><stop offset="100%" stop-color="#0080F9"></stop></linearGradient><linearGradient id="lobe-icons-meta-9-_R_0_" x1="30.282%" x2="61.081%" y1="59.32%" y2="33.244%"><stop offset="0%" stop-color="#027AF3"></stop><stop offset="100%" stop-color="#0080F9"></stop></linearGradient><linearGradient id="lobe-icons-meta-10-_R_0_" x1="20.433%" x2="82.112%" y1="50.001%" y2="50.001%"><stop offset="0%" stop-color="#0377EF"></stop><stop offset="99.94%" stop-color="#0279F1"></stop></linearGradient><linearGradient id="lobe-icons-meta-11-_R_0_" x1="40.303%" x2="72.394%" y1="35.298%" y2="57.811%"><stop offset=".19%" stop-color="#0471E9"></stop><stop offset="100%" stop-color="#0377EF"></stop></linearGradient><linearGradient id="lobe-icons-meta-12-_R_0_" x1="32.254%" x2="68.003%" y1="19.719%" y2="84.908%"><stop offset="27.65%" stop-color="#0867DF"></stop><stop offset="100%" stop-color="#0471E9"></stop></linearGradient></defs></svg>
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "id": "model-slider",
4
- "version": "0.1.11",
4
+ "version": "0.1.12",
5
5
  "label": {
6
6
  "en": "Model Slider",
7
7
  "zh-CN": "模型滑条"
@@ -22,6 +22,7 @@
22
22
  "xai": "xAI",
23
23
  "zhipu": "Zhipu AI",
24
24
  "google": "Google",
25
+ "meta": "Meta",
25
26
  "anthropic": "Anthropic",
26
27
  "openai": "OpenAI"
27
28
  },
@@ -50,6 +51,7 @@
50
51
  { "id": "minimax", "keywords": ["minimax"], "icon": { "resource": "assets/providers/minimax.svg" } },
51
52
  { "id": "qwen", "keywords": ["qwen", "qwq"], "icon": { "resource": "assets/providers/qwen.svg" } },
52
53
  { "id": "hunyuan", "keywords": ["hunyuan"], "icon": { "resource": "assets/providers/hunyuan.svg" } },
54
+ { "id": "muse", "keywords": ["muse", "muse-spark"], "icon": { "resource": "assets/providers/muse.svg" } },
53
55
  { "id": "zhipu", "keywords": ["zhipu", "chatglm", "glm"], "icon": { "resource": "assets/providers/zhipu.svg" } },
54
56
  { "id": "openai", "keywords": ["openai", "gpt", "codex", "o1", "o3", "o4"], "icon": { "resource": "assets/providers/openai.svg" } }
55
57
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minecodex",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "description": "Lightweight, local-first plugins for the Codex desktop app.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -298,6 +298,12 @@ export function createInjectionSource(features, {
298
298
  let modelSelectorTriggerObserver = null;
299
299
  let modelSelectorObservedTrigger = null;
300
300
  let modelSelectorNativeFastIcons = null;
301
+ // Fast 图标来源标记:bundle 静态提取 vs DOM 克隆,DOM 克隆优先以保持像素一致。
302
+ let modelSelectorNativeFastIconSources = null;
303
+ // DOM 克隆可见性门控:仅在 Fast 控件从无到有时扫描一次,常驻会话不逐帧扫描。
304
+ let modelSelectorFastControlVisible = false;
305
+ // 持久化脏标记:仅在图标实际变化时写一次 localStorage。
306
+ let modelSelectorNativeFastIconsDirty = false;
301
307
  const modelSelectorStyle = document.createElement("style");
302
308
  modelSelectorStyle.setAttribute("data-codex-model-slider-style", "");
303
309
  modelSelectorStyle.textContent = `
@@ -376,6 +382,17 @@ export function createInjectionSource(features, {
376
382
  if (modelSelectorFeature) {
377
383
  document.documentElement.setAttribute("data-codex-model-slider-catalog-ready", "");
378
384
  document.documentElement.append(modelSelectorStyle);
385
+ // 启动阶段就解析 Fast 图标:先读持久化的原生克隆,再从 app bundle 静态提取兜底,
386
+ // 避免首次会话必须等用户点开弹窗后 DOM 里才出现图标。
387
+ const persistedIcons = loadPersistedNativeFastIcons();
388
+ if (persistedIcons) {
389
+ modelSelectorNativeFastIcons = persistedIcons;
390
+ modelSelectorNativeFastIconSources = {};
391
+ for (const state of Object.keys(persistedIcons)) {
392
+ modelSelectorNativeFastIconSources[state] = "persisted";
393
+ }
394
+ }
395
+ void loadNativeFastIconFromBundle();
379
396
  }
380
397
 
381
398
  for (const feature of pageScriptFeatures) {
@@ -649,28 +666,149 @@ export function createInjectionSource(features, {
649
666
  return null;
650
667
  }
651
668
 
669
+ const modelSelectorFastIconStorageKey = "codex-model-slider:fast-icons:v1";
670
+
671
+ function persistNativeFastIcons() {
672
+ if (!modelSelectorNativeFastIcons || !modelSelectorNativeFastIconsDirty) return;
673
+ try {
674
+ const payload = {};
675
+ for (const [state, template] of Object.entries(modelSelectorNativeFastIcons)) {
676
+ payload[state] = template.outerHTML;
677
+ }
678
+ localStorage.setItem(modelSelectorFastIconStorageKey, JSON.stringify(payload));
679
+ modelSelectorNativeFastIconsDirty = false;
680
+ } catch {
681
+ // localStorage 不可用时静默降级,仅本次会话内生效。
682
+ }
683
+ }
684
+
685
+ function loadPersistedNativeFastIcons() {
686
+ try {
687
+ const payload = JSON.parse(localStorage.getItem(modelSelectorFastIconStorageKey) ?? "null");
688
+ if (!payload || typeof payload !== "object") return null;
689
+ const icons = {};
690
+ for (const [state, markup] of Object.entries(payload)) {
691
+ if (typeof markup !== "string") continue;
692
+ const container = document.createElement("template");
693
+ container.innerHTML = markup;
694
+ const svg = container.content.querySelector("svg");
695
+ if (svg) icons[state] = svg;
696
+ }
697
+ return Object.keys(icons).length ? icons : null;
698
+ } catch {
699
+ return null;
700
+ }
701
+ }
702
+
703
+ function extractFastIconPath(source, prefix) {
704
+ const match = String(source).match(new RegExp(`d:\`(${prefix}[^\`]*)\``));
705
+ return match?.[1] ?? null;
706
+ }
707
+
708
+ async function loadNativeFastIconFromBundle() {
709
+ // 两个图标均已就绪(持久化或 DOM 克隆)时不再拉取整包 app-initial 资源。
710
+ if (modelSelectorNativeFastIcons?.active && modelSelectorNativeFastIcons?.inactive) return;
711
+ const sourceLink = document.querySelector('link[href*="/assets/app-initial-"][href$=".js"]');
712
+ if (!sourceLink?.href) return;
713
+ try {
714
+ const source = await fetch(sourceLink.href).then((response) => response.text());
715
+ if (!modelSelectorNativeFastIcons) {
716
+ modelSelectorNativeFastIcons = {};
717
+ modelSelectorNativeFastIconSources = {};
718
+ }
719
+ let gained = false;
720
+ const define = (state, prefix, definition) => {
721
+ if (modelSelectorNativeFastIcons[state]) return;
722
+ const path = extractFastIconPath(source, prefix);
723
+ if (!path) return;
724
+ const svg = createModelSelectorIcon({
725
+ viewBox: definition.viewBox,
726
+ markup: definition.transform
727
+ ? `<g transform="${definition.transform}"><path d="${path}" fill="currentColor" /></g>`
728
+ : `<path d="${path}" fill="currentColor" />`,
729
+ }, { brand: true });
730
+ if (!svg) return;
731
+ modelSelectorNativeFastIcons[state] = svg;
732
+ modelSelectorNativeFastIconSources[state] = "bundle";
733
+ gained = true;
734
+ };
735
+ define("active", "M11\\.9125 21\\.4125", { viewBox: "0 0 24 24" });
736
+ define("inactive", "M7\\.38 16\\.2207", { viewBox: "0 0 20 20", transform: "translate(2.43 1.609)" });
737
+ if (gained) {
738
+ modelSelectorNativeFastIconsDirty = true;
739
+ persistNativeFastIcons();
740
+ applyNativeFastIconsToShells();
741
+ queueEnsure();
742
+ }
743
+ } catch {
744
+ // 私有资源移动时保持原生控件不变,等待 DOM 克隆兜底。
745
+ }
746
+ }
747
+
748
+ function hasVisibleFastControl() {
749
+ return Boolean(
750
+ document.querySelector("[data-codex-model-slider-fast]")
751
+ || document.querySelector('[class*="_FastModeToggle_"]')
752
+ || document.querySelector('[class*="_ModelPickerTriggerFastIndicator_"] svg'),
753
+ );
754
+ }
755
+
652
756
  function captureNativeFastIcon() {
653
- const capture = (state, pathPrefix) => {
654
- if (modelSelectorNativeFastIcons?.[state]) return;
655
- const path = Array.from(document.querySelectorAll("svg path")).find(
656
- (candidate) => candidate.getAttribute("d")?.startsWith(pathPrefix),
657
- );
658
- const svg = path?.closest("svg");
659
- if (!svg) return;
660
- const template = svg.cloneNode(true);
661
- template.removeAttribute("id");
662
- template.removeAttribute("width");
663
- template.removeAttribute("height");
664
- if (!modelSelectorNativeFastIcons) modelSelectorNativeFastIcons = {};
665
- modelSelectorNativeFastIcons[state] = template;
666
- };
667
- capture("active", "M11.9125 21.4125");
668
- capture("inactive", "M7.38 16.2207");
757
+ // Fast 图标只在 Fast 控件渲染后才出现在 DOM(打开菜单或 GPT 模型);
758
+ // 只在控件可见性从无到有时做一次全页扫描(升级为 DOM 克隆),
759
+ // 常驻会话不逐帧扫描整个文档;缺图标时由 bundle/持久化兜底。
760
+ const visible = hasVisibleFastControl();
761
+ const justBecameVisible = visible && !modelSelectorFastControlVisible;
762
+ modelSelectorFastControlVisible = visible;
763
+ if (visible && justBecameVisible) {
764
+ const capture = (state, pathPrefix) => {
765
+ const current = modelSelectorNativeFastIcons?.[state];
766
+ const currentSource = modelSelectorNativeFastIconSources?.[state];
767
+ if (current && currentSource === "dom") return;
768
+ const path = Array.from(document.querySelectorAll("svg path")).find(
769
+ (candidate) => candidate.getAttribute("d")?.startsWith(pathPrefix),
770
+ );
771
+ const svg = path?.closest("svg");
772
+ if (!svg) return;
773
+ const template = svg.cloneNode(true);
774
+ template.removeAttribute("id");
775
+ template.removeAttribute("width");
776
+ template.removeAttribute("height");
777
+ if (!modelSelectorNativeFastIcons) {
778
+ modelSelectorNativeFastIcons = {};
779
+ modelSelectorNativeFastIconSources = {};
780
+ }
781
+ modelSelectorNativeFastIcons[state] = template;
782
+ modelSelectorNativeFastIconSources[state] = "dom";
783
+ modelSelectorNativeFastIconsDirty = true;
784
+ };
785
+ capture("active", "M11.9125 21.4125");
786
+ capture("inactive", "M7.38 16.2207");
787
+ }
788
+ const ready = Boolean(
789
+ modelSelectorNativeFastIcons?.active && modelSelectorNativeFastIcons?.inactive,
790
+ );
791
+ if (ready) {
792
+ persistNativeFastIcons();
793
+ applyNativeFastIconsToShells();
794
+ }
669
795
  return modelSelectorNativeFastIcons;
670
796
  }
671
797
 
798
+ function applyNativeFastIconsToShells() {
799
+ document.querySelectorAll("[data-codex-model-slider-fast]").forEach((button) => {
800
+ const content = button.querySelector("[data-codex-model-slider-fast-content]");
801
+ // 幂等补装:只给还没有图标的按钮补上,避免每次 ensure 都重建节点。
802
+ if (!content || content.querySelector("svg")) return;
803
+ const icon = createNativeFastIcon(button.getAttribute("aria-pressed") === "true");
804
+ if (icon) content.replaceChildren(icon);
805
+ });
806
+ }
807
+
672
808
  function createNativeFastIcon(active) {
673
- const template = captureNativeFastIcon()?.[active ? "active" : "inactive"];
809
+ // 直接读取已就绪的图标存储(启动时由 bundle/持久化填充,运行期由
810
+ // captureNativeFastIcon 升级为 DOM 克隆),避免再次触发捕获造成递归。
811
+ const template = modelSelectorNativeFastIcons?.[active ? "active" : "inactive"];
674
812
  if (!template) return null;
675
813
  const icon = template.cloneNode(true);
676
814
  icon.setAttribute("aria-hidden", "true");
@@ -1240,18 +1378,19 @@ export function createInjectionSource(features, {
1240
1378
  }
1241
1379
 
1242
1380
  function enhanceModelItem(item, menu, selector, index) {
1243
- if (item.querySelector("[data-codex-model-slider-row]")) return;
1244
1381
  const observedRaw = modelItemRawValue(item);
1245
1382
  if (!observedRaw) return;
1246
- if (!modelSelectorOriginalItems.has(item)) modelSelectorOriginalItems.set(item, item.innerHTML);
1247
1383
  const observedIdentity = modelIdentity(observedRaw);
1248
1384
  const nativeDescriptor = nativeModelDescriptorForItem(item, observedRaw);
1249
1385
  const catalogModel = catalogModelFor(observedIdentity, selector);
1250
1386
  const raw = catalogModel?.slug ?? nativeDescriptor?.slug ?? observedRaw;
1387
+ // 先刷新 dataset:React 原地复用菜单节点时,raw/排序/收藏键不能基于过期值。
1251
1388
  item.dataset.codexModelSliderRaw = raw;
1252
1389
  item.dataset.codexModelSliderItem = "";
1253
1390
  item.dataset.codexModelSliderOrder = String(index);
1254
1391
  item.dataset.codexModelSliderIdentitySource = catalogModel ? "catalog" : nativeDescriptor ? "native" : "visible";
1392
+ if (item.querySelector("[data-codex-model-slider-row]")) return;
1393
+ if (!modelSelectorOriginalItems.has(item)) modelSelectorOriginalItems.set(item, item.innerHTML);
1255
1394
 
1256
1395
  const identity = modelIdentity(raw);
1257
1396
  const display = modelDisplayLabel(identity, selector);
@@ -1955,7 +2094,6 @@ export function createInjectionSource(features, {
1955
2094
  function ensureModelSelector() {
1956
2095
  const selector = modelSelectorFeature?.modelSelector;
1957
2096
  if (!selector) return;
1958
- captureNativeFastIcon();
1959
2097
  pruneModelSubmenuPositionObservers();
1960
2098
  if (!modelSelectorStyle.isConnected) document.head?.append(modelSelectorStyle);
1961
2099
  enhanceComposerModelTrigger(selector);
@@ -1967,11 +2105,17 @@ export function createInjectionSource(features, {
1967
2105
  returnToNativeModelSlider();
1968
2106
  }, 80);
1969
2107
  }
1970
- return;
2108
+ } else {
2109
+ enhanceNativeModelPicker(parentMenu, selector);
2110
+ const submenu = nativeModelSubmenu(parentMenu);
2111
+ if (submenu) enhanceNativeModelSubmenu(submenu, selector);
2112
+ }
2113
+ try {
2114
+ // 图标工作与菜单增强解耦:放在最后并隔离异常,任何失败都不影响标签与菜单。
2115
+ captureNativeFastIcon();
2116
+ } catch (error) {
2117
+ console.warn("[codex-personal] fast icon capture failed", error);
1971
2118
  }
1972
- enhanceNativeModelPicker(parentMenu, selector);
1973
- const submenu = nativeModelSubmenu(parentMenu);
1974
- if (submenu) enhanceNativeModelSubmenu(submenu, selector);
1975
2119
  }
1976
2120
 
1977
2121
  function restoreModelSelectorEnhancements() {
@@ -2277,16 +2421,23 @@ export function createInjectionSource(features, {
2277
2421
  ]);
2278
2422
  for (const element of selected) {
2279
2423
  if (element.hasAttribute(entryMarker) || alreadySuppressed.has(element)) continue;
2280
- suppressedSelections.push({
2424
+ const selection = {
2281
2425
  element,
2282
2426
  ariaCurrent: element.getAttribute("aria-current"),
2283
2427
  threadSelected: element.getAttribute("data-app-action-sidebar-thread-selected"),
2428
+ hadPrimaryGhost: element.classList.contains("bg-primary-ghost-hover"),
2429
+ hadHoverPrimaryGhost: element.classList.contains("hover:bg-primary-ghost-hover"),
2284
2430
  hadActiveClass: element.classList.contains("bg-token-list-hover-background"),
2285
2431
  hadHoverClass: element.classList.contains("hover:bg-token-list-hover-background"),
2286
- });
2432
+ };
2433
+ suppressedSelections.push(selection);
2287
2434
  element.removeAttribute("aria-current");
2288
2435
  element.removeAttribute("data-app-action-sidebar-thread-selected");
2436
+ // 原生选中态(灰底)现在由 bg-primary-ghost-hover 承载,需一并移除;
2437
+ // 旧 token 类仍保留处理,以便兼容旧主题。
2438
+ element.classList.remove("bg-primary-ghost-hover");
2289
2439
  element.classList.remove("bg-token-list-hover-background");
2440
+ if (!selection.hadHoverPrimaryGhost) element.classList.add("hover:bg-primary-ghost-hover");
2290
2441
  element.classList.add("hover:bg-token-list-hover-background");
2291
2442
  }
2292
2443
  }
@@ -2299,6 +2450,8 @@ export function createInjectionSource(features, {
2299
2450
  if (selection.threadSelected !== null) {
2300
2451
  element.setAttribute("data-app-action-sidebar-thread-selected", selection.threadSelected);
2301
2452
  }
2453
+ element.classList.toggle("bg-primary-ghost-hover", Boolean(selection.hadPrimaryGhost));
2454
+ element.classList.toggle("hover:bg-primary-ghost-hover", Boolean(selection.hadHoverPrimaryGhost));
2302
2455
  element.classList.toggle("bg-token-list-hover-background", selection.hadActiveClass);
2303
2456
  element.classList.toggle("hover:bg-token-list-hover-background", selection.hadHoverClass);
2304
2457
  }
@@ -2310,6 +2463,7 @@ export function createInjectionSource(features, {
2310
2463
  const entry = findEntry(feature.id);
2311
2464
  if (!entry) continue;
2312
2465
  const selected = feature.id === activePageFeatureId;
2466
+ entry.classList.toggle("bg-primary-ghost-hover", selected);
2313
2467
  entry.classList.toggle("bg-token-list-hover-background", selected);
2314
2468
  entry.classList.toggle("hover:bg-token-list-hover-background", !selected);
2315
2469
  if (selected) entry.setAttribute("aria-current", "page");