minecodex 1.0.7 → 1.0.9
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/model-slider/README.md +7 -4
- package/features/model-slider/codex-feature.json +0 -7
- package/features/notes/src/http-server.mjs +3 -0
- package/features/notes/web/app.js +9 -303
- package/features/notes/web/file-icons.mjs +68 -0
- package/features/notes/web/i18n.mjs +237 -0
- package/features/notes/web/todo-drag.mjs +18 -0
- package/package.json +1 -1
- package/packages/runtime-host/src/codex-cdp.mjs +153 -0
- package/packages/runtime-host/src/codex-design-contract.mjs +116 -0
- package/packages/runtime-host/src/codex-efforts.mjs +47 -0
- package/packages/runtime-host/src/codex-injection.mjs +4545 -0
- package/packages/runtime-host/src/codex-runtime.mjs +63 -4930
- package/packages/runtime-host/src/feature-registry.mjs +1 -13
- package/packages/runtime-host/src/host-actions.mjs +221 -0
- package/packages/runtime-host/src/main.mjs +7 -9
- package/packages/runtime-host/src/model-capabilities.mjs +0 -86
|
@@ -5,11 +5,13 @@ Codex Composer 原生模型选择器的轻量增强功能。它通过同级
|
|
|
5
5
|
|
|
6
6
|
功能范围:
|
|
7
7
|
|
|
8
|
-
-
|
|
8
|
+
- Codex 普通任务与 Work 沿用原生面板布局:上方思考强度、下方模型名,点击进入模型菜单;
|
|
9
9
|
- 为第三方模型显示厂商图标、格式化模型名和 Provider 副文本;
|
|
10
10
|
- 收藏常用 `provider/model`,保持原目录顺序;
|
|
11
11
|
- 较长的模型列表保持紧凑、可滚动;
|
|
12
|
-
-
|
|
12
|
+
- 所有模型共用 Slider,动态读取模型支持的思考档位;
|
|
13
|
+
- 沿用原生面板的间距、重置入口;仅实际支持 Fast 时显示闪电按钮;
|
|
14
|
+
- Chat/Temporary Chat 完全使用原生模型选择器,不注入增强面板或拦截模型菜单。
|
|
13
15
|
|
|
14
16
|
## 验证
|
|
15
17
|
|
|
@@ -20,10 +22,11 @@ npm test
|
|
|
20
22
|
真实 UI 仅在隔离 profile 中验证:
|
|
21
23
|
|
|
22
24
|
```bash
|
|
23
|
-
|
|
25
|
+
CODEX_FEATURES_ROOT="$PWD/features" \
|
|
26
|
+
MINECODEX_ENABLED_FEATURES=model-slider \
|
|
24
27
|
CODEX_RUNTIME_PROFILE_DIR="$HOME/.codex-model-slider/codex-profile" \
|
|
25
28
|
CODEX_RUNTIME_CDP_PORT=9233 \
|
|
26
|
-
|
|
29
|
+
node packages/runtime-host/src/main.mjs
|
|
27
30
|
```
|
|
28
31
|
|
|
29
32
|
这是非官方 CDP / DOM Adapter 的功能声明。Codex 私有 DOM 的适配实现统一留在
|
|
@@ -16,13 +16,6 @@
|
|
|
16
16
|
"modelSelector": {
|
|
17
17
|
"maxVisibleItems": 6,
|
|
18
18
|
"favoriteStorageKey": "codex-model-slider:favorites:v1",
|
|
19
|
-
"reasoningEffortOverrides": {
|
|
20
|
-
"command-code/Qwen-Qwen3.8-Flash": ["low", "medium", "xhigh"],
|
|
21
|
-
"command-code/xai-grok-4.6": ["low", "medium", "high", "xhigh"],
|
|
22
|
-
"command-code/xiaomi-mimo-v2.5": ["low", "medium", "high", "xhigh"],
|
|
23
|
-
"opencode-go/hy3": ["low", "medium", "high"],
|
|
24
|
-
"opencode-go/muse-spark-1.2-contributor": ["low", "medium", "high", "xhigh"]
|
|
25
|
-
},
|
|
26
19
|
"providerAliases": {
|
|
27
20
|
"opencode-go": "OpenCode Go",
|
|
28
21
|
"packyapi": "PackyAPI",
|
|
@@ -85,6 +85,9 @@ const STATIC_FILES = new Map([
|
|
|
85
85
|
["/", { file: "index.html", type: "text/html; charset=utf-8" }],
|
|
86
86
|
["/app.js", { file: "app.js", type: "text/javascript; charset=utf-8" }],
|
|
87
87
|
["/styles.css", { file: "styles.css", type: "text/css; charset=utf-8" }],
|
|
88
|
+
["/i18n.mjs", { file: "i18n.mjs", type: "text/javascript; charset=utf-8" }],
|
|
89
|
+
["/file-icons.mjs", { file: "file-icons.mjs", type: "text/javascript; charset=utf-8" }],
|
|
90
|
+
["/todo-drag.mjs", { file: "todo-drag.mjs", type: "text/javascript; charset=utf-8" }],
|
|
88
91
|
...LUCIDE_ICON_NAMES.map((name) => [
|
|
89
92
|
`/icons/${name}.svg`,
|
|
90
93
|
{
|
|
@@ -1,226 +1,11 @@
|
|
|
1
1
|
const params = new URL(document.baseURI).searchParams;
|
|
2
|
-
|
|
2
|
+
import { setLocale, getLocale, t, localizedErrorMessage } from "./i18n.mjs";
|
|
3
|
+
import { CODEX_COLOR_FILE_ICONS, codexFileIconKind } from "./file-icons.mjs";
|
|
4
|
+
import { todoDropPosition, reorderedTodoIds } from "./todo-drag.mjs";
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
if (candidate.toLowerCase().startsWith("zh")) return "zh-CN";
|
|
7
|
-
return SUPPORTED_LOCALES.has(candidate) ? candidate : "en";
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const MESSAGES = Object.freeze({
|
|
11
|
-
en: {
|
|
12
|
-
requestFailed: ({ status }) => `Request failed (${status})`,
|
|
13
|
-
temporarilyUnavailable: "CodexNotes is temporarily unavailable",
|
|
14
|
-
unavailable: "CodexNotes is unavailable",
|
|
15
|
-
retry: "Retry",
|
|
16
|
-
hostUnavailable: "Codex host is not connected",
|
|
17
|
-
hostTimeout: "Codex did not respond",
|
|
18
|
-
hostActionFailed: "Host action failed",
|
|
19
|
-
restoreTodo: "Restore Todo",
|
|
20
|
-
completeTodo: "Complete Todo",
|
|
21
|
-
edit: "Edit",
|
|
22
|
-
delete: "Delete",
|
|
23
|
-
editTodo: "Edit Todo",
|
|
24
|
-
newTodo: "New Todo",
|
|
25
|
-
todoText: "Todo text",
|
|
26
|
-
todoPlaceholder: "What do you want to remember?",
|
|
27
|
-
saveTodo: "Save Todo",
|
|
28
|
-
addTodo: "Add Todo",
|
|
29
|
-
cancel: "Cancel",
|
|
30
|
-
editPrompt: "Edit Prompt",
|
|
31
|
-
newPrompt: "New Prompt",
|
|
32
|
-
optionalTitle: "Optional title",
|
|
33
|
-
promptTitle: "Prompt title",
|
|
34
|
-
promptBody: "Prompt body",
|
|
35
|
-
promptPlaceholder: "Write a reusable prompt…",
|
|
36
|
-
savePrompt: "Save Prompt",
|
|
37
|
-
addPrompt: "Add Prompt",
|
|
38
|
-
insertPrompt: "Insert Prompt",
|
|
39
|
-
morePromptActions: "More prompt actions",
|
|
40
|
-
star: "Star",
|
|
41
|
-
unstar: "Unstar",
|
|
42
|
-
showInFolder: "Show in folder",
|
|
43
|
-
attachToChat: "Attach to chat",
|
|
44
|
-
viewAll: "View all",
|
|
45
|
-
todo: "Todo",
|
|
46
|
-
noTodo: "No Todo yet",
|
|
47
|
-
prompts: "Prompts",
|
|
48
|
-
noPrompts: "No prompts yet",
|
|
49
|
-
files: "Files",
|
|
50
|
-
pinFiles: "Pin files",
|
|
51
|
-
noPinnedFiles: "No pinned files",
|
|
52
|
-
active: "Active",
|
|
53
|
-
add: "Add",
|
|
54
|
-
nothingActive: "Nothing active",
|
|
55
|
-
completed: "Completed",
|
|
56
|
-
deleteAll: "Delete all",
|
|
57
|
-
deleteCompletedTitle: "Delete all completed Todo",
|
|
58
|
-
noCompletedTodo: "No completed Todo",
|
|
59
|
-
starred: "Starred",
|
|
60
|
-
noStarredPrompts: "No starred prompts",
|
|
61
|
-
pinnedFiles: "Pinned files",
|
|
62
|
-
loading: "Loading CodexNotes…",
|
|
63
|
-
insertedAtCaret: "Inserted at the Composer caret",
|
|
64
|
-
addedToComposer: "Added to Composer",
|
|
65
|
-
insertedFilePath: "Inserted file path",
|
|
66
|
-
filesPinned: ({ count }) => `${count} file${count === 1 ? "" : "s"} pinned`,
|
|
67
|
-
alreadyPinned: "Already pinned",
|
|
68
|
-
todoEmpty: "Todo text cannot be empty",
|
|
69
|
-
promptEmpty: "Prompt body cannot be empty",
|
|
70
|
-
editorItemMissing: "This item no longer exists.",
|
|
71
|
-
close: "Close",
|
|
72
|
-
invalidRequest: "The request is invalid",
|
|
73
|
-
invalidContentType: "The request must use JSON",
|
|
74
|
-
invalidJson: "The request body is not valid JSON",
|
|
75
|
-
bodyTooLarge: "The request is too large",
|
|
76
|
-
notFound: "The item was not found",
|
|
77
|
-
invalidOrder: "The Todo order is no longer current",
|
|
78
|
-
invalidUse: "The requested action could not be recorded",
|
|
79
|
-
fileUnavailable: "The file is unavailable",
|
|
80
|
-
fileMissing: "The source file no longer exists",
|
|
81
|
-
directoryNotAllowed: "Folders cannot be pinned; choose regular files",
|
|
82
|
-
pathNotAbsolute: "Only absolute paths can be pinned",
|
|
83
|
-
pickerUnavailable: "The system file picker is unavailable",
|
|
84
|
-
pickerFailed: "The system file picker could not be opened",
|
|
85
|
-
revealUnavailable: "Show in folder is unavailable on this platform",
|
|
86
|
-
revealFailed: "The file could not be shown in the system file manager",
|
|
87
|
-
previewUnavailable: "This file does not have an image preview",
|
|
88
|
-
previewTooLarge: "The image is too large to preview",
|
|
89
|
-
internalError: "CodexNotes could not complete the request",
|
|
90
|
-
actionNotAllowed: "This action is not available",
|
|
91
|
-
},
|
|
92
|
-
"zh-CN": {
|
|
93
|
-
requestFailed: ({ status }) => `请求失败(${status})`,
|
|
94
|
-
temporarilyUnavailable: "CodexNotes 暂时不可用",
|
|
95
|
-
unavailable: "CodexNotes 不可用",
|
|
96
|
-
retry: "重试",
|
|
97
|
-
hostUnavailable: "尚未连接 Codex 宿主",
|
|
98
|
-
hostTimeout: "Codex 未响应",
|
|
99
|
-
hostActionFailed: "宿主操作失败",
|
|
100
|
-
restoreTodo: "恢复待办",
|
|
101
|
-
completeTodo: "完成待办",
|
|
102
|
-
edit: "编辑",
|
|
103
|
-
delete: "删除",
|
|
104
|
-
editTodo: "编辑待办",
|
|
105
|
-
newTodo: "新建待办",
|
|
106
|
-
todoText: "待办内容",
|
|
107
|
-
todoPlaceholder: "你想记住什么?",
|
|
108
|
-
saveTodo: "保存待办",
|
|
109
|
-
addTodo: "添加待办",
|
|
110
|
-
cancel: "取消",
|
|
111
|
-
editPrompt: "编辑提示词",
|
|
112
|
-
newPrompt: "新建提示词",
|
|
113
|
-
optionalTitle: "可选标题",
|
|
114
|
-
promptTitle: "提示词标题",
|
|
115
|
-
promptBody: "提示词正文",
|
|
116
|
-
promptPlaceholder: "编写一条可复用的提示词…",
|
|
117
|
-
savePrompt: "保存提示词",
|
|
118
|
-
addPrompt: "添加提示词",
|
|
119
|
-
insertPrompt: "插入提示词",
|
|
120
|
-
morePromptActions: "更多提示词操作",
|
|
121
|
-
star: "收藏",
|
|
122
|
-
unstar: "取消收藏",
|
|
123
|
-
showInFolder: "在文件夹中显示",
|
|
124
|
-
attachToChat: "添加到对话",
|
|
125
|
-
viewAll: "查看全部",
|
|
126
|
-
todo: "待办",
|
|
127
|
-
noTodo: "暂无待办",
|
|
128
|
-
prompts: "提示词",
|
|
129
|
-
noPrompts: "暂无提示词",
|
|
130
|
-
files: "文件",
|
|
131
|
-
pinFiles: "固定文件",
|
|
132
|
-
noPinnedFiles: "暂无固定文件",
|
|
133
|
-
active: "进行中",
|
|
134
|
-
add: "添加",
|
|
135
|
-
nothingActive: "暂无进行中的待办",
|
|
136
|
-
completed: "已完成",
|
|
137
|
-
deleteAll: "全部删除",
|
|
138
|
-
deleteCompletedTitle: "删除全部已完成待办",
|
|
139
|
-
noCompletedTodo: "暂无已完成待办",
|
|
140
|
-
starred: "已收藏",
|
|
141
|
-
noStarredPrompts: "暂无收藏的提示词",
|
|
142
|
-
pinnedFiles: "已固定文件",
|
|
143
|
-
loading: "正在加载 CodexNotes…",
|
|
144
|
-
insertedAtCaret: "已插入到消息输入框光标处",
|
|
145
|
-
addedToComposer: "已添加到消息输入框",
|
|
146
|
-
insertedFilePath: "已插入文件路径",
|
|
147
|
-
filesPinned: ({ count }) => `已固定 ${count} 个文件`,
|
|
148
|
-
alreadyPinned: "文件已固定",
|
|
149
|
-
todoEmpty: "待办内容不能为空",
|
|
150
|
-
promptEmpty: "提示词正文不能为空",
|
|
151
|
-
editorItemMissing: "该项目已不存在。",
|
|
152
|
-
close: "关闭",
|
|
153
|
-
invalidRequest: "请求无效",
|
|
154
|
-
invalidContentType: "请求必须使用 JSON",
|
|
155
|
-
invalidJson: "请求正文不是有效的 JSON",
|
|
156
|
-
bodyTooLarge: "请求内容过大",
|
|
157
|
-
notFound: "找不到该项目",
|
|
158
|
-
invalidOrder: "待办顺序已发生变化",
|
|
159
|
-
invalidUse: "无法记录这次操作",
|
|
160
|
-
fileUnavailable: "文件不可用",
|
|
161
|
-
fileMissing: "源文件已不存在",
|
|
162
|
-
directoryNotAllowed: "不能固定文件夹,请选择普通文件",
|
|
163
|
-
pathNotAbsolute: "只能固定绝对路径",
|
|
164
|
-
pickerUnavailable: "系统文件选择器不可用",
|
|
165
|
-
pickerFailed: "无法打开系统文件选择器",
|
|
166
|
-
revealUnavailable: "当前平台不支持在文件夹中显示",
|
|
167
|
-
revealFailed: "无法在系统文件管理器中显示该文件",
|
|
168
|
-
previewUnavailable: "该文件没有图片预览",
|
|
169
|
-
previewTooLarge: "图片过大,无法预览",
|
|
170
|
-
internalError: "CodexNotes 无法完成请求",
|
|
171
|
-
actionNotAllowed: "当前操作不可用",
|
|
172
|
-
},
|
|
173
|
-
});
|
|
6
|
+
setLocale(params.get("locale") ?? navigator.languages?.[0] ?? navigator.language);
|
|
7
|
+
document.documentElement.lang = getLocale();
|
|
174
8
|
|
|
175
|
-
let locale = normalizeLocale(params.get("locale") ?? navigator.languages?.[0] ?? navigator.language);
|
|
176
|
-
document.documentElement.lang = locale;
|
|
177
|
-
|
|
178
|
-
function t(key, params = {}) {
|
|
179
|
-
const value = MESSAGES[locale]?.[key] ?? MESSAGES.en[key] ?? key;
|
|
180
|
-
return typeof value === "function" ? value(params) : value;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const ERROR_MESSAGE_KEYS = Object.freeze({
|
|
184
|
-
ACTION_NOT_ALLOWED: "actionNotAllowed",
|
|
185
|
-
BODY_TOO_LARGE: "bodyTooLarge",
|
|
186
|
-
DIRECTORY_NOT_ALLOWED: "directoryNotAllowed",
|
|
187
|
-
EMPTY_BODY: "invalidRequest",
|
|
188
|
-
FILE_MISSING: "fileMissing",
|
|
189
|
-
FILE_UNAVAILABLE: "fileUnavailable",
|
|
190
|
-
HOST_ACTION_FAILED: "hostActionFailed",
|
|
191
|
-
HOST_NOT_ALLOWED: "actionNotAllowed",
|
|
192
|
-
HOST_TIMEOUT: "hostTimeout",
|
|
193
|
-
HOST_UNAVAILABLE: "hostUnavailable",
|
|
194
|
-
INTERNAL_ERROR: "internalError",
|
|
195
|
-
INVALID_BODY: "invalidRequest",
|
|
196
|
-
INVALID_CHANGES: "invalidRequest",
|
|
197
|
-
INVALID_COMPLETED: "invalidRequest",
|
|
198
|
-
INVALID_CONTENT_TYPE: "invalidContentType",
|
|
199
|
-
INVALID_ID: "invalidRequest",
|
|
200
|
-
INVALID_JSON: "invalidJson",
|
|
201
|
-
INVALID_ORDER: "invalidOrder",
|
|
202
|
-
INVALID_PATHS: "invalidRequest",
|
|
203
|
-
INVALID_REQUEST: "invalidRequest",
|
|
204
|
-
INVALID_SURFACE: "invalidRequest",
|
|
205
|
-
INVALID_ORIGIN: "actionNotAllowed",
|
|
206
|
-
INVALID_STAR: "invalidRequest",
|
|
207
|
-
INVALID_TITLE: "invalidRequest",
|
|
208
|
-
INVALID_USE: "invalidUse",
|
|
209
|
-
NOT_FOUND: "notFound",
|
|
210
|
-
ORIGIN_NOT_ALLOWED: "actionNotAllowed",
|
|
211
|
-
PATH_NOT_ABSOLUTE: "pathNotAbsolute",
|
|
212
|
-
PICKER_FAILED: "pickerFailed",
|
|
213
|
-
PICKER_UNAVAILABLE: "pickerUnavailable",
|
|
214
|
-
PREVIEW_TOO_LARGE: "previewTooLarge",
|
|
215
|
-
PREVIEW_UNAVAILABLE: "previewUnavailable",
|
|
216
|
-
REVEAL_FAILED: "revealFailed",
|
|
217
|
-
REVEAL_UNAVAILABLE: "revealUnavailable",
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
function localizedErrorMessage(code, fallback, params = {}) {
|
|
221
|
-
const key = ERROR_MESSAGE_KEYS[code];
|
|
222
|
-
return key ? t(key, params) : fallback;
|
|
223
|
-
}
|
|
224
9
|
|
|
225
10
|
const requestedSurface = params.get("surface") === "notes" ? "prompts" : params.get("surface");
|
|
226
11
|
const surface = new Set(["summary", "todo", "prompts", "files", "editor"]).has(requestedSurface)
|
|
@@ -241,44 +26,6 @@ const DOMAIN_ICON_BY_SURFACE = Object.freeze({
|
|
|
241
26
|
prompts: "message-square-quote",
|
|
242
27
|
files: "paperclip",
|
|
243
28
|
});
|
|
244
|
-
const CODEX_FILE_ICON_BY_EXTENSION = new Map([
|
|
245
|
-
["ts", "typescript"],
|
|
246
|
-
["tsx", "react"], ["jsx", "react"],
|
|
247
|
-
["js", "javascript"], ["mjs", "javascript"], ["cjs", "javascript"], ["hs", "javascript"],
|
|
248
|
-
["py", "python"],
|
|
249
|
-
["java", "java"],
|
|
250
|
-
["rs", "rust"],
|
|
251
|
-
["php", "php"],
|
|
252
|
-
["css", "css"], ["scss", "css"], ["less", "css"], ["sass", "css"],
|
|
253
|
-
["cpp", "cplusplus"], ["cxx", "cplusplus"], ["cc", "cplusplus"], ["c", "cplusplus"],
|
|
254
|
-
["hpp", "cplusplus"], ["hh", "cplusplus"], ["h", "cplusplus"],
|
|
255
|
-
["rb", "code"], ["go", "code"], ["kt", "code"], ["swift", "code"], ["m", "code"],
|
|
256
|
-
["mm", "code"], ["cs", "code"], ["sql", "code"],
|
|
257
|
-
["json", "json"], ["jsonc", "json"],
|
|
258
|
-
["md", "document"], ["mdx", "document"], ["markdown", "document"],
|
|
259
|
-
["mkd", "document"], ["mdown", "document"],
|
|
260
|
-
["html", "html"], ["htm", "html"],
|
|
261
|
-
["yaml", "file"], ["yml", "file"],
|
|
262
|
-
["toml", "toml"],
|
|
263
|
-
["xml", "document"],
|
|
264
|
-
["csv", "spreadsheet"], ["tsv", "spreadsheet"], ["xls", "spreadsheet"],
|
|
265
|
-
["xlsm", "spreadsheet"], ["xlsx", "spreadsheet"],
|
|
266
|
-
["doc", "artifact-document"], ["docx", "artifact-document"],
|
|
267
|
-
["ipynb", "notebook"],
|
|
268
|
-
["ppt", "presentation"], ["pptx", "presentation"],
|
|
269
|
-
["sh", "shell"], ["bash", "shell"], ["zsh", "shell"], ["fish", "shell"], ["ps1", "shell"],
|
|
270
|
-
["dockerfile", "terminal"],
|
|
271
|
-
["env", "document"], ["dotenv", "document"], ["gitignore", "document"], ["lock", "document"],
|
|
272
|
-
["png", "image"], ["jpg", "image"], ["jpeg", "image"], ["gif", "image"],
|
|
273
|
-
["webp", "image"], ["bmp", "image"], ["svg", "image"], ["ico", "image"],
|
|
274
|
-
["build", "build"], ["bazel", "build"], ["bzl", "build"], ["ninja", "build"],
|
|
275
|
-
["gradle", "build"], ["mk", "build"], ["makefile", "build"],
|
|
276
|
-
["sha", "hashes"], ["sha1", "hashes"], ["sha256", "hashes"],
|
|
277
|
-
["md5", "hashes"], ["checksum", "hashes"], ["sum", "hashes"],
|
|
278
|
-
["pdf", "pdf"],
|
|
279
|
-
["zip", "folder"], ["gz", "folder"], ["tgz", "folder"], ["tar", "folder"],
|
|
280
|
-
]);
|
|
281
|
-
const CODEX_COLOR_FILE_ICONS = new Set(["artifact-document", "pdf", "presentation", "spreadsheet"]);
|
|
282
29
|
const ui = {
|
|
283
30
|
state: null,
|
|
284
31
|
todoById: new Map(),
|
|
@@ -312,14 +59,6 @@ const escapeHtml = (value) => String(value ?? "")
|
|
|
312
59
|
.replaceAll('"', """)
|
|
313
60
|
.replaceAll("'", "'");
|
|
314
61
|
|
|
315
|
-
function setLocale(value) {
|
|
316
|
-
const next = normalizeLocale(value);
|
|
317
|
-
if (next === locale) return;
|
|
318
|
-
locale = next;
|
|
319
|
-
document.documentElement.lang = locale;
|
|
320
|
-
if (ui.state) render();
|
|
321
|
-
}
|
|
322
|
-
|
|
323
62
|
function postToHost(message) {
|
|
324
63
|
window.parent.postMessage(message, ui.hostOrigin ?? "*");
|
|
325
64
|
}
|
|
@@ -489,7 +228,10 @@ window.addEventListener("message", (event) => {
|
|
|
489
228
|
if (!message || typeof message !== "object") return;
|
|
490
229
|
if (!ui.hostOrigin && event.origin && event.origin !== "null") ui.hostOrigin = event.origin;
|
|
491
230
|
if (message.type === "codex-personal:theme") {
|
|
492
|
-
if (message.locale
|
|
231
|
+
if (message.locale && setLocale(message.locale)) {
|
|
232
|
+
document.documentElement.lang = getLocale();
|
|
233
|
+
if (ui.state) render();
|
|
234
|
+
}
|
|
493
235
|
const tokens = message.tokens ?? message.theme?.tokens ?? {};
|
|
494
236
|
for (const [name, value] of Object.entries(tokens)) {
|
|
495
237
|
if (typeof value === "string" && (name.startsWith("--") || name.startsWith("codex-"))) {
|
|
@@ -539,29 +281,6 @@ function iconName(action) {
|
|
|
539
281
|
}[action] ?? null;
|
|
540
282
|
}
|
|
541
283
|
|
|
542
|
-
function codexFileIconKind(file) {
|
|
543
|
-
const rawPath = String(file.path || file.name || "").toLowerCase();
|
|
544
|
-
if (/[\\/]$/.test(rawPath)) return "folder";
|
|
545
|
-
const name = rawPath.split(/[\\/]/).at(-1) || "";
|
|
546
|
-
if (name === "skill.md") return "skill";
|
|
547
|
-
const dot = name.lastIndexOf(".");
|
|
548
|
-
const extension = dot > 0 && dot < name.length - 1
|
|
549
|
-
? name.slice(dot + 1)
|
|
550
|
-
: dot === 0 && name.length > 1
|
|
551
|
-
? name.slice(1)
|
|
552
|
-
: dot === -1
|
|
553
|
-
? name
|
|
554
|
-
: null;
|
|
555
|
-
const extensionKind = extension ? CODEX_FILE_ICON_BY_EXTENSION.get(extension) : null;
|
|
556
|
-
if (extensionKind) return extensionKind;
|
|
557
|
-
const mimeType = String(file.mimeType || "").toLowerCase();
|
|
558
|
-
if (mimeType.startsWith("image/")) return "image";
|
|
559
|
-
if (mimeType.startsWith("text/")) return "document";
|
|
560
|
-
if (mimeType.startsWith("application/pdf")) return "pdf";
|
|
561
|
-
if (mimeType.startsWith("application/zip") || mimeType.startsWith("application/gzip")) return "folder";
|
|
562
|
-
return "file";
|
|
563
|
-
}
|
|
564
|
-
|
|
565
284
|
function fileIconMarkup(kind) {
|
|
566
285
|
if (CODEX_COLOR_FILE_ICONS.has(kind)) {
|
|
567
286
|
return `<img class="file-kind is-color" src="/icons/file-${kind}.svg" alt="" aria-hidden="true">`;
|
|
@@ -1285,10 +1004,6 @@ function setTodoDropTarget(row, position) {
|
|
|
1285
1004
|
row.dataset.dropPosition = position;
|
|
1286
1005
|
}
|
|
1287
1006
|
|
|
1288
|
-
function todoDropPosition(event, row) {
|
|
1289
|
-
const rect = row.getBoundingClientRect();
|
|
1290
|
-
return event.clientY < rect.top + rect.height / 2 ? "before" : "after";
|
|
1291
|
-
}
|
|
1292
1007
|
|
|
1293
1008
|
function dragLeavesApp(event) {
|
|
1294
1009
|
if (event.relatedTarget instanceof Node && app.contains(event.relatedTarget)) return false;
|
|
@@ -1358,15 +1073,6 @@ function clearTodoDraggingState() {
|
|
|
1358
1073
|
ui.draggingTodo = null;
|
|
1359
1074
|
}
|
|
1360
1075
|
|
|
1361
|
-
function reorderedTodoIds(ids, draggedId, targetId, position) {
|
|
1362
|
-
if (!ids.includes(draggedId) || !ids.includes(targetId) || draggedId === targetId) return ids;
|
|
1363
|
-
const reordered = ids.filter((id) => id !== draggedId);
|
|
1364
|
-
const targetIndex = reordered.indexOf(targetId);
|
|
1365
|
-
const insertAt = targetIndex + (position === "after" ? 1 : 0);
|
|
1366
|
-
reordered.splice(insertAt, 0, draggedId);
|
|
1367
|
-
return reordered;
|
|
1368
|
-
}
|
|
1369
|
-
|
|
1370
1076
|
app.addEventListener("dragstart", (event) => {
|
|
1371
1077
|
const row = event.target.closest('.todo-row[draggable="true"]');
|
|
1372
1078
|
if (!row) return;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Codex 文件类型图标规则:文件扩展名 / MIME 到 Codex file-* 图标的映射。
|
|
2
|
+
// 这是文件图标的单一事实来源;app.js 只负责渲染。
|
|
3
|
+
|
|
4
|
+
const CODEX_FILE_ICON_BY_EXTENSION = new Map([
|
|
5
|
+
["ts", "typescript"],
|
|
6
|
+
["tsx", "react"], ["jsx", "react"],
|
|
7
|
+
["js", "javascript"], ["mjs", "javascript"], ["cjs", "javascript"], ["hs", "javascript"],
|
|
8
|
+
["py", "python"],
|
|
9
|
+
["java", "java"],
|
|
10
|
+
["rs", "rust"],
|
|
11
|
+
["php", "php"],
|
|
12
|
+
["css", "css"], ["scss", "css"], ["less", "css"], ["sass", "css"],
|
|
13
|
+
["cpp", "cplusplus"], ["cxx", "cplusplus"], ["cc", "cplusplus"], ["c", "cplusplus"],
|
|
14
|
+
["hpp", "cplusplus"], ["hh", "cplusplus"], ["h", "cplusplus"],
|
|
15
|
+
["rb", "code"], ["go", "code"], ["kt", "code"], ["swift", "code"], ["m", "code"],
|
|
16
|
+
["mm", "code"], ["cs", "code"], ["sql", "code"],
|
|
17
|
+
["json", "json"], ["jsonc", "json"],
|
|
18
|
+
["md", "document"], ["mdx", "document"], ["markdown", "document"],
|
|
19
|
+
["mkd", "document"], ["mdown", "document"],
|
|
20
|
+
["html", "html"], ["htm", "html"],
|
|
21
|
+
["yaml", "file"], ["yml", "file"],
|
|
22
|
+
["toml", "toml"],
|
|
23
|
+
["xml", "document"],
|
|
24
|
+
["csv", "spreadsheet"], ["tsv", "spreadsheet"], ["xls", "spreadsheet"],
|
|
25
|
+
["xlsm", "spreadsheet"], ["xlsx", "spreadsheet"],
|
|
26
|
+
["doc", "artifact-document"], ["docx", "artifact-document"],
|
|
27
|
+
["ipynb", "notebook"],
|
|
28
|
+
["ppt", "presentation"], ["pptx", "presentation"],
|
|
29
|
+
["sh", "shell"], ["bash", "shell"], ["zsh", "shell"], ["fish", "shell"], ["ps1", "shell"],
|
|
30
|
+
["dockerfile", "terminal"],
|
|
31
|
+
["env", "document"], ["dotenv", "document"], ["gitignore", "document"], ["lock", "document"],
|
|
32
|
+
["png", "image"], ["jpg", "image"], ["jpeg", "image"], ["gif", "image"],
|
|
33
|
+
["webp", "image"], ["bmp", "image"], ["svg", "image"], ["ico", "image"],
|
|
34
|
+
["build", "build"], ["bazel", "build"], ["bzl", "build"], ["ninja", "build"],
|
|
35
|
+
["gradle", "build"], ["mk", "build"], ["makefile", "build"],
|
|
36
|
+
["sha", "hashes"], ["sha1", "hashes"], ["sha256", "hashes"],
|
|
37
|
+
["md5", "hashes"], ["checksum", "hashes"], ["sum", "hashes"],
|
|
38
|
+
["pdf", "pdf"],
|
|
39
|
+
["zip", "folder"], ["gz", "folder"], ["tgz", "folder"], ["tar", "folder"],
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
const CODEX_COLOR_FILE_ICONS = new Set(["artifact-document", "pdf", "presentation", "spreadsheet"]);
|
|
43
|
+
|
|
44
|
+
export function codexFileIconKind(file) {
|
|
45
|
+
const rawPath = String(file.path || file.name || "").toLowerCase();
|
|
46
|
+
if (/[\\/]$/.test(rawPath)) return "folder";
|
|
47
|
+
const name = rawPath.split(/[\\/]/).at(-1) || "";
|
|
48
|
+
if (name === "skill.md") return "skill";
|
|
49
|
+
const dot = name.lastIndexOf(".");
|
|
50
|
+
const extension = dot > 0 && dot < name.length - 1
|
|
51
|
+
? name.slice(dot + 1)
|
|
52
|
+
: dot === 0 && name.length > 1
|
|
53
|
+
? name.slice(1)
|
|
54
|
+
: dot === -1
|
|
55
|
+
? name
|
|
56
|
+
: null;
|
|
57
|
+
const extensionKind = extension ? CODEX_FILE_ICON_BY_EXTENSION.get(extension) : null;
|
|
58
|
+
if (extensionKind) return extensionKind;
|
|
59
|
+
const mimeType = String(file.mimeType || "").toLowerCase();
|
|
60
|
+
if (mimeType.startsWith("image/")) return "image";
|
|
61
|
+
if (mimeType.startsWith("text/")) return "document";
|
|
62
|
+
if (mimeType.startsWith("application/pdf")) return "pdf";
|
|
63
|
+
if (mimeType.startsWith("application/zip") || mimeType.startsWith("application/gzip")) return "folder";
|
|
64
|
+
return "file";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
export { CODEX_COLOR_FILE_ICONS };
|