codex-plus-patcher 0.5.0 → 0.7.0

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.
Files changed (48) hide show
  1. package/README.md +12 -1
  2. package/package.json +5 -2
  3. package/src/cli.js +97 -0
  4. package/src/patches/26.616.41845-4198.js +2 -0
  5. package/src/patches/26.616.51431-4212.js +2 -0
  6. package/src/patches/26.616.71553-4265.js +6 -0
  7. package/src/patches/26.616.81150-4306.js +7 -0
  8. package/src/patches/lib/common-patches.js +175 -58
  9. package/src/patches/lib/hooks/about.js +7 -0
  10. package/src/patches/lib/hooks/diagnostics.js +7 -0
  11. package/src/patches/lib/hooks/mermaid.js +7 -0
  12. package/src/patches/lib/hooks/message-composer.js +7 -0
  13. package/src/patches/lib/hooks/native-main.js +7 -0
  14. package/src/patches/lib/hooks/project-selector.js +12 -0
  15. package/src/patches/lib/hooks/review.js +7 -0
  16. package/src/patches/lib/hooks/settings-commands.js +12 -0
  17. package/src/patches/lib/hooks/sidebar.js +12 -0
  18. package/src/patches/lib/hooks/thread-header.js +7 -0
  19. package/src/patches/lib/hooks/worker.js +7 -0
  20. package/src/patches/lib/project-selector-shortcut-patch.js +55 -0
  21. package/src/runtime/api/about.js +16 -0
  22. package/src/runtime/api/commands.js +85 -0
  23. package/src/runtime/api/composer.js +14 -0
  24. package/src/runtime/api/diagnostics.js +38 -0
  25. package/src/runtime/api/errors.js +20 -0
  26. package/src/runtime/api/index.js +79 -0
  27. package/src/runtime/api/mermaid.js +14 -0
  28. package/src/runtime/api/message.js +14 -0
  29. package/src/runtime/api/modules.js +57 -0
  30. package/src/runtime/api/native.js +14 -0
  31. package/src/runtime/api/patches.js +31 -0
  32. package/src/runtime/api/review.js +20 -0
  33. package/src/runtime/api/settings.js +76 -0
  34. package/src/runtime/api/sidebar.js +24 -0
  35. package/src/runtime/api/styles.js +28 -0
  36. package/src/runtime/api/threadHeader.js +41 -0
  37. package/src/runtime/assets.js +58 -13
  38. package/src/runtime/host/messageComposer.js +16 -0
  39. package/src/runtime/host/nativeMain.js +159 -0
  40. package/src/runtime/host/projectSelector.js +58 -0
  41. package/src/runtime/host/review.js +62 -0
  42. package/src/runtime/host/sidebar.js +21 -0
  43. package/src/runtime/host/threadHeader.js +9 -0
  44. package/src/runtime/{worker.js → host/worker.js} +7 -0
  45. package/src/runtime/plugins/devTools.js +33 -0
  46. package/src/runtime/plugins/projectPathHeader.js +116 -0
  47. package/src/runtime/plugins/projectSelectorShortcut.js +207 -0
  48. package/src/runtime/runtime.js +22 -355
