codex-plus-patcher 0.6.0 → 0.7.1
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/README.md +3 -1
- package/package.json +5 -3
- package/src/cli.js +88 -0
- package/src/core/dev-mode.js +274 -0
- package/src/core/plugin-audit.js +1267 -0
- package/src/patches/26.623.41415-4505.js +44 -0
- package/src/patches/index.js +8 -1
- package/src/patches/lib/common-patches.js +411 -248
- package/src/patches/lib/hooks/about.js +7 -0
- package/src/patches/lib/hooks/diagnostics.js +7 -0
- package/src/patches/lib/hooks/mermaid.js +7 -0
- package/src/patches/lib/hooks/message-composer.js +7 -0
- package/src/patches/lib/hooks/native-main.js +7 -0
- package/src/patches/lib/hooks/project-selector.js +12 -0
- package/src/patches/lib/hooks/review.js +9 -0
- package/src/patches/lib/hooks/settings-commands.js +13 -0
- package/src/patches/lib/hooks/sidebar.js +7 -0
- package/src/patches/lib/hooks/thread-header.js +7 -0
- package/src/patches/lib/hooks/worker.js +7 -0
- package/src/patches/lib/project-selector-shortcut-patch.js +84 -8
- package/src/runtime/api/about.js +16 -0
- package/src/runtime/api/commands.js +85 -0
- package/src/runtime/api/composer.js +14 -0
- package/src/runtime/api/diagnostics.js +38 -0
- package/src/runtime/api/errors.js +20 -0
- package/src/runtime/api/index.js +82 -0
- package/src/runtime/api/mermaid.js +14 -0
- package/src/runtime/api/message.js +14 -0
- package/src/runtime/api/modules.js +57 -0
- package/src/runtime/api/native.js +14 -0
- package/src/runtime/api/patches.js +31 -0
- package/src/runtime/api/review.js +20 -0
- package/src/runtime/api/settings.js +76 -0
- package/src/runtime/api/sidebar.js +24 -0
- package/src/runtime/api/styles.js +28 -0
- package/src/runtime/api/threadHeader.js +41 -0
- package/src/runtime/assets.js +59 -18
- package/src/runtime/host/messageComposer.js +16 -0
- package/src/runtime/host/nativeMain.js +159 -0
- package/src/runtime/host/projectSelector.js +58 -0
- package/src/runtime/host/review.js +62 -0
- package/src/runtime/host/sidebar.js +21 -0
- package/src/runtime/host/threadHeader.js +9 -0
- package/src/runtime/{worker.js → host/worker.js} +7 -0
- package/src/runtime/plugins/mermaidFullscreen.js +19 -6
- package/src/runtime/plugins/nestedRepositories.js +72 -11
- package/src/runtime/plugins/projectColors.js +94 -7
- package/src/runtime/plugins/projectSelectorShortcut.js +67 -12
- package/src/runtime/plugins/sidebarNameBlur.js +1 -1
- package/src/runtime/runtime.js +23 -441
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
const globalObject = typeof window !== "undefined" ? window : globalThis;
|
|
3
|
+
const { core, safeId } = globalObject.__CodexPlusRuntime;
|
|
4
|
+
|
|
5
|
+
function getPluginStore(pluginId) {
|
|
6
|
+
const key = `${core.storagePrefix}${pluginId}`;
|
|
7
|
+
try {
|
|
8
|
+
return JSON.parse(globalObject.localStorage?.getItem(key) || "{}") || {};
|
|
9
|
+
} catch {
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function writePluginStore(pluginId, store) {
|
|
15
|
+
const key = `${core.storagePrefix}${pluginId}`;
|
|
16
|
+
globalObject.localStorage?.setItem(key, JSON.stringify(store));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function emitSetting(pluginId, key, value) {
|
|
20
|
+
const listenerKey = `${pluginId}:${key}`;
|
|
21
|
+
for (const listener of core.settingsListeners.get(listenerKey) || []) listener(value);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function define(pluginId, definitions) {
|
|
25
|
+
const id = safeId(pluginId);
|
|
26
|
+
const store = getPluginStore(id);
|
|
27
|
+
for (const [key, definition] of Object.entries(definitions || {})) {
|
|
28
|
+
if (!(key in store) && "default" in definition) store[key] = definition.default;
|
|
29
|
+
}
|
|
30
|
+
writePluginStore(id, store);
|
|
31
|
+
return {
|
|
32
|
+
definitions,
|
|
33
|
+
get(key) {
|
|
34
|
+
return getPluginStore(id)[key];
|
|
35
|
+
},
|
|
36
|
+
set(key, value) {
|
|
37
|
+
const next = getPluginStore(id);
|
|
38
|
+
next[key] = value;
|
|
39
|
+
writePluginStore(id, next);
|
|
40
|
+
emitSetting(id, key, value);
|
|
41
|
+
},
|
|
42
|
+
use(key, listener) {
|
|
43
|
+
const listenerKey = `${id}:${key}`;
|
|
44
|
+
const listeners = core.settingsListeners.get(listenerKey) || new Set();
|
|
45
|
+
listeners.add(listener);
|
|
46
|
+
core.settingsListeners.set(listenerKey, listeners);
|
|
47
|
+
listener(getPluginStore(id)[key]);
|
|
48
|
+
return () => listeners.delete(listener);
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function AppearanceRowHost({ row, deps, variant }) {
|
|
54
|
+
return row.render?.({ ...deps, variant, row }) ?? null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const appearance = {
|
|
58
|
+
rows: [],
|
|
59
|
+
addRow(row) {
|
|
60
|
+
this.rows.push(row);
|
|
61
|
+
return row;
|
|
62
|
+
},
|
|
63
|
+
renderRows({ deps, variant, section = "appearance" } = {}) {
|
|
64
|
+
const jsx = deps?.jsx;
|
|
65
|
+
if (typeof jsx !== "function") return [];
|
|
66
|
+
return this.rows
|
|
67
|
+
.filter((row) => (row.section || "appearance") === section)
|
|
68
|
+
.slice()
|
|
69
|
+
.sort((left, right) => (left.order || 0) - (right.order || 0))
|
|
70
|
+
.map((row) => jsx(AppearanceRowHost, { row, deps, variant }, row.id));
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
globalObject.CodexPlus.ui.settings = { appearance };
|
|
75
|
+
globalObject.CodexPlus.settings = { define };
|
|
76
|
+
})();
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
const globalObject = typeof window !== "undefined" ? window : globalThis;
|
|
3
|
+
const { applyDecorators, mergeDataAttributes } = globalObject.__CodexPlusRuntime;
|
|
4
|
+
const sidebar = {
|
|
5
|
+
projectDecorators: [],
|
|
6
|
+
threadDecorators: [],
|
|
7
|
+
decorateProjectRow(fn) {
|
|
8
|
+
this.projectDecorators.push(fn);
|
|
9
|
+
return fn;
|
|
10
|
+
},
|
|
11
|
+
decorateThreadRow(fn) {
|
|
12
|
+
this.threadDecorators.push(fn);
|
|
13
|
+
return fn;
|
|
14
|
+
},
|
|
15
|
+
mergeDataAttributes,
|
|
16
|
+
projectRowProps(props) {
|
|
17
|
+
return applyDecorators(props, this.projectDecorators);
|
|
18
|
+
},
|
|
19
|
+
threadRowProps(props) {
|
|
20
|
+
return applyDecorators(props, this.threadDecorators);
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
globalObject.CodexPlus.ui.sidebar = sidebar;
|
|
24
|
+
})();
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
const globalObject = typeof window !== "undefined" ? window : globalThis;
|
|
3
|
+
const { core, safeId } = globalObject.__CodexPlusRuntime;
|
|
4
|
+
|
|
5
|
+
function register(pluginId, cssText) {
|
|
6
|
+
if (typeof document === "undefined") return null;
|
|
7
|
+
const id = `codex-plus-style-${safeId(pluginId)}`;
|
|
8
|
+
let element = core.styleElements.get(id) || document.getElementById(id);
|
|
9
|
+
if (!element) {
|
|
10
|
+
element = document.createElement("style");
|
|
11
|
+
element.id = id;
|
|
12
|
+
document.head?.appendChild(element);
|
|
13
|
+
}
|
|
14
|
+
element.textContent = cssText;
|
|
15
|
+
core.styleElements.set(id, element);
|
|
16
|
+
return element;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function setRootVars(vars) {
|
|
20
|
+
if (typeof document === "undefined") return;
|
|
21
|
+
for (const [key, value] of Object.entries(vars || {})) {
|
|
22
|
+
if (value == null) document.documentElement.style.removeProperty(key);
|
|
23
|
+
else document.documentElement.style.setProperty(key, value);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
globalObject.CodexPlus.styles = { register, setRootVars };
|
|
28
|
+
})();
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
const globalObject = typeof window !== "undefined" ? window : globalThis;
|
|
3
|
+
|
|
4
|
+
function ThreadHeaderAccessoryHost({ accessory, context, deps }) {
|
|
5
|
+
const rendered = accessory?.({ context, ...deps }) ?? null;
|
|
6
|
+
globalObject.CodexPlus.diagnostics.log("threadHeader.accessoryHost.render", {
|
|
7
|
+
accessoryName: accessory?.name || null,
|
|
8
|
+
cwd: typeof context?.cwd === "string" ? context.cwd : null,
|
|
9
|
+
rendered: rendered != null,
|
|
10
|
+
});
|
|
11
|
+
return rendered;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function renderAccessories({ context, deps } = {}) {
|
|
15
|
+
const jsx = deps?.jsx;
|
|
16
|
+
if (typeof jsx !== "function") {
|
|
17
|
+
globalObject.CodexPlus.diagnostics.log("threadHeader.render.skip", { reason: "missing-jsx" });
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
globalObject.CodexPlus.diagnostics.log("threadHeader.render", {
|
|
21
|
+
accessoryCount: globalObject.CodexPlus.ui.threadHeader.accessories.length,
|
|
22
|
+
cwd: typeof context?.cwd === "string" ? context.cwd : null,
|
|
23
|
+
hostId: context?.hostId ?? null,
|
|
24
|
+
header: context?.header ?? null,
|
|
25
|
+
});
|
|
26
|
+
const rendered = globalObject.CodexPlus.ui.threadHeader.accessories.map((accessory, index) =>
|
|
27
|
+
jsx(ThreadHeaderAccessoryHost, { accessory, context, deps }, `thread-header-accessory:${index}`),
|
|
28
|
+
);
|
|
29
|
+
return rendered.length === 0 ? null : rendered;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
globalObject.CodexPlus.ui.threadHeader = {
|
|
33
|
+
accessories: [],
|
|
34
|
+
addAccessory(fn) {
|
|
35
|
+
this.accessories.push(fn);
|
|
36
|
+
globalObject.CodexPlus.diagnostics.log("threadHeader.addAccessory", { accessoryName: fn?.name || null, accessoryCount: this.accessories.length });
|
|
37
|
+
return fn;
|
|
38
|
+
},
|
|
39
|
+
renderAccessories,
|
|
40
|
+
};
|
|
41
|
+
})();
|
package/src/runtime/assets.js
CHANGED
|
@@ -2,31 +2,72 @@ const fs = require("node:fs");
|
|
|
2
2
|
const path = require("node:path");
|
|
3
3
|
|
|
4
4
|
const runtimeRoot = __dirname;
|
|
5
|
-
const
|
|
5
|
+
const browserRuntimeFiles = [
|
|
6
|
+
"api/index.js",
|
|
7
|
+
"api/diagnostics.js",
|
|
8
|
+
"api/modules.js",
|
|
9
|
+
"api/settings.js",
|
|
10
|
+
"api/patches.js",
|
|
11
|
+
"api/commands.js",
|
|
12
|
+
"api/styles.js",
|
|
13
|
+
"api/sidebar.js",
|
|
14
|
+
"api/message.js",
|
|
15
|
+
"api/composer.js",
|
|
16
|
+
"api/about.js",
|
|
17
|
+
"api/review.js",
|
|
18
|
+
"api/native.js",
|
|
19
|
+
"api/errors.js",
|
|
20
|
+
"api/threadHeader.js",
|
|
21
|
+
"api/mermaid.js",
|
|
22
|
+
"host/review.js",
|
|
23
|
+
"host/sidebar.js",
|
|
24
|
+
"host/messageComposer.js",
|
|
25
|
+
"host/projectSelector.js",
|
|
26
|
+
"host/threadHeader.js",
|
|
27
|
+
"vendor/fzf.umd.js",
|
|
28
|
+
"plugins/aboutMetadata.js",
|
|
29
|
+
"plugins/nestedRepositories.js",
|
|
30
|
+
"plugins/diagnosticErrors.js",
|
|
31
|
+
"plugins/userBubbleColors.js",
|
|
32
|
+
"plugins/projectColors.js",
|
|
33
|
+
"plugins/projectPathHeader.js",
|
|
34
|
+
"plugins/sidebarNameBlur.js",
|
|
35
|
+
"plugins/devTools.js",
|
|
36
|
+
"plugins/projectSelectorShortcut.js",
|
|
37
|
+
"plugins/mermaidFullscreen.js",
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
const nodeRuntimeFiles = [
|
|
6
41
|
[".vite/build/codex-plus-aboutMetadata.js", "plugins/aboutMetadata.js"],
|
|
7
|
-
[".vite/build/codex-plus-
|
|
42
|
+
[".vite/build/codex-plus-native-main.js", "host/nativeMain.js"],
|
|
43
|
+
[".vite/build/codex-plus-worker.js", "host/worker.js"],
|
|
8
44
|
["webview/assets/codex-plus/runtime.js", "runtime.js"],
|
|
9
|
-
["webview/assets/codex-plus/plugins/aboutMetadata.js", "plugins/aboutMetadata.js"],
|
|
10
|
-
["webview/assets/codex-plus/plugins/nestedRepositories.js", "plugins/nestedRepositories.js"],
|
|
11
|
-
["webview/assets/codex-plus/plugins/diagnosticErrors.js", "plugins/diagnosticErrors.js"],
|
|
12
|
-
["webview/assets/codex-plus/plugins/userBubbleColors.js", "plugins/userBubbleColors.js"],
|
|
13
|
-
["webview/assets/codex-plus/plugins/projectColors.js", "plugins/projectColors.js"],
|
|
14
|
-
["webview/assets/codex-plus/plugins/projectPathHeader.js", "plugins/projectPathHeader.js"],
|
|
15
|
-
["webview/assets/codex-plus/plugins/sidebarNameBlur.js", "plugins/sidebarNameBlur.js"],
|
|
16
|
-
["webview/assets/codex-plus/plugins/devTools.js", "plugins/devTools.js"],
|
|
17
|
-
["webview/assets/codex-plus/vendor/fzf.umd.js", "../../node_modules/fzf/dist/fzf.umd.js"],
|
|
18
|
-
["webview/assets/codex-plus/plugins/projectSelectorShortcut.js", "plugins/projectSelectorShortcut.js"],
|
|
19
|
-
["webview/assets/codex-plus/plugins/mermaidFullscreen.js", "plugins/mermaidFullscreen.js"],
|
|
20
45
|
];
|
|
21
46
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
47
|
+
const browserRuntimeAssets = browserRuntimeFiles.map((filePath) => [
|
|
48
|
+
`webview/assets/codex-plus/${filePath}`,
|
|
49
|
+
filePath.startsWith("vendor/") ? "../../node_modules/fzf/dist/fzf.umd.js" : filePath,
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
const runtimeFiles = [
|
|
53
|
+
...nodeRuntimeFiles,
|
|
54
|
+
["webview/assets/codex-plus/runtime-manifest.js", null],
|
|
55
|
+
...browserRuntimeAssets,
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
function browserRuntimeManifest(config = {}) {
|
|
59
|
+
return `window.__CodexPlusRuntimeConfig=${JSON.stringify(config)};window.__CodexPlusRuntimeFiles=${JSON.stringify(browserRuntimeFiles)};window.__CodexPlusLoadRuntimeFiles?.(window.__CodexPlusRuntimeFiles);\n`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function codexPlusRuntimeAssets(config = {}) {
|
|
63
|
+
return runtimeFiles.map(([asarPath, localPath]) => {
|
|
64
|
+
const content = localPath == null ? browserRuntimeManifest(config) : fs.readFileSync(path.join(runtimeRoot, localPath), "utf8");
|
|
65
|
+
return [asarPath, content];
|
|
66
|
+
});
|
|
27
67
|
}
|
|
28
68
|
|
|
29
69
|
module.exports = {
|
|
70
|
+
browserRuntimeFiles,
|
|
30
71
|
codexPlusRuntimeAssets,
|
|
31
72
|
runtimeFiles,
|
|
32
73
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
const globalObject = typeof window !== "undefined" ? window : globalThis;
|
|
3
|
+
|
|
4
|
+
function userBubbleProps(props) {
|
|
5
|
+
return globalObject.CodexPlus?.ui?.message?.userBubbleProps?.(props);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function composerSurfaceProps(props) {
|
|
9
|
+
return globalObject.CodexPlus?.ui?.composer?.surfaceProps?.(props);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
globalObject.CodexPlusHost.adapters.messageComposer = {
|
|
13
|
+
composerSurfaceProps,
|
|
14
|
+
userBubbleProps,
|
|
15
|
+
};
|
|
16
|
+
})();
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
const fs = require("node:fs");
|
|
2
|
+
const os = require("node:os");
|
|
3
|
+
const path = require("node:path");
|
|
4
|
+
const { randomUUID } = require("node:crypto");
|
|
5
|
+
const { pathToFileURL } = require("node:url");
|
|
6
|
+
|
|
7
|
+
function create({ electron }) {
|
|
8
|
+
let nativeMenuItems = [];
|
|
9
|
+
let refreshApplicationMenu = null;
|
|
10
|
+
|
|
11
|
+
function menuSnapshot(menu) {
|
|
12
|
+
return menu?.items?.map((item) => ({
|
|
13
|
+
id: item.id,
|
|
14
|
+
label: item.label,
|
|
15
|
+
enabled: item.enabled,
|
|
16
|
+
visible: item.visible,
|
|
17
|
+
accelerator: item.accelerator,
|
|
18
|
+
submenu: menuSnapshot(item.submenu),
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function logMenuDiagnostics() {
|
|
23
|
+
try {
|
|
24
|
+
const menu = menuSnapshot(electron.Menu.getApplicationMenu()) ?? [];
|
|
25
|
+
const text = JSON.stringify(menu);
|
|
26
|
+
const hasOpenDeveloperTools = text.includes("codexPlusOpenDevTools") || text.includes("Open Developer Tools");
|
|
27
|
+
if (process.env.CODEX_PLUS_MENU_DIAGNOSTICS === "1" || !hasOpenDeveloperTools) {
|
|
28
|
+
console.log(`[Codex Plus menu diagnostics] ${JSON.stringify({ hasOpenDeveloperTools, menu })}`);
|
|
29
|
+
}
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.log(`[Codex Plus menu diagnostics] ${JSON.stringify({ error: String(error?.message ?? error) })}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function openDevTools(event) {
|
|
36
|
+
try {
|
|
37
|
+
const webContents = event?.sender;
|
|
38
|
+
if (typeof webContents?.openDevTools !== "function") return { ok: false };
|
|
39
|
+
webContents.openDevTools();
|
|
40
|
+
return { ok: true };
|
|
41
|
+
} catch {
|
|
42
|
+
return { ok: false };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function focusedEvent() {
|
|
47
|
+
const window = electron.BrowserWindow.getFocusedWindow();
|
|
48
|
+
return window && !window.isDestroyed() ? { sender: window.webContents } : null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function runNativeMenuRequest(request) {
|
|
52
|
+
switch (request?.method) {
|
|
53
|
+
case "devtools/open":
|
|
54
|
+
return openDevTools(focusedEvent());
|
|
55
|
+
default:
|
|
56
|
+
return { ok: false };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function templateItems(menuId) {
|
|
61
|
+
return nativeMenuItems
|
|
62
|
+
.filter((item) => item.menuId === menuId)
|
|
63
|
+
.map((item) => ({
|
|
64
|
+
id: item.id,
|
|
65
|
+
label: item.label,
|
|
66
|
+
click: () => {
|
|
67
|
+
runNativeMenuRequest(item.nativeRequest);
|
|
68
|
+
},
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function registerNativeMenuItem(item) {
|
|
73
|
+
if (item?.id == null || item?.menuId == null || item?.label == null || item?.nativeRequest?.method == null) {
|
|
74
|
+
return { ok: false };
|
|
75
|
+
}
|
|
76
|
+
const nextItem = {
|
|
77
|
+
id: String(item.id),
|
|
78
|
+
menuId: String(item.menuId),
|
|
79
|
+
label: String(item.label),
|
|
80
|
+
nativeRequest: {
|
|
81
|
+
method: String(item.nativeRequest.method),
|
|
82
|
+
params: item.nativeRequest.params,
|
|
83
|
+
},
|
|
84
|
+
afterId: item.afterId == null ? null : String(item.afterId),
|
|
85
|
+
afterLabel: item.afterLabel == null ? null : String(item.afterLabel),
|
|
86
|
+
};
|
|
87
|
+
nativeMenuItems = nativeMenuItems.filter((existing) => existing.id !== nextItem.id);
|
|
88
|
+
nativeMenuItems.push(nextItem);
|
|
89
|
+
try {
|
|
90
|
+
refreshApplicationMenu?.();
|
|
91
|
+
} catch {}
|
|
92
|
+
logMenuDiagnostics();
|
|
93
|
+
return { ok: true };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function openMermaidViewer(params) {
|
|
97
|
+
const html = params?.html;
|
|
98
|
+
if (typeof html !== "string" || html.length === 0) return { ok: false };
|
|
99
|
+
const filePath = path.join(os.tmpdir(), `codex-plus-mermaid-${randomUUID()}.html`);
|
|
100
|
+
fs.writeFileSync(filePath, html, "utf8");
|
|
101
|
+
const window = new electron.BrowserWindow({
|
|
102
|
+
height: 900,
|
|
103
|
+
resizable: true,
|
|
104
|
+
show: true,
|
|
105
|
+
title: "Mermaid diagram viewer",
|
|
106
|
+
webPreferences: {
|
|
107
|
+
contextIsolation: true,
|
|
108
|
+
nodeIntegration: false,
|
|
109
|
+
sandbox: true,
|
|
110
|
+
},
|
|
111
|
+
width: 1400,
|
|
112
|
+
});
|
|
113
|
+
window.webContents.setWindowOpenHandler((event) => {
|
|
114
|
+
try {
|
|
115
|
+
const url = new URL(event.url);
|
|
116
|
+
if (url.protocol === "https:" && url.hostname === "mermaid.live") electron.shell.openExternal(event.url);
|
|
117
|
+
} catch {}
|
|
118
|
+
return { action: "deny" };
|
|
119
|
+
});
|
|
120
|
+
window.on("closed", () => {
|
|
121
|
+
try {
|
|
122
|
+
fs.unlinkSync(filePath);
|
|
123
|
+
} catch {}
|
|
124
|
+
});
|
|
125
|
+
window.loadURL(pathToFileURL(filePath).toString()).catch(() => {});
|
|
126
|
+
return { ok: true };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function registerNativeRequest({ isTrustedIpcEvent }) {
|
|
130
|
+
return electron.ipcMain.handle("codex_plus:native-request", async (event, request) => {
|
|
131
|
+
if (!isTrustedIpcEvent(event)) return { ok: false };
|
|
132
|
+
switch (request?.method) {
|
|
133
|
+
case "native-menu/register-item":
|
|
134
|
+
return registerNativeMenuItem(request.params);
|
|
135
|
+
case "devtools/open":
|
|
136
|
+
return openDevTools(event);
|
|
137
|
+
case "mermaid/openViewer":
|
|
138
|
+
return openMermaidViewer(request.params);
|
|
139
|
+
default:
|
|
140
|
+
return { ok: false };
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function setRefreshApplicationMenu(refresh) {
|
|
146
|
+
refreshApplicationMenu = refresh;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
logMenuDiagnostics,
|
|
151
|
+
registerNativeRequest,
|
|
152
|
+
setRefreshApplicationMenu,
|
|
153
|
+
templateItems,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
module.exports = {
|
|
158
|
+
create,
|
|
159
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
const globalObject = typeof window !== "undefined" ? window : globalThis;
|
|
3
|
+
|
|
4
|
+
function fuzzyFilter(projects, query) {
|
|
5
|
+
const needle = String(query ?? "").trim().toLowerCase();
|
|
6
|
+
return globalObject.CodexPlus?.ui?.projectSelector?.fuzzyFilter?.(projects, query) ??
|
|
7
|
+
(needle
|
|
8
|
+
? projects.filter((project) =>
|
|
9
|
+
[project.label, project.repositoryData?.rootFolder ?? "", project.path ?? "", project.hostDisplayName ?? ""].some((value) =>
|
|
10
|
+
String(value ?? "").toLowerCase().includes(needle),
|
|
11
|
+
),
|
|
12
|
+
)
|
|
13
|
+
: projects);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function fuzzyHighlight(text, query, jsx) {
|
|
17
|
+
return globalObject.CodexPlus?.ui?.projectSelector?.fuzzyHighlight?.({ text, query, jsx }) ?? text;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function closeDropdown(event) {
|
|
21
|
+
const KeyboardEventConstructor = globalObject.KeyboardEvent;
|
|
22
|
+
if (typeof KeyboardEventConstructor !== "function") return;
|
|
23
|
+
|
|
24
|
+
const target = event?.target;
|
|
25
|
+
const dispatchTarget = typeof target?.dispatchEvent === "function" ? target : globalObject.document;
|
|
26
|
+
dispatchTarget?.dispatchEvent?.(new KeyboardEventConstructor("keydown", {
|
|
27
|
+
bubbles: true,
|
|
28
|
+
cancelable: true,
|
|
29
|
+
key: "Escape",
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function acceptFirst(event, projects, selectProjectId, query) {
|
|
34
|
+
const project = projects?.[0];
|
|
35
|
+
if (event?.key !== "Enter" || String(query ?? "").trim().length === 0 || project == null) return;
|
|
36
|
+
event.preventDefault?.();
|
|
37
|
+
event.stopPropagation?.();
|
|
38
|
+
selectProjectId(project.projectId);
|
|
39
|
+
closeDropdown(event);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function trigger(element, variant, React) {
|
|
43
|
+
return React.isValidElement(element)
|
|
44
|
+
? React.cloneElement(element, {
|
|
45
|
+
...element.props,
|
|
46
|
+
"data-codex-plus-project-selector-trigger": true,
|
|
47
|
+
"data-codex-plus-project-selector-variant": variant,
|
|
48
|
+
})
|
|
49
|
+
: element;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
globalObject.CodexPlusHost.adapters.projectSelector = {
|
|
53
|
+
acceptFirst,
|
|
54
|
+
fuzzyFilter,
|
|
55
|
+
fuzzyHighlight,
|
|
56
|
+
trigger,
|
|
57
|
+
};
|
|
58
|
+
})();
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
const globalObject = typeof window !== "undefined" ? window : globalThis;
|
|
3
|
+
|
|
4
|
+
function renderBodyFromHost(props, hostDeps) {
|
|
5
|
+
const [
|
|
6
|
+
jsxRuntime,
|
|
7
|
+
React,
|
|
8
|
+
useStore,
|
|
9
|
+
useAtom,
|
|
10
|
+
routeAtom,
|
|
11
|
+
cwdAtom,
|
|
12
|
+
hostIdAtom,
|
|
13
|
+
hostConfigAtom,
|
|
14
|
+
conversationIdAtom,
|
|
15
|
+
gitRequest,
|
|
16
|
+
pathValue,
|
|
17
|
+
DefaultReview,
|
|
18
|
+
Button,
|
|
19
|
+
Tooltip,
|
|
20
|
+
Icon,
|
|
21
|
+
Dropdown,
|
|
22
|
+
DropdownMenu,
|
|
23
|
+
BranchPickerDropdownContent,
|
|
24
|
+
ReviewToolbar,
|
|
25
|
+
parseDiff,
|
|
26
|
+
DiffCard,
|
|
27
|
+
] = hostDeps;
|
|
28
|
+
const deps = {
|
|
29
|
+
jsx: jsxRuntime.jsx,
|
|
30
|
+
jsxs: jsxRuntime.jsxs,
|
|
31
|
+
Fragment: jsxRuntime.Fragment,
|
|
32
|
+
createElement: React.createElement,
|
|
33
|
+
React,
|
|
34
|
+
useStore,
|
|
35
|
+
useAtom,
|
|
36
|
+
routeAtom,
|
|
37
|
+
cwdAtom,
|
|
38
|
+
hostIdAtom,
|
|
39
|
+
hostConfigAtom,
|
|
40
|
+
conversationIdAtom,
|
|
41
|
+
gitRequest,
|
|
42
|
+
pathValue,
|
|
43
|
+
DefaultReview,
|
|
44
|
+
Button,
|
|
45
|
+
Tooltip,
|
|
46
|
+
Icon,
|
|
47
|
+
Dropdown,
|
|
48
|
+
DropdownMenu,
|
|
49
|
+
BranchPickerDropdownContent,
|
|
50
|
+
ReviewToolbar,
|
|
51
|
+
parseDiff,
|
|
52
|
+
DiffCard,
|
|
53
|
+
};
|
|
54
|
+
return globalObject.CodexPlus.ui.review.renderBody({
|
|
55
|
+
props,
|
|
56
|
+
deps,
|
|
57
|
+
defaultBody: props.mainReviewContent ?? deps.jsx(DefaultReview, props),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
globalObject.CodexPlusHost.adapters.review = { renderBodyFromHost };
|
|
62
|
+
})();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
const globalObject = typeof window !== "undefined" ? window : globalThis;
|
|
3
|
+
|
|
4
|
+
function projectRowProps(project) {
|
|
5
|
+
return globalObject.CodexPlus?.ui?.sidebar?.projectRowProps?.({ project });
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function threadRowProps(project) {
|
|
9
|
+
return globalObject.CodexPlus?.ui?.sidebar?.threadRowProps?.({ project });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function mergeThreadRowAttributes(base, extra) {
|
|
13
|
+
return globalObject.CodexPlus?.ui?.sidebar?.mergeDataAttributes?.(base, extra);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
globalObject.CodexPlusHost.adapters.sidebar = {
|
|
17
|
+
mergeThreadRowAttributes,
|
|
18
|
+
projectRowProps,
|
|
19
|
+
threadRowProps,
|
|
20
|
+
};
|
|
21
|
+
})();
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
const globalObject = typeof window !== "undefined" ? window : globalThis;
|
|
3
|
+
|
|
4
|
+
function accessories(context, deps) {
|
|
5
|
+
return globalObject.CodexPlus?.ui?.threadHeader?.renderAccessories?.({ context, deps }) ?? null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
globalObject.CodexPlusHost.adapters.threadHeader = { accessories };
|
|
9
|
+
})();
|
|
@@ -221,8 +221,15 @@ function isReadOnlyBranchRequest(requestKind, source) {
|
|
|
221
221
|
return source === "codex_plus_review" && (requestKind === "recent-branches" || requestKind === "search-branches");
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
+
function repositoryTargetsFromHost(gitManager, params, platform, signal, getSubmodulePaths) {
|
|
225
|
+
return repositoryTargets(gitManager, params, platform, signal, (root, submoduleSignal) =>
|
|
226
|
+
getSubmodulePaths(gitManager.getWorktreeRepositoryForRoot(root, platform), submoduleSignal),
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
224
230
|
module.exports = {
|
|
225
231
|
isReadOnlyBranchRequest,
|
|
232
|
+
repositoryTargetsFromHost,
|
|
226
233
|
repositoryTargets,
|
|
227
234
|
traceRequest,
|
|
228
235
|
};
|
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
const BUTTON_CLASS = "codex-plus-mermaid-expand-button";
|
|
5
5
|
|
|
6
6
|
function sourceFor(container) {
|
|
7
|
-
return container.querySelector("pre.sr-only")?.textContent ||
|
|
7
|
+
return container.querySelector("pre.sr-only")?.textContent ||
|
|
8
|
+
container.parentElement?.querySelector(":scope > pre.sr-only")?.textContent ||
|
|
9
|
+
"";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function hostFor(container) {
|
|
13
|
+
return container.closest('[data-markdown-copy="code-block"]') || container;
|
|
8
14
|
}
|
|
9
15
|
|
|
10
16
|
function assetUrl(assetPath) {
|
|
@@ -13,6 +19,10 @@
|
|
|
13
19
|
return new URL(`assets/${assetPath}`, document.baseURI).href;
|
|
14
20
|
}
|
|
15
21
|
|
|
22
|
+
function mermaidCoreAsset() {
|
|
23
|
+
return CodexPlus.config?.mermaidCoreAsset || "mermaid.core.js";
|
|
24
|
+
}
|
|
25
|
+
|
|
16
26
|
function escapeHtml(value) {
|
|
17
27
|
return String(value).replace(/[&<>"']/g, (char) => ({
|
|
18
28
|
"&": "&",
|
|
@@ -219,17 +229,19 @@ renderFromSource().catch((error) => {
|
|
|
219
229
|
const isDark = document.documentElement.classList.contains("dark") || document.documentElement.classList.contains("electron-dark");
|
|
220
230
|
const debug = localStorage.getItem("codexPlusMermaidDebug") === "1";
|
|
221
231
|
const html = source
|
|
222
|
-
? viewerHtml({ source, isDark, mermaidModuleUrl: assetUrl(
|
|
232
|
+
? viewerHtml({ source, isDark, mermaidModuleUrl: assetUrl(mermaidCoreAsset()), debug })
|
|
223
233
|
: `<!doctype html><meta charset="utf-8"><body>${escapeHtml("No Mermaid source was found.")}</body>`;
|
|
224
234
|
CodexPlus.native.request("mermaid/openViewer", { html }).catch(() => {});
|
|
225
235
|
}
|
|
226
236
|
|
|
227
237
|
function decorate(container) {
|
|
228
|
-
|
|
229
|
-
|
|
238
|
+
const host = hostFor(container);
|
|
239
|
+
if (host.querySelector(`:scope > .${BUTTON_CLASS}`)) return;
|
|
240
|
+
host.setAttribute("data-codex-plus-mermaid-host", "");
|
|
241
|
+
host.style.position ||= "relative";
|
|
230
242
|
const control = button("Open Mermaid diagram fullscreen");
|
|
231
|
-
control.addEventListener("click", () => openViewer(
|
|
232
|
-
|
|
243
|
+
control.addEventListener("click", () => openViewer(host));
|
|
244
|
+
host.prepend(control);
|
|
233
245
|
}
|
|
234
246
|
|
|
235
247
|
function decorateAll(root = document) {
|
|
@@ -243,6 +255,7 @@ renderFromSource().catch((error) => {
|
|
|
243
255
|
description: "Adds a separate fullscreen viewer with zoom controls to rendered Mermaid diagrams.",
|
|
244
256
|
required: true,
|
|
245
257
|
styles:
|
|
258
|
+
`[data-codex-plus-mermaid-host]{position:relative}` +
|
|
246
259
|
`[data-codex-plus-mermaid-diagram]{position:relative}` +
|
|
247
260
|
`.${BUTTON_CLASS}{position:absolute;left:.5rem;top:.5rem;z-index:30;display:inline-flex;width:1.75rem;height:1.75rem;align-items:center;justify-content:center;border:1px solid var(--color-token-input-border,rgba(127,127,127,.35));border-radius:.375rem;background:var(--color-background-elevated-primary,#fff);color:var(--color-token-foreground,#111);box-shadow:0 2px 8px rgba(0,0,0,.12);opacity:.82}` +
|
|
248
261
|
`.${BUTTON_CLASS}::before,.${BUTTON_CLASS}::after{content:"";position:absolute;width:.42rem;height:.42rem;border-color:currentColor;border-style:solid}` +
|