minecodex 1.2.1 → 1.2.2
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.
- package/features/images/codex-feature.json +11 -2
- package/features/images/src/http-server.mjs +15 -5
- package/features/images/src/image-library.mjs +37 -9
- package/features/images/src/prompt-index.mjs +27 -24
- package/features/images/src/prompt-source.mjs +100 -0
- package/features/images/web/app.js +180 -67
- package/features/images/web/i18n.mjs +5 -0
- package/features/images/web/index.html +29 -24
- package/features/images/web/styles.css +169 -139
- package/features/model-slider/README.md +13 -1
- package/features/session-status/DESIGN.md +1 -1
- package/features/session-status/README.md +7 -1
- package/features/session-status/src/session-status.mjs +68 -7
- package/package.json +5 -2
- package/packages/runtime-host/src/cloud-images.mjs +123 -0
- package/packages/runtime-host/src/codex-cdp.mjs +42 -8
- package/packages/runtime-host/src/codex-design-contract.mjs +10 -0
- package/packages/runtime-host/src/codex-injection.mjs +326 -113
- package/packages/runtime-host/src/codex-runtime.mjs +75 -19
- package/packages/runtime-host/src/feature-registry.mjs +2 -0
- package/packages/runtime-host/src/session-status-reader.mjs +25 -8
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// CodexRuntime 与 CDP 客户端在 codex-runtime.mjs / codex-cdp.mjs。
|
|
4
4
|
|
|
5
5
|
import { randomBytes } from "node:crypto";
|
|
6
|
-
import { CODEX_THEME_TOKENS } from "./codex-design-contract.mjs";
|
|
6
|
+
import { CODEX_THEME_TOKENS, CODEX_THEME_TOKEN_ALIASES } from "./codex-design-contract.mjs";
|
|
7
7
|
|
|
8
8
|
export const RUNTIME_VERSION = 101;
|
|
9
9
|
export const HOST_BINDING_NAME = "__codexPersonalHostAction";
|
|
@@ -158,6 +158,7 @@ export function createInjectionSource(features, {
|
|
|
158
158
|
temporaryChat: ["temporary chat", "临时聊天", "临时对话"],
|
|
159
159
|
};
|
|
160
160
|
const themeTokenNames = config.themeTokens;
|
|
161
|
+
const themeTokenAliases = config.themeTokenAliases;
|
|
161
162
|
|
|
162
163
|
function normalizedLocale(value) {
|
|
163
164
|
return String(value ?? "").toLowerCase().startsWith("zh") ? "zh-CN" : "en";
|
|
@@ -251,10 +252,6 @@ export function createInjectionSource(features, {
|
|
|
251
252
|
let modelSelectorObservedTrigger = null;
|
|
252
253
|
let modelSelectorControllerMenu = null;
|
|
253
254
|
let modelSelectorNativeFastIcons = null;
|
|
254
|
-
// Fast 图标来源标记:bundle 静态提取 vs DOM 克隆,DOM 克隆优先以保持像素一致。
|
|
255
|
-
let modelSelectorNativeFastIconSources = null;
|
|
256
|
-
// DOM 克隆可见性门控:仅在 Fast 控件从无到有时扫描一次,常驻会话不逐帧扫描。
|
|
257
|
-
let modelSelectorFastControlVisible = false;
|
|
258
255
|
// 持久化脏标记:仅在图标实际变化时写一次 localStorage。
|
|
259
256
|
let modelSelectorNativeFastIconsDirty = false;
|
|
260
257
|
const modelSelectorFastIconStorageKey = "codex-model-slider:fast-icons:v1";
|
|
@@ -344,10 +341,12 @@ export function createInjectionSource(features, {
|
|
|
344
341
|
}
|
|
345
342
|
[data-codex-model-slider-generic] { box-sizing: border-box; width: 100%; }
|
|
346
343
|
[data-codex-model-slider-fast] { border: 0; background: transparent; cursor: pointer; }
|
|
344
|
+
[data-codex-model-slider-fast]:not(:hover) [data-codex-model-slider-fast-content] { background: transparent; }
|
|
347
345
|
[data-codex-model-slider-fast]:focus-visible { outline: 2px solid var(--color-border-focus); outline-offset: 1px; }
|
|
348
346
|
[data-codex-model-slider-rail][data-codex-model-slider-fallback] { position: relative; height: 38px; margin: 1px 8px 0; outline: none; cursor: pointer; }
|
|
349
347
|
[data-codex-model-slider-track] { position: absolute; top: 12px; right: 0; left: 0; height: 16px; border-radius: 999px; background: var(--color-background-control); }
|
|
350
348
|
[data-codex-model-slider-range] { position: absolute; top: 0; bottom: 0; left: 0; width: var(--codex-model-slider-progress); border-radius: inherit; background: var(--color-text-accent); }
|
|
349
|
+
[data-codex-model-slider-rail][data-max="true"] [data-codex-model-slider-range] { background: var(--color-chart-purple); }
|
|
351
350
|
[data-codex-model-slider-fallback] [data-codex-model-slider-tick] {
|
|
352
351
|
position: absolute; top: 16px; width: 6px; height: 6px; transform: translate(-50%, -50%); border: 0; border-radius: 50%;
|
|
353
352
|
padding: 0; background: var(--color-text-foreground-tertiary); pointer-events: none;
|
|
@@ -371,10 +370,6 @@ export function createInjectionSource(features, {
|
|
|
371
370
|
const persistedIcons = loadPersistedNativeFastIcons(modelSelectorFastIconStorageKey);
|
|
372
371
|
if (persistedIcons) {
|
|
373
372
|
modelSelectorNativeFastIcons = persistedIcons;
|
|
374
|
-
modelSelectorNativeFastIconSources = {};
|
|
375
|
-
for (const state of Object.keys(persistedIcons)) {
|
|
376
|
-
modelSelectorNativeFastIconSources[state] = "persisted";
|
|
377
|
-
}
|
|
378
373
|
}
|
|
379
374
|
void loadNativeFastIconFromBundle();
|
|
380
375
|
}
|
|
@@ -789,7 +784,6 @@ export function createInjectionSource(features, {
|
|
|
789
784
|
const source = await fetch(sourceLink.href).then((response) => response.text());
|
|
790
785
|
if (!modelSelectorNativeFastIcons) {
|
|
791
786
|
modelSelectorNativeFastIcons = {};
|
|
792
|
-
modelSelectorNativeFastIconSources = {};
|
|
793
787
|
}
|
|
794
788
|
let gained = false;
|
|
795
789
|
const define = (state, prefix, definition) => {
|
|
@@ -806,7 +800,6 @@ export function createInjectionSource(features, {
|
|
|
806
800
|
svg.setAttribute("width", "20");
|
|
807
801
|
svg.setAttribute("height", "20");
|
|
808
802
|
modelSelectorNativeFastIcons[state] = svg;
|
|
809
|
-
modelSelectorNativeFastIconSources[state] = "bundle";
|
|
810
803
|
gained = true;
|
|
811
804
|
};
|
|
812
805
|
define("active", "M11\\.9125 21\\.4125", { viewBox: "0 0 24 24" });
|
|
@@ -822,50 +815,24 @@ export function createInjectionSource(features, {
|
|
|
822
815
|
}
|
|
823
816
|
}
|
|
824
817
|
|
|
825
|
-
function hasVisibleFastControl() {
|
|
826
|
-
return Boolean(
|
|
827
|
-
document.querySelector("[data-codex-model-slider-fast]")
|
|
828
|
-
|| document.querySelector('[class*="_FastModeToggle_"]')
|
|
829
|
-
|| document.querySelector('[class*="_ModelPickerTriggerFastIndicator_"] svg'),
|
|
830
|
-
);
|
|
831
|
-
}
|
|
832
|
-
|
|
833
818
|
function captureNativeFastIcon() {
|
|
834
|
-
//
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
const
|
|
838
|
-
const
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
template.removeAttribute("id");
|
|
852
|
-
// 统一呈现尺寸:保留原生 viewBox,固定 20px。DOM 克隆来源是
|
|
853
|
-
// 原生 active(24px)/inactive(20px) 两套 SVG,若不归一化,
|
|
854
|
-
// active 24px 放进 26px 的 Fast content 容器会显得大一圈。
|
|
855
|
-
template.setAttribute("width", "20");
|
|
856
|
-
template.setAttribute("height", "20");
|
|
857
|
-
if (!modelSelectorNativeFastIcons) {
|
|
858
|
-
modelSelectorNativeFastIcons = {};
|
|
859
|
-
modelSelectorNativeFastIconSources = {};
|
|
860
|
-
}
|
|
861
|
-
modelSelectorNativeFastIcons[state] = template;
|
|
862
|
-
modelSelectorNativeFastIconSources[state] = "dom";
|
|
863
|
-
modelSelectorNativeFastIconsDirty = true;
|
|
864
|
-
};
|
|
865
|
-
capture("active", "M11.9125 21.4125");
|
|
866
|
-
// 现版 app 的 inactive bolt 为 20x20,路径以 M9.80999 开头;
|
|
867
|
-
// 旧的 M7.38 前缀已匹配不到任何图标,导致 inactive 缺失。
|
|
868
|
-
capture("inactive", "M9.80999 17.8302");
|
|
819
|
+
// 原生控件每次只渲染当前状态;状态切换后按控件本身更新缓存。
|
|
820
|
+
const nativeContent = Array.from(document.querySelectorAll('[class*="_FastModeToggleContent_"]'))
|
|
821
|
+
.find((content) => !content.closest("[data-codex-model-slider-generic]"));
|
|
822
|
+
const toggle = nativeContent?.closest('[class*="_FastModeToggle_"]');
|
|
823
|
+
const state = toggle?.getAttribute("data-fast-mode-enabled") === "true" ? "active" : "inactive";
|
|
824
|
+
const svg = nativeContent?.querySelector("svg");
|
|
825
|
+
const current = modelSelectorNativeFastIcons?.[state];
|
|
826
|
+
if (svg && (current?.innerHTML !== svg.innerHTML
|
|
827
|
+
|| current.getAttribute("viewBox") !== svg.getAttribute("viewBox")
|
|
828
|
+
|| current.getAttribute("width") !== svg.getAttribute("width"))) {
|
|
829
|
+
const template = svg.cloneNode(true);
|
|
830
|
+
template.removeAttribute("id");
|
|
831
|
+
if (!modelSelectorNativeFastIcons) {
|
|
832
|
+
modelSelectorNativeFastIcons = {};
|
|
833
|
+
}
|
|
834
|
+
modelSelectorNativeFastIcons[state] = template;
|
|
835
|
+
modelSelectorNativeFastIconsDirty = true;
|
|
869
836
|
}
|
|
870
837
|
const ready = Boolean(
|
|
871
838
|
modelSelectorNativeFastIcons?.active && modelSelectorNativeFastIcons?.inactive,
|
|
@@ -874,31 +841,42 @@ export function createInjectionSource(features, {
|
|
|
874
841
|
persistNativeFastIcons();
|
|
875
842
|
applyNativeFastIconsToShells();
|
|
876
843
|
}
|
|
877
|
-
return modelSelectorNativeFastIcons;
|
|
878
844
|
}
|
|
879
845
|
|
|
880
846
|
function applyNativeFastIconsToShells() {
|
|
881
847
|
document.querySelectorAll("[data-codex-model-slider-fast]").forEach((button) => {
|
|
882
848
|
const content = button.querySelector("[data-codex-model-slider-fast-content]");
|
|
883
|
-
|
|
884
|
-
if (!content || content.querySelector("svg")) return;
|
|
849
|
+
if (!content) return;
|
|
885
850
|
const icon = createNativeFastIcon(button.getAttribute("aria-pressed") === "true");
|
|
886
|
-
if (icon)
|
|
851
|
+
if (!icon) return;
|
|
852
|
+
const current = content.querySelector("svg");
|
|
853
|
+
if (!current || current.innerHTML !== icon.innerHTML
|
|
854
|
+
|| current.getAttribute("viewBox") !== icon.getAttribute("viewBox")
|
|
855
|
+
|| current.getAttribute("width") !== icon.getAttribute("width")) {
|
|
856
|
+
content.replaceChildren(icon);
|
|
857
|
+
}
|
|
887
858
|
});
|
|
888
859
|
}
|
|
889
860
|
|
|
890
861
|
function createNativeFastIcon(active) {
|
|
891
862
|
// 直接读取已就绪的图标存储(启动时由 bundle/持久化填充,运行期由
|
|
892
863
|
// captureNativeFastIcon 升级为 DOM 克隆),避免再次触发捕获造成递归。
|
|
893
|
-
//
|
|
894
|
-
const
|
|
895
|
-
.
|
|
896
|
-
|
|
897
|
-
|
|
864
|
+
// 优先克隆当前面板的图标;切换瞬间使用缓存时,也沿用原生图标尺寸。
|
|
865
|
+
const nativeContents = Array.from(document.querySelectorAll('[class*="_FastModeToggleContent_"]'))
|
|
866
|
+
.filter((content) => !content.closest("[data-codex-model-slider-generic]"));
|
|
867
|
+
const nativeContent = nativeContents.find((content) => (
|
|
868
|
+
(content.getAttribute("data-fast-mode-enabled")
|
|
869
|
+
?? content.closest('[class*="_FastModeToggle_"]')?.getAttribute("data-fast-mode-enabled")) === String(active)
|
|
870
|
+
));
|
|
871
|
+
const nativeSvg = nativeContent?.querySelector("svg") ?? nativeContents[0]?.querySelector("svg");
|
|
898
872
|
const template = nativeContent?.querySelector("svg")
|
|
899
873
|
?? modelSelectorNativeFastIcons?.[active ? "active" : "inactive"];
|
|
900
874
|
if (!template) return null;
|
|
901
875
|
const icon = template.cloneNode(true);
|
|
876
|
+
if (nativeSvg) {
|
|
877
|
+
icon.setAttribute("width", nativeSvg.getAttribute("width") ?? "16");
|
|
878
|
+
icon.setAttribute("height", nativeSvg.getAttribute("height") ?? "16");
|
|
879
|
+
}
|
|
902
880
|
icon.setAttribute("aria-hidden", "true");
|
|
903
881
|
return icon;
|
|
904
882
|
}
|
|
@@ -994,9 +972,25 @@ export function createInjectionSource(features, {
|
|
|
994
972
|
function nativeComposerModelController() {
|
|
995
973
|
const trigger = nativeComposerTrigger();
|
|
996
974
|
let fiber = currentReactFiber(trigger);
|
|
975
|
+
let powerController = null;
|
|
997
976
|
for (let depth = 0; fiber && depth < 40; depth += 1, fiber = fiber.return) {
|
|
998
977
|
const props = fiber.memoizedProps;
|
|
978
|
+
if (
|
|
979
|
+
Array.isArray(props?.powerSelections)
|
|
980
|
+
&& typeof props.onCommitPower === "function"
|
|
981
|
+
) powerController ??= props;
|
|
999
982
|
if (Array.isArray(props?.models) && typeof props.onSelectReasoningEffort === "function") return props;
|
|
983
|
+
if (Array.isArray(props?.models) && typeof props.onCommitPower === "function") {
|
|
984
|
+
const power = powerController ?? props;
|
|
985
|
+
return {
|
|
986
|
+
...props,
|
|
987
|
+
powerSelections: power.powerSelections ?? props.powerSelections,
|
|
988
|
+
selectedPowerSelection: power.selectedPowerSelection ?? props.selectedPowerSelection,
|
|
989
|
+
fallbackPowerSelection: power.fallbackPowerSelection ?? props.fallbackPowerSelection,
|
|
990
|
+
onSelectPower: power.onSelectPower ?? props.onSelectPower,
|
|
991
|
+
onCommitPower: power.onCommitPower,
|
|
992
|
+
};
|
|
993
|
+
}
|
|
1000
994
|
// Chat/Temporary Chat 使用原生模型选择器,不进入任务模型适配。
|
|
1001
995
|
if (props?.selectedModel && typeof props.onModelChange === "function") return null;
|
|
1002
996
|
}
|
|
@@ -1663,7 +1657,11 @@ export function createInjectionSource(features, {
|
|
|
1663
1657
|
if (!wrapper?.hasAttribute("data-radix-popper-content-wrapper")) return;
|
|
1664
1658
|
const existing = modelSelectorPositionObservers.get(wrapper);
|
|
1665
1659
|
if (existing) {
|
|
1666
|
-
existing.menu
|
|
1660
|
+
if (existing.menu !== menu) {
|
|
1661
|
+
existing.resizeObserver.unobserve(existing.menu);
|
|
1662
|
+
existing.resizeObserver.observe(menu);
|
|
1663
|
+
existing.menu = menu;
|
|
1664
|
+
}
|
|
1667
1665
|
return;
|
|
1668
1666
|
}
|
|
1669
1667
|
const record = {
|
|
@@ -1713,6 +1711,32 @@ export function createInjectionSource(features, {
|
|
|
1713
1711
|
}
|
|
1714
1712
|
}
|
|
1715
1713
|
|
|
1714
|
+
function restoreGenericModelPicker(container) {
|
|
1715
|
+
const original = modelSelectorGenericContainers.get(container);
|
|
1716
|
+
if (!original) return;
|
|
1717
|
+
original.effectMirror?.disconnect();
|
|
1718
|
+
if (container.isConnected) {
|
|
1719
|
+
const menu = container.parentElement;
|
|
1720
|
+
menu?.querySelector(":scope > [data-codex-model-slider-generic]")?.remove();
|
|
1721
|
+
menu?.removeAttribute("data-codex-model-slider-generic-menu");
|
|
1722
|
+
menu?.style.removeProperty("--codex-model-slider-menu-center-shift");
|
|
1723
|
+
container.style.position = original.position;
|
|
1724
|
+
container.style.inset = original.inset;
|
|
1725
|
+
container.style.opacity = original.opacity;
|
|
1726
|
+
container.style.pointerEvents = original.pointerEvents;
|
|
1727
|
+
if (original.ariaHidden == null) container.removeAttribute("aria-hidden");
|
|
1728
|
+
else container.setAttribute("aria-hidden", original.ariaHidden);
|
|
1729
|
+
container.inert = original.inert;
|
|
1730
|
+
}
|
|
1731
|
+
modelSelectorGenericContainers.delete(container);
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
function pruneGenericModelPickerContainers() {
|
|
1735
|
+
for (const container of modelSelectorGenericContainers.keys()) {
|
|
1736
|
+
if (!container.isConnected) restoreGenericModelPicker(container);
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1716
1740
|
function closeControllerModelMenu() {
|
|
1717
1741
|
const record = modelSelectorControllerMenu;
|
|
1718
1742
|
if (!record) return false;
|
|
@@ -1853,22 +1877,6 @@ export function createInjectionSource(features, {
|
|
|
1853
1877
|
// 原生 effort 子菜单与滑条视觉 — effort 事务、Fast/通用滑条视觉与状态同步
|
|
1854
1878
|
// ========================================================================
|
|
1855
1879
|
|
|
1856
|
-
function openNativeModelSubmenu(parentMenu) {
|
|
1857
|
-
let attempts = 0;
|
|
1858
|
-
const open = () => {
|
|
1859
|
-
attempts += 1;
|
|
1860
|
-
const currentMenu = nativeModelPickerMenu() ?? parentMenu;
|
|
1861
|
-
const row = nativeModelPickerRow(currentMenu);
|
|
1862
|
-
if (row) {
|
|
1863
|
-
setReactControlledMenuOpen(row, true);
|
|
1864
|
-
setTimeout(queueEnsure, 0);
|
|
1865
|
-
return;
|
|
1866
|
-
}
|
|
1867
|
-
if (attempts < 12) requestAnimationFrame(open);
|
|
1868
|
-
};
|
|
1869
|
-
setTimeout(open, 80);
|
|
1870
|
-
}
|
|
1871
|
-
|
|
1872
1880
|
function nativeEffortRow(parentMenu) {
|
|
1873
1881
|
return Array.from(parentMenu?.querySelectorAll('[role="menuitem"][aria-haspopup="menu"]') ?? [])
|
|
1874
1882
|
.filter((row) => !row.hasAttribute("data-model-picker-view-toggle"))[1] ?? null;
|
|
@@ -1895,6 +1903,30 @@ export function createInjectionSource(features, {
|
|
|
1895
1903
|
queueEnsure();
|
|
1896
1904
|
return;
|
|
1897
1905
|
}
|
|
1906
|
+
if (typeof controller?.onCommitPower === "function") {
|
|
1907
|
+
const normalizedEffort = String(effort).toLowerCase();
|
|
1908
|
+
const activeModel = String(controller.model ?? nativeCatalogModelFor(identity)?.slug ?? identity.raw);
|
|
1909
|
+
const next = controller.powerSelections?.find((selection) => (
|
|
1910
|
+
String(selection?.model ?? "") === activeModel
|
|
1911
|
+
&& String(selection?.reasoningEffort ?? "").toLowerCase() === normalizedEffort
|
|
1912
|
+
));
|
|
1913
|
+
const previous = controller.selectedPowerSelection
|
|
1914
|
+
?? controller.powerSelections?.find((selection) => (
|
|
1915
|
+
String(selection?.model ?? "") === activeModel
|
|
1916
|
+
&& String(selection?.reasoningEffort ?? "").toLowerCase()
|
|
1917
|
+
=== String(controller.reasoningEffort ?? "").toLowerCase()
|
|
1918
|
+
))
|
|
1919
|
+
?? controller.fallbackPowerSelection;
|
|
1920
|
+
if (!next || !previous) return;
|
|
1921
|
+
controller.onSelectPower?.(next);
|
|
1922
|
+
controller.onCommitPower(next, previous);
|
|
1923
|
+
if (modelSelectorPendingOutsideDismiss) {
|
|
1924
|
+
closeReactControlledMenu(nativeComposerTrigger());
|
|
1925
|
+
modelSelectorPendingOutsideDismiss = false;
|
|
1926
|
+
}
|
|
1927
|
+
queueEnsure();
|
|
1928
|
+
return;
|
|
1929
|
+
}
|
|
1898
1930
|
const row = nativeEffortRow(parentMenu);
|
|
1899
1931
|
if (!row) return;
|
|
1900
1932
|
beginNativeEffortTransaction();
|
|
@@ -1960,6 +1992,19 @@ export function createInjectionSource(features, {
|
|
|
1960
1992
|
rail.setAttribute("aria-valuenow", String(index));
|
|
1961
1993
|
}
|
|
1962
1994
|
|
|
1995
|
+
function syncGenericMaximumVisual(rail, effortText, fastButton, maximum) {
|
|
1996
|
+
const value = String(maximum);
|
|
1997
|
+
rail?.setAttribute("data-highlighted", value);
|
|
1998
|
+
rail?.setAttribute("data-max", value);
|
|
1999
|
+
effortText?.setAttribute("data-maximum", value);
|
|
2000
|
+
fastButton?.setAttribute("data-max-power-selection", value);
|
|
2001
|
+
const maxEffects = rail?.querySelector('[class*="_MaxEffects_"]');
|
|
2002
|
+
if (maxEffects) {
|
|
2003
|
+
maxEffects.style.opacity = maximum ? "1" : "0";
|
|
2004
|
+
if (maximum) maxEffects.style.transform = "translateX(0)";
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
|
|
1963
2008
|
function neutralizeStaleSliderVisual(rail) {
|
|
1964
2009
|
for (const selector of [
|
|
1965
2010
|
"[data-codex-model-slider-native-range]",
|
|
@@ -1983,9 +2028,21 @@ export function createInjectionSource(features, {
|
|
|
1983
2028
|
return value - Math.floor(value);
|
|
1984
2029
|
}
|
|
1985
2030
|
|
|
1986
|
-
function syncNativeFastParticles(rail, enabled) {
|
|
2031
|
+
function syncNativeFastParticles(rail, enabled, entering = false) {
|
|
1987
2032
|
const track = Array.from(rail.children).find((element) => String(element.className).includes("_Track_"));
|
|
1988
2033
|
if (!track) return;
|
|
2034
|
+
const staticParticles = rail.querySelector('[class*="_ParticleTransition_"]');
|
|
2035
|
+
if (staticParticles && !matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
|
2036
|
+
staticParticles.__codexStaticParticleFade?.cancel();
|
|
2037
|
+
staticParticles.style.opacity = enabled ? "0" : "1";
|
|
2038
|
+
if (entering) {
|
|
2039
|
+
// 动态粒子渐显时,让原有星点在同一时间淡出。
|
|
2040
|
+
staticParticles.__codexStaticParticleFade = staticParticles.animate(
|
|
2041
|
+
[{ opacity: 1 }, { opacity: 0 }],
|
|
2042
|
+
{ duration: 240, easing: "ease-out" },
|
|
2043
|
+
);
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
1989
2046
|
track.querySelectorAll('[class*="_FastParticleClip_"]').forEach((element) => element.remove());
|
|
1990
2047
|
if (!enabled || matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
|
1991
2048
|
const clipClass = nativeCssModuleClass("FastParticleClip");
|
|
@@ -2025,6 +2082,10 @@ export function createInjectionSource(features, {
|
|
|
2025
2082
|
clip.append(particles);
|
|
2026
2083
|
const tickRail = Array.from(track.children).find((element) => String(element.className).includes("_TickRail_"));
|
|
2027
2084
|
track.insertBefore(clip, tickRail ?? null);
|
|
2085
|
+
if (entering) {
|
|
2086
|
+
// 首帧沿用关闭 Fast 时的星点,再渐显流动粒子。
|
|
2087
|
+
clip.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 240, easing: "ease-out" });
|
|
2088
|
+
}
|
|
2028
2089
|
}
|
|
2029
2090
|
|
|
2030
2091
|
function syncGenericFastVisual(shell, enabled, maxPowerSelection, animate = false) {
|
|
@@ -2033,7 +2094,12 @@ export function createInjectionSource(features, {
|
|
|
2033
2094
|
if (!button || !rail) return;
|
|
2034
2095
|
button.setAttribute("aria-pressed", String(enabled));
|
|
2035
2096
|
button.setAttribute("data-fast-mode-enabled", String(enabled));
|
|
2036
|
-
|
|
2097
|
+
syncGenericMaximumVisual(
|
|
2098
|
+
rail,
|
|
2099
|
+
shell.querySelector("[data-codex-model-slider-effort-row] [data-maximum]"),
|
|
2100
|
+
button,
|
|
2101
|
+
maxPowerSelection,
|
|
2102
|
+
);
|
|
2037
2103
|
button.setAttribute("aria-label", enabled
|
|
2038
2104
|
? (locale === "zh-CN" ? "关闭 Fast 模式" : "Disable Fast mode")
|
|
2039
2105
|
: (locale === "zh-CN" ? "开启 Fast 模式" : "Enable Fast mode"));
|
|
@@ -2042,8 +2108,18 @@ export function createInjectionSource(features, {
|
|
|
2042
2108
|
if (icon && content) content.replaceChildren(icon);
|
|
2043
2109
|
rail.setAttribute("data-fast-mode", String(enabled));
|
|
2044
2110
|
rail.setAttribute("data-fast-mode-dot-transition", animate ? (enabled ? "entering" : "exiting") : (enabled ? "active" : "inactive"));
|
|
2045
|
-
syncNativeFastParticles(rail, enabled);
|
|
2111
|
+
syncNativeFastParticles(rail, enabled, animate && enabled);
|
|
2046
2112
|
setGenericSliderVisual(rail, Number(rail.getAttribute("aria-valuenow") ?? 0), Number(rail.getAttribute("aria-valuemax") ?? 0) + 1);
|
|
2113
|
+
if (animate && enabled) {
|
|
2114
|
+
shell.setAttribute("data-codex-model-slider-fast-transitioning", "");
|
|
2115
|
+
setTimeout(() => {
|
|
2116
|
+
if (!shell.isConnected) return;
|
|
2117
|
+
shell.removeAttribute("data-codex-model-slider-fast-transitioning");
|
|
2118
|
+
queueEnsure();
|
|
2119
|
+
}, 260);
|
|
2120
|
+
} else {
|
|
2121
|
+
shell.removeAttribute("data-codex-model-slider-fast-transitioning");
|
|
2122
|
+
}
|
|
2047
2123
|
if (animate) {
|
|
2048
2124
|
setTimeout(() => {
|
|
2049
2125
|
if (rail.isConnected && rail.getAttribute("data-fast-mode") === String(enabled)) {
|
|
@@ -2102,6 +2178,88 @@ export function createInjectionSource(features, {
|
|
|
2102
2178
|
return clone;
|
|
2103
2179
|
}
|
|
2104
2180
|
|
|
2181
|
+
function syncClonedPowerEffects(nativeContainer, shell) {
|
|
2182
|
+
const source = nativeContainer.querySelector("[data-model-picker-power-slider]");
|
|
2183
|
+
const clone = shell.querySelector("[data-codex-model-slider-native-slider]");
|
|
2184
|
+
if (!source || !clone) return null;
|
|
2185
|
+
const sourceCanvas = source.querySelector("canvas");
|
|
2186
|
+
const cloneCanvas = clone.querySelector("canvas");
|
|
2187
|
+
let canvasTimer = null;
|
|
2188
|
+
let observer = null;
|
|
2189
|
+
const disconnect = () => {
|
|
2190
|
+
observer?.disconnect();
|
|
2191
|
+
if (canvasTimer != null) clearInterval(canvasTimer);
|
|
2192
|
+
};
|
|
2193
|
+
if (sourceCanvas && cloneCanvas) {
|
|
2194
|
+
// 原生 canvas 可能在首次克隆时尚未绘制;持续同步其高光纹理。
|
|
2195
|
+
const scratch = document.createElement("canvas");
|
|
2196
|
+
const scratchContext = scratch.getContext("2d");
|
|
2197
|
+
const cloneContext = cloneCanvas.getContext("2d");
|
|
2198
|
+
const copyCanvas = () => {
|
|
2199
|
+
if (!source.isConnected || !shell.isConnected) {
|
|
2200
|
+
disconnect();
|
|
2201
|
+
return;
|
|
2202
|
+
}
|
|
2203
|
+
if (scratch.width !== sourceCanvas.width || scratch.height !== sourceCanvas.height) {
|
|
2204
|
+
scratch.width = sourceCanvas.width;
|
|
2205
|
+
scratch.height = sourceCanvas.height;
|
|
2206
|
+
cloneCanvas.width = sourceCanvas.width;
|
|
2207
|
+
cloneCanvas.height = sourceCanvas.height;
|
|
2208
|
+
}
|
|
2209
|
+
scratchContext.clearRect(0, 0, scratch.width, scratch.height);
|
|
2210
|
+
scratchContext.drawImage(sourceCanvas, 0, 0);
|
|
2211
|
+
const y = Math.floor(scratch.height / 2);
|
|
2212
|
+
const hasDrawing = [0.25, 0.5, 0.75].some((ratio) => (
|
|
2213
|
+
scratchContext.getImageData(Math.floor(scratch.width * ratio), y, 1, 1).data[3] > 0
|
|
2214
|
+
));
|
|
2215
|
+
if (!hasDrawing) return;
|
|
2216
|
+
cloneContext.clearRect(0, 0, cloneCanvas.width, cloneCanvas.height);
|
|
2217
|
+
cloneContext.drawImage(scratch, 0, 0);
|
|
2218
|
+
};
|
|
2219
|
+
copyCanvas();
|
|
2220
|
+
canvasTimer = setInterval(copyCanvas, 80);
|
|
2221
|
+
}
|
|
2222
|
+
const sourceEffects = source.querySelector('[class*="_MaxEffects_"]');
|
|
2223
|
+
const cloneEffects = clone.querySelector('[class*="_MaxEffects_"]');
|
|
2224
|
+
if (!sourceEffects || !cloneEffects) return { disconnect };
|
|
2225
|
+
const sync = () => {
|
|
2226
|
+
if (!source.isConnected || !shell.isConnected) {
|
|
2227
|
+
disconnect();
|
|
2228
|
+
return;
|
|
2229
|
+
}
|
|
2230
|
+
const sourceTransition = sourceEffects.querySelector('[class*="_ParticleTransition_"]');
|
|
2231
|
+
let cloneTransition = cloneEffects.querySelector('[class*="_ParticleTransition_"]');
|
|
2232
|
+
if (!sourceTransition) {
|
|
2233
|
+
if (clone.querySelector("[data-fast-mode]")?.getAttribute("data-fast-mode") !== "true") {
|
|
2234
|
+
cloneTransition?.remove();
|
|
2235
|
+
}
|
|
2236
|
+
return;
|
|
2237
|
+
}
|
|
2238
|
+
const sourceParticles = sourceTransition.querySelectorAll('[class*="_TrackParticle_"]');
|
|
2239
|
+
let cloneParticles = cloneTransition?.querySelectorAll('[class*="_TrackParticle_"]');
|
|
2240
|
+
if (!cloneTransition || sourceParticles.length !== cloneParticles.length) {
|
|
2241
|
+
cloneTransition?.remove();
|
|
2242
|
+
cloneTransition = sourceTransition.cloneNode(true);
|
|
2243
|
+
cloneEffects.append(cloneTransition);
|
|
2244
|
+
cloneParticles = cloneTransition.querySelectorAll('[class*="_TrackParticle_"]');
|
|
2245
|
+
}
|
|
2246
|
+
cloneTransition.setAttribute("style", sourceTransition.getAttribute("style") ?? "");
|
|
2247
|
+
sourceParticles.forEach((particle, index) => {
|
|
2248
|
+
const style = particle.getAttribute("style") ?? "";
|
|
2249
|
+
if (cloneParticles[index].getAttribute("style") !== style) {
|
|
2250
|
+
cloneParticles[index].setAttribute("style", style);
|
|
2251
|
+
}
|
|
2252
|
+
});
|
|
2253
|
+
if (!matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
|
2254
|
+
cloneTransition.style.opacity = clone.querySelector("[data-fast-mode]")?.getAttribute("data-fast-mode") === "true" ? "0" : "1";
|
|
2255
|
+
}
|
|
2256
|
+
};
|
|
2257
|
+
observer = new MutationObserver(sync);
|
|
2258
|
+
observer.observe(sourceEffects, { attributes: true, attributeFilter: ["style"], childList: true, subtree: true });
|
|
2259
|
+
sync();
|
|
2260
|
+
return { disconnect };
|
|
2261
|
+
}
|
|
2262
|
+
|
|
2105
2263
|
function createGenericModelSlider(parentMenu, selector, identity, model, selectedEffort) {
|
|
2106
2264
|
const efforts = model.supportedReasoningLevels.map((level) => level.effort);
|
|
2107
2265
|
const selectedIndex = efforts.indexOf(selectedEffort);
|
|
@@ -2158,9 +2316,21 @@ export function createInjectionSource(features, {
|
|
|
2158
2316
|
buttonLabel.className = classes.ViewToggleModelLabel;
|
|
2159
2317
|
buttonLabel.setAttribute("data-codex-model-slider-model-label", "");
|
|
2160
2318
|
buttonLabel.textContent = modelDisplayLabel(identity, selector);
|
|
2319
|
+
const modelRow = document.createElement("span");
|
|
2320
|
+
modelRow.setAttribute("data-codex-model-slider-model-row", "");
|
|
2321
|
+
modelRow.style.display = "inline-flex";
|
|
2322
|
+
modelRow.style.alignItems = "center";
|
|
2323
|
+
modelRow.style.justifyContent = "center";
|
|
2324
|
+
modelRow.style.gap = "calc(var(--spacing) / 2)";
|
|
2325
|
+
modelRow.style.minWidth = "0";
|
|
2326
|
+
modelRow.style.maxWidth = "100%";
|
|
2327
|
+
modelRow.append(buttonLabel);
|
|
2161
2328
|
const effortText = selectedEffortVisible ? document.createElement("span") : null;
|
|
2162
2329
|
if (effortText) {
|
|
2163
2330
|
effortText.className = classes.ViewToggleEffortLabel;
|
|
2331
|
+
// 原生标签会跨满整行;收回中列,使上下两行各自居中。
|
|
2332
|
+
effortText.style.gridColumn = "2";
|
|
2333
|
+
effortText.style.gridRow = "1";
|
|
2164
2334
|
effortText.textContent = effortLabel(selectedEffort);
|
|
2165
2335
|
effortText.setAttribute("data-accent", "true");
|
|
2166
2336
|
effortText.setAttribute("data-effort-only", "false");
|
|
@@ -2174,13 +2344,13 @@ export function createInjectionSource(features, {
|
|
|
2174
2344
|
)?.cloneNode(true);
|
|
2175
2345
|
if (nativeChevron) {
|
|
2176
2346
|
nativeChevron.classList.add(classes.ViewToggleIcon);
|
|
2347
|
+
nativeChevron.style.gridArea = "auto";
|
|
2348
|
+
nativeChevron.style.flexShrink = "0";
|
|
2177
2349
|
nativeChevron.setAttribute("aria-hidden", "true");
|
|
2178
|
-
|
|
2179
|
-
effortRow.append(nativeChevron);
|
|
2180
|
-
} else {
|
|
2181
|
-
if (effortText) effortRow.append(effortText);
|
|
2350
|
+
modelRow.append(nativeChevron);
|
|
2182
2351
|
}
|
|
2183
|
-
|
|
2352
|
+
if (effortText) effortRow.append(effortText);
|
|
2353
|
+
buttonContent.append(effortRow, modelRow);
|
|
2184
2354
|
modelButton.append(buttonContent);
|
|
2185
2355
|
const openModels = () => openControllerModelMenu(parentMenu, modelButton, selector);
|
|
2186
2356
|
modelButton.addEventListener("click", openModels);
|
|
@@ -2297,7 +2467,12 @@ export function createInjectionSource(features, {
|
|
|
2297
2467
|
}
|
|
2298
2468
|
setGenericSliderVisual(rail, selectedIndex, efforts.length);
|
|
2299
2469
|
syncNativeFastParticles(rail, fastEnabled);
|
|
2300
|
-
|
|
2470
|
+
syncGenericMaximumVisual(
|
|
2471
|
+
rail,
|
|
2472
|
+
effortText,
|
|
2473
|
+
shell.querySelector("[data-codex-model-slider-fast]"),
|
|
2474
|
+
selectedEffort === "ultra",
|
|
2475
|
+
);
|
|
2301
2476
|
|
|
2302
2477
|
let pendingIndex = selectedIndex;
|
|
2303
2478
|
let dragging = false;
|
|
@@ -2316,10 +2491,9 @@ export function createInjectionSource(features, {
|
|
|
2316
2491
|
rail.setAttribute("aria-valuetext", effortLabel(effort));
|
|
2317
2492
|
if (effortText) {
|
|
2318
2493
|
effortText.textContent = effortLabel(effort);
|
|
2319
|
-
effortText.setAttribute("data-maximum", String(effort === "ultra"));
|
|
2320
2494
|
}
|
|
2321
2495
|
const fastButton = shell.querySelector("[data-codex-model-slider-fast]");
|
|
2322
|
-
|
|
2496
|
+
syncGenericMaximumVisual(rail, effortText, fastButton, effort === "ultra");
|
|
2323
2497
|
};
|
|
2324
2498
|
const preview = (clientX) => previewIndex(indexAt(clientX));
|
|
2325
2499
|
rail.addEventListener("pointerdown", (event) => {
|
|
@@ -2421,7 +2595,8 @@ export function createInjectionSource(features, {
|
|
|
2421
2595
|
const selectedServiceTier = nativeComposerModelController()?.selectedServiceTier;
|
|
2422
2596
|
const serviceTierKey = selectedServiceTier?.id ?? selectedServiceTier ?? "standard";
|
|
2423
2597
|
const selectionMode = nativeComposerModelController()?.selectionMode ?? "";
|
|
2424
|
-
const
|
|
2598
|
+
const structureKey = `${identity.raw}:${selectedEffortValue}:${selectionMode}:${efforts.map((level) => level.effort).join(",")}`;
|
|
2599
|
+
const key = `${structureKey}:${serviceTierKey}`;
|
|
2425
2600
|
const existing = parentMenu.querySelector(":scope > [data-codex-model-slider-generic]");
|
|
2426
2601
|
const nativeTemplateReady = Boolean(
|
|
2427
2602
|
parentMenu.firstElementChild
|
|
@@ -2439,11 +2614,24 @@ export function createInjectionSource(features, {
|
|
|
2439
2614
|
const canUpgradeFallback = Boolean(
|
|
2440
2615
|
existing?.querySelector("[data-codex-model-slider-fallback]") && nativeTemplateReady,
|
|
2441
2616
|
);
|
|
2617
|
+
if (existing?.getAttribute("data-codex-model-slider-structure-key") === structureKey
|
|
2618
|
+
&& existing.hasAttribute("data-codex-model-slider-fast-transitioning") && !canUpgradeFallback) {
|
|
2619
|
+
centerGenericModelPicker(parentMenu);
|
|
2620
|
+
return true;
|
|
2621
|
+
}
|
|
2622
|
+
const currentFast = existing?.querySelector("[data-codex-model-slider-rail]")?.getAttribute("data-fast-mode");
|
|
2623
|
+
const nativeFast = parentMenu.firstElementChild
|
|
2624
|
+
?.querySelector("[data-model-picker-power-slider] [data-fast-mode]")?.getAttribute("data-fast-mode");
|
|
2625
|
+
if (existing?.getAttribute("data-codex-model-slider-structure-key") === structureKey
|
|
2626
|
+
&& currentFast != null && currentFast === nativeFast && !canUpgradeFallback) {
|
|
2627
|
+
existing.setAttribute("data-codex-model-slider-generic", key);
|
|
2628
|
+
centerGenericModelPicker(parentMenu);
|
|
2629
|
+
return true;
|
|
2630
|
+
}
|
|
2442
2631
|
if (existing?.getAttribute("data-codex-model-slider-generic") === key && !canUpgradeFallback) {
|
|
2443
2632
|
centerGenericModelPicker(parentMenu);
|
|
2444
2633
|
return true;
|
|
2445
2634
|
}
|
|
2446
|
-
existing?.remove();
|
|
2447
2635
|
const nativeContainer = parentMenu.firstElementChild;
|
|
2448
2636
|
if (!nativeContainer) return false;
|
|
2449
2637
|
const shell = createGenericModelSlider(
|
|
@@ -2453,29 +2641,37 @@ export function createInjectionSource(features, {
|
|
|
2453
2641
|
displayModel,
|
|
2454
2642
|
selectedEffortValue,
|
|
2455
2643
|
);
|
|
2456
|
-
if (!shell)
|
|
2644
|
+
if (!shell) {
|
|
2645
|
+
restoreGenericModelPicker(nativeContainer);
|
|
2646
|
+
return false;
|
|
2647
|
+
}
|
|
2648
|
+
modelSelectorGenericContainers.get(nativeContainer)?.effectMirror?.disconnect();
|
|
2649
|
+
existing?.remove();
|
|
2457
2650
|
if (!modelSelectorGenericContainers.has(nativeContainer)) {
|
|
2458
|
-
modelSelectorGenericContainers.set(nativeContainer,
|
|
2651
|
+
modelSelectorGenericContainers.set(nativeContainer, {
|
|
2652
|
+
position: nativeContainer.style.position,
|
|
2653
|
+
inset: nativeContainer.style.inset,
|
|
2654
|
+
opacity: nativeContainer.style.opacity,
|
|
2655
|
+
pointerEvents: nativeContainer.style.pointerEvents,
|
|
2656
|
+
ariaHidden: nativeContainer.getAttribute("aria-hidden"),
|
|
2657
|
+
inert: nativeContainer.inert,
|
|
2658
|
+
});
|
|
2459
2659
|
}
|
|
2460
2660
|
parentMenu.setAttribute("data-codex-model-slider-generic-menu", "");
|
|
2461
|
-
nativeContainer.style.
|
|
2661
|
+
nativeContainer.style.position = "absolute";
|
|
2662
|
+
nativeContainer.style.inset = "0";
|
|
2663
|
+
nativeContainer.style.opacity = "0";
|
|
2664
|
+
nativeContainer.style.pointerEvents = "none";
|
|
2665
|
+
nativeContainer.setAttribute("aria-hidden", "true");
|
|
2666
|
+
nativeContainer.inert = true;
|
|
2462
2667
|
shell.setAttribute("data-codex-model-slider-generic", key);
|
|
2668
|
+
shell.setAttribute("data-codex-model-slider-structure-key", structureKey);
|
|
2463
2669
|
parentMenu.append(shell);
|
|
2670
|
+
modelSelectorGenericContainers.get(nativeContainer).effectMirror = syncClonedPowerEffects(nativeContainer, shell);
|
|
2464
2671
|
centerGenericModelPicker(parentMenu);
|
|
2465
2672
|
return true;
|
|
2466
2673
|
}
|
|
2467
2674
|
|
|
2468
|
-
function restoreGenericModelPicker(parentMenu) {
|
|
2469
|
-
parentMenu.querySelector(":scope > [data-codex-model-slider-generic]")?.remove();
|
|
2470
|
-
parentMenu.removeAttribute("data-codex-model-slider-generic-menu");
|
|
2471
|
-
parentMenu.style.removeProperty("--codex-model-slider-menu-center-shift");
|
|
2472
|
-
const nativeContainer = parentMenu.firstElementChild;
|
|
2473
|
-
if (nativeContainer && modelSelectorGenericContainers.has(nativeContainer)) {
|
|
2474
|
-
nativeContainer.style.display = modelSelectorGenericContainers.get(nativeContainer);
|
|
2475
|
-
modelSelectorGenericContainers.delete(nativeContainer);
|
|
2476
|
-
}
|
|
2477
|
-
}
|
|
2478
|
-
|
|
2479
2675
|
function enhanceNativeModelPicker(parentMenu, selector) {
|
|
2480
2676
|
if (enhanceGenericModelPicker(parentMenu, selector)) return;
|
|
2481
2677
|
const toggle = parentMenu.querySelector("[data-model-picker-view-toggle]");
|
|
@@ -2597,6 +2793,7 @@ export function createInjectionSource(features, {
|
|
|
2597
2793
|
}
|
|
2598
2794
|
pruneModelSubmenuPositionObservers();
|
|
2599
2795
|
pruneNativeModelToggleHandlers();
|
|
2796
|
+
pruneGenericModelPickerContainers();
|
|
2600
2797
|
document.documentElement.setAttribute("data-codex-model-slider-catalog-ready", "");
|
|
2601
2798
|
if (!modelSelectorStyle.isConnected) document.head?.append(modelSelectorStyle);
|
|
2602
2799
|
enhanceComposerModelTrigger(selector);
|
|
@@ -2667,8 +2864,8 @@ export function createInjectionSource(features, {
|
|
|
2667
2864
|
if (handlers.originalAriaExpanded == null) toggle.removeAttribute("aria-expanded");
|
|
2668
2865
|
else toggle.setAttribute("aria-expanded", handlers.originalAriaExpanded);
|
|
2669
2866
|
}
|
|
2670
|
-
for (const
|
|
2671
|
-
|
|
2867
|
+
for (const container of modelSelectorGenericContainers.keys()) {
|
|
2868
|
+
restoreGenericModelPicker(container);
|
|
2672
2869
|
}
|
|
2673
2870
|
document.querySelectorAll("[data-codex-model-slider-divider]")
|
|
2674
2871
|
.forEach((element) => element.remove());
|
|
@@ -2685,7 +2882,6 @@ export function createInjectionSource(features, {
|
|
|
2685
2882
|
document.documentElement.removeAttribute("data-codex-model-slider-catalog-ready");
|
|
2686
2883
|
modelSelectorStyle.remove();
|
|
2687
2884
|
modelSelectorOriginalToggleLabels.clear();
|
|
2688
|
-
modelSelectorGenericContainers.clear();
|
|
2689
2885
|
modelSelectorNativeToggleHandlers.clear();
|
|
2690
2886
|
}
|
|
2691
2887
|
|
|
@@ -2728,6 +2924,10 @@ export function createInjectionSource(features, {
|
|
|
2728
2924
|
// CDP 重连会轮换 token;保留已有页面和草稿,只重试未就绪的页面。
|
|
2729
2925
|
config.bindingName = bindingName;
|
|
2730
2926
|
config.bindingToken = bindingToken;
|
|
2927
|
+
for (const feature of pageScriptFeatures) {
|
|
2928
|
+
feature.pageScript.config.bindingName = bindingName;
|
|
2929
|
+
feature.pageScript.config.bindingToken = bindingToken;
|
|
2930
|
+
}
|
|
2731
2931
|
for (const [requestId, pending] of pendingHostActions) {
|
|
2732
2932
|
if (pending.kind === "surface-load") pendingHostActions.delete(requestId);
|
|
2733
2933
|
}
|
|
@@ -2821,7 +3021,17 @@ export function createInjectionSource(features, {
|
|
|
2821
3021
|
.map((name) => [name, style.getPropertyValue(name).trim()])
|
|
2822
3022
|
.filter(([, value]) => value),
|
|
2823
3023
|
);
|
|
2824
|
-
|
|
3024
|
+
// 按语义和命名空间解析,不依赖构建哈希或猜测近似名称。
|
|
3025
|
+
for (const name of themeTokenNames) {
|
|
3026
|
+
if (tokens[name]) continue;
|
|
3027
|
+
const semanticNames = [name, ...(themeTokenAliases[name] ?? [])];
|
|
3028
|
+
const candidates = semanticNames.flatMap((candidate) => candidate.startsWith("--color-")
|
|
3029
|
+
? [candidate, candidate.replace("--color-", "--app-color-")]
|
|
3030
|
+
: [candidate]);
|
|
3031
|
+
const value = candidates.map((candidate) => style.getPropertyValue(candidate).trim()).find(Boolean);
|
|
3032
|
+
if (value) tokens[name] = value;
|
|
3033
|
+
}
|
|
3034
|
+
const mode = root.dataset.theme === "dark" || root.classList.contains("electron-dark") || style.colorScheme === "dark"
|
|
2825
3035
|
? "dark"
|
|
2826
3036
|
: "light";
|
|
2827
3037
|
return { root, style, tokens, mode };
|
|
@@ -3091,6 +3301,8 @@ export function createInjectionSource(features, {
|
|
|
3091
3301
|
frame.title = title;
|
|
3092
3302
|
frame.allow = "clipboard-write";
|
|
3093
3303
|
frame.style.cssText = "width:100%;height:100%;border:0;display:block;background:transparent";
|
|
3304
|
+
// 图片页覆盖原生标题栏,避免控件点击被窗口拖拽区域吞掉。
|
|
3305
|
+
if (featureId === "images") frame.style.setProperty("-webkit-app-region", "no-drag");
|
|
3094
3306
|
return frame;
|
|
3095
3307
|
}
|
|
3096
3308
|
|
|
@@ -4290,7 +4502,7 @@ export function createInjectionSource(features, {
|
|
|
4290
4502
|
respondToSurface(record, requestId, { ok: true, result: { paths } });
|
|
4291
4503
|
return;
|
|
4292
4504
|
}
|
|
4293
|
-
if (["attach-file", "create-image-edit-thread"].includes(action)) {
|
|
4505
|
+
if (["attach-file", "create-image-edit-thread", "list-cloud-images", "import-cloud-image"].includes(action)) {
|
|
4294
4506
|
if (typeof globalThis[config.bindingName] !== "function") {
|
|
4295
4507
|
throw new Error("The native file bridge is unavailable");
|
|
4296
4508
|
}
|
|
@@ -4472,7 +4684,7 @@ export function createInjectionSource(features, {
|
|
|
4472
4684
|
const themeObserver = new MutationObserver(queueTheme);
|
|
4473
4685
|
themeObserver.observe(document.documentElement, {
|
|
4474
4686
|
attributes: true,
|
|
4475
|
-
attributeFilter: ["class", "style"],
|
|
4687
|
+
attributeFilter: ["class", "style", "data-theme"],
|
|
4476
4688
|
});
|
|
4477
4689
|
domObservers.push(themeObserver);
|
|
4478
4690
|
|
|
@@ -4559,6 +4771,7 @@ export function createInjectionSource(features, {
|
|
|
4559
4771
|
summaryLayout: RESPONSIVE_SUMMARY_LAYOUT,
|
|
4560
4772
|
rightPanelRouteKinds: RIGHT_PANEL_ROUTE_KINDS,
|
|
4561
4773
|
themeTokens: CODEX_THEME_TOKENS,
|
|
4774
|
+
themeTokenAliases: CODEX_THEME_TOKEN_ALIASES,
|
|
4562
4775
|
features: runtimeFeatures,
|
|
4563
4776
|
})})`;
|
|
4564
4777
|
}
|