codex-plus-patcher 0.4.1 → 0.6.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 CHANGED
@@ -29,7 +29,11 @@ patches plus readable runtime plugins:
29
29
  - add diagnostic detail for selected app-shell errors
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
+ - show the active project path in the thread header with a copy action
32
33
  - add the `Toggle sidebar blur` command palette entry to blur sidebar chat and project names for the current session
34
+ - add an `Open Developer Tools` panels command for opening the current Codex window's DevTools
35
+ - add the `Focus project selector` command and `CmdOrCtrl+.` shortcut for new-chat project selection
36
+ - add a fullscreen Mermaid diagram viewer with zoom controls
33
37
 
34
38
  The generated app includes a readable Codex Plus runtime under
35
39
  `webview/assets/codex-plus/`. Versioned ASAR patches install the runtime,
@@ -111,6 +115,13 @@ Print the machine-readable result:
111
115
  codex-plus-patcher apply --dry-run --json
112
116
  ```
113
117
 
118
+ Inspect menu-related patch markers in a generated app:
119
+
120
+ ```bash
121
+ codex-plus-patcher menu-diagnostics \
122
+ --asar "~/Applications/Codex Plus.app/Contents/Resources/app.asar"
123
+ ```
124
+
114
125
  For local development of the CLI wrapper:
115
126
 
116
127
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-plus-patcher",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "private": false,
5
5
  "description": "Patch queue tool for building a local Codex Plus.app from an installed Codex.app.",
6
6
  "repository": {
@@ -28,6 +28,7 @@
28
28
  },
29
29
  "license": "Apache-2.0",
30
30
  "dependencies": {
31
+ "fzf": "0.5.2",
31
32
  "ora": "^9.4.0"
32
33
  }
33
34
  }
package/src/cli.js CHANGED
@@ -62,6 +62,7 @@ function helpText() {
62
62
  return `Usage:
63
63
  codex-plus-patcher
64
64
  codex-plus-patcher apply [options]
65
+ codex-plus-patcher menu-diagnostics --asar <path> [--json]
65
66
  codex-plus-patcher asar-list --asar <path> [--contains <text>] [--json]
66
67
  codex-plus-patcher asar-cat --asar <path> --file <asar-path> [--json]
67
68
 
@@ -156,6 +157,67 @@ function readAsarFile({ asar, file }) {
156
157
  return { asar, file, size, content };
157
158
  }
158
159
 
160
+ function readPackedEntry(archive, node) {
161
+ if (node.unpacked) return null;
162
+ const size = Number(node.size || 0);
163
+ const start = archive.dataStart + Number(node.offset || 0);
164
+ return archive.buffer.subarray(start, start + size).toString("utf8");
165
+ }
166
+
167
+ function menuDiagnostics({ asar }) {
168
+ if (!asar) throw new Error("--asar is required");
169
+ const archive = readAsar(asar);
170
+ const files = walkFiles(archive.header);
171
+ const commandId = "codexPlusOpenDevTools";
172
+ const menuTitle = "Open Developer Tools";
173
+ const commandMetadataFiles = [];
174
+ const nativeBridgeFiles = [];
175
+ const runtimePluginFiles = [];
176
+ const applicationMenuFiles = [];
177
+
178
+ for (const [file, node] of files) {
179
+ if (!file.endsWith(".js")) continue;
180
+ const content = readPackedEntry(archive, node);
181
+ if (content == null) continue;
182
+ const hasDevToolsCommand = content.includes(commandId);
183
+ const hasMenuTitle = content.includes(menuTitle);
184
+ const hasToggleBottomPanel = content.includes("Toggle Bottom Panel") || content.includes("toggleBottomPanel");
185
+ const hasPanelsGroup = content.includes("commandMenuGroupKey:`panels`") || content.includes('commandMenuGroupKey:"panels"');
186
+ const hasNativeBridge = content.includes("devtools/open") || content.includes("CPXOpenDevTools");
187
+ const hasRuntimePlugin = file.endsWith("/devTools.js") || content.includes('id: "devTools"');
188
+ const hasApplicationMenu = content.includes("Menu.setApplicationMenu") || content.includes("refreshApplicationMenu");
189
+
190
+ if (hasPanelsGroup || hasDevToolsCommand || file.includes("electron-menu-shortcuts")) {
191
+ commandMetadataFiles.push({
192
+ file,
193
+ hasDevToolsCommand,
194
+ hasMenuTitle,
195
+ hasToggleBottomPanel,
196
+ hasPanelsGroup,
197
+ });
198
+ }
199
+ if (hasNativeBridge) nativeBridgeFiles.push({ file, hasDevToolsOpenRequest: content.includes("devtools/open"), hasOpenDevToolsCall: content.includes("openDevTools") });
200
+ if (hasRuntimePlugin) runtimePluginFiles.push({ file, hasDevToolsCommand, hasDevToolsOpenRequest: content.includes("devtools/open") });
201
+ if (hasApplicationMenu) applicationMenuFiles.push({ file, hasDiagnosticsHook: content.includes("CPXLogMenuDiagnostics"), hasDevToolsCommand });
202
+ }
203
+
204
+ return {
205
+ asar,
206
+ commandId,
207
+ menuTitle,
208
+ commandMetadataFiles,
209
+ nativeBridgeFiles,
210
+ runtimePluginFiles,
211
+ applicationMenuFiles,
212
+ summary: {
213
+ commandMetadataFilesWithCommand: commandMetadataFiles.filter((entry) => entry.hasDevToolsCommand).map((entry) => entry.file),
214
+ nativeBridgeFilesWithRequest: nativeBridgeFiles.filter((entry) => entry.hasDevToolsOpenRequest).map((entry) => entry.file),
215
+ runtimePluginFilesWithCommand: runtimePluginFiles.filter((entry) => entry.hasDevToolsCommand).map((entry) => entry.file),
216
+ applicationMenuFilesWithDiagnostics: applicationMenuFiles.filter((entry) => entry.hasDiagnosticsHook).map((entry) => entry.file),
217
+ },
218
+ };
219
+ }
220
+
159
221
  function formatAsarListResult(result) {
160
222
  return result.files.length > 0 ? `${result.files.join("\n")}\n` : "";
161
223
  }
