codex-plus-patcher 0.4.0 → 0.5.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.
- package/README.md +1 -0
- package/package.json +1 -1
- package/src/patches/26.616.71553-4265.js +1 -0
- package/src/patches/26.616.81150-4306.js +1 -0
- package/src/patches/lib/common-patches.js +60 -2
- package/src/runtime/assets.js +1 -0
- package/src/runtime/plugins/mermaidFullscreen.js +275 -0
- package/src/runtime/runtime.js +7 -1
package/README.md
CHANGED
|
@@ -30,6 +30,7 @@ patches plus readable runtime plugins:
|
|
|
30
30
|
- add user-message bubble color controls in Appearance settings
|
|
31
31
|
- add adaptive project colors for sidebar projects, grouped threads, pinned threads, user-message accents, and the composer
|
|
32
32
|
- add the `Toggle sidebar blur` command palette entry to blur sidebar chat and project names for the current session
|
|
33
|
+
- add a fullscreen Mermaid diagram viewer with zoom controls
|
|
33
34
|
|
|
34
35
|
The generated app includes a readable Codex Plus runtime under
|
|
35
36
|
`webview/assets/codex-plus/`. Versioned ASAR patches install the runtime,
|
package/package.json
CHANGED
|
@@ -16,6 +16,7 @@ module.exports = buildCodexPlusPatchSet({
|
|
|
16
16
|
"userMessageAttachments": "webview/assets/user-message-attachments-5G1ZKim-.js",
|
|
17
17
|
"composer": "webview/assets/composer-DlMDPaCL.js",
|
|
18
18
|
"localTaskRow": "webview/assets/local-task-row-CoPNn6SW.js",
|
|
19
|
+
"mermaidDiagramShell": "webview/assets/mermaid-diagram-shell-BO-t9BGx.js",
|
|
19
20
|
"keyboardShortcutsSearchInput": "webview/assets/keyboard-shortcuts-search-input-C1dmntOi.js",
|
|
20
21
|
"src": "src-l0hbMZ-p.js",
|
|
21
22
|
"sidebarThreadKeys": "sidebar-thread-keys-Ch_amVKz.js",
|
|
@@ -16,6 +16,7 @@ module.exports = buildCodexPlusPatchSet({
|
|
|
16
16
|
"userMessageAttachments": "webview/assets/user-message-attachments-5G1ZKim-.js",
|
|
17
17
|
"composer": "webview/assets/composer-DlMDPaCL.js",
|
|
18
18
|
"localTaskRow": "webview/assets/local-task-row-CoPNn6SW.js",
|
|
19
|
+
"mermaidDiagramShell": "webview/assets/mermaid-diagram-shell-BO-t9BGx.js",
|
|
19
20
|
"keyboardShortcutsSearchInput": "webview/assets/keyboard-shortcuts-search-input-C1dmntOi.js",
|
|
20
21
|
"src": "src-l0hbMZ-p.js",
|
|
21
22
|
"sidebarThreadKeys": "sidebar-thread-keys-Ch_amVKz.js",
|
|
@@ -7,6 +7,7 @@ function buildCodexPlusPatchSet(config) {
|
|
|
7
7
|
const newTitle = '<title>Codex Plus</title><script src="./assets/codex-plus/runtime.js"></script>';
|
|
8
8
|
const titleFile = "webview/index.html";
|
|
9
9
|
const workerFile = ".vite/build/worker.js";
|
|
10
|
+
const preloadFile = ".vite/build/preload.js";
|
|
10
11
|
const files = config.files;
|
|
11
12
|
const anchors = config.anchors;
|
|
12
13
|
const mainFile = files.main;
|
|
@@ -19,6 +20,7 @@ function buildCodexPlusPatchSet(config) {
|
|
|
19
20
|
const userMessageAttachmentsFile = files.userMessageAttachments;
|
|
20
21
|
const composerFile = files.composer;
|
|
21
22
|
const localTaskRowFile = files.localTaskRow;
|
|
23
|
+
const mermaidDiagramShellFile = files.mermaidDiagramShell;
|
|
22
24
|
const electronMenuShortcutsFile = files.electronMenuShortcuts;
|
|
23
25
|
const keyboardShortcutsSearchInputFile = files.keyboardShortcutsSearchInput;
|
|
24
26
|
const srcFile = files.src;
|
|
@@ -50,8 +52,8 @@ function patchAboutDialog(text, context = {}) {
|
|
|
50
52
|
);
|
|
51
53
|
patched = replaceOnce(
|
|
52
54
|
patched,
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
+
"g=d.formatMessage({messageId:A0,defaultMessage:j0}),_=V0(o),v=_.length===0?h:[h,``,..._].join(`\n`),",
|
|
56
|
+
"g=d.formatMessage({messageId:A0,defaultMessage:j0}),_=CPXAbout.buildInfoLines,v=_.length===0?h:[h,``,..._].join(`\n`),",
|
|
55
57
|
"about dialog build information anchor",
|
|
56
58
|
);
|
|
57
59
|
patched = replaceOnce(
|
|
@@ -128,6 +130,12 @@ const codexPlusSubrepoDiffHelpers = `
|
|
|
128
130
|
const codexPlusDiagnosticHelpers = `
|
|
129
131
|
function CPXDiagnosticDetails(e){return window.CodexPlus?.ui?.errors?.renderDetails?.(e)??null}`;
|
|
130
132
|
|
|
133
|
+
const codexPlusMermaidHelpers = `
|
|
134
|
+
function CPXMermaidDiagramProps(e){return window.CodexPlus?.ui?.mermaid?.diagramProps?.(e)}`;
|
|
135
|
+
|
|
136
|
+
const codexPlusNativeMainHelpers = `
|
|
137
|
+
function CPXOpenMermaidViewer(e){let t=e?.html;if(typeof t!==\`string\`||t.length===0)return{ok:!1};let n=(0,s.join)((0,o.tmpdir)(),\`codex-plus-mermaid-\${(0,u.randomUUID)()}.html\`);(0,l.writeFileSync)(n,t,\`utf8\`);let r=new a.BrowserWindow({height:900,resizable:!0,show:!0,title:\`Mermaid diagram viewer\`,webPreferences:{contextIsolation:!0,nodeIntegration:!1,sandbox:!0},width:1400});return r.webContents.setWindowOpenHandler(e=>{try{let t=new URL(e.url);if(t.protocol===\`https:\`&&t.hostname===\`mermaid.live\`)a.shell.openExternal(e.url)}catch{}return{action:\`deny\`}}),r.on(\`closed\`,()=>{try{(0,l.unlinkSync)(n)}catch{}}),r.loadURL((0,S.pathToFileURL)(n).toString()).catch(()=>{}),{ok:!0}}function CPXRegisterNativeRequest(e){return a.ipcMain.handle(\`codex_plus:native-request\`,async(t,n)=>{if(!e.isTrustedIpcEvent(t))return{ok:!1};switch(n?.method){case\`mermaid/openViewer\`:return CPXOpenMermaidViewer(n.params);default:return{ok:!1}}})}`;
|
|
138
|
+
|
|
131
139
|
function patchThreadSidePanelTabs(text) {
|
|
132
140
|
let patched = replaceOnce(
|
|
133
141
|
text,
|
|
@@ -536,6 +544,45 @@ function patchLocalTaskRow(text) {
|
|
|
536
544
|
);
|
|
537
545
|
}
|
|
538
546
|
|
|
547
|
+
function patchMermaidDiagramShell(text) {
|
|
548
|
+
let patched = replaceOnce(
|
|
549
|
+
text,
|
|
550
|
+
"function d(e){let t=(0,s.c)(18),{Renderer:n,className:r,code:i,fallback:d,isCodeFenceOpen:f,wideBlockKind:p}=e,",
|
|
551
|
+
`${codexPlusMermaidHelpers}function d(e){let t=(0,s.c)(18),{Renderer:n,className:r,code:i,fallback:d,isCodeFenceOpen:f,wideBlockKind:p}=e,`,
|
|
552
|
+
"mermaid diagram shell helper insertion anchor",
|
|
553
|
+
);
|
|
554
|
+
return replaceOnce(
|
|
555
|
+
patched,
|
|
556
|
+
"O=(0,c.jsx)(`div`,{className:T,\"data-wide-markdown-block\":E,\"data-wide-markdown-block-kind\":p,children:D})",
|
|
557
|
+
"O=(0,c.jsx)(`div`,{className:T,...CPXMermaidDiagramProps({code:i}),\"data-wide-markdown-block\":E,\"data-wide-markdown-block-kind\":p,children:D})",
|
|
558
|
+
"mermaid diagram shell host props anchor",
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
function patchPreloadNativeBridge(text) {
|
|
563
|
+
return replaceOnce(
|
|
564
|
+
text,
|
|
565
|
+
"e.contextBridge.exposeInMainWorld(`codexWindowType`,m),e.contextBridge.exposeInMainWorld(`electronBridge`,D),typeof window<`u`",
|
|
566
|
+
"e.contextBridge.exposeInMainWorld(`codexWindowType`,m),e.contextBridge.exposeInMainWorld(`electronBridge`,D),e.contextBridge.exposeInMainWorld(`codexPlusNative`,{request:(t,n)=>e.ipcRenderer.invoke(`codex_plus:native-request`,{method:t,params:n})}),typeof window<`u`",
|
|
567
|
+
"codex plus native preload bridge anchor",
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function patchMainNativeBridge(text) {
|
|
572
|
+
let patched = replaceOnce(
|
|
573
|
+
text,
|
|
574
|
+
"function z1(e){return a.ipcMain.handle(Tl,async(t,n)=>{",
|
|
575
|
+
`${codexPlusNativeMainHelpers}function z1(e){return a.ipcMain.handle(Tl,async(t,n)=>{`,
|
|
576
|
+
"codex plus native main helper insertion anchor",
|
|
577
|
+
);
|
|
578
|
+
return replaceOnce(
|
|
579
|
+
patched,
|
|
580
|
+
"v0({buildFlavor:i,getContextForWebContents:N.getContextForWebContents,isTrustedIpcEvent:te,usesOwlAppShell:y}),a.ipcMain.on(kl,",
|
|
581
|
+
"v0({buildFlavor:i,getContextForWebContents:N.getContextForWebContents,isTrustedIpcEvent:te,usesOwlAppShell:y}),CPXRegisterNativeRequest({isTrustedIpcEvent:te}),a.ipcMain.on(kl,",
|
|
582
|
+
"codex plus native main registration anchor",
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
|
|
539
586
|
return makePatchSet({
|
|
540
587
|
id: config.id,
|
|
541
588
|
codexVersion: config.codexVersion,
|
|
@@ -597,6 +644,17 @@ return makePatchSet({
|
|
|
597
644
|
[keyboardShortcutsSearchInputFile, patchKeyboardShortcutsSearchInput],
|
|
598
645
|
],
|
|
599
646
|
},
|
|
647
|
+
...(mermaidDiagramShellFile ? [{
|
|
648
|
+
id: "codex-plus-native-bridge",
|
|
649
|
+
fileTransforms: [
|
|
650
|
+
[preloadFile, patchPreloadNativeBridge],
|
|
651
|
+
[mainFile, patchMainNativeBridge],
|
|
652
|
+
],
|
|
653
|
+
}] : []),
|
|
654
|
+
...(mermaidDiagramShellFile ? [{
|
|
655
|
+
id: "mermaid-fullscreen-viewer",
|
|
656
|
+
fileTransforms: [[mermaidDiagramShellFile, patchMermaidDiagramShell]],
|
|
657
|
+
}] : []),
|
|
600
658
|
],
|
|
601
659
|
});
|
|
602
660
|
}
|
package/src/runtime/assets.js
CHANGED
|
@@ -12,6 +12,7 @@ const runtimeFiles = [
|
|
|
12
12
|
["webview/assets/codex-plus/plugins/userBubbleColors.js", "plugins/userBubbleColors.js"],
|
|
13
13
|
["webview/assets/codex-plus/plugins/projectColors.js", "plugins/projectColors.js"],
|
|
14
14
|
["webview/assets/codex-plus/plugins/sidebarNameBlur.js", "plugins/sidebarNameBlur.js"],
|
|
15
|
+
["webview/assets/codex-plus/plugins/mermaidFullscreen.js", "plugins/mermaidFullscreen.js"],
|
|
15
16
|
];
|
|
16
17
|
|
|
17
18
|
function codexPlusRuntimeAssets() {
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
const CodexPlus = window.CodexPlus;
|
|
3
|
+
const SELECTOR = "[data-codex-plus-mermaid-diagram]";
|
|
4
|
+
const BUTTON_CLASS = "codex-plus-mermaid-expand-button";
|
|
5
|
+
|
|
6
|
+
function sourceFor(container) {
|
|
7
|
+
return container.querySelector("pre.sr-only")?.textContent || "";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function assetUrl(assetPath) {
|
|
11
|
+
const appScript = document.querySelector('script[type="module"][src*="/assets/"],script[type="module"][src^="./assets/"]');
|
|
12
|
+
if (appScript?.src) return new URL(assetPath, new URL(".", appScript.src)).href;
|
|
13
|
+
return new URL(`assets/${assetPath}`, document.baseURI).href;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function escapeHtml(value) {
|
|
17
|
+
return String(value).replace(/[&<>"']/g, (char) => ({
|
|
18
|
+
"&": "&",
|
|
19
|
+
"<": "<",
|
|
20
|
+
">": ">",
|
|
21
|
+
'"': """,
|
|
22
|
+
"'": "'",
|
|
23
|
+
})[char]);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function mermaidLiveUrl(source) {
|
|
27
|
+
const state = JSON.stringify({
|
|
28
|
+
code: source,
|
|
29
|
+
mermaid: { theme: "default" },
|
|
30
|
+
updateEditor: false,
|
|
31
|
+
});
|
|
32
|
+
return `https://mermaid.live/edit#base64:${btoa(unescape(encodeURIComponent(state))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "")}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function viewerHtml({ source, isDark, mermaidModuleUrl, debug }) {
|
|
36
|
+
return `<!doctype html>
|
|
37
|
+
<html>
|
|
38
|
+
<head>
|
|
39
|
+
<meta charset="utf-8">
|
|
40
|
+
<title>Mermaid diagram viewer</title>
|
|
41
|
+
<style>
|
|
42
|
+
:root{color-scheme:${isDark ? "dark" : "light"};--viewer-bg:${isDark ? "#0a0a0a" : "#fff"};--viewer-fg:${isDark ? "#fff" : "#111"};--viewer-toolbar-bg:${isDark ? "#252525" : "#f7f7f7"};--viewer-border:${isDark ? "rgba(255,255,255,.18)" : "rgba(0,0,0,.16)"};--viewer-button-border:${isDark ? "rgba(255,255,255,.22)" : "rgba(0,0,0,.18)"};--viewer-button-bg:${isDark ? "rgba(255,255,255,.08)" : "rgba(0,0,0,.06)"};--viewer-muted:${isDark ? "#cfcfcf" : "#333"}}
|
|
43
|
+
:root[data-theme="dark"]{color-scheme:dark;--viewer-bg:#0a0a0a;--viewer-fg:#fff;--viewer-toolbar-bg:#252525;--viewer-border:rgba(255,255,255,.18);--viewer-button-border:rgba(255,255,255,.22);--viewer-button-bg:rgba(255,255,255,.08);--viewer-muted:#cfcfcf}
|
|
44
|
+
:root[data-theme="light"]{color-scheme:light;--viewer-bg:#fff;--viewer-fg:#111;--viewer-toolbar-bg:#f7f7f7;--viewer-border:rgba(0,0,0,.16);--viewer-button-border:rgba(0,0,0,.18);--viewer-button-bg:rgba(0,0,0,.06);--viewer-muted:#333}
|
|
45
|
+
*{box-sizing:border-box}
|
|
46
|
+
html,body{height:100%;margin:0}
|
|
47
|
+
body{display:flex;flex-direction:column;background:var(--viewer-bg);color:var(--viewer-fg);font:13px -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
|
|
48
|
+
.toolbar{display:flex;gap:8px;justify-content:flex-end;padding:10px;border-bottom:1px solid var(--viewer-border);background:var(--viewer-toolbar-bg)}
|
|
49
|
+
button{min-width:42px;border:1px solid var(--viewer-button-border);border-radius:6px;background:var(--viewer-button-bg);color:inherit;padding:6px 10px}
|
|
50
|
+
button:focus-visible{outline:2px solid #60a5fa;outline-offset:2px}
|
|
51
|
+
.viewport{flex:1;overflow:auto;padding:16px}
|
|
52
|
+
.stage{width:max-content;min-width:100%}
|
|
53
|
+
.stage svg{display:block;max-width:none;background:var(--viewer-bg)}
|
|
54
|
+
.render-status{position:fixed;left:12px;bottom:10px;z-index:10;color:var(--viewer-muted);font-size:12px}
|
|
55
|
+
.render-status[hidden]{display:none}
|
|
56
|
+
</style>
|
|
57
|
+
</head>
|
|
58
|
+
<body>
|
|
59
|
+
<div class="toolbar">
|
|
60
|
+
<button id="zoom-fit" type="button" aria-label="Zoom to fit">Fit</button>
|
|
61
|
+
<button id="zoom-width" type="button" aria-label="Zoom to width">Width</button>
|
|
62
|
+
<button id="zoom-height" type="button" aria-label="Zoom to height">Height</button>
|
|
63
|
+
<button id="zoom-out" type="button" aria-label="Zoom out">-</button>
|
|
64
|
+
<button id="zoom-reset" type="button" aria-label="Reset zoom">100%</button>
|
|
65
|
+
<button id="zoom-in" type="button" aria-label="Zoom in">+</button>
|
|
66
|
+
<button id="theme-toggle" type="button" aria-label="Toggle Mermaid theme">${isDark ? "Dark" : "Light"}</button>
|
|
67
|
+
<button id="open-live" type="button" aria-label="Open in Mermaid Live">Live</button>
|
|
68
|
+
<button id="close" type="button" aria-label="Close Mermaid diagram viewer">Close</button>
|
|
69
|
+
</div>
|
|
70
|
+
<div class="viewport"><div class="stage" id="stage"></div></div>
|
|
71
|
+
<div class="render-status" id="render-status" hidden>Rendering Mermaid source...</div>
|
|
72
|
+
<script type="module">
|
|
73
|
+
let scale = 1;
|
|
74
|
+
let darkTheme = ${isDark ? "true" : "false"};
|
|
75
|
+
const source = ${JSON.stringify(source)};
|
|
76
|
+
const mermaidModuleUrl = ${JSON.stringify(mermaidModuleUrl)};
|
|
77
|
+
const liveUrl = ${JSON.stringify(mermaidLiveUrl(source))};
|
|
78
|
+
const debug = ${debug ? "true" : "false"} || localStorage.getItem("codexPlusMermaidDebug") === "1";
|
|
79
|
+
const stage = document.getElementById("stage");
|
|
80
|
+
const viewport = document.querySelector(".viewport");
|
|
81
|
+
const reset = document.getElementById("zoom-reset");
|
|
82
|
+
const themeToggle = document.getElementById("theme-toggle");
|
|
83
|
+
const renderStatus = document.getElementById("render-status");
|
|
84
|
+
let fitMode = "fit";
|
|
85
|
+
let renderCount = 0;
|
|
86
|
+
let renderInFlight = false;
|
|
87
|
+
let renderQueued = false;
|
|
88
|
+
function diagram() {
|
|
89
|
+
return stage.querySelector("svg");
|
|
90
|
+
}
|
|
91
|
+
function baseSize() {
|
|
92
|
+
const svg = diagram();
|
|
93
|
+
const viewBox = svg?.viewBox?.baseVal;
|
|
94
|
+
if (viewBox && viewBox.width > 0 && viewBox.height > 0) return { width: viewBox.width, height: viewBox.height };
|
|
95
|
+
const rect = svg?.getBoundingClientRect();
|
|
96
|
+
return { width: rect?.width || 800, height: rect?.height || 600 };
|
|
97
|
+
}
|
|
98
|
+
let base = { width: 800, height: 600 };
|
|
99
|
+
function setScale(next, mode = null) {
|
|
100
|
+
fitMode = mode;
|
|
101
|
+
scale = Math.max(0.05, Math.min(8, next));
|
|
102
|
+
const svg = diagram();
|
|
103
|
+
if (svg) {
|
|
104
|
+
svg.removeAttribute("width");
|
|
105
|
+
svg.removeAttribute("height");
|
|
106
|
+
svg.style.width = Math.round(base.width * scale) + "px";
|
|
107
|
+
svg.style.height = Math.round(base.height * scale) + "px";
|
|
108
|
+
}
|
|
109
|
+
reset.textContent = Math.round(scale * 100) + "%";
|
|
110
|
+
}
|
|
111
|
+
function fitScale(mode) {
|
|
112
|
+
const width = Math.max(1, viewport.clientWidth - 32);
|
|
113
|
+
const height = Math.max(1, viewport.clientHeight - 32);
|
|
114
|
+
const byWidth = width / base.width;
|
|
115
|
+
const byHeight = height / base.height;
|
|
116
|
+
if (mode === "width") return byWidth;
|
|
117
|
+
if (mode === "height") return byHeight;
|
|
118
|
+
return Math.min(byWidth, byHeight);
|
|
119
|
+
}
|
|
120
|
+
function applyFit(mode) {
|
|
121
|
+
setScale(fitScale(mode), mode);
|
|
122
|
+
}
|
|
123
|
+
function applyThemeChrome() {
|
|
124
|
+
document.documentElement.dataset.theme = darkTheme ? "dark" : "light";
|
|
125
|
+
document.documentElement.style.colorScheme = darkTheme ? "dark" : "light";
|
|
126
|
+
themeToggle.textContent = darkTheme ? "Dark" : "Light";
|
|
127
|
+
themeToggle.setAttribute("aria-pressed", String(darkTheme));
|
|
128
|
+
}
|
|
129
|
+
function themeDirective() {
|
|
130
|
+
return "%%{init: " + JSON.stringify({ theme: darkTheme ? "dark" : "default" }) + "}%%" + String.fromCharCode(10);
|
|
131
|
+
}
|
|
132
|
+
function sourceForTheme() {
|
|
133
|
+
const trimmed = source.trimStart();
|
|
134
|
+
if (!trimmed.startsWith("%%{")) return themeDirective() + source;
|
|
135
|
+
const markerEnd = trimmed.indexOf("}%%");
|
|
136
|
+
if (markerEnd < 0) return themeDirective() + source;
|
|
137
|
+
const directive = trimmed.slice(0, markerEnd + 3).toLowerCase();
|
|
138
|
+
if (!directive.startsWith("%%{init:") && !directive.startsWith("%%{initialize:")) return themeDirective() + source;
|
|
139
|
+
let rest = trimmed.slice(markerEnd + 3);
|
|
140
|
+
while ([9, 10, 13, 32].includes(rest.charCodeAt(0))) rest = rest.slice(1);
|
|
141
|
+
return themeDirective() + rest;
|
|
142
|
+
}
|
|
143
|
+
async function renderFromSource() {
|
|
144
|
+
if (renderInFlight) {
|
|
145
|
+
renderQueued = true;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
renderInFlight = true;
|
|
149
|
+
renderStatus.hidden = !debug;
|
|
150
|
+
renderStatus.textContent = "Rendering Mermaid source...";
|
|
151
|
+
themeToggle.disabled = true;
|
|
152
|
+
try {
|
|
153
|
+
const mermaid = (await import(mermaidModuleUrl)).default;
|
|
154
|
+
mermaid.initialize({
|
|
155
|
+
startOnLoad: false,
|
|
156
|
+
securityLevel: "strict",
|
|
157
|
+
suppressErrorRendering: true,
|
|
158
|
+
deterministicIds: true,
|
|
159
|
+
deterministicIDSeed: "codex-plus-mermaid-viewer",
|
|
160
|
+
htmlLabels: false,
|
|
161
|
+
flowchart: { htmlLabels: false },
|
|
162
|
+
darkMode: darkTheme,
|
|
163
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
164
|
+
theme: darkTheme ? "dark" : "default",
|
|
165
|
+
});
|
|
166
|
+
applyThemeChrome();
|
|
167
|
+
const rendered = await mermaid.render("codex-plus-mermaid-viewer-" + String(renderCount += 1), sourceForTheme());
|
|
168
|
+
stage.innerHTML = rendered.svg;
|
|
169
|
+
renderStatus.textContent = "Rendered from Mermaid source";
|
|
170
|
+
base = baseSize();
|
|
171
|
+
applyFit(fitMode || "fit");
|
|
172
|
+
} finally {
|
|
173
|
+
renderInFlight = false;
|
|
174
|
+
themeToggle.disabled = false;
|
|
175
|
+
if (renderQueued) {
|
|
176
|
+
renderQueued = false;
|
|
177
|
+
renderFromSource();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
document.getElementById("zoom-fit").addEventListener("click", () => applyFit("fit"));
|
|
182
|
+
document.getElementById("zoom-width").addEventListener("click", () => applyFit("width"));
|
|
183
|
+
document.getElementById("zoom-height").addEventListener("click", () => applyFit("height"));
|
|
184
|
+
document.getElementById("zoom-out").addEventListener("click", () => setScale(scale - 0.2));
|
|
185
|
+
reset.addEventListener("click", () => setScale(1));
|
|
186
|
+
document.getElementById("zoom-in").addEventListener("click", () => setScale(scale + 0.2));
|
|
187
|
+
themeToggle.addEventListener("click", () => { darkTheme = !darkTheme; applyThemeChrome(); renderQueued = true; renderFromSource(); });
|
|
188
|
+
document.getElementById("open-live").addEventListener("click", () => window.open(liveUrl, "_blank", "noopener"));
|
|
189
|
+
document.getElementById("close").addEventListener("click", () => window.close());
|
|
190
|
+
window.addEventListener("resize", () => { if (fitMode) applyFit(fitMode); });
|
|
191
|
+
document.addEventListener("keydown", (event) => {
|
|
192
|
+
if (event.key === "Escape") window.close();
|
|
193
|
+
if ((event.metaKey || event.ctrlKey) && event.key === "=") { event.preventDefault(); setScale(scale + 0.2); }
|
|
194
|
+
if ((event.metaKey || event.ctrlKey) && event.key === "-") { event.preventDefault(); setScale(scale - 0.2); }
|
|
195
|
+
if ((event.metaKey || event.ctrlKey) && event.key === "0") { event.preventDefault(); setScale(1); }
|
|
196
|
+
});
|
|
197
|
+
applyThemeChrome();
|
|
198
|
+
renderFromSource().catch((error) => {
|
|
199
|
+
renderStatus.hidden = false;
|
|
200
|
+
renderStatus.textContent = "Mermaid render failed: " + String(error?.message || error);
|
|
201
|
+
console.error("[Codex Plus] Mermaid source render failed", error);
|
|
202
|
+
});
|
|
203
|
+
</script>
|
|
204
|
+
</body>
|
|
205
|
+
</html>`;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function button(label) {
|
|
209
|
+
const element = document.createElement("button");
|
|
210
|
+
element.type = "button";
|
|
211
|
+
element.className = BUTTON_CLASS;
|
|
212
|
+
element.setAttribute("aria-label", label);
|
|
213
|
+
element.title = label;
|
|
214
|
+
return element;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function openViewer(container) {
|
|
218
|
+
const source = sourceFor(container);
|
|
219
|
+
const isDark = document.documentElement.classList.contains("dark") || document.documentElement.classList.contains("electron-dark");
|
|
220
|
+
const debug = localStorage.getItem("codexPlusMermaidDebug") === "1";
|
|
221
|
+
const html = source
|
|
222
|
+
? viewerHtml({ source, isDark, mermaidModuleUrl: assetUrl("mermaid.core-eIokQLcr.js"), debug })
|
|
223
|
+
: `<!doctype html><meta charset="utf-8"><body>${escapeHtml("No Mermaid source was found.")}</body>`;
|
|
224
|
+
CodexPlus.native.request("mermaid/openViewer", { html }).catch(() => {});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function decorate(container) {
|
|
228
|
+
if (container.querySelector(`:scope > .${BUTTON_CLASS}`)) return;
|
|
229
|
+
container.style.position ||= "relative";
|
|
230
|
+
const control = button("Open Mermaid diagram fullscreen");
|
|
231
|
+
control.addEventListener("click", () => openViewer(container));
|
|
232
|
+
container.prepend(control);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function decorateAll(root = document) {
|
|
236
|
+
for (const container of root.querySelectorAll(SELECTOR)) decorate(container);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
CodexPlus.registerPlugin(
|
|
240
|
+
CodexPlus.definePlugin({
|
|
241
|
+
id: "mermaidFullscreen",
|
|
242
|
+
name: "Mermaid Fullscreen Viewer",
|
|
243
|
+
description: "Adds a separate fullscreen viewer with zoom controls to rendered Mermaid diagrams.",
|
|
244
|
+
required: true,
|
|
245
|
+
styles:
|
|
246
|
+
`[data-codex-plus-mermaid-diagram]{position:relative}` +
|
|
247
|
+
`.${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
|
+
`.${BUTTON_CLASS}::before,.${BUTTON_CLASS}::after{content:"";position:absolute;width:.42rem;height:.42rem;border-color:currentColor;border-style:solid}` +
|
|
249
|
+
`.${BUTTON_CLASS}::before{right:.42rem;top:.42rem;border-width:2px 2px 0 0}` +
|
|
250
|
+
`.${BUTTON_CLASS}::after{left:.42rem;bottom:.42rem;border-width:0 0 2px 2px}` +
|
|
251
|
+
`.${BUTTON_CLASS}:hover,.${BUTTON_CLASS}:focus-visible{opacity:1;outline:2px solid var(--color-token-focus-border,#3b82f6);outline-offset:2px}` +
|
|
252
|
+
`:root.dark .${BUTTON_CLASS},:root.electron-dark .${BUTTON_CLASS}{background:var(--color-background-elevated-primary,#111);color:var(--color-token-foreground,#fff);box-shadow:0 2px 10px rgba(0,0,0,.45)}`,
|
|
253
|
+
exports: { decorateAll, openViewer },
|
|
254
|
+
start(api) {
|
|
255
|
+
api.ui.mermaid.decorateDiagram(() => ({ "data-codex-plus-mermaid-diagram": "" }));
|
|
256
|
+
const install = () => {
|
|
257
|
+
if (!document.body) return;
|
|
258
|
+
decorateAll();
|
|
259
|
+
const observer = new MutationObserver((records) => {
|
|
260
|
+
for (const record of records) {
|
|
261
|
+
for (const node of record.addedNodes) {
|
|
262
|
+
if (node.nodeType !== Node.ELEMENT_NODE) continue;
|
|
263
|
+
if (node.matches?.(SELECTOR)) decorate(node);
|
|
264
|
+
decorateAll(node);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
269
|
+
};
|
|
270
|
+
if (document.body) install();
|
|
271
|
+
else document.addEventListener("DOMContentLoaded", install, { once: true });
|
|
272
|
+
},
|
|
273
|
+
}),
|
|
274
|
+
);
|
|
275
|
+
})();
|
package/src/runtime/runtime.js
CHANGED
|
@@ -330,10 +330,15 @@
|
|
|
330
330
|
composer: { surfaceDecorators: [], decorateSurface(fn) { this.surfaceDecorators.push(fn); return fn; }, surfaceProps(props) { return applyDecorators(props, this.surfaceDecorators); } },
|
|
331
331
|
about: { buildInfo: [], addBuildInfo(fn) { this.buildInfo.push(fn); return fn; } },
|
|
332
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
|
+
},
|
|
333
338
|
},
|
|
334
339
|
commands: { register: registerCommand, run: runCommand, all: () => Array.from(commands.values()), menuItems: (group) => Array.from(commands.values()).filter((command) => commandGroups(command).includes(group)) },
|
|
335
340
|
settings: { define: defineSettings },
|
|
336
|
-
native: { async request(method, params) { return globalObject.CodexPlusHost?.nativeRequest?.(method, params); } },
|
|
341
|
+
native: { async request(method, params) { return globalObject.codexPlusNative?.request?.(method, params) ?? globalObject.CodexPlusHost?.nativeRequest?.(method, params); } },
|
|
337
342
|
styles: { register: registerStyle, setRootVars },
|
|
338
343
|
};
|
|
339
344
|
|
|
@@ -348,6 +353,7 @@
|
|
|
348
353
|
"plugins/userBubbleColors.js",
|
|
349
354
|
"plugins/projectColors.js",
|
|
350
355
|
"plugins/sidebarNameBlur.js",
|
|
356
|
+
"plugins/mermaidFullscreen.js",
|
|
351
357
|
];
|
|
352
358
|
|
|
353
359
|
if (typeof document !== "undefined") {
|