@@ -0,0 +1,207 @@
1
+ (function () {
2
+ const CodexPlus = window.CodexPlus;
3
+ const triggerSelector = "[data-codex-plus-project-selector-trigger]";
4
+ let keydownHandler = null;
5
+
6
+ function normalizeForFzf(value) {
7
+ const source = String(value ?? "");
8
+ const map = [];
9
+ let text = "";
10
+ let inSeparator = false;
11
+
12
+ for (let index = 0; index < source.length; index += 1) {
13
+ const char = source[index];
14
+ if (/[\s-]/.test(char)) {
15
+ if (!inSeparator) {
16
+ text += " ";
17
+ map.push(index);
18
+ inSeparator = true;
19
+ }
20
+ continue;
21
+ }
22
+ text += char;
23
+ map.push(index);
24
+ inSeparator = false;
25
+ }
26
+
27
+ return { map, text };
28
+ }
29
+
30
+ function projectSearchText(project) {
31
+ return [
32
+ project?.label,
33
+ project?.repositoryData?.rootFolder,
34
+ project?.path,
35
+ project?.hostDisplayName,
36
+ ].map((value) => normalizeForFzf(value).text.trim()).filter(Boolean).join(" ");
37
+ }
38
+
39
+ function fzfConstructor() {
40
+ return window.fzf?.Fzf;
41
+ }
42
+
43
+ function fallbackFilter(items, query) {
44
+ const needle = normalizeForFzf(query).text.trim().toLowerCase();
45
+ if (!needle) return items;
46
+ return items.filter((item) => projectSearchText(item).toLowerCase().includes(needle));
47
+ }
48
+
49
+ function fuzzyFilter(items, query) {
50
+ const list = Array.isArray(items) ? items : [];
51
+ const normalizedQuery = normalizeForFzf(query).text.trim();
52
+ if (!normalizedQuery) return list;
53
+
54
+ const Fzf = fzfConstructor();
55
+ if (typeof Fzf !== "function") return fallbackFilter(list, query);
56
+
57
+ return new Fzf(
58
+ list.map((project) => ({ project, searchText: projectSearchText(project) })),
59
+ { selector: (entry) => entry.searchText },
60
+ ).find(normalizedQuery).map((entry) => entry.item.project);
61
+ }
62
+
63
+ function labelPositions(text, query) {
64
+ const Fzf = fzfConstructor();
65
+ if (typeof Fzf !== "function") return null;
66
+
67
+ const normalizedText = normalizeForFzf(text);
68
+ const normalizedQuery = normalizeForFzf(query).text.trim();
69
+ if (!normalizedText.text || !normalizedQuery) return null;
70
+
71
+ const [entry] = new Fzf([normalizedText.text]).find(normalizedQuery);
72
+ if (!entry || typeof entry.positions?.forEach !== "function") return null;
73
+
74
+ const positions = [];
75
+ entry.positions.forEach((index) => positions.push(index));
76
+ return positions.map((index) => normalizedText.map[index]).filter((index) => Number.isInteger(index));
77
+ }
78
+
79
+ function fuzzyHighlight({ text, query, jsx }) {
80
+ if (typeof jsx !== "function") return text;
81
+
82
+ const positions = labelPositions(text, query);
83
+ if (positions == null || positions.length === 0) return text;
84
+
85
+ const matchedIndices = new Set(positions);
86
+ const parts = [];
87
+ let index = 0;
88
+ let key = 0;
89
+ const style = {
90
+ color: "var(--color-token-text-link-foreground, #2563eb)",
91
+ };
92
+
93
+ while (index < text.length) {
94
+ const isMatched = matchedIndices.has(index);
95
+ const start = index;
96
+ while (index < text.length && matchedIndices.has(index) === isMatched) index += 1;
97
+
98
+ const value = text.slice(start, index);
99
+ parts.push(
100
+ isMatched
101
+ ? jsx("strong", {
102
+ className: "font-semibold",
103
+ style,
104
+ children: value,
105
+ }, key++)
106
+ : value,
107
+ );
108
+ }
109
+
110
+ return parts;
111
+ }
112
+
113
+ function dispatchMouseEvent(target, type) {
114
+ if (typeof target.dispatchEvent !== "function") return false;
115
+ const EventConstructor = type === "pointerdown"
116
+ ? window.PointerEvent || window.MouseEvent
117
+ : window.MouseEvent;
118
+ if (typeof EventConstructor !== "function") return false;
119
+ target.dispatchEvent(new EventConstructor(type, {
120
+ bubbles: true,
121
+ button: 0,
122
+ buttons: type === "pointerdown" || type === "mousedown" ? 1 : 0,
123
+ cancelable: true,
124
+ ctrlKey: false,
125
+ view: window,
126
+ }));
127
+ return true;
128
+ }
129
+
130
+ function visibleTriggerCandidates() {
131
+ return Array.from(document.querySelectorAll(triggerSelector)).filter((trigger) => {
132
+ if (!(trigger instanceof HTMLElement)) return false;
133
+ if (trigger.disabled || trigger.getAttribute("aria-disabled") === "true") return false;
134
+ const rect = trigger.getBoundingClientRect?.();
135
+ return rect && rect.width > 0 && rect.height > 0;
136
+ });
137
+ }
138
+
139
+ function triggerPriority(trigger) {
140
+ const variant = trigger.getAttribute("data-codex-plus-project-selector-variant");
141
+ if (variant === "default") return 0;
142
+ if (variant == null || variant === "") return 1;
143
+ return 2;
144
+ }
145
+
146
+ function projectSelectorTrigger() {
147
+ const [trigger] = visibleTriggerCandidates().sort((left, right) => {
148
+ const priority = triggerPriority(left) - triggerPriority(right);
149
+ if (priority !== 0) return priority;
150
+ const leftRect = left.getBoundingClientRect();
151
+ const rightRect = right.getBoundingClientRect();
152
+ return rightRect.top - leftRect.top || rightRect.left - leftRect.left;
153
+ });
154
+ return trigger ?? null;
155
+ }
156
+
157
+ function focusProjectSelector() {
158
+ const trigger = projectSelectorTrigger();
159
+ if (trigger == null) return false;
160
+ trigger.focus?.();
161
+ const dispatched = [
162
+ dispatchMouseEvent(trigger, "pointerdown"),
163
+ dispatchMouseEvent(trigger, "mousedown"),
164
+ dispatchMouseEvent(trigger, "mouseup"),
165
+ dispatchMouseEvent(trigger, "click"),
166
+ ].some(Boolean);
167
+ if (!dispatched) trigger.click?.();
168
+ return true;
169
+ }
170
+
171
+ CodexPlus.registerPlugin(
172
+ CodexPlus.definePlugin({
173
+ id: "projectSelectorShortcut",
174
+ name: "Project Selector Shortcut",
175
+ description: "Registers the Focus project selector command.",
176
+ required: true,
177
+ commands: [
178
+ {
179
+ id: "codexPlus.focusProjectSelector",
180
+ title: "Focus project selector",
181
+ description: "Focus or open the new chat project selector",
182
+ menu: { groups: ["suggested", "workspace"] },
183
+ palette: { enabled: true, keywords: ["project", "selector", "new chat"] },
184
+ shortcut: { defaultKeybindings: [{ key: "CmdOrCtrl+." }] },
185
+ run: focusProjectSelector,
186
+ },
187
+ ],
188
+ start(api) {
189
+ api.ui.projectSelector = {
190
+ fuzzyFilter,
191
+ fuzzyHighlight,
192
+ };
193
+ keydownHandler = (event) => {
194
+ if (event.defaultPrevented || event.key !== "." || (!event.metaKey && !event.ctrlKey) || event.altKey || event.shiftKey) {
195
+ return;
196
+ }
197
+ if (api.commands.run("codexPlus.focusProjectSelector")) event.preventDefault();
198
+ };
199
+ document.addEventListener("keydown", keydownHandler, true);
200
+ },
201
+ stop() {
202
+ if (keydownHandler) document.removeEventListener("keydown", keydownHandler, true);
203
+ keydownHandler = null;
204
+ },
205
+ }),
206
+ );
207
+ })();
@@ -1,369 +1,36 @@
1
1
  (function () {
2
2
  const globalObject = typeof window !== "undefined" ? window : globalThis;
3
- const plugins = new Map();
4
- const startedPlugins = new Set();
5
- const hostModules = new Map();
6
- const waiters = [];
7
- const patchDescriptors = [];
8
- const commands = new Map();
9
- const styleElements = new Map();
10
- const settingsListeners = new Map();
11
- const storagePrefix = "codex-plus:plugin:";
3
+ if (typeof document === "undefined") return;
12
4
 
13
- function safeId(id) {
14
- if (typeof id !== "string" || id.trim() === "") throw new Error("Codex Plus plugin ids must be non-empty strings");
15
- return id.trim();
16
- }
17
-
18
- function definePlugin(definition) {
19
- const id = safeId(definition.id || definition.name);
20
- return { ...definition, id };
21
- }
22
-
23
- function notifyWaiters(name, value) {
24
- for (let index = waiters.length - 1; index >= 0; index -= 1) {
25
- const waiter = waiters[index];
26
- if (!waiter.filter(value, name)) continue;
27
- waiters.splice(index, 1);
28
- waiter.resolve(value);
29
- }
30
- }
31
-
32
- function registerHostModule(name, value) {
33
- hostModules.set(name, value);
34
- notifyWaiters(name, value);
35
- return value;
36
- }
37
-
38
- function moduleValues() {
39
- return Array.from(hostModules.values());
40
- }
5
+ const base = new URL(".", document.currentScript?.src || globalObject.location?.href || "");
6
+ const loadWithDocumentWrite = document.readyState === "loading" && typeof document.write === "function";
41
7
 
42
- function findByProps(...props) {
43
- return moduleValues().find((value) => value && props.every((prop) => prop in value));
8
+ function scriptUrl(file) {
9
+ return new URL(file, base).href;
44
10
  }
45
11
 
46
- function findByCode(text) {
47
- return moduleValues().find((value) => {
48
- try {
49
- return String(value).includes(text);
50
- } catch {
51
- return false;
52
- }
53
- });
54
- }
55
-
56
- function findComponentByCode(text) {
57
- return moduleValues().find((value) => {
58
- try {
59
- return typeof value === "function" && String(value).includes(text);
60
- } catch {
61
- return false;
62
- }
63
- });
64
- }
65
-
66
- function waitFor(filter) {
67
- for (const [name, value] of hostModules) {
68
- if (filter(value, name)) return Promise.resolve(value);
69
- }
70
- return new Promise((resolve) => waiters.push({ filter, resolve }));
71
- }
72
-
73
- function getPluginStore(pluginId) {
74
- const key = `${storagePrefix}${pluginId}`;
75
- try {
76
- return JSON.parse(globalObject.localStorage?.getItem(key) || "{}") || {};
77
- } catch {
78
- return {};
12
+ function loadScripts(files) {
13
+ const diagnose = globalObject.CodexPlus?.diagnostics?.log;
14
+ if (loadWithDocumentWrite) {
15
+ diagnose?.("runtime.load", { mode: "document.write", count: files.length });
16
+ for (const file of files) document.write(`<script src="${scriptUrl(file).replace(/"/g, "&quot;")}"><\/script>`);
17
+ return;
79
18
  }
80
- }
81
-
82
- function writePluginStore(pluginId, store) {
83
- const key = `${storagePrefix}${pluginId}`;
84
- globalObject.localStorage?.setItem(key, JSON.stringify(store));
85
- }
86
-
87
- function emitSetting(pluginId, key, value) {
88
- const listenerKey = `${pluginId}:${key}`;
89
- for (const listener of settingsListeners.get(listenerKey) || []) listener(value);
90
- }
91
-
92
- function defineSettings(pluginId, definitions) {
93
- const id = safeId(pluginId);
94
- const store = getPluginStore(id);
95
- for (const [key, definition] of Object.entries(definitions || {})) {
96
- if (!(key in store) && "default" in definition) store[key] = definition.default;
97
- }
98
- writePluginStore(id, store);
99
- return {
100
- definitions,
101
- get(key) {
102
- return getPluginStore(id)[key];
103
- },
104
- set(key, value) {
105
- const next = getPluginStore(id);
106
- next[key] = value;
107
- writePluginStore(id, next);
108
- emitSetting(id, key, value);
109
- },
110
- use(key, listener) {
111
- const listenerKey = `${id}:${key}`;
112
- const listeners = settingsListeners.get(listenerKey) || new Set();
113
- listeners.add(listener);
114
- settingsListeners.set(listenerKey, listeners);
115
- listener(getPluginStore(id)[key]);
116
- return () => listeners.delete(listener);
117
- },
118
- };
119
- }
120
-
121
- function registerPatch(descriptor) {
122
- patchDescriptors.push(descriptor);
123
- return descriptor;
124
- }
125
-
126
- function applyPatchDescriptors(source, descriptors = patchDescriptors) {
127
- let output = source;
128
- for (const descriptor of descriptors) {
129
- const moduleMatches =
130
- typeof descriptor.find === "string" ? output.includes(descriptor.find) : descriptor.find.test(output);
131
- if (!moduleMatches) continue;
132
- const replacements = Array.isArray(descriptor.replacement) ? descriptor.replacement : [descriptor.replacement];
133
- const beforeGroup = output;
134
- let appliedGroup = true;
135
- for (const replacement of replacements) {
136
- const before = output;
137
- output = output.replace(replacement.match, replacement.replace);
138
- if (before === output) appliedGroup = false;
139
- }
140
- if (descriptor.group && !appliedGroup) output = beforeGroup;
141
- if (!descriptor.all) break;
142
- }
143
- return output;
144
- }
145
-
146
- function registerCommand(command) {
147
- commands.set(safeId(command.id), command);
148
- return command;
149
- }
150
-
151
- function runCommand(id, ...args) {
152
- const command = commands.get(id);
153
- if (!command) throw new Error(`Unknown Codex Plus command: ${id}`);
154
- return command.run?.(...args);
155
- }
156
-
157
- function commandGroups(command) {
158
- return command.menu?.groups || [];
159
- }
160
-
161
- function commandKeybindings(command) {
162
- return command.shortcut?.defaultKeybindings || [];
163
- }
164
-
165
- function commandMetadata() {
166
- return Array.from(commands.values())
167
- .filter((command) => command.palette?.enabled !== false)
168
- .map((command) => {
169
- const groups = commandGroups(command);
170
- return {
171
- id: command.id,
172
- title: command.title,
173
- description: command.description,
174
- menuGroups: groups,
175
- defaultKeybindings: commandKeybindings(command),
176
- commandMenuGroupKey: groups.includes("panels") ? "panels" : groups[0],
177
- commandMenu: true,
178
- electron: {
179
- menuTitle: command.title,
180
- defaultKeybindings: commandKeybindings(command),
181
- },
182
- };
183
- });
184
- }
185
-
186
- function CommandMenuItemHost({ command, deps, group, close }) {
187
- const jsx = deps?.jsx;
188
- const MenuItem = deps?.MenuItem;
189
- if (typeof jsx !== "function" || MenuItem == null) return null;
190
- const render = (closeMenu) =>
191
- jsx(
192
- MenuItem,
193
- {
194
- value: command.title,
195
- title: command.title,
196
- description: command.description,
197
- onSelect() {
198
- runCommand(command.id);
199
- closeMenu?.();
200
- close?.();
201
- },
202
- },
203
- command.id,
204
- );
205
- deps?.register?.(command.id, () => runCommand(command.id), {
206
- menuItem: { id: command.id, groupKey: group, render },
207
- });
208
- return null;
209
- }
210
-
211
- function renderCommandMenuItems({ group, deps, close } = {}) {
212
- const jsx = deps?.jsx;
213
- if (typeof jsx !== "function") return [];
214
- return Array.from(commands.values())
215
- .filter((command) => commandGroups(command).includes(group))
216
- .map((command) => jsx(CommandMenuItemHost, { command, deps, group, close }, command.id));
217
- }
218
-
219
- function mergeDataAttributes(base, extra) {
220
- if (extra == null) return base;
221
- if (base == null) return extra;
222
- return { ...base, ...extra, style: { ...base.style, ...extra.style } };
223
- }
224
-
225
- function applyDecorators(props, decorators) {
226
- let result;
227
- for (const decorator of decorators) result = mergeDataAttributes(result, decorator(props));
228
- return result;
229
- }
230
-
231
- function AppearanceRowHost({ row, deps, variant }) {
232
- return row.render?.({ ...deps, variant, row }) ?? null;
233
- }
234
-
235
- function renderAppearanceRows({ deps, variant, section = "appearance" } = {}) {
236
- const jsx = deps?.jsx;
237
- if (typeof jsx !== "function") return [];
238
- return CodexPlus.ui.settings.appearance.rows
239
- .filter((row) => (row.section || "appearance") === section)
240
- .slice()
241
- .sort((left, right) => (left.order || 0) - (right.order || 0))
242
- .map((row) => jsx(AppearanceRowHost, { row, deps, variant }, row.id));
243
- }
244
-
245
- function renderReviewBody({ props, deps, defaultBody } = {}) {
246
- let body = defaultBody;
247
- for (const wrapper of CodexPlus.ui.review.wrappers) {
248
- body = wrapper({ ...props, mainReviewContent: body }, deps);
249
- }
250
- return body;
251
- }
252
-
253
- function renderErrorDetails({ jsx, error, componentStack } = {}) {
254
- for (const decorator of CodexPlus.ui.errors.boundaryDecorators) {
255
- const detail = decorator({ jsx, error, componentStack });
256
- if (detail != null) return detail;
257
- }
258
- return null;
259
- }
260
-
261
- function registerStyle(pluginId, cssText) {
262
- if (typeof document === "undefined") return null;
263
- const id = `codex-plus-style-${safeId(pluginId)}`;
264
- let element = styleElements.get(id) || document.getElementById(id);
265
- if (!element) {
266
- element = document.createElement("style");
267
- element.id = id;
268
- document.head?.appendChild(element);
269
- }
270
- element.textContent = cssText;
271
- styleElements.set(id, element);
272
- return element;
273
- }
274
-
275
- function setRootVars(vars) {
276
- if (typeof document === "undefined") return;
277
- for (const [key, value] of Object.entries(vars || {})) {
278
- if (value == null) document.documentElement.style.removeProperty(key);
279
- else document.documentElement.style.setProperty(key, value);
280
- }
281
- }
282
-
283
- function registerPlugin(definition) {
284
- const plugin = definePlugin(definition);
285
- plugins.set(plugin.id, plugin);
286
- if (plugin.settings) plugin.settingsStore = defineSettings(plugin.id, plugin.settings);
287
- for (const descriptor of plugin.patches || []) registerPatch({ ...descriptor, plugin: plugin.id });
288
- for (const command of plugin.commands || []) registerCommand({ ...command, plugin: plugin.id });
289
- if (plugin.styles) registerStyle(plugin.id, plugin.styles);
290
- if (plugin.required || plugin.enabledByDefault) startPlugin(plugin.id);
291
- return plugin;
292
- }
293
-
294
- function startPlugin(id) {
295
- const plugin = plugins.get(id);
296
- if (!plugin || startedPlugins.has(id)) return;
297
- plugin.start?.(CodexPlus);
298
- startedPlugins.add(id);
299
- }
300
-
301
- function stopPlugin(id) {
302
- const plugin = plugins.get(id);
303
- if (!plugin || !startedPlugins.has(id)) return;
304
- plugin.stop?.(CodexPlus);
305
- startedPlugins.delete(id);
306
- }
307
-
308
- const CodexPlus = {
309
- definePlugin,
310
- registerPlugin,
311
- startPlugin,
312
- stopPlugin,
313
- plugins,
314
- patches: { register: registerPatch, apply: applyPatchDescriptors, all: patchDescriptors },
315
- modules: { registerHostModule, findByCode, findByProps, findComponentByCode, waitFor },
316
- ui: {
317
- commands: { renderMenuItems: renderCommandMenuItems, commandMetadata },
318
- settings: { appearance: { rows: [], addRow(row) { this.rows.push(row); return row; }, renderRows: renderAppearanceRows } },
319
- review: { wrappers: [], wrapBody(wrapper) { this.wrappers.push(wrapper); return wrapper; }, renderBody: renderReviewBody },
320
- sidebar: {
321
- projectDecorators: [],
322
- threadDecorators: [],
323
- decorateProjectRow(fn) { this.projectDecorators.push(fn); return fn; },
324
- decorateThreadRow(fn) { this.threadDecorators.push(fn); return fn; },
325
- mergeDataAttributes,
326
- projectRowProps(props) { return applyDecorators(props, this.projectDecorators); },
327
- threadRowProps(props) { return applyDecorators(props, this.threadDecorators); },
328
- },
329
- message: { userBubbleDecorators: [], decorateUserBubble(fn) { this.userBubbleDecorators.push(fn); return fn; }, userBubbleProps(props) { return applyDecorators(props, this.userBubbleDecorators); } },
330
- composer: { surfaceDecorators: [], decorateSurface(fn) { this.surfaceDecorators.push(fn); return fn; }, surfaceProps(props) { return applyDecorators(props, this.surfaceDecorators); } },
331
- about: { buildInfo: [], addBuildInfo(fn) { this.buildInfo.push(fn); return fn; } },
332
- errors: { boundaryDecorators: [], decorateBoundary(fn) { this.boundaryDecorators.push(fn); return fn; }, renderDetails: renderErrorDetails },
333
- mermaid: {
334
- diagramDecorators: [],
335
- decorateDiagram(fn) { this.diagramDecorators.push(fn); return fn; },
336
- diagramProps(props) { return applyDecorators(props, this.diagramDecorators); },
337
- },
338
- },
339
- commands: { register: registerCommand, run: runCommand, all: () => Array.from(commands.values()), menuItems: (group) => Array.from(commands.values()).filter((command) => commandGroups(command).includes(group)) },
340
- settings: { define: defineSettings },
341
- native: { async request(method, params) { return globalObject.codexPlusNative?.request?.(method, params) ?? globalObject.CodexPlusHost?.nativeRequest?.(method, params); } },
342
- styles: { register: registerStyle, setRootVars },
343
- };
344
-
345
- globalObject.CodexPlus = CodexPlus;
346
- globalObject.CodexPlusHost ||= {};
347
- globalObject.CodexPlusHost.register = registerHostModule;
348
-
349
- const pluginFiles = [
350
- "plugins/aboutMetadata.js",
351
- "plugins/nestedRepositories.js",
352
- "plugins/diagnosticErrors.js",
353
- "plugins/userBubbleColors.js",
354
- "plugins/projectColors.js",
355
- "plugins/sidebarNameBlur.js",
356
- "plugins/mermaidFullscreen.js",
357
- ];
358
-
359
- if (typeof document !== "undefined") {
360
- const base = new URL(".", document.currentScript?.src || globalObject.location?.href || "");
361
- for (const file of pluginFiles) {
19
+ diagnose?.("runtime.load", { mode: "appendChild", count: files.length });
20
+ for (const file of files) {
362
21
  const script = document.createElement("script");
363
- script.src = new URL(file, base).href;
22
+ script.src = scriptUrl(file);
364
23
  script.async = false;
365
24
  script.defer = false;
366
25
  document.head?.appendChild(script);
367
26
  }
368
27
  }
28
+
29
+ globalObject.__CodexPlusLoadRuntimeFiles = loadScripts;
30
+
31
+ if (loadWithDocumentWrite) {
32
+ document.write(`<script src="${scriptUrl("runtime-manifest.js").replace(/"/g, "&quot;")}"><\/script>`);
33
+ } else {
34
+ loadScripts(["runtime-manifest.js"]);
35
+ }
369
36
  })();