@@ -164,6 +226,34 @@ function formatAsarCatResult(result) {
164
226
  return result.content;
165
227
  }
166
228
 
229
+ function formatMenuDiagnosticsResult(result) {
230
+ const lines = [
231
+ `ASAR: ${result.asar}`,
232
+ `Command: ${result.commandId}`,
233
+ "",
234
+ "Command metadata bundles:",
235
+ ...result.commandMetadataFiles.map((entry) =>
236
+ `- ${entry.file}: command=${entry.hasDevToolsCommand ? "yes" : "no"}, title=${entry.hasMenuTitle ? "yes" : "no"}, bottomPanel=${entry.hasToggleBottomPanel ? "yes" : "no"}, panels=${entry.hasPanelsGroup ? "yes" : "no"}`,
237
+ ),
238
+ "",
239
+ "Native bridge bundles:",
240
+ ...result.nativeBridgeFiles.map((entry) =>
241
+ `- ${entry.file}: request=${entry.hasDevToolsOpenRequest ? "yes" : "no"}, openDevTools=${entry.hasOpenDevToolsCall ? "yes" : "no"}`,
242
+ ),
243
+ "",
244
+ "Runtime plugin bundles:",
245
+ ...result.runtimePluginFiles.map((entry) =>
246
+ `- ${entry.file}: command=${entry.hasDevToolsCommand ? "yes" : "no"}, request=${entry.hasDevToolsOpenRequest ? "yes" : "no"}`,
247
+ ),
248
+ "",
249
+ "Application menu bundles:",
250
+ ...result.applicationMenuFiles.map((entry) =>
251
+ `- ${entry.file}: diagnosticsHook=${entry.hasDiagnosticsHook ? "yes" : "no"}, command=${entry.hasDevToolsCommand ? "yes" : "no"}`,
252
+ ),
253
+ ];
254
+ return `${lines.join("\n")}\n`;
255
+ }
256
+
167
257
  function formatError(error, { debug = false } = {}) {
168
258
  if (debug || process.env.CODEX_PLUS_PATCHER_DEBUG === "1") return error.stack || error.message || String(error);
169
259
  return `Error: ${error.message || String(error)}`;
@@ -232,6 +322,11 @@ async function main() {
232
322
  process.stdout.write(args.json ? `${JSON.stringify(result, null, 2)}\n` : formatAsarCatResult(result));
233
323
  return;
234
324
  }
325
+ if (args.command === "menu-diagnostics") {
326
+ const result = menuDiagnostics(args);
327
+ process.stdout.write(args.json ? `${JSON.stringify(result, null, 2)}\n` : formatMenuDiagnosticsResult(result));
328
+ return;
329
+ }
235
330
  if (args.command !== "apply") throw new Error(`Unknown command: ${args.command}`);
236
331
 
237
332
  const patchSets = await loadPatchSets(args);
@@ -266,10 +361,12 @@ module.exports = {
266
361
  formatAsarCatResult,
267
362
  formatAsarListResult,
268
363
  formatError,
364
+ formatMenuDiagnosticsResult,
269
365
  formatResult,
270
366
  helpText,
271
367
  listAsarFiles,
272
368
  loadPatchSets,
369
+ menuDiagnostics,
273
370
  parseArgs,
274
371
  readAsarFile,
275
372
  requirePatchSetModule,
@@ -15,6 +15,8 @@ module.exports = buildCodexPlusPatchSet({
15
15
  "threadSidePanelTabs": "webview/assets/thread-side-panel-tabs-D0dd27Zf.js",
16
16
  "userMessageAttachments": "webview/assets/user-message-attachments-CgyXEK9U.js",
17
17
  "composer": "webview/assets/composer-CCuv6v-2.js",
18
+ "localActiveWorkspaceRootDropdown": "webview/assets/local-active-workspace-root-dropdown-ymhXI2RF.js",
19
+ "runCommand": "webview/assets/run-command-D6apgII3.js",
18
20
  "localTaskRow": "webview/assets/local-task-row-vTrSC6Rc.js",
19
21
  "keyboardShortcutsSearchInput": "webview/assets/keyboard-shortcuts-search-input-DjVpifwp.js",
20
22
  "src": "src-C7fSIbpz.js",
@@ -15,6 +15,8 @@ module.exports = buildCodexPlusPatchSet({
15
15
  "threadSidePanelTabs": "webview/assets/thread-side-panel-tabs-D0dd27Zf.js",
16
16
  "userMessageAttachments": "webview/assets/user-message-attachments-CgyXEK9U.js",
17
17
  "composer": "webview/assets/composer-CCuv6v-2.js",
18
+ "localActiveWorkspaceRootDropdown": "webview/assets/local-active-workspace-root-dropdown-ymhXI2RF.js",
19
+ "runCommand": "webview/assets/run-command-D6apgII3.js",
18
20
  "localTaskRow": "webview/assets/local-task-row-vTrSC6Rc.js",
19
21
  "keyboardShortcutsSearchInput": "webview/assets/keyboard-shortcuts-search-input-DjVpifwp.js",
20
22
  "src": "src-C7fSIbpz.js",
@@ -11,11 +11,18 @@ module.exports = buildCodexPlusPatchSet({
11
11
  "appShell": "webview/assets/app-shell-0b-x_r3Z.js",
12
12
  "errorBoundary": "webview/assets/error-boundary-BOla93vo.js",
13
13
  "generalSettings": "webview/assets/general-settings-U7DFIZBC.js",
14
+ "header": "webview/assets/header-DgzE38hF.js",
15
+ "threadPageHeader": "webview/assets/thread-page-header-D_hZ50OA.js",
16
+ "localConversationPage": "webview/assets/local-conversation-page-dVDt8SxG.js",
17
+ "threadContext": "webview/assets/thread-context-B0hBrRyZ.js",
14
18
  "sidebarProjectHoverCardSourceRows": "webview/assets/sidebar-project-hover-card-source-rows-DtE7St1r.js",
15
19
  "threadSidePanelTabs": "webview/assets/thread-side-panel-tabs-CLuB2SaS.js",
16
20
  "userMessageAttachments": "webview/assets/user-message-attachments-5G1ZKim-.js",
17
21
  "composer": "webview/assets/composer-DlMDPaCL.js",
22
+ "localActiveWorkspaceRootDropdown": "webview/assets/local-active-workspace-root-dropdown-B28GluSz.js",
23
+ "runCommand": "webview/assets/run-command-B0E8hx7Q.js",
18
24
  "localTaskRow": "webview/assets/local-task-row-CoPNn6SW.js",
25
+ "mermaidDiagramShell": "webview/assets/mermaid-diagram-shell-BO-t9BGx.js",
19
26
  "keyboardShortcutsSearchInput": "webview/assets/keyboard-shortcuts-search-input-C1dmntOi.js",
20
27
  "src": "src-l0hbMZ-p.js",
21
28
  "sidebarThreadKeys": "sidebar-thread-keys-Ch_amVKz.js",
@@ -7,15 +7,23 @@ module.exports = buildCodexPlusPatchSet({
7
7
  "asarSha256": "7f45c6a6bad9c6fabe2226e8f7e0aae3792eca8c59f24305b5f2996ee4b37e40",
8
8
  "files": {
9
9
  "main": ".vite/build/main-dSxbxAhH.js",
10
+ "electronCommandSource": ".vite/build/src-DBVh5FZA.js",
10
11
  "appMain": "webview/assets/app-main-Dldh3K_n.js",
11
12
  "appShell": "webview/assets/app-shell-0b-x_r3Z.js",
12
13
  "errorBoundary": "webview/assets/error-boundary-BOla93vo.js",
13
14
  "generalSettings": "webview/assets/general-settings-U7DFIZBC.js",
15
+ "header": "webview/assets/header-DgzE38hF.js",
16
+ "threadPageHeader": "webview/assets/thread-page-header-D_hZ50OA.js",
17
+ "localConversationPage": "webview/assets/local-conversation-page-dVDt8SxG.js",
18
+ "threadContext": "webview/assets/thread-context-B0hBrRyZ.js",
14
19
  "sidebarProjectHoverCardSourceRows": "webview/assets/sidebar-project-hover-card-source-rows-DtE7St1r.js",
15
20
  "threadSidePanelTabs": "webview/assets/thread-side-panel-tabs-CLuB2SaS.js",
16
21
  "userMessageAttachments": "webview/assets/user-message-attachments-5G1ZKim-.js",
17
22
  "composer": "webview/assets/composer-DlMDPaCL.js",
23
+ "localActiveWorkspaceRootDropdown": "webview/assets/local-active-workspace-root-dropdown-B28GluSz.js",
24
+ "runCommand": "webview/assets/run-command-B0E8hx7Q.js",
18
25
  "localTaskRow": "webview/assets/local-task-row-CoPNn6SW.js",
26
+ "mermaidDiagramShell": "webview/assets/mermaid-diagram-shell-BO-t9BGx.js",
19
27
  "keyboardShortcutsSearchInput": "webview/assets/keyboard-shortcuts-search-input-C1dmntOi.js",
20
28
  "src": "src-l0hbMZ-p.js",
21
29
  "sidebarThreadKeys": "sidebar-thread-keys-Ch_amVKz.js",
@@ -1,24 +1,38 @@
1
1
  const { codexPlusRuntimeAssets } = require("../../runtime/assets");
2
2
  const { replaceOnce } = require("./replace");
3
3
  const { makePatchSet } = require("./make-patch-set");
4
+ const {
5
+ patchLocalActiveWorkspaceRootDropdownProjectSelectorShortcut,
6
+ patchRunCommandProjectSelectorShortcut,
7
+ } = require("./project-selector-shortcut-patch");
4
8
 
5
9
  function buildCodexPlusPatchSet(config) {
6
10
  const oldTitle = "<title>Codex</title>";
7
11
  const newTitle = '<title>Codex Plus</title><script src="./assets/codex-plus/runtime.js"></script>';
8
12
  const titleFile = "webview/index.html";
9
13
  const workerFile = ".vite/build/worker.js";
14
+ const preloadFile = ".vite/build/preload.js";
10
15
  const files = config.files;
11
16
  const anchors = config.anchors;
12
17
  const mainFile = files.main;
18
+ const electronCommandSourceFile = files.electronCommandSource;
13
19
  const appMainFile = files.appMain;
14
20
  const appShellFile = files.appShell;
15
21
  const errorBoundaryFile = files.errorBoundary;
16
22
  const generalSettingsFile = files.generalSettings;
17
23
  const sidebarProjectHoverCardSourceRowsFile = files.sidebarProjectHoverCardSourceRows;
24
+ const headerFile = files.header;
25
+ const threadPageHeaderFile = files.threadPageHeader;
26
+ const localConversationPageFile = files.localConversationPage;
27
+ const threadContextFile = files.threadContext;
28
+ const threadContextImportFile = threadContextFile?.split("/").pop();
18
29
  const threadSidePanelTabsFile = files.threadSidePanelTabs;
19
30
  const userMessageAttachmentsFile = files.userMessageAttachments;
20
31
  const composerFile = files.composer;
32
+ const localActiveWorkspaceRootDropdownFile = files.localActiveWorkspaceRootDropdown;
33
+ const runCommandFile = files.runCommand;
21
34
  const localTaskRowFile = files.localTaskRow;
35
+ const mermaidDiagramShellFile = files.mermaidDiagramShell;
22
36
  const electronMenuShortcutsFile = files.electronMenuShortcuts;
23
37
  const keyboardShortcutsSearchInputFile = files.keyboardShortcutsSearchInput;
24
38
  const srcFile = files.src;
@@ -128,6 +142,15 @@ const codexPlusSubrepoDiffHelpers = `
128
142
  const codexPlusDiagnosticHelpers = `
129
143
  function CPXDiagnosticDetails(e){return window.CodexPlus?.ui?.errors?.renderDetails?.(e)??null}`;
130
144
 
145
+ const codexPlusMermaidHelpers = `
146
+ function CPXMermaidDiagramProps(e){return window.CodexPlus?.ui?.mermaid?.diagramProps?.(e)}`;
147
+
148
+ const codexPlusThreadHeaderHelpers = `
149
+ function CPXThreadHeaderAccessories(e){return window.CodexPlus?.ui?.threadHeader?.renderAccessories?.(e)??null}`;
150
+
151
+ const codexPlusNativeMainHelpers = `
152
+ let CPXNativeMenuItems=[],CPXRefreshApplicationMenu=null;function CPXMenuSnapshot(e){return e?.items?.map(e=>({id:e.id,label:e.label,enabled:e.enabled,visible:e.visible,accelerator:e.accelerator,submenu:CPXMenuSnapshot(e.submenu)}))}function CPXLogMenuDiagnostics(){try{let e=CPXMenuSnapshot(a.Menu.getApplicationMenu())??[],t=JSON.stringify(e),n=t.includes(\`codexPlusOpenDevTools\`)||t.includes(\`Open Developer Tools\`);if(process.env.CODEX_PLUS_MENU_DIAGNOSTICS===\`1\`||!n)console.log(\`[Codex Plus menu diagnostics] \${JSON.stringify({hasOpenDeveloperTools:n,menu:e})}\`)}catch(e){console.log(\`[Codex Plus menu diagnostics] \${JSON.stringify({error:String(e?.message??e)})}\`)}}function CPXOpenDevTools(e){try{let t=e?.sender;if(typeof t?.openDevTools!==\`function\`)return{ok:!1};return t.openDevTools(),{ok:!0}}catch{return{ok:!1}}}function CPXFocusedEvent(){let e=a.BrowserWindow.getFocusedWindow();return e&&!e.isDestroyed()?{sender:e.webContents}:null}function CPXRunNativeMenuRequest(e){switch(e?.method){case\`devtools/open\`:return CPXOpenDevTools(CPXFocusedEvent());default:return{ok:!1}}}function CPXNativeMenuTemplateItems(e){return CPXNativeMenuItems.filter(t=>t.menuId===e).map(e=>({id:e.id,label:e.label,click:()=>{CPXRunNativeMenuRequest(e.nativeRequest)}}))}function CPXRegisterNativeMenuItem(e){if(e?.id==null||e?.menuId==null||e?.label==null||e?.nativeRequest?.method==null)return{ok:!1};let t={id:String(e.id),menuId:String(e.menuId),label:String(e.label),nativeRequest:{method:String(e.nativeRequest.method),params:e.nativeRequest.params},afterId:e.afterId==null?null:String(e.afterId),afterLabel:e.afterLabel==null?null:String(e.afterLabel)};CPXNativeMenuItems=CPXNativeMenuItems.filter(e=>e.id!==t.id),CPXNativeMenuItems.push(t);try{CPXRefreshApplicationMenu?.()}catch{}return CPXLogMenuDiagnostics(),{ok:!0}}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\`native-menu/register-item\`:return CPXRegisterNativeMenuItem(n.params);case\`devtools/open\`:return CPXOpenDevTools(t);case\`mermaid/openViewer\`:return CPXOpenMermaidViewer(n.params);default:return{ok:!1}}})}`;
153
+
131
154
  function patchThreadSidePanelTabs(text) {
132
155
  let patched = replaceOnce(
133
156
  text,
@@ -376,6 +399,91 @@ function patchSidebarProjectHoverCardSourceRows(text) {
376
399
  );
377
400
  }
378
401
 
402
+ function patchHeader(text) {
403
+ let patched = replaceOnce(
404
+ text,
405
+ `import{Z as r,a as i,s as a}from"./app-scope-CWE-zIhQ.js";`,
406
+ `import{Z as r,a as i,a as CPX_readAtom,s as a}from"./app-scope-CWE-zIhQ.js";`,
407
+ "thread header atom reader alias import anchor",
408
+ );
409
+ patched = replaceOnce(
410
+ patched,
411
+ `import{t as ee}from"./tooltip-B-u9JAuV.js";`,
412
+ `import{t as ee,t as CPX_Tooltip}from"./tooltip-B-u9JAuV.js";`,
413
+ "thread header tooltip alias import anchor",
414
+ );
415
+ patched = replaceOnce(
416
+ patched,
417
+ `import{t as _e}from"./dock-DAmmeMut.js";`,
418
+ `import{t as _e}from"./dock-DAmmeMut.js";import{n as CPX_headerCwd,i as CPX_headerHostId}from"./${threadContextImportFile}";`,
419
+ "thread header context import anchor",
420
+ );
421
+ patched = replaceOnce(
422
+ patched,
423
+ "function lt(e){let t=(0,Z.c)(68),",
424
+ `${codexPlusThreadHeaderHelpers}function lt(e){let t=(0,Z.c)(72),`,
425
+ "thread header accessory helper insertion anchor",
426
+ );
427
+ patched = replaceOnce(
428
+ patched,
429
+ "let C;t[36]!==c||t[37]!==g||t[38]!==i?(C=(0,Q.jsx)(`div`,{className:`mr-3 line-clamp-1 flex min-w-0 flex-1 items-center gap-1 truncate`,style:{viewTransitionName:`header-title`},children:i?(0,Q.jsxs)(`div`,{className:`flex min-w-0 flex-1 items-center gap-1`,children:[(0,Q.jsx)(mt,{onClick:c}),(0,Q.jsx)(x,{color:`ghostActive`,type:`button`,onClick:u,className:`min-w-0 flex-1 truncate !px-0 !py-0 text-left text-sm text-token-foreground hover:!bg-transparent hover:opacity-80 electron:font-medium`,children:(0,Q.jsx)(`span`,{className:`truncate`,children:i})})]}):(0,Q.jsx)(`span`,{className:`text-token-description-foreground`,children:(0,Q.jsx)(pt,{mergedTasks:g,onBack:c,showBackButton:!0})})}),t[36]=c,t[37]=g,t[38]=i,t[39]=C):C=t[39];",
430
+ "let CPX_headerContext={cwd:CPX_readAtom(CPX_headerCwd),hostId:CPX_readAtom(CPX_headerHostId)},CPX_headerAccessories=CPXThreadHeaderAccessories({context:CPX_headerContext,deps:{jsx:Q.jsx,jsxs:Q.jsxs,Tooltip:CPX_Tooltip}});let C;t[36]!==c||t[37]!==g||t[38]!==i||t[68]!==CPX_headerAccessories?(C=(0,Q.jsx)(`div`,{className:`mr-3 line-clamp-1 flex min-w-0 flex-1 items-center gap-1 truncate`,style:{viewTransitionName:`header-title`},children:i?(0,Q.jsxs)(`div`,{className:`flex min-w-0 flex-1 items-center gap-1`,children:[(0,Q.jsx)(mt,{onClick:c}),(0,Q.jsx)(x,{color:`ghostActive`,type:`button`,onClick:u,className:`min-w-0 flex-1 truncate !px-0 !py-0 text-left text-sm text-token-foreground hover:!bg-transparent hover:opacity-80 electron:font-medium`,children:(0,Q.jsx)(`span`,{className:`truncate`,children:i})}),CPX_headerAccessories]}):(0,Q.jsx)(`span`,{className:`text-token-description-foreground`,children:(0,Q.jsx)(pt,{mergedTasks:g,onBack:c,showBackButton:!0})})}),t[36]=c,t[37]=g,t[38]=i,t[68]=CPX_headerAccessories,t[39]=C):C=t[39];",
431
+ "thread header accessory render anchor",
432
+ );
433
+ patched = replaceOnce(
434
+ patched,
435
+ "t[53]!==A||t[54]!==b||t[55]!==S||t[56]!==C?(M=(0,Q.jsxs)(`div`,{className:b,children:[S,C,A]}),t[53]=A,t[54]=b,t[55]=S,t[56]=C,t[57]=M):M=t[57]",
436
+ "t[53]!==A||t[54]!==b||t[55]!==S||t[56]!==C?(M=(0,Q.jsxs)(`div`,{className:b,children:[S,C,A]}),t[53]=A,t[54]=b,t[55]=S,t[56]=C,t[57]=M):M=t[57]",
437
+ "thread header accessory mount anchor",
438
+ );
439
+ return patched;
440
+ }
441
+
442
+ function patchThreadPageHeader(text) {
443
+ let patched = text;
444
+ patched = replaceOnce(
445
+ patched,
446
+ "function c(e){let t=(0,o.c)(21),",
447
+ `${codexPlusThreadHeaderHelpers}function c(e){let t=(0,o.c)(24),`,
448
+ "thread page header helper insertion anchor",
449
+ );
450
+ patched = replaceOnce(
451
+ patched,
452
+ "let t=(0,o.c)(24),{start:c,startActions:l,env:u,secondary:d,trailing:f,hostConfig:p}=e,m;",
453
+ "let t=(0,o.c)(24),{start:c,startActions:l,env:u,secondary:d,trailing:f,hostConfig:p,cwd:CPX_headerCwd}=e,CPX_headerContext={cwd:CPX_headerCwd,hostId:p?.id??null,header:{env:u,hostDisplayName:p?.display_name??null,startText:typeof c==`string`?c:null,secondaryText:typeof d==`string`?d:null,hasTrailing:f!=null}},CPX_headerAccessories=CPXThreadHeaderAccessories({context:CPX_headerContext,deps:{jsx:s.jsx,jsxs:s.jsxs}}),m;",
454
+ "thread page header accessory render anchor",
455
+ );
456
+ patched = replaceOnce(
457
+ patched,
458
+ "t[8]!==l||t[9]!==v||t[10]!==y||t[11]!==b?(x=(0,s.jsxs)(`div`,{className:`text-md flex min-w-0 items-center gap-2 truncate text-base electron:font-medium`,children:[v,y,b,l]}),t[8]=l,t[9]=v,t[10]=y,t[11]=b,t[12]=x):x=t[12]",
459
+ "t[8]!==l||t[9]!==v||t[10]!==y||t[11]!==b||t[21]!==CPX_headerAccessories?(x=(0,s.jsxs)(`div`,{className:`text-md flex min-w-0 items-center gap-2 truncate text-base electron:font-medium`,children:[v,y,b,CPX_headerAccessories,l]}),t[8]=l,t[9]=v,t[10]=y,t[11]=b,t[21]=CPX_headerAccessories,t[12]=x):x=t[12]",
460
+ "thread page header accessory mount anchor",
461
+ );
462
+ return patched;
463
+ }
464
+
465
+ function patchLocalConversationPageHeader(text) {
466
+ let patched = replaceOnce(
467
+ text,
468
+ "function Tt(e){let t=(0,Y.c)(42),",
469
+ `${codexPlusThreadHeaderHelpers}function Tt(e){let t=(0,Y.c)(45),`,
470
+ "local conversation header helper insertion anchor",
471
+ );
472
+ patched = replaceOnce(
473
+ patched,
474
+ "let t=(0,Y.c)(45),{conversationId:n,getConversationMarkdown:r,markdownParentConversationId:a,title:o,titleSuffix:s,cwd:c,canPin:l,hideProjectMetadata:d,hideForkActions:f}=e,p=l===void 0?!0:l,m=d===void 0?!1:d,h=A(),g;",
475
+ "let t=(0,Y.c)(45),{conversationId:n,getConversationMarkdown:r,markdownParentConversationId:a,title:o,titleSuffix:s,cwd:c,canPin:l,hideProjectMetadata:d,hideForkActions:f}=e,CPX_headerContext={cwd:c,hostId:u(i(O,n)).id,header:{surface:`local-conversation`,titleText:typeof o==`string`?o:null}},CPX_headerAccessories=CPXThreadHeaderAccessories({context:CPX_headerContext,deps:{jsx:Z.jsx,jsxs:Z.jsxs}}),p=l===void 0?!0:l,m=d===void 0?!1:d,h=A(),g;",
476
+ "local conversation header accessory render anchor",
477
+ );
478
+ patched = replaceOnce(
479
+ patched,
480
+ "t[38]!==F||t[39]!==I||t[40]!==L?(z=(0,Z.jsx)(`div`,{className:`draggable grid w-full min-w-0 grid-cols-[minmax(0,1fr)] items-center gap-x-4 electron:h-toolbar extension:py-row-y`,children:(0,Z.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2 truncate text-base electron:font-medium`,children:[F,I,L,R]})}),t[38]=F,t[39]=I,t[40]=L,t[41]=z):z=t[41]",
481
+ "t[38]!==F||t[39]!==I||t[40]!==L||t[42]!==CPX_headerAccessories?(z=(0,Z.jsx)(`div`,{className:`draggable grid w-full min-w-0 grid-cols-[minmax(0,1fr)] items-center gap-x-4 electron:h-toolbar extension:py-row-y`,children:(0,Z.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2 truncate text-base electron:font-medium`,children:[F,I,L,CPX_headerAccessories,R]})}),t[38]=F,t[39]=I,t[40]=L,t[42]=CPX_headerAccessories,t[41]=z):z=t[41]",
482
+ "local conversation header accessory mount anchor",
483
+ );
484
+ return patched;
485
+ }
486
+
379
487
  const codexPlusProjectColorHelpers = `
380
488
  function CPXHostProjectRowProps(e){return window.CodexPlus?.ui?.sidebar?.projectRowProps?.({project:e})}function CPXHostThreadRowProps(e){return window.CodexPlus?.ui?.sidebar?.threadRowProps?.({project:e})}function CPXHostUserBubbleProps(e){return window.CodexPlus?.ui?.message?.userBubbleProps?.(e)}function CPXHostComposerSurfaceProps(e){return window.CodexPlus?.ui?.composer?.surfaceProps?.(e)}`;
381
489
 
@@ -489,7 +597,7 @@ function patchElectronMenuShortcuts(text) {
489
597
  return replaceOnce(
490
598
  text,
491
599
  "{id:`toggleSidebar`,titleIntlId:`codex.command.toggleSidebar`,descriptionIntlId:`codex.commandDescription.toggleSidebar`,commandMenuGroupKey:`panels`,commandMenu:!0,electron:{menuTitle:`Toggle Sidebar`,menuTitleIntlId:`codex.commandMenuTitle.toggleSidebar`,defaultKeybindings:[{key:`CmdOrCtrl+B`}]}},{id:`toggleBottomPanel`,",
492
- "{id:`toggleSidebar`,titleIntlId:`codex.command.toggleSidebar`,descriptionIntlId:`codex.commandDescription.toggleSidebar`,commandMenuGroupKey:`panels`,commandMenu:!0,electron:{menuTitle:`Toggle Sidebar`,menuTitleIntlId:`codex.commandMenuTitle.toggleSidebar`,defaultKeybindings:[{key:`CmdOrCtrl+B`}]}},...(window.CodexPlus?.ui?.commands?.commandMetadata?.()??[]),{id:`toggleBottomPanel`,",
600
+ "{id:`toggleSidebar`,titleIntlId:`codex.command.toggleSidebar`,descriptionIntlId:`codex.commandDescription.toggleSidebar`,commandMenuGroupKey:`panels`,commandMenu:!0,electron:{menuTitle:`Toggle Sidebar`,menuTitleIntlId:`codex.commandMenuTitle.toggleSidebar`,defaultKeybindings:[{key:`CmdOrCtrl+B`}]}},{id:`codexPlus.focusProjectSelector`,title:`Focus project selector`,description:`Focus or open the new chat project selector`,commandMenuGroupKey:`workspace`,commandMenu:!0,electron:{menuTitle:`Focus project selector`,defaultKeybindings:[{key:`CmdOrCtrl+.`}]}},...(window.CodexPlus?.ui?.commands?.commandMetadata?.()?.filter?.(e=>e.id!==`codexPlus.focusProjectSelector`)??[]),{id:`toggleBottomPanel`,",
493
601
  "sidebar blur command palette metadata anchor",
494
602
  );
495
603
  }
@@ -536,6 +644,66 @@ function patchLocalTaskRow(text) {
536
644
  );
537
645
  }
538
646
 
647
+ function patchMermaidDiagramShell(text) {
648
+ let patched = replaceOnce(
649
+ text,
650
+ "function d(e){let t=(0,s.c)(18),{Renderer:n,className:r,code:i,fallback:d,isCodeFenceOpen:f,wideBlockKind:p}=e,",
651
+ `${codexPlusMermaidHelpers}function d(e){let t=(0,s.c)(18),{Renderer:n,className:r,code:i,fallback:d,isCodeFenceOpen:f,wideBlockKind:p}=e,`,
652
+ "mermaid diagram shell helper insertion anchor",
653
+ );
654
+ return replaceOnce(
655
+ patched,
656
+ "O=(0,c.jsx)(`div`,{className:T,\"data-wide-markdown-block\":E,\"data-wide-markdown-block-kind\":p,children:D})",
657
+ "O=(0,c.jsx)(`div`,{className:T,...CPXMermaidDiagramProps({code:i}),\"data-wide-markdown-block\":E,\"data-wide-markdown-block-kind\":p,children:D})",
658
+ "mermaid diagram shell host props anchor",
659
+ );
660
+ }
661
+
662
+ function patchPreloadNativeBridge(text) {
663
+ return replaceOnce(
664
+ text,
665
+ "e.contextBridge.exposeInMainWorld(`codexWindowType`,m),e.contextBridge.exposeInMainWorld(`electronBridge`,D),typeof window<`u`",
666
+ "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`",
667
+ "codex plus native preload bridge anchor",
668
+ );
669
+ }
670
+
671
+ function patchMainNativeBridge(text) {
672
+ let patched = replaceOnce(
673
+ text,
674
+ "function z1(e){return a.ipcMain.handle(Tl,async(t,n)=>{",
675
+ `${codexPlusNativeMainHelpers}function z1(e){return a.ipcMain.handle(Tl,async(t,n)=>{`,
676
+ "codex plus native main helper insertion anchor",
677
+ );
678
+ return replaceOnce(
679
+ patched,
680
+ "v0({buildFlavor:i,getContextForWebContents:N.getContextForWebContents,isTrustedIpcEvent:te,usesOwlAppShell:y}),a.ipcMain.on(kl,",
681
+ "v0({buildFlavor:i,getContextForWebContents:N.getContextForWebContents,isTrustedIpcEvent:te,usesOwlAppShell:y}),CPXRegisterNativeRequest({isTrustedIpcEvent:te}),a.ipcMain.on(kl,",
682
+ "codex plus native main registration anchor",
683
+ );
684
+ }
685
+
686
+ function patchMainMenuDiagnostics(text) {
687
+ let patched = replaceOnce(
688
+ text,
689
+ "He={...b(`toggleSidePanel`),click:async()=>{let e=await y();e&&_.sendMessageToWindow(e,{type:`toggle-diff-panel`})}},Ue=",
690
+ "He={...b(`toggleSidePanel`),click:async()=>{let e=await y();e&&_.sendMessageToWindow(e,{type:`toggle-diff-panel`})}},Ue=",
691
+ "codex plus menu template helper presence anchor",
692
+ );
693
+ patched = replaceOnce(
694
+ patched,
695
+ "He,We,{type:`separator`}",
696
+ "He,We,...CPXNativeMenuTemplateItems(`view-menu`),{type:`separator`}",
697
+ "codex plus view menu template items anchor",
698
+ );
699
+ return replaceOnce(
700
+ patched,
701
+ "me.refreshApplicationMenu(),w(`application menu refreshed`,A),",
702
+ "CPXRefreshApplicationMenu=()=>me.refreshApplicationMenu(),me.refreshApplicationMenu(),CPXLogMenuDiagnostics(),w(`application menu refreshed`,A),",
703
+ "codex plus menu diagnostics refresh anchor",
704
+ );
705
+ }
706
+
539
707
  return makePatchSet({
540
708
  id: config.id,
541
709
  codexVersion: config.codexVersion,
@@ -589,6 +757,14 @@ return makePatchSet({
589
757
  [composerFile, patchComposerProjectColors],
590
758
  ],
591
759
  },
760
+ ...(headerFile ? [{
761
+ id: "project-path-header",
762
+ fileTransforms: [
763
+ [headerFile, patchHeader],
764
+ ...(threadPageHeaderFile ? [[threadPageHeaderFile, patchThreadPageHeader]] : []),
765
+ ...(localConversationPageFile ? [[localConversationPageFile, patchLocalConversationPageHeader]] : []),
766
+ ],
767
+ }] : []),
592
768
  {
593
769
  id: "sidebar-name-blur",
594
770
  fileTransforms: [
@@ -597,6 +773,25 @@ return makePatchSet({
597
773
  [keyboardShortcutsSearchInputFile, patchKeyboardShortcutsSearchInput],
598
774
  ],
599
775
  },
776
+ {
777
+ id: "project-selector-shortcut",
778
+ fileTransforms: [
779
+ [localActiveWorkspaceRootDropdownFile, patchLocalActiveWorkspaceRootDropdownProjectSelectorShortcut],
780
+ [runCommandFile, patchRunCommandProjectSelectorShortcut],
781
+ ],
782
+ },
783
+ ...(mainFile ? [{
784
+ id: "codex-plus-native-bridge",
785
+ fileTransforms: [
786
+ [preloadFile, patchPreloadNativeBridge],
787
+ [mainFile, patchMainNativeBridge],
788
+ ...(electronCommandSourceFile ? [[mainFile, patchMainMenuDiagnostics]] : []),
789
+ ],
790
+ }] : []),
791
+ ...(mermaidDiagramShellFile ? [{
792
+ id: "mermaid-fullscreen-viewer",
793
+ fileTransforms: [[mermaidDiagramShellFile, patchMermaidDiagramShell]],
794
+ }] : []),
600
795
  ],
601
796
  });
602
797
  }
@@ -0,0 +1,54 @@
1
+ const { replaceOnce } = require("./replace");
2
+
3
+ function patchLocalActiveWorkspaceRootDropdownProjectSelectorShortcut(text) {
4
+ let patched = replaceOnce(
5
+ text,
6
+ "Ne=r();function Pe(e){let t=(0,Ne.c)(42),",
7
+ "Ne=r();function CPXProjectSelectorFuzzyFilter(e,t){let n=String(t??``).trim().toLowerCase();return window.CodexPlus?.ui?.projectSelector?.fuzzyFilter?.(e,t)??(n?e.filter(e=>[e.label,e.repositoryData?.rootFolder??``,e.path??``,e.hostDisplayName??``].some(e=>String(e??``).toLowerCase().includes(n))):e)}function CPXProjectSelectorFuzzyHighlight(e,t){return window.CodexPlus?.ui?.projectSelector?.fuzzyHighlight?.({text:e,query:t,jsx:H.jsx})??e}function CPXProjectSelectorAcceptFirst(e,t,n,r){let i=t?.[0];if(e?.key!==`Enter`||String(r??``).trim().length===0||i==null)return;e.preventDefault?.(),e.stopPropagation?.(),n(i.projectId)}function Pe(e){let t=(0,Ne.c)(42),",
8
+ "project selector fuzzy search adapter insertion anchor",
9
+ );
10
+ patched = replaceOnce(
11
+ patched,
12
+ "let e=h.trim().toLowerCase();v=n.filter(t=>{if(!e)return!0;let n=t.repositoryData?.rootFolder??``;return[t.label,n,t.path??``,t.hostDisplayName??``].some(t=>t.toLowerCase().includes(e))});",
13
+ "v=CPXProjectSelectorFuzzyFilter(n,h);",
14
+ "project selector fuzzy search filter anchor",
15
+ );
16
+ patched = replaceOnce(
17
+ patched,
18
+ "S=(0,H.jsx)(ve,{value:h,onChange:o,placeholder:s,className:`mb-1`})",
19
+ "S=(0,H.jsx)(ve,{value:h,onChange:o,onKeyDown:e=>CPXProjectSelectorAcceptFirst(e,v,i,h),placeholder:s,className:`mb-1`})",
20
+ "project selector accept first match keydown anchor",
21
+ );
22
+ patched = replaceOnce(
23
+ patched,
24
+ "(0,H.jsx)(`span`,{className:`truncate`,children:e.label})",
25
+ "(0,H.jsx)(`span`,{className:`truncate`,children:CPXProjectSelectorFuzzyHighlight(e.label,h)})",
26
+ "project selector fuzzy search highlight anchor",
27
+ );
28
+ patched = replaceOnce(
29
+ patched,
30
+ "function Ie(e){let t=(0,Ne.c)(81),",
31
+ "function CPXProjectSelectorTrigger(e,t){return Me.isValidElement(e)?Me.cloneElement(e,{...e.props,\"data-codex-plus-project-selector-trigger\":!0,\"data-codex-plus-project-selector-variant\":t}):e}function Ie(e){let t=(0,Ne.c)(81),",
32
+ "project selector shortcut helper insertion anchor",
33
+ );
34
+ return replaceOnce(
35
+ patched,
36
+ "at=(0,H.jsx)(ye,{open:f,onOpenChange:g,onCloseAutoFocus:Y,align:tt,disabled:O,triggerButton:X,contentWidth:`workspace`,contentMaxHeight:`tall`,children:$})",
37
+ "at=(0,H.jsx)(ye,{open:f,onOpenChange:g,onCloseAutoFocus:Y,align:tt,disabled:O,triggerButton:CPXProjectSelectorTrigger(X,k),contentWidth:`workspace`,contentMaxHeight:`tall`,children:$})",
38
+ "project selector shortcut final dropdown trigger anchor",
39
+ );
40
+ }
41
+
42
+ function patchRunCommandProjectSelectorShortcut(text) {
43
+ return replaceOnce(
44
+ text,
45
+ "],[`openFolder`,()=>{r()}],[`toggleSidebar`,",
46
+ "],[`codexPlus.focusProjectSelector`,()=>{window.CodexPlus?.commands?.run?.(`codexPlus.focusProjectSelector`)}],[`openFolder`,()=>{r()}],[`toggleSidebar`,",
47
+ "project selector shortcut command dispatch anchor",
48
+ );
49
+ }
50
+
51
+ module.exports = {
52
+ patchLocalActiveWorkspaceRootDropdownProjectSelectorShortcut,
53
+ patchRunCommandProjectSelectorShortcut,
54
+ };
@@ -11,7 +11,12 @@ const runtimeFiles = [
11
11
  ["webview/assets/codex-plus/plugins/diagnosticErrors.js", "plugins/diagnosticErrors.js"],
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
+ ["webview/assets/codex-plus/plugins/projectPathHeader.js", "plugins/projectPathHeader.js"],
14
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"],
15
20
  ];
16
21
 
17
22
  function codexPlusRuntimeAssets() {
@@ -0,0 +1,33 @@
1
+ (function () {
2
+ const CodexPlus = window.CodexPlus;
3
+ CodexPlus.registerPlugin(
4
+ CodexPlus.definePlugin({
5
+ id: "devTools",
6
+ name: "Developer Tools",
7
+ description: "Registers the Open Developer Tools command.",
8
+ required: true,
9
+ commands: [
10
+ {
11
+ id: "codexPlusOpenDevTools",
12
+ title: "Open Developer Tools",
13
+ description: "Open DevTools for the current Codex window",
14
+ menu: { groups: ["panels"] },
15
+ palette: { enabled: true, keywords: ["devtools", "developer", "console"] },
16
+ shortcut: { defaultKeybindings: [] },
17
+ run() {
18
+ return CodexPlus.native.request("devtools/open").catch(() => ({ ok: false }));
19
+ },
20
+ },
21
+ ],
22
+ start(api) {
23
+ api.nativeMenus.registerItem({
24
+ id: "codexPlusOpenDevTools",
25
+ menuId: "view-menu",
26
+ afterLabel: "Find",
27
+ label: "Open Developer Tools",
28
+ nativeRequest: { method: "devtools/open" },
29
+ });
30
+ },
31
+ }),
32
+ );
33
+ })();