minecodex 2.0.2 → 2.0.4
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/DESIGN.md +149 -0
- package/apps/dashboard/DESIGN.md +45 -0
- package/features/images/DESIGN.md +71 -0
- package/features/images/README.md +2 -0
- package/features/images/codex-feature.json +2 -1
- package/features/images/src/adapters/codex/cloud-import.mjs +52 -0
- package/features/images/src/adapters/codex/image-prompt.mjs +22 -0
- package/features/images/src/adapters/codex/library-api.mjs +55 -0
- package/features/images/src/adapters/codex/library-viewer.mjs +18 -0
- package/features/images/src/adapters/codex/prompt-resolver-source.mjs +8 -0
- package/features/images/src/backend/cloud-library.mjs +10 -0
- package/features/images/src/domain/library-prompt.mjs +7 -0
- package/features/images/src/library-prompt-page.mjs +7 -75
- package/features/images/src/library-prompt-source.mjs +10 -0
- package/features/images/web/app.js +9 -9
- package/features/images/web/styles.css +219 -219
- package/features/model-slider/DESIGN.md +34 -0
- package/features/model-slider/src/adapters/codex/controller.mjs +93 -0
- package/features/model-slider/src/domain/model-rules.mjs +143 -0
- package/features/model-slider/src/frontend/install.mjs +2110 -0
- package/features/model-slider/src/injection-source.mjs +10 -0
- package/features/notes/DESIGN.md +13 -15
- package/features/notes/README.md +7 -1
- package/features/notes/assets/icons/book-open-nav-fill.svg +1 -0
- package/features/notes/codex-feature.json +5 -1
- package/features/notes/src/{default-prompts.mjs → backend/default-prompts.mjs} +1 -1
- package/features/notes/src/{product.mjs → backend/product.mjs} +1 -1
- package/features/notes/src/{quick-prompts-server.mjs → backend/quick-prompts-server.mjs} +2 -2
- package/features/notes/src/{quick-prompts-page.mjs → frontend/entry.mjs} +64 -257
- package/features/notes/src/frontend/icons.json +13 -0
- package/features/notes/src/frontend/styles.css +110 -0
- package/features/notes/src/main.mjs +2 -2
- package/features/notes/vendor/phosphor-LICENSE +21 -0
- package/features/session-status/DESIGN.md +10 -8
- package/features/session-status/README.md +4 -0
- package/features/session-status/codex-feature.json +1 -1
- package/features/session-status/src/adapters/codex/context-usage.mjs +46 -0
- package/features/session-status/src/adapters/codex/session-log.mjs +81 -0
- package/features/session-status/src/adapters/codex/session-reader.mjs +106 -0
- package/features/session-status/src/domain/session-stats.mjs +141 -0
- package/features/session-status/src/injection-source.mjs +6 -0
- package/features/session-status/src/session-status.mjs +42 -102
- package/features/sidebar-focus/DESIGN.md +6 -5
- package/features/sidebar-focus/codex-feature.json +2 -1
- package/features/sidebar-focus/src/adapters/codex/renderer.mjs +28 -0
- package/features/sidebar-focus/src/injection-source.mjs +6 -0
- package/features/sidebar-focus/src/sidebar-focus.mjs +19 -66
- package/features/sites/DESIGN.md +60 -0
- package/features/sites/README.md +27 -0
- package/features/sites/codex-feature.json +28 -0
- package/features/sites/src/adapters/codex/catalog.mjs +26 -0
- package/features/sites/src/adapters/codex/renderer.mjs +83 -0
- package/features/sites/src/backend/discovery.mjs +73 -0
- package/features/sites/src/backend/project-sites.mjs +34 -0
- package/features/sites/src/backend/site-launcher.mjs +54 -0
- package/features/sites/src/backend/sites-preview.mjs +53 -0
- package/features/sites/src/backend/sites-service.mjs +162 -0
- package/features/sites/src/domain/site.mjs +40 -0
- package/features/sites/src/frontend/entry.mjs +383 -0
- package/features/sites/src/frontend/icons.json +12 -0
- package/features/sites/src/frontend/styles.css +72 -0
- package/features/sites/src/injection-source.mjs +7 -0
- package/features/sites/vendor/lucide-LICENSE +35 -0
- package/features/unread-inbox/DESIGN.md +6 -2
- package/features/unread-inbox/codex-feature.json +2 -1
- package/features/unread-inbox/src/adapters/codex/renderer.mjs +66 -0
- package/features/unread-inbox/src/injection-source.mjs +6 -0
- package/features/unread-inbox/src/unread-inbox.mjs +34 -89
- package/features/usage-meter/DESIGN.md +9 -9
- package/features/usage-meter/README.md +2 -1
- package/features/usage-meter/codex-feature.json +3 -3
- package/features/usage-meter/src/adapters/codex/renderer.mjs +61 -0
- package/features/usage-meter/src/injection-source.mjs +6 -0
- package/features/usage-meter/src/usage-meter.mjs +27 -66
- package/package.json +17 -4
- package/packages/cli/src/config.mjs +2 -1
- package/packages/codex-adapter/README.md +15 -0
- package/packages/codex-adapter/package.json +1 -0
- package/packages/codex-adapter/src/renderer/environment.mjs +53 -0
- package/packages/runtime-host/README.md +27 -0
- package/packages/runtime-host/src/codex-injection.mjs +32 -2393
- package/packages/runtime-host/src/codex-runtime.mjs +21 -3
- package/packages/runtime-host/src/feature-registry.mjs +28 -3
- package/packages/ui/DESIGN.md +31 -0
- package/packages/ui/package.json +8 -0
- package/packages/ui/src/components/button.mjs +21 -0
- package/packages/ui/src/components/composer-menu.mjs +18 -0
- package/packages/ui/src/components/dialog.mjs +55 -0
- package/packages/ui/src/components/navigation-rail.mjs +77 -0
- package/packages/ui/src/components/page.mjs +8 -0
- package/packages/ui/src/components/tooltip.mjs +96 -0
- package/packages/ui/src/foundation/layers.mjs +14 -0
- package/packages/ui/src/foundation/surfaces.mjs +34 -0
- package/packages/ui/src/install.mjs +22 -0
- package/packages/ui/src/native/navigation-rail.mjs +33 -0
- package/packages/ui/src/native/theme.mjs +35 -0
- package/packages/{runtime-host/src/codex-design-contract.mjs → ui/src/native/tokens.mjs} +67 -24
- package/packages/runtime-host/src/cloud-images.mjs +0 -123
- package/packages/runtime-host/src/feature-tooltips.mjs +0 -65
- package/packages/runtime-host/src/session-status-reader.mjs +0 -340
- /package/features/notes/src/{prompt-schema.mjs → backend/prompt-schema.mjs} +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Design System: Codex Model Slider
|
|
2
|
+
|
|
3
|
+
通用视觉、材质、按钮、Tooltip 与原生 sidebar 层级遵循根 [DESIGN.md](../../DESIGN.md)。本文件只记录功能特有组件、布局、数据语义与通用组件选型。
|
|
4
|
+
|
|
5
|
+
## 1. Visual Theme & Atmosphere
|
|
6
|
+
|
|
7
|
+
Model Slider 延续 Codex 原生的克制、紧凑和功能优先。增强层只改善模型识别与选择效率,不创建独立视觉语言。
|
|
8
|
+
|
|
9
|
+
## 2. Color Palette & Roles
|
|
10
|
+
|
|
11
|
+
- **Reasoning Accent Blue** (`var(--color-chart-blue)`): Slider 打开时的普通推理强度,沿用 Codex 原生 active effort 语义。
|
|
12
|
+
- **Maximum Effort Purple** (`var(--color-chart-purple)`): 仅用于 Ultra 推理强度及其相关状态。
|
|
13
|
+
|
|
14
|
+
模型名为主要内容,Provider 为辅助信息;通用文字与交互角色遵循根规范。
|
|
15
|
+
|
|
16
|
+
## 3. Typography Rules
|
|
17
|
+
|
|
18
|
+
Composer 触发器区分两种状态:占位态("Select model" / "Select effort")沿用原生 160px Content 宽度;真实模型态让芯片 HUG 完整模型名,长名称不被截断,reasoning effort 始终完整可见。Slider 顶部的模型名最多显示两行。
|
|
19
|
+
|
|
20
|
+
## 4. Component Stylings
|
|
21
|
+
|
|
22
|
+
- **Composer Trigger:** 占位态恢复原生 160px Content 宽度;真实模型态 HUG 完整名称,头部保留原生可点击 chevron。
|
|
23
|
+
- **Slider Header:** 模型名最多两行,容器高度随内容自然撑开;effort 与 chevron 不收缩。无 Fast 时移除原生 12px 预留占位,让按钮与面板严格居中。
|
|
24
|
+
- **Model List:** 保持 264px 原生宽度、Provider 副文本、收藏与最多六项可视高度;收藏与未收藏之间保留分隔线,弹窗内容四周保留约 6px 内边距;模型弹窗与原生 Slider 一致地水平居中于触发器。列表打开期间,Composer 占位文案从 `Select effort` 切为原生语义 `Select model`,关闭后恢复。
|
|
25
|
+
- OCX 自动发现的 GPT 路由若与原生模型同名且没有独立推理档位,列表只显示原生项;当前已选中的路由仍显示,避免失去切换入口。有独立档位的 Provider 路由继续显示并标注 Provider。
|
|
26
|
+
- Slider 预览档位时同步 effort 标签与 Fast 控件的 Ultra 状态;离开 Ultra 后立即恢复普通档位颜色。
|
|
27
|
+
|
|
28
|
+
## 5. Layout Principles
|
|
29
|
+
|
|
30
|
+
宽度遵循原生菜单,垂直方向允许内容自然增长。长名称优先换行而不是扩大面板;Composer 的紧凑触发器优先保留 effort 可读性。
|
|
31
|
+
|
|
32
|
+
## 代码归属
|
|
33
|
+
|
|
34
|
+
模型身份、品牌与别名、目录匹配、路由去重及档位交集由 `src/domain/model-rules.mjs` 维护,不读取 DOM 或收藏存储。`src/frontend/install.mjs` 维护原生控制器适配、收藏存储、菜单、滑条与节点恢复,使用共享 Dropdown 材质。`src/injection-source.mjs` 将两者组装为自包含 Renderer 安装函数;宿主只提供配置、locale、共享 UI、图标工厂与刷新调度,调用 ensure/dispose。
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
export function createModelControllerAdapter(codex,comparableModelLabel) {
|
|
2
|
+
function controllerView(props) {const view=Object.fromEntries(["models","model","reasoningEffort","powerSelections","selectedPowerSelection","fallbackPowerSelection","serviceTierOptions","selectedServiceTier","onSelectModel","onSelectReasoningEffort","onSelectPower","onCommitPower","onSelectServiceTier"].map(key=>[key,props[key]]));codex.report('model-controller',true);return view;}
|
|
3
|
+
function nativeComposerTrigger() {
|
|
4
|
+
const triggers = Array.from(document.querySelectorAll("[data-codex-intelligence-trigger]"))
|
|
5
|
+
.filter((trigger) => trigger.checkVisibility({ checkVisibilityCSS: true, checkOpacity: true }));
|
|
6
|
+
return triggers.find((trigger) => trigger.getAttribute("data-state") === "open")
|
|
7
|
+
?? triggers[0] ?? null;
|
|
8
|
+
}
|
|
9
|
+
function nativeComposerModelController() {
|
|
10
|
+
codex.report("model-controller",false,"task-controller-unavailable");
|
|
11
|
+
const trigger = nativeComposerTrigger();
|
|
12
|
+
let fiber = codex.currentFiber(trigger);
|
|
13
|
+
let powerController = null;
|
|
14
|
+
for (let depth = 0; fiber && depth < 40; depth += 1, fiber = fiber.return) {
|
|
15
|
+
const props = fiber.memoizedProps;
|
|
16
|
+
if (
|
|
17
|
+
Array.isArray(props?.powerSelections)
|
|
18
|
+
&& typeof props.onCommitPower === "function"
|
|
19
|
+
) powerController ??= props;
|
|
20
|
+
if (Array.isArray(props?.models) && typeof props.onSelectReasoningEffort === "function") return controllerView(props);
|
|
21
|
+
if (Array.isArray(props?.models) && typeof props.onCommitPower === "function") {
|
|
22
|
+
const power = powerController ?? props;
|
|
23
|
+
return controllerView({
|
|
24
|
+
...props,
|
|
25
|
+
powerSelections: power.powerSelections ?? props.powerSelections,
|
|
26
|
+
selectedPowerSelection: power.selectedPowerSelection ?? props.selectedPowerSelection,
|
|
27
|
+
fallbackPowerSelection: power.fallbackPowerSelection ?? props.fallbackPowerSelection,
|
|
28
|
+
onSelectPower: power.onSelectPower ?? props.onSelectPower,
|
|
29
|
+
onCommitPower: power.onCommitPower,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
// Chat/Temporary Chat 使用原生模型选择器,不进入任务模型适配。
|
|
33
|
+
if (props?.selectedModel && typeof props.onModelChange === "function") return null;
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
function nativeModelDescriptorForItem(item, observedRaw) {
|
|
38
|
+
const target = comparableModelLabel(observedRaw);
|
|
39
|
+
if (!target) return null;
|
|
40
|
+
let fiber = codex.currentFiber(item);
|
|
41
|
+
for (let depth = 0; fiber && depth < 35; depth += 1, fiber = fiber.return) {
|
|
42
|
+
if (!Array.isArray(fiber.memoizedProps)) continue;
|
|
43
|
+
const matches = fiber.memoizedProps.map((element) => {
|
|
44
|
+
const props = element?.props?.children?.props;
|
|
45
|
+
const slug = String(props?.model ?? element?.key ?? "").trim();
|
|
46
|
+
const displayName = String(props?.displayName ?? "").trim();
|
|
47
|
+
return slug && (
|
|
48
|
+
comparableModelLabel(slug) === target || comparableModelLabel(displayName) === target
|
|
49
|
+
) ? { slug, displayName } : null;
|
|
50
|
+
}).filter(Boolean);
|
|
51
|
+
if (matches.length === 1) return matches[0];
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
function setReactControlledMenuOpen(element, open) {
|
|
56
|
+
if (!element) return false;
|
|
57
|
+
let fiber = codex.currentFiber(element);
|
|
58
|
+
for (let depth = 0; fiber && depth < 40; depth += 1, fiber = fiber.return) {
|
|
59
|
+
const props = fiber.memoizedProps;
|
|
60
|
+
if (typeof props?.open === "boolean" && props.open !== open && typeof props.onOpenChange === "function") {
|
|
61
|
+
props.onOpenChange(open);
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
function invokeNativeMenuItemSelect(item) {
|
|
68
|
+
if (!item) return false;
|
|
69
|
+
let fiber = codex.currentFiber(item);
|
|
70
|
+
for (let depth = 0; fiber && depth < 24; depth += 1, fiber = fiber.return) {
|
|
71
|
+
const onSelect = fiber.memoizedProps?.onSelect;
|
|
72
|
+
if (typeof onSelect !== "function") continue;
|
|
73
|
+
onSelect({ preventDefault() {}, defaultPrevented: false });
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
function extractFastIconPath(source, prefix) {
|
|
79
|
+
const match = String(source).match(new RegExp(`d:\`(${prefix}[^\`]*)\``));
|
|
80
|
+
return match?.[1] ?? null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function fastIcons(signal) {
|
|
84
|
+
const sourceLink=document.querySelector('link[href*="/assets/app-primary-"][href$=".js"]')
|
|
85
|
+
?? document.querySelector('link[href*="/assets/app-initial-"][href$=".js"]');
|
|
86
|
+
if (!sourceLink?.href) {codex.report("fast-icons",false,"bundle-unavailable");return null;}
|
|
87
|
+
const source=await (await fetch(sourceLink.href,{signal})).text();
|
|
88
|
+
const active=extractFastIconPath(source,"M11\\.9125 21\\.4125"),inactive=extractFastIconPath(source,"M9\\.80999 17\\.8302");
|
|
89
|
+
codex.report("fast-icons",Boolean(active && inactive),active && inactive ? null : "icon-resource-contract-changed");
|
|
90
|
+
return {active:{path:active,viewBox:"0 0 24 24"},inactive:{path:inactive,viewBox:"0 0 20 20"}};
|
|
91
|
+
}
|
|
92
|
+
return {fastIcons,trigger:nativeComposerTrigger,read:nativeComposerModelController,descriptor:nativeModelDescriptorForItem,setOpen:setReactControlledMenuOpen,selectItem:invokeNativeMenuItemSelect};
|
|
93
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// 模型身份、品牌、目录匹配与档位口径;不读取 DOM、存储或宿主状态。
|
|
2
|
+
export function createModelRules() {
|
|
3
|
+
function modelIdentity(rawValue) {
|
|
4
|
+
const raw = String(rawValue ?? "").trim();
|
|
5
|
+
const slash = raw.indexOf("/");
|
|
6
|
+
return {
|
|
7
|
+
raw,
|
|
8
|
+
provider: slash > 0 ? raw.slice(0, slash).trim() : "",
|
|
9
|
+
backend: slash > 0 ? raw.slice(slash + 1).trim() : raw,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function formatIdentifier(value) {
|
|
14
|
+
const raw = String(value ?? "").trim();
|
|
15
|
+
if (!raw) return "";
|
|
16
|
+
if (/\s/.test(raw) && !raw.includes("/") && !raw.includes("_") && !raw.includes("-")) return raw;
|
|
17
|
+
const canonical = new Map([
|
|
18
|
+
["ai", "AI"], ["api", "API"], ["chatglm", "ChatGLM"], ["claude", "Claude"],
|
|
19
|
+
["codex", "Codex"], ["deepseek", "DeepSeek"], ["gemini", "Gemini"], ["gemma", "Gemma"],
|
|
20
|
+
["glm", "GLM"], ["gpt", "GPT"], ["grok", "Grok"], ["kimi", "Kimi"],
|
|
21
|
+
["minimax", "MiniMax"], ["mimo", "MiMo"], ["moonshot", "Moonshot"], ["qwen", "Qwen"],
|
|
22
|
+
["qwq", "QwQ"], ["xai", "xAI"], ["xiaomi", "Xiaomi"], ["zhipu", "Zhipu"],
|
|
23
|
+
]);
|
|
24
|
+
return raw
|
|
25
|
+
.replace(/[_-]+/g, " ")
|
|
26
|
+
.split(/\s+/)
|
|
27
|
+
.filter(Boolean)
|
|
28
|
+
.map((token) => {
|
|
29
|
+
const lower = token.toLowerCase();
|
|
30
|
+
if (canonical.has(lower)) return canonical.get(lower);
|
|
31
|
+
if (/^v\d/i.test(token)) return `V${token.slice(1)}`;
|
|
32
|
+
if (/^o\d/i.test(token)) return token.toUpperCase();
|
|
33
|
+
return token.charAt(0).toUpperCase() + token.slice(1);
|
|
34
|
+
})
|
|
35
|
+
.join(" ");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function providerLabel(provider, selector) {
|
|
39
|
+
if (!provider) return "";
|
|
40
|
+
return selector.providerAliases?.[provider.toLowerCase()] ?? formatIdentifier(provider);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function compactModelLabel(rawValue) {
|
|
44
|
+
const raw = String(rawValue ?? "").trim();
|
|
45
|
+
return formatIdentifier(raw.slice(raw.lastIndexOf("/") + 1));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function brandForModel(identity, selector) {
|
|
49
|
+
const haystack = `${identity.backend} ${identity.raw}`.toLowerCase();
|
|
50
|
+
return selector.brands?.find((brand) => (
|
|
51
|
+
brand.keywords.some((keyword) => haystack.includes(keyword))
|
|
52
|
+
)) ?? null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function comparableModelLabel(value) {
|
|
56
|
+
return formatIdentifier(modelIdentity(value).backend)
|
|
57
|
+
.toLowerCase()
|
|
58
|
+
.replace(/^gpt\s+/, "")
|
|
59
|
+
.trim();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function modelWithOpenCodexEfforts(identity, selector, model) {
|
|
63
|
+
if (!model || !selector?.openCodexRealEfforts) return model;
|
|
64
|
+
const provenance = model.opencodex_capability_provenance;
|
|
65
|
+
const catalogModel = provenance?.provider && provenance?.model_id
|
|
66
|
+
? null
|
|
67
|
+
: catalogModelFor(identity, selector);
|
|
68
|
+
const source = catalogModel ?? model;
|
|
69
|
+
const sourceProvenance = source.opencodex_capability_provenance;
|
|
70
|
+
const key = sourceProvenance?.provider && sourceProvenance?.model_id
|
|
71
|
+
? `${sourceProvenance.provider}/${sourceProvenance.model_id}`.toLowerCase()
|
|
72
|
+
: identity.raw.toLowerCase();
|
|
73
|
+
const realEfforts = selector.openCodexRealEfforts[key];
|
|
74
|
+
if (!realEfforts) return model;
|
|
75
|
+
const allowed = new Set(realEfforts);
|
|
76
|
+
const levels = (model.supportedReasoningLevels ?? []).filter((level) => {
|
|
77
|
+
const effort = String(level?.effort ?? level ?? "").toLowerCase();
|
|
78
|
+
return allowed.has(effort);
|
|
79
|
+
});
|
|
80
|
+
return { ...model, supportedReasoningLevels: levels };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function catalogModelFor(identity, selector) {
|
|
84
|
+
const raw = identity.raw.toLowerCase();
|
|
85
|
+
const backend = identity.backend.toLowerCase();
|
|
86
|
+
const exact = selector.models?.find((model) => (
|
|
87
|
+
model.slug.toLowerCase() === raw
|
|
88
|
+
|| model.slug.toLowerCase() === backend
|
|
89
|
+
|| model.displayName.toLowerCase() === raw
|
|
90
|
+
)) ?? null;
|
|
91
|
+
if (exact) return exact;
|
|
92
|
+
const target = comparableModelLabel(identity.backend);
|
|
93
|
+
if (!target) return null;
|
|
94
|
+
const aliases = (selector.models ?? []).filter((model) => (
|
|
95
|
+
comparableModelLabel(model.slug) === target || comparableModelLabel(model.displayName) === target
|
|
96
|
+
));
|
|
97
|
+
return resolveModelAlias(aliases, identity);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function resolveModelAlias(aliases, identity) {
|
|
101
|
+
if (aliases.length === 1) return aliases[0];
|
|
102
|
+
if (aliases.length < 2 || identity.provider) return null;
|
|
103
|
+
const native = aliases.find((model) => !model.slug.includes("/"));
|
|
104
|
+
const thirdParty = aliases.find((model) => model.slug.includes("/"));
|
|
105
|
+
if (!native || !thirdParty) return null;
|
|
106
|
+
if (!modelIdentity(native.slug).backend.toLowerCase().startsWith("gpt")) return null;
|
|
107
|
+
const nativeLabel = formatIdentifier(
|
|
108
|
+
modelIdentity(String(native.displayName ?? native.slug)).backend,
|
|
109
|
+
).toLowerCase();
|
|
110
|
+
return formatIdentifier(identity.backend).toLowerCase() === nativeLabel ? native : thirdParty;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function nativeGptSortRank(rawValue) {
|
|
114
|
+
const known = new Map([
|
|
115
|
+
["gpt-6-astra", 1], ["gpt-5.6-sol", 2], ["gpt-5.6-terra", 3], ["gpt-5.6-luna", 4],
|
|
116
|
+
["gpt-5.5", 5], ["gpt-5.4", 6], ["gpt-5.4-mini", 7], ["gpt-5.3-codex-spark", 8],
|
|
117
|
+
]);
|
|
118
|
+
const raw = String(rawValue ?? "").trim();
|
|
119
|
+
const knownRank = known.get(raw);
|
|
120
|
+
if (knownRank != null) return { official: true, rank: knownRank };
|
|
121
|
+
const identity = modelIdentity(raw);
|
|
122
|
+
return identity.provider ? null : { official: true, rank: 100 };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function visibleControllerModels(controller) {
|
|
126
|
+
const models = controller.models.filter((model) => !model.hidden && String(model.model ?? "").trim());
|
|
127
|
+
const nativeGptIds = new Set(models
|
|
128
|
+
.filter((model) => !String(model.model).includes("/") && /^gpt-/i.test(model.model))
|
|
129
|
+
.map((model) => String(model.model).toLowerCase()));
|
|
130
|
+
// OCX 自动发现的同名路由若没有独立档位,会与原生 GPT 显示成两项。
|
|
131
|
+
return models.filter((model) => {
|
|
132
|
+
const slug = String(model.model);
|
|
133
|
+
const backend = modelIdentity(slug).backend.toLowerCase();
|
|
134
|
+
return slug === controller.model
|
|
135
|
+
|| !(
|
|
136
|
+
slug.includes("/")
|
|
137
|
+
&& nativeGptIds.has(backend)
|
|
138
|
+
&& !(model.supportedReasoningEfforts ?? []).length
|
|
139
|
+
);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
return { modelIdentity, formatIdentifier, providerLabel, compactModelLabel, brandForModel, comparableModelLabel, catalogModelFor, resolveModelAlias, modelWithOpenCodexEfforts, nativeGptSortRank, visibleControllerModels };
|
|
143
|
+
}
|