codex-plus-patcher 0.7.2 → 0.8.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-plus-patcher",
3
- "version": "0.7.2",
3
+ "version": "0.8.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": {
@@ -252,6 +252,70 @@ async function verifyMermaidViewerRender(appCdp, port, { Session = CdpSession, t
252
252
  }
253
253
  }
254
254
 
255
+ async function verifyProjectSelectorShortcutKey(cdp, { wait = delay, timeoutMs = 5000 } = {}) {
256
+ const setup = await cdp.evaluate(`new Promise((resolve) => {
257
+ document.dispatchEvent(new KeyboardEvent("keydown", { bubbles: true, cancelable: true, key: "Escape" }));
258
+ const newChatButton = Array.from(document.querySelectorAll("button")).find((button) => {
259
+ const rect = button.getBoundingClientRect();
260
+ return rect.width > 0 && rect.height > 0 && (button.innerText || "").includes("New chat");
261
+ });
262
+ newChatButton?.click?.();
263
+ let attempts = 0;
264
+ const check = () => {
265
+ const triggerCount = document.querySelectorAll("[data-codex-plus-project-selector-trigger]").length;
266
+ if (triggerCount > 0 || attempts >= 30) {
267
+ resolve({ triggerCount, clickedNewChat: Boolean(newChatButton) });
268
+ return;
269
+ }
270
+ attempts += 1;
271
+ setTimeout(check, 100);
272
+ };
273
+ check();
274
+ })`);
275
+ if (!setup?.triggerCount) {
276
+ return { ok: false, ...setup, message: "Project selector shortcut trigger marker is missing from the main composer" };
277
+ }
278
+
279
+ await cdp.send("Input.dispatchKeyEvent", {
280
+ type: "keyDown",
281
+ key: ".",
282
+ code: "Period",
283
+ windowsVirtualKeyCode: 190,
284
+ nativeVirtualKeyCode: 47,
285
+ modifiers: 4,
286
+ });
287
+ await cdp.send("Input.dispatchKeyEvent", {
288
+ type: "keyUp",
289
+ key: ".",
290
+ code: "Period",
291
+ windowsVirtualKeyCode: 190,
292
+ nativeVirtualKeyCode: 47,
293
+ modifiers: 4,
294
+ });
295
+
296
+ const deadline = Date.now() + timeoutMs;
297
+ let status = null;
298
+ while (Date.now() < deadline) {
299
+ status = await cdp.evaluate(`(() => {
300
+ const searchInput = document.querySelector("input[placeholder='Search projects']");
301
+ const menuCount = document.querySelectorAll("[data-radix-menu-content], [data-radix-popper-content-wrapper], [role='menu']").length;
302
+ return {
303
+ triggerCount: document.querySelectorAll("[data-codex-plus-project-selector-trigger]").length,
304
+ menuCount,
305
+ opened: Boolean(searchInput || menuCount > 0),
306
+ activePlaceholder: document.activeElement?.getAttribute?.("placeholder") ?? "",
307
+ };
308
+ })()`);
309
+ if (status.opened) {
310
+ await cdp.send("Input.dispatchKeyEvent", { type: "keyDown", key: "Escape", code: "Escape", windowsVirtualKeyCode: 27, nativeVirtualKeyCode: 53 });
311
+ await cdp.send("Input.dispatchKeyEvent", { type: "keyUp", key: "Escape", code: "Escape", windowsVirtualKeyCode: 27, nativeVirtualKeyCode: 53 });
312
+ return { ok: true, ...setup, ...status };
313
+ }
314
+ await wait(100);
315
+ }
316
+ return { ok: false, ...setup, ...status, message: `Cmd+. did not open the project selector: ${JSON.stringify(status)}` };
317
+ }
318
+
255
319
  function listRunningAuditApps({
256
320
  targetApp = DEFAULT_TARGET,
257
321
  electronUserDataPath = DEFAULT_ELECTRON_USER_DATA,
@@ -1237,7 +1301,34 @@ function pluginAuditExpression({ includeNativeOpenProbes = false } = {}) {
1237
1301
  if (ranked[0] !== "hassio-dev") throw new Error(`Fuzzy ranking returned ${ranked.join(", ")}`);
1238
1302
  if (highlightCount === 0) throw new Error("Fuzzy match highlight did not render");
1239
1303
  if (selected[0] !== "hassio-dev" || events.length !== 2) throw new Error("Enter-to-first-result adapter did not select first ranked result");
1240
- pass("projectSelectorShortcut", { ...details, ranked, highlightCount, selected });
1304
+ document.dispatchEvent(new KeyboardEvent("keydown", { bubbles: true, cancelable: true, key: "Escape" }));
1305
+ const newChatButton = Array.from(document.querySelectorAll("button")).find((button) => {
1306
+ const rect = button.getBoundingClientRect();
1307
+ return rect.width > 0 && rect.height > 0 && (button.innerText || "").includes("New chat");
1308
+ });
1309
+ newChatButton?.click?.();
1310
+ let triggerCount = 0;
1311
+ for (let attempt = 0; attempt < 20; attempt += 1) {
1312
+ triggerCount = document.querySelectorAll("[data-codex-plus-project-selector-trigger]").length;
1313
+ if (triggerCount > 0) break;
1314
+ await new Promise((resolve) => setTimeout(resolve, 100));
1315
+ }
1316
+ if (triggerCount === 0) throw new Error("Project selector shortcut trigger marker is missing from the main composer");
1317
+ const syntheticShortcut = await new Promise((resolve) => {
1318
+ const event = new KeyboardEvent("keydown", { bubbles: true, cancelable: true, key: ".", metaKey: true });
1319
+ document.dispatchEvent(event);
1320
+ setTimeout(() => {
1321
+ const searchInput = document.querySelector("input[placeholder='Search projects']");
1322
+ const menu = document.querySelector("[data-radix-menu-content], [data-radix-popper-content-wrapper], [role='menu']");
1323
+ resolve({
1324
+ defaultPrevented: event.defaultPrevented,
1325
+ opened: Boolean(searchInput || menu),
1326
+ activePlaceholder: document.activeElement?.getAttribute?.("placeholder") ?? "",
1327
+ });
1328
+ }, 400);
1329
+ });
1330
+ document.dispatchEvent(new KeyboardEvent("keydown", { bubbles: true, cancelable: true, key: "Escape" }));
1331
+ pass("projectSelectorShortcut", { ...details, ranked, highlightCount, selected, triggerCount, syntheticShortcut });
1241
1332
  } catch (error) {
1242
1333
  fail("projectSelectorShortcut", error);
1243
1334
  }
@@ -1310,6 +1401,7 @@ async function runAudit(args, {
1310
1401
  const waitRuntime = operations.waitForLiveRuntime || waitForLiveRuntime;
1311
1402
  const waitAppShell = operations.waitForAppShellMounted || waitForAppShellMounted;
1312
1403
  const verifyMermaidViewer = operations.verifyMermaidViewerRender || verifyMermaidViewerRender;
1404
+ const verifyProjectSelectorShortcut = operations.verifyProjectSelectorShortcutKey || verifyProjectSelectorShortcutKey;
1313
1405
  const cleanupApp = operations.cleanupLaunchedAuditApp || cleanupLaunchedAuditApp;
1314
1406
  const checkStability = operations.checkKeepOpenAppStability || checkKeepOpenAppStability;
1315
1407
  const preflightAudit = operations.auditPreflight || auditPreflight;
@@ -1394,6 +1486,23 @@ async function runAudit(args, {
1394
1486
  "Probed plugins",
1395
1487
  () => cdp.evaluate(pluginAuditExpression({ includeNativeOpenProbes: args.includeNativeOpenProbes })),
1396
1488
  );
1489
+ if (live.pluginResults?.projectSelectorShortcut?.ok) {
1490
+ const shortcut = await withAuditProgress(
1491
+ progress,
1492
+ "Verifying project selector shortcut",
1493
+ "Project selector shortcut opened",
1494
+ () => verifyProjectSelectorShortcut(cdp),
1495
+ );
1496
+ live.pluginResults.projectSelectorShortcut.shortcut = shortcut;
1497
+ if (!shortcut.ok) {
1498
+ live.ok = false;
1499
+ live.pluginResults.projectSelectorShortcut.ok = false;
1500
+ live.failures.push({
1501
+ plugin: "projectSelectorShortcut",
1502
+ message: shortcut.message || "Cmd+. did not open the project selector",
1503
+ });
1504
+ }
1505
+ }
1397
1506
  const shouldProbeMermaidViewer = live.pluginResults?.mermaidFullscreen?.ok;
1398
1507
  const mermaidViewerRender = shouldProbeMermaidViewer
1399
1508
  ? await withAuditProgress(
@@ -1601,5 +1710,6 @@ module.exports = {
1601
1710
  waitForAppShellMounted,
1602
1711
  waitForLiveRuntime,
1603
1712
  verifyMermaidViewerRender,
1713
+ verifyProjectSelectorShortcutKey,
1604
1714
  waitForRendererTarget,
1605
1715
  };
@@ -0,0 +1,45 @@
1
+ const { buildCodexPlusPatchSet } = require("./lib/common-patches");
2
+
3
+ module.exports = buildCodexPlusPatchSet({
4
+ id: "codex-26.623.31921-4452",
5
+ codexVersion: "26.623.31921",
6
+ bundleVersion: "4452",
7
+ asarSha256: "04287710b058bc0977031481c21f5ab69cfb2bcf4fa5c1919b9b590c38960543",
8
+ files: {
9
+ main: ".vite/build/main--VWTbRdF.js",
10
+ electronCommandSource: ".vite/build/src-Bnhv1yNK.js",
11
+ appMain: "webview/assets/app-initial~app-main~automations-page-bHJfYUGr.js",
12
+ appShell: "webview/assets/app-initial~app-main~automations-page-bHJfYUGr.js",
13
+ errorBoundary: "webview/assets/app-initial~app-main~automations-page-bHJfYUGr.js",
14
+ generalSettings: "webview/assets/general-settings-CONLsqSe.js",
15
+ header: "webview/assets/header-CT44CGhD.js",
16
+ threadPageHeader: null,
17
+ localConversationPage: "webview/assets/local-conversation-page-D7JrTtqP.js",
18
+ threadContext: null,
19
+ sidebarProjectHoverCardSourceRows: "webview/assets/app-initial~app-main~remote-conversation-page~new-thread-panel-page~projects-index-page~app~ovcriy74-KTK3czaX.js",
20
+ threadSidePanelTabs: "webview/assets/app-initial~app-main~onboarding-page~profile-QLPeiknY.js",
21
+ userMessageAttachments: "webview/assets/app-initial~app-main~onboarding-page~profile-QLPeiknY.js",
22
+ composer: "webview/assets/app-initial~app-main~remote-conversation-page~new-thread-panel-page~appgen-library-page~hot~djo67r4n-CIs8dplf.js",
23
+ localActiveWorkspaceRootDropdown: "webview/assets/app-initial~app-main~projects-index-page~hotkey-window-new-thread-page~hotkey-window-home-p~hswrsggc-D-_P9low.js",
24
+ homeProjectDropdown: null,
25
+ runCommand: "webview/assets/app-initial~app-main~automations-page-bHJfYUGr.js",
26
+ localTaskRow: "webview/assets/app-initial~app-main~remote-conversation-page~projects-index-page~hotkey-window-thread-page~hc7acb17-o4mgW3b3.js",
27
+ mermaidDiagramShell: "webview/assets/app-initial~app-main~worktree-init-v2-page~remote-conversation-page~pull-requests-page~new-~kvpgbdy1-mhRp2VYQ.js",
28
+ keyboardShortcutsSearchInput: "webview/assets/app-initial~app-main~automations-page-bHJfYUGr.js",
29
+ statsigStartup: "webview/assets/app-initial~app-main~automations-page-bHJfYUGr.js",
30
+ src: "webview/assets/src-BJ6aJX-_.js",
31
+ sidebarThreadKeys: null,
32
+ sidebarThreadRowSignals: null,
33
+ branchPickerDropdownContent: "webview/assets/git-branch-switcher-BokkKYIT.js",
34
+ electronMenuShortcuts: ".vite/build/src-Bnhv1yNK.js",
35
+ keyboardShortcutsTitleFallback: "webview/assets/app-initial~app-main~keyboard-shortcuts-settings-DhAbzvy6.js",
36
+ },
37
+ anchors: {
38
+ composerProjectImports: null,
39
+ composerProjectStyleCaller: "Ls=(0,PY.jsx)(Lte,{active:Ra.ui?.active===!0&&Ra.ui.activation===`synthetic`,onOpen:()=>{ns.prepare(),fn.toggleContextSuggestions()}});return",
40
+ composerProjectAccentCaller: null,
41
+ },
42
+ runtimeConfig: {
43
+ mermaidCoreAsset: "mermaid.core-C6FbNonK.js",
44
+ },
45
+ });
@@ -1,5 +1,6 @@
1
1
  const codex_26_623_42026_4514 = require("./26.623.42026-4514");
2
2
  const codex_26_623_41415_4505 = require("./26.623.41415-4505");
3
+ const codex_26_623_31921_4452 = require("./26.623.31921-4452");
3
4
  const codex_26_616_81150_4306 = require("./26.616.81150-4306");
4
5
  const codex_26_616_71553_4265 = require("./26.616.71553-4265");
5
6
  const codex_26_616_41845_4198 = require("./26.616.41845-4198");
@@ -8,6 +9,7 @@ const codex_26_616_51431_4212 = require("./26.616.51431-4212");
8
9
  const patchSets = [
9
10
  codex_26_623_42026_4514,
10
11
  codex_26_623_41415_4505,
12
+ codex_26_623_31921_4452,
11
13
  codex_26_616_81150_4306,
12
14
  codex_26_616_71553_4265,
13
15
  codex_26_616_51431_4212,
@@ -46,6 +46,7 @@ function buildCodexPlusPatchSet(config) {
46
46
  const mermaidDiagramShellFile = files.mermaidDiagramShell;
47
47
  const electronMenuShortcutsFile = files.electronMenuShortcuts;
48
48
  const keyboardShortcutsSearchInputFile = files.keyboardShortcutsSearchInput;
49
+ const keyboardShortcutsTitleFallbackFile = files.keyboardShortcutsTitleFallback || keyboardShortcutsSearchInputFile;
49
50
  const statsigStartupFile = files.statsigStartup;
50
51
  const srcFile = files.src;
51
52
  const sidebarThreadKeysFile = files.sidebarThreadKeys;
@@ -57,6 +58,14 @@ function patchTitle(text) {
57
58
  }
58
59
 
59
60
  function patchDevModeStatsigFallback(text) {
61
+ if (text.includes("function q1(e){let t=(0,i0.c)(27),")) {
62
+ return replaceOnce(
63
+ text,
64
+ "function q1(e){let t=(0,i0.c)(27),{auth:n,appVersion:r,currentAccount:i,hostBuildFlavor:a,plan:o,statsigClientKey:s,systemName:c,systemVersion:l,children:u}=e,d=o===void 0?null:o,f=s===void 0?qre:s,p,m,h;if",
65
+ "function q1(e){let t=(0,i0.c)(27),{auth:n,appVersion:r,currentAccount:i,hostBuildFlavor:a,plan:o,statsigClientKey:s,systemName:c,systemVersion:l,children:u}=e,d=o===void 0?null:o,f=s===void 0?qre:s,p,m,h;if(window.__CodexPlusRuntimeConfig?.devModeStatsigFallback)return u;if",
66
+ "dev mode statsig fallback anchor",
67
+ );
68
+ }
60
69
  if (text.includes("function XY(e){let t=(0,sX.c)(27),")) {
61
70
  return replaceOnce(
62
71
  text,
@@ -80,6 +89,58 @@ function patchAboutDialog(text, context = {}) {
80
89
  sourceAsarSha256: context.sourceAsarSha256 || "unknown",
81
90
  appliedPatches: context.appliedPatches || [],
82
91
  };
92
+ if (text.includes("function $4({appDisplayName:e,buildInfoLabel:t,buildInfoText:n,iconDataUrl:r,isDark:i,okLabel:a,title:o})")) {
93
+ let patched = replaceOnce(
94
+ text,
95
+ "let i=a.app.getName(),o=a.app.getVersion()",
96
+ `let CPXAbout=${aboutMetadataRequire()}.aboutPayload(${JSON.stringify(aboutContext)}),i=CPXAbout.appDisplayName,o=a.app.getVersion()`,
97
+ "about dialog app name anchor",
98
+ );
99
+ patched = replaceOnce(
100
+ patched,
101
+ "g=d.formatMessage({messageId:L4,defaultMessage:R4}),_=J4(o),v=_.length===0?h:[h,``,..._].join(`\n`),",
102
+ "g=d.formatMessage({messageId:L4,defaultMessage:R4}),_=CPXAbout.buildInfoLines,v=_.length===0?h:[h,``,..._].join(`\n`),",
103
+ "about dialog build information anchor",
104
+ );
105
+ patched = replaceOnce(
106
+ patched,
107
+ "$4({appDisplayName:i,buildInfoLabel:g,buildInfoText:v,iconDataUrl:f.htmlIconDataUrl,isDark:b,okLabel:m,title:p})",
108
+ "$4({appDisplayName:i,buildInfoLabel:g,buildInfoText:v,codexPlusDisclaimerHeading:CPXAbout.disclaimerHeading,codexPlusDisclaimerBody:CPXAbout.disclaimerBody,iconDataUrl:f.htmlIconDataUrl,isDark:b,okLabel:m,title:p})",
109
+ "about dialog renderer call anchor",
110
+ );
111
+ patched = replaceOnce(
112
+ patched,
113
+ "function $4({appDisplayName:e,buildInfoLabel:t,buildInfoText:n,iconDataUrl:r,isDark:i,okLabel:a,title:o}){let s=r==null?``:",
114
+ "function $4({appDisplayName:e,buildInfoLabel:t,buildInfoText:n,codexPlusDisclaimerHeading:D,codexPlusDisclaimerBody:O,iconDataUrl:r,isDark:i,okLabel:a,title:o}){let CPXAboutMetadata=" +
115
+ aboutMetadataRequire() +
116
+ ",q=CPXAboutMetadata.disclaimerMarkup({escape:sV.default,heading:D,body:O}),s=r==null?``:",
117
+ "about dialog renderer signature anchor",
118
+ );
119
+ patched = replaceOnce(
120
+ patched,
121
+ " .build-info {\n width: 100%;\n margin: 0;\n line-height: 1.45;",
122
+ "${CPXAboutMetadata.disclaimerStyles()}\n\n .build-info {\n width: 100%;\n margin: 0;\n line-height: 1.45;",
123
+ "about dialog disclaimer styles anchor",
124
+ );
125
+ patched = replaceOnce(
126
+ patched,
127
+ " color: var(--muted-text);\n white-space: pre-wrap;",
128
+ " color: var(--muted-text);\n text-align: left;\n white-space: pre-wrap;",
129
+ "about dialog build info left align anchor",
130
+ );
131
+ patched = replaceOnce(
132
+ patched,
133
+ " .app-name,\n .build-info,\n .copyright {",
134
+ " .app-name,\n .codex-plus-disclaimer,\n .build-info,\n .copyright {",
135
+ "about dialog selectable disclaimer anchor",
136
+ );
137
+ return replaceOnce(
138
+ patched,
139
+ ' <div class="app-name" id="app-name">${(0,sV.default)(e)}</div>\n <pre class="build-info" aria-label="${(0,sV.default)(t)}">${(0,sV.default)(n)}</pre>',
140
+ ' <div class="app-name" id="app-name">${(0,sV.default)(e)}</div>\n ${q}\n <pre class="build-info" aria-label="${(0,sV.default)(t)}">${(0,sV.default)(n)}</pre>',
141
+ "about dialog disclaimer insertion anchor",
142
+ );
143
+ }
83
144
  if (text.includes("function Q4({appDisplayName:e,buildInfoLabel:t,buildInfoText:n,iconDataUrl:r,isDark:i,okLabel:a,title:o})")) {
84
145
  let patched = replaceOnce(
85
146
  text,
@@ -266,6 +327,20 @@ function patchThreadSidePanelTabs(text) {
266
327
  "review body mux anchor",
267
328
  );
268
329
  }
330
+ if (text.includes("function WPe(e){let t=(0,xN.c)(14),{expandedActionsPortalTarget:n,setTabState:r,tabState:i}=e")) {
331
+ let patched = replaceOnce(
332
+ text,
333
+ "function WPe(e){let t=(0,xN.c)(14),{expandedActionsPortalTarget:n,setTabState:r,tabState:i}=e",
334
+ `${reviewHook("[SN,typeof VE!==`undefined`?VE:null,Ie,Y,xn,null,null,null,null,null,ce,xje,null,null,null,null,null,null,null,null,null]")}function WPe(e){let t=(0,xN.c)(14),{expandedActionsPortalTarget:n,setTabState:r,tabState:i}=e`,
335
+ "review host hook insertion anchor",
336
+ );
337
+ return replaceOnce(
338
+ patched,
339
+ "let s;t[1]!==a||t[2]!==r||t[3]!==i?(s=(0,SN.jsx)(xje,{diffMode:a,setTabState:r,tabState:i}),t[1]=a,t[2]=r,t[3]=i,t[4]=s):s=t[4];",
340
+ "let s;t[1]!==a||t[2]!==r||t[3]!==i?(s=(0,SN.jsx)(CPXRM,{mainReviewContent:(0,SN.jsx)(xje,{diffMode:a,setTabState:r,tabState:i}),diffMode:a,setTabState:r,tabState:i}),t[1]=a,t[2]=r,t[3]=i,t[4]=s):s=t[4];",
341
+ "review body mux anchor",
342
+ );
343
+ }
269
344
  let patched = replaceOnce(
270
345
  text,
271
346
  "import{r as vi,t as yi}from\"./dropdown-CTBRoADH.js\";",
@@ -286,6 +361,20 @@ function patchThreadSidePanelTabs(text) {
286
361
  );
287
362
  }
288
363
  function patchAppShell(text) {
364
+ if (text.includes("function Sie(){let e=(0,hj.c)(3),t,n;")) {
365
+ let patched = replaceOnce(
366
+ text,
367
+ "function Sie(){let e=(0,hj.c)(3),t,n;",
368
+ `${diagnosticDetailsHook()}function Sie(){let e=(0,hj.c)(3),t,n;`,
369
+ "app shell error fallback prop anchor",
370
+ );
371
+ return replaceOnce(
372
+ patched,
373
+ "children:[t,n,(0,gj.jsx)(Le,{onClick:Cie,children:(0,gj.jsx)(X,{id:`codex.errorBoundary.goHome`,defaultMessage:`Try again`,description:`Button label to navigate to the home page after an error`})})]",
374
+ "children:[t,n,CPXDiagnosticDetails({jsx:gj.jsx,error:null}),(0,gj.jsx)(Le,{onClick:Cie,children:(0,gj.jsx)(X,{id:`codex.errorBoundary.goHome`,defaultMessage:`Try again`,description:`Button label to navigate to the home page after an error`})})]",
375
+ "app shell error detail insertion anchor",
376
+ );
377
+ }
289
378
  if (text.includes("function QUe(e){let t=(0,NP.c)(4),{onRetry:n}=e,")) {
290
379
  let patched = replaceOnce(
291
380
  text,
@@ -342,6 +431,9 @@ function patchAppShell(text) {
342
431
  }
343
432
 
344
433
  function patchErrorBoundary(text) {
434
+ if (text.includes("function Sie(){let e=(0,hj.c)(3),t,n;") && text.includes("CPXDiagnosticDetails({jsx:gj.jsx,error:null})")) {
435
+ return text;
436
+ }
345
437
  if (
346
438
  !text.includes("function Xf(e){let t=(0,Vf.c)(9),{resetError:n}=e,r=ee(),i,a;") &&
347
439
  text.includes("function QUe(e){let t=(0,NP.c)(4),{onRetry:n,error:CPX_error}=e,")
@@ -375,6 +467,26 @@ function patchErrorBoundary(text) {
375
467
  }
376
468
 
377
469
  function patchAppMainProjectColors(text) {
470
+ if (text.includes("function SV(e){let t=(0,EV.c)(57),") && text.includes("function nH(e){let t=(0,OH.c)(120),")) {
471
+ let patched = replaceOnce(
472
+ text,
473
+ "function SV(e){let t=(0,EV.c)(57),",
474
+ `${projectColorHook()}function SV(e){let t=(0,EV.c)(57),`,
475
+ "project color app main helper insertion anchor",
476
+ );
477
+ patched = replaceOnce(
478
+ patched,
479
+ "O=yl.sidebarProjectRow({collapsed:a,label:p,projectId:_})",
480
+ "O={...yl.sidebarProjectRow({collapsed:a,label:p,projectId:_}),...CPXPR({projectId:_,label:p})}",
481
+ "project header row color attributes anchor",
482
+ );
483
+ return replaceOnce(
484
+ patched,
485
+ "ne=(0,Z.jsx)(`div`,{...R,children:te})",
486
+ "ne=(0,Z.jsx)(`div`,{...R,...CPXPR(a),children:te})",
487
+ "project group color render anchor",
488
+ );
489
+ }
378
490
  if (
379
491
  text.includes("function Vm(e){let t=(0,Gm.c)(57),") &&
380
492
  text.includes("return t[41]!==Y||t[42]!==H?(ne=(0,$.jsx)(`div`,{...H,children:Y})")
@@ -446,6 +558,14 @@ function patchAppMainProjectColors(text) {
446
558
  }
447
559
 
448
560
  function patchAppMainSidebarBlur(text) {
561
+ if (text.includes("function SV(e){let t=(0,EV.c)(57),")) {
562
+ return replaceOnce(
563
+ text,
564
+ "V=(0,DV.jsx)(`span`,{className:`min-w-0 truncate pr-1`,children:p})",
565
+ "V=(0,DV.jsx)(`span`,{\"data-codex-plus-sidebar-name\":``,className:`min-w-0 truncate pr-1`,children:p})",
566
+ "project header sidebar blur label anchor",
567
+ );
568
+ }
449
569
  if (text.includes("function vh(e){let t=(0,qh.c)(15),")) {
450
570
  return replaceOnce(
451
571
  text,
@@ -507,6 +627,29 @@ function patchHeader(text) {
507
627
  "thread header accessory render anchor",
508
628
  );
509
629
  }
630
+ if (
631
+ text.includes("function Jn(e){let t=(0,$n.c)(66),") &&
632
+ text.includes("(0,$.jsx)(l,{color:`ghostActive`,type:`button`,onClick:p,")
633
+ ) {
634
+ let patched = replaceOnce(
635
+ text,
636
+ "function Jn(e){let t=(0,$n.c)(66),",
637
+ `${threadHeaderHook()}function Jn(e){let t=(0,$n.c)(66),`,
638
+ "thread header accessory helper insertion anchor",
639
+ );
640
+ patched = replaceOnce(
641
+ patched,
642
+ "let w;t[35]!==u||t[36]!==y||t[37]!==i?",
643
+ "let CPX_headerContext={cwd:null,hostId:null,header:{surface:`header`,titleText:typeof i==`string`?i:null}},CPX_headerAccessories=CPXThreadHeaderAccessories({context:CPX_headerContext,deps:{jsx:$.jsx,jsxs:$.jsxs}});let w;t[35]!==u||t[36]!==y||t[37]!==i?",
644
+ "thread header accessory context anchor",
645
+ );
646
+ return replaceOnce(
647
+ patched,
648
+ "children:(0,$.jsx)(`span`,{className:`truncate`,children:i})})]}):",
649
+ "children:(0,$.jsx)(`span`,{className:`truncate`,children:i})}),CPX_headerAccessories]}):",
650
+ "thread header accessory render anchor",
651
+ );
652
+ }
510
653
  if (text.includes("function Jn(e){let t=(0,$n.c)(66),")) {
511
654
  let patched = replaceOnce(
512
655
  text,
@@ -594,6 +737,26 @@ function patchLocalConversationPageHeader(text) {
594
737
  return patched;
595
738
  }
596
739
  if (text.includes("function mi(e){let t=(0,U.c)(32),")) {
740
+ if (text.includes("projectIcon:a,projectHoverCardContent:o,projectName:s,title:c,titleSuffix:l,cwd:u,canPin:d,hideForkActions:f")) {
741
+ let patched = replaceOnce(
742
+ text,
743
+ "function mi(e){let t=(0,U.c)(32),",
744
+ `${threadHeaderHook()}function mi(e){let t=(0,U.c)(32),`,
745
+ "local conversation header helper insertion anchor",
746
+ );
747
+ patched = replaceOnce(
748
+ patched,
749
+ "let D;t[26]===Symbol.for(`react.memo_cache_sentinel`)?(D=null,t[26]=D):D=t[26];",
750
+ "let CPX_headerContext={cwd:u,hostId:null,header:{surface:`local-conversation`,titleText:typeof c==`string`?c:null,projectName:s??null}};let D;t[26]===Symbol.for(`react.memo_cache_sentinel`)?(D=null,t[26]=D):D=t[26];",
751
+ "local conversation header accessory context anchor",
752
+ );
753
+ return replaceOnce(
754
+ patched,
755
+ "children:[x,w,T,E,D]",
756
+ "children:[x,w,T,E,CPXThreadHeaderAccessories({context:CPX_headerContext,deps:{jsx:W.jsx,jsxs:W.jsxs,Tooltip:Ge}}),D]",
757
+ "local conversation header accessory mount anchor",
758
+ );
759
+ }
597
760
  let patched = replaceOnce(
598
761
  text,
599
762
  "function mi(e){let t=(0,U.c)(32),",
@@ -658,6 +821,23 @@ function patchGeneralSettingsUserBubbleColors(text) {
658
821
  "user bubble settings row anchor",
659
822
  );
660
823
  }
824
+ if (
825
+ text.includes("function Lr({showCodeFont:e,showTranslucentSidebarToggle:t,variant:n}){") &&
826
+ text.includes("children:[D.map(e=>(0,J.jsx)(U,{control:(0,J.jsx)(Hr,{ariaLabel:e.ariaLabel,value:x[e.role],onChange:t=>{k(e.role,t)}}),label:e.label,variant:`nested`},e.role)),O.map")
827
+ ) {
828
+ let patched = replaceOnce(
829
+ text,
830
+ "function Lr({showCodeFont:e,showTranslucentSidebarToggle:t,variant:n}){",
831
+ `${appearanceSettingsHook("{React:ti,jsx:J.jsx,SettingRow:U,ColorInput:Hr,Switch:I}")}function Lr({showCodeFont:e,showTranslucentSidebarToggle:t,variant:n}){`,
832
+ "user bubble settings helper insertion anchor",
833
+ );
834
+ return replaceOnce(
835
+ patched,
836
+ "children:[D.map(e=>(0,J.jsx)(U,{control:(0,J.jsx)(Hr,{ariaLabel:e.ariaLabel,value:x[e.role],onChange:t=>{k(e.role,t)}}),label:e.label,variant:`nested`},e.role)),O.map",
837
+ "children:[D.map(e=>(0,J.jsx)(U,{control:(0,J.jsx)(Hr,{ariaLabel:e.ariaLabel,value:x[e.role],onChange:t=>{k(e.role,t)}}),label:e.label,variant:`nested`},e.role)),...CPXAppearanceRows(n),O.map",
838
+ "user bubble settings row anchor",
839
+ );
840
+ }
661
841
  if (text.includes("function Lr({showCodeFont:e,showTranslucentSidebarToggle:t,variant:n}){")) {
662
842
  let patched = replaceOnce(
663
843
  text,
@@ -687,6 +867,26 @@ function patchGeneralSettingsUserBubbleColors(text) {
687
867
  }
688
868
 
689
869
  function patchUserMessageAttachmentsBubbleColors(text) {
870
+ if (text.includes("function IVe({cwd:e,hostId:t,initialMessage:n,onCancel:r,onDraftChange:i,onSubmit:a}){")) {
871
+ let patched = replaceOnce(
872
+ text,
873
+ "function IVe({cwd:e,hostId:t,initialMessage:n,onCancel:r,onDraftChange:i,onSubmit:a}){",
874
+ `${messageComposerHook()}function IVe({cwd:e,hostId:t,initialMessage:n,onCancel:r,onDraftChange:i,onSubmit:a}){`,
875
+ "user bubble helper insertion anchor",
876
+ );
877
+ patched = replaceOnce(
878
+ patched,
879
+ "return(0,HU.jsx)(`form`,{className:`relative flex w-full flex-col rounded-3xl bg-token-foreground/5`,onSubmit:e=>{e.preventDefault(),v()},children:",
880
+ "return(0,HU.jsx)(`form`,{\"data-codex-plus-user-entry\":!0,className:`relative flex w-full flex-col rounded-3xl bg-token-foreground/5`,onSubmit:e=>{e.preventDefault(),v()},children:",
881
+ "edit user message entry marker anchor",
882
+ );
883
+ return replaceOnce(
884
+ patched,
885
+ "he=V?(0,KU.jsx)(`div`,{className:`w-full p-px`,children:(0,KU.jsx)(IVe,{cwd:x??null,hostId:S,initialMessage:B.trim(),onCancel:()=>{ie(null)},onDraftChange:e=>{ie(e)},onSubmit:oe})}):q?(0,KU.jsx)(`div`,{\"data-user-message-bubble\":!0,role:L?`button`:void 0,",
886
+ "he=V?(0,KU.jsx)(`div`,{className:`w-full p-px`,children:(0,KU.jsx)(IVe,{cwd:x??null,hostId:S,initialMessage:B.trim(),onCancel:()=>{ie(null)},onDraftChange:e=>{ie(e)},onSubmit:oe})}):q?(0,KU.jsx)(`div`,{\"data-user-message-bubble\":!0,...CPXBubbleProps({}),role:L?`button`:void 0,",
887
+ "user bubble marker attribute anchor",
888
+ );
889
+ }
690
890
  if (text.includes("function xst({cwd:e,hostId:t,initialMessage:n,onCancel:r,onDraftChange:i,onSubmit:a}){")) {
691
891
  let patched = replaceOnce(
692
892
  text,
@@ -748,6 +948,14 @@ function patchUserMessageAttachmentsBubbleColors(text) {
748
948
  }
749
949
 
750
950
  function patchUserMessageAttachmentsProjectColors(text) {
951
+ if (text.includes("\"data-user-message-bubble\":!0,...CPXBubbleProps({}),role:L?`button`:void 0,")) {
952
+ return replaceOnce(
953
+ text,
954
+ "\"data-user-message-bubble\":!0,...CPXBubbleProps({}),role:L?`button`:void 0,",
955
+ "\"data-user-message-bubble\":!0,...CPXBubbleProps({project:{cwd:x,hostId:S}}),role:L?`button`:void 0,",
956
+ "user bubble project marker attribute anchor",
957
+ );
958
+ }
751
959
  if (text.includes("\"data-user-message-bubble\":!0,...CPXBubbleProps({}),role:I?`button`:void 0,")) {
752
960
  return replaceOnce(
753
961
  text,
@@ -777,6 +985,26 @@ function patchUserMessageAttachmentsProjectColors(text) {
777
985
  }
778
986
 
779
987
  function patchComposerBubbleColors(text) {
988
+ if (text.includes("function II(e){let t=(0,XI.c)(13),")) {
989
+ let patched = replaceOnce(
990
+ text,
991
+ "function II(e){let t=(0,XI.c)(13),",
992
+ `${messageComposerHook()}function II(e){let t=(0,XI.c)(13),`,
993
+ "composer user bubble helper insertion anchor",
994
+ );
995
+ patched = replaceOnce(
996
+ patched,
997
+ "function II(e){let t=(0,XI.c)(13),{children:n,className:r,externalFooterVariant:i,inert:a,isDragActive:o,layout:s,onDragEnter:c,onDragLeave:l,onDragOver:u,onDrop:d}=e,",
998
+ "function II(e){let t=(0,XI.c)(13),{children:n,className:r,externalFooterVariant:i,inert:a,isDragActive:o,layout:s,onDragEnter:c,onDragLeave:l,onDragOver:u,onDrop:d,codexPlusProps:CPX_surfaceProps}=e,CPX_resolvedSurfaceProps=CPX_surfaceProps??CPXSurfaceProps({}),",
999
+ "composer host surface props anchor",
1000
+ );
1001
+ return replaceOnce(
1002
+ patched,
1003
+ "(0,ZI.jsx)(T.div,{inert:a,className:v,",
1004
+ "(0,ZI.jsx)(T.div,{inert:a,...CPX_resolvedSurfaceProps,className:v,",
1005
+ "composer user entry marker render anchor",
1006
+ );
1007
+ }
780
1008
  if (text.includes("function FN(e){let t=(0,YN.c)(13),")) {
781
1009
  let patched = replaceOnce(
782
1010
  text,
@@ -856,6 +1084,14 @@ function patchComposerBubbleColors(text) {
856
1084
  }
857
1085
 
858
1086
  function patchComposerProjectColors(text) {
1087
+ if (text.includes("(0,$q.jsx)(Yq,{className:O,externalFooterVariant:D,hasDropTargetPortal:_c,")) {
1088
+ return replaceOnce(
1089
+ text,
1090
+ "(0,$q.jsx)(Yq,{className:O,externalFooterVariant:D,hasDropTargetPortal:_c,",
1091
+ "(0,$q.jsx)(Yq,{key:CPXSurfaceProps({project:{cwd:Cn,hostId:xr}})?.[`data-codex-plus-project-color`]??``,codexPlusProps:CPXSurfaceProps({project:{cwd:Cn,hostId:xr}}),className:O,externalFooterVariant:D,hasDropTargetPortal:_c,",
1092
+ "composer project accent style caller anchor",
1093
+ );
1094
+ }
859
1095
  if (text.includes("(0,iW.jsx)(eW,{className:A,externalFooterVariant:k,hasDropTargetPortal:fc,")) {
860
1096
  return replaceOnce(
861
1097
  text,
@@ -909,6 +1145,14 @@ function patchElectronMenuShortcuts(text) {
909
1145
  }
910
1146
 
911
1147
  function patchKeyboardShortcutsSearchInput(text) {
1148
+ if (text.includes("function p(e,t){return`titleIntlId`in e?")) {
1149
+ return replaceOnce(
1150
+ text,
1151
+ "function p(e,t){return`titleIntlId`in e?h(g,e.titleIntlId)?t.formatMessage(g[e.titleIntlId]):``:t.formatMessage(_[e.electron.menuTitleIntlId])}",
1152
+ "function p(e,t){return`titleIntlId`in e?h(g,e.titleIntlId)?t.formatMessage(g[e.titleIntlId]):``:e.title??e.electron?.menuTitle??t.formatMessage(_[e.electron.menuTitleIntlId])}",
1153
+ "generic command metadata title fallback anchor",
1154
+ );
1155
+ }
912
1156
  if (text.includes("function qX(e,t){return`titleIntlId`in e?")) {
913
1157
  return replaceOnce(
914
1158
  text,
@@ -951,6 +1195,20 @@ function patchCommandMenuRuntimeCommands(text) {
951
1195
  }
952
1196
 
953
1197
  function patchLocalTaskRow(text) {
1198
+ if (text.includes("function yr(e){let t=(0,xr.c)(134),")) {
1199
+ let patched = replaceOnce(
1200
+ text,
1201
+ "function yr(e){let t=(0,xr.c)(134),",
1202
+ `${projectColorHook()}function yr(e){let t=(0,xr.c)(134),`,
1203
+ "local task row project color helper insertion anchor",
1204
+ );
1205
+ return replaceOnce(
1206
+ patched,
1207
+ "threadSummary:B,dataAttributes:Le}=e,V=u===void 0?!1:u,",
1208
+ "threadSummary:B,dataAttributes:Le=CPXPR({projectId:Fe,label:Ie,path:a,cwd:a})}=e,V=u===void 0?!1:u,",
1209
+ "local task row project assignment anchor",
1210
+ );
1211
+ }
954
1212
  if (text.includes("function Ef(e){let t=(0,Of.c)(134),")) {
955
1213
  let patched = replaceOnce(
956
1214
  text,
@@ -996,6 +1254,20 @@ function patchLocalTaskRow(text) {
996
1254
  }
997
1255
 
998
1256
  function patchMermaidDiagramShell(text) {
1257
+ if (text.includes("function Npe(e){let t=(0,_4.c)(19),")) {
1258
+ let patched = replaceOnce(
1259
+ text,
1260
+ "function Npe(e){let t=(0,_4.c)(19),",
1261
+ `${mermaidDiagramHook()}function Npe(e){let t=(0,_4.c)(19),`,
1262
+ "mermaid diagram shell helper insertion anchor",
1263
+ );
1264
+ return replaceOnce(
1265
+ patched,
1266
+ 'D=(0,y4.jsx)(`div`,{ref:d,className:C,"data-wide-markdown-block":T,"data-wide-markdown-block-kind":c,children:E})',
1267
+ 'D=(0,y4.jsx)(`div`,{ref:d,...CPXMermaidDiagramProps({code:a}),className:C,"data-wide-markdown-block":T,"data-wide-markdown-block-kind":c,children:E})',
1268
+ "mermaid diagram shell host props anchor",
1269
+ );
1270
+ }
999
1271
  if (text.includes("function xbe(e){let t=(0,E2.c)(19),")) {
1000
1272
  let patched = replaceOnce(
1001
1273
  text,
@@ -1048,6 +1320,20 @@ function patchPreloadNativeBridge(text) {
1048
1320
  }
1049
1321
 
1050
1322
  function patchMainNativeBridge(text) {
1323
+ if (text.includes("function b4(e){let{") && text.includes("K2(l,k),H2(k);let A=!1;")) {
1324
+ let patched = replaceOnce(
1325
+ text,
1326
+ "function b4(e){let{",
1327
+ `${nativeMainHook()}function b4(e){let{`,
1328
+ "codex plus native main helper insertion anchor",
1329
+ );
1330
+ return replaceOnce(
1331
+ patched,
1332
+ "K2(l,k),H2(k);let A=!1;",
1333
+ "K2(l,k),H2(k),CPXNative.registerNativeRequest({isTrustedIpcEvent:k});let A=!1;",
1334
+ "codex plus native main registration anchor",
1335
+ );
1336
+ }
1051
1337
  if (text.includes("function y4(e){let{") && text.includes("G2(l,k),V2(k);let A=!1;")) {
1052
1338
  let patched = replaceOnce(
1053
1339
  text,
@@ -1170,7 +1456,7 @@ return makePatchSet({
1170
1456
  fileTransforms: [
1171
1457
  [appMainFile, patchAppMainSidebarBlur],
1172
1458
  [electronMenuShortcutsFile, patchElectronMenuShortcuts],
1173
- [keyboardShortcutsSearchInputFile, patchKeyboardShortcutsSearchInput],
1459
+ [keyboardShortcutsTitleFallbackFile, patchKeyboardShortcutsSearchInput],
1174
1460
  [keyboardShortcutsSearchInputFile, patchCommandMenuRuntimeCommands],
1175
1461
  ],
1176
1462
  },
@@ -2,6 +2,62 @@ const { replaceOnce } = require("./replace");
2
2
  const { projectSelectorSearchHook, projectSelectorTriggerHook } = require("./hooks/project-selector");
3
3
 
4
4
  function patchLocalActiveWorkspaceRootDropdownProjectSelectorShortcut(text) {
5
+ if (text.includes("function rt(e){let t=(0,it.c)(44),") && text.includes("function St({activeProjectIdOverride:e,")) {
6
+ let patched = replaceOnce(
7
+ text,
8
+ "var et,tt,nt=e((()=>{et=L(),Je(),_e(),tt=o()}));function rt(e){let t=(0,it.c)(44),",
9
+ `var et,tt,nt=e((()=>{et=L(),Je(),_e(),tt=o()}));${projectSelectorSearchHook()}function rt(e){let t=(0,it.c)(44),`,
10
+ "project selector fuzzy search adapter insertion anchor",
11
+ );
12
+ patched = replaceOnce(
13
+ patched,
14
+ "let e=_.trim().toLowerCase();b=r.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))});",
15
+ "b=CPXP.fuzzyFilter(r,_);",
16
+ "project selector fuzzy search filter anchor",
17
+ );
18
+ patched = replaceOnce(
19
+ patched,
20
+ "T=(0,X.jsx)(fe,{value:_,onChange:s,placeholder:c,className:`mb-1`})",
21
+ "T=(0,X.jsx)(fe,{value:_,onChange:s,onKeyDown:e=>CPXP.acceptFirst(e,b,o,_),placeholder:c,className:`mb-1`})",
22
+ "project selector accept first match keydown anchor",
23
+ );
24
+ patched = replaceOnce(
25
+ patched,
26
+ "(0,X.jsx)(`span`,{className:`truncate`,children:e.label})",
27
+ "(0,X.jsx)(`span`,{className:`truncate`,children:CPXP.fuzzyHighlight(e.label,_,X.jsx)})",
28
+ "project selector fuzzy search highlight anchor",
29
+ );
30
+ patched = replaceOnce(
31
+ patched,
32
+ "var wt,$,Tt=e((()=>{Se(),F(),r(),ge(),wt=t(b(),1),",
33
+ `${projectSelectorTriggerHook("wt")}var wt,$,Tt=e((()=>{Se(),F(),r(),ge(),wt=t(b(),1),`,
34
+ "project selector shortcut helper insertion anchor",
35
+ );
36
+ patched = replaceOnce(
37
+ patched,
38
+ "children:(0,$.jsx)(gt,{categoryLabel:(0,$.jsx)(z,{id:`composer.localCwdDropdown.footerCategory`",
39
+ "children:(0,$.jsx)(gt,{\"data-codex-plus-project-selector-trigger\":!0,\"data-codex-plus-project-selector-variant\":c,categoryLabel:(0,$.jsx)(z,{id:`composer.localCwdDropdown.footerCategory`",
40
+ "project selector default button marker anchor",
41
+ );
42
+ patched = replaceOnce(
43
+ patched,
44
+ "Ze=()=>(0,$.jsxs)(`button`,{className:a(`heading-xl text-token-text-tertiary",
45
+ "Ze=()=>(0,$.jsxs)(`button`,{\"data-codex-plus-project-selector-trigger\":!0,\"data-codex-plus-project-selector-variant\":c,className:a(`heading-xl text-token-text-tertiary",
46
+ "project selector hero button marker anchor",
47
+ );
48
+ patched = replaceOnce(
49
+ patched,
50
+ "triggerButton:h??J(),contentWidth:`menu`",
51
+ "triggerButton:CPXPST(h??J(),c),contentWidth:`menu`",
52
+ "project selector empty trigger anchor",
53
+ );
54
+ return replaceOnce(
55
+ patched,
56
+ "triggerButton:h??(c===`hero`?Ze():c===`home`?J():Je()),contentWidth:`workspace`",
57
+ "triggerButton:CPXPST(h??(c===`hero`?Ze():c===`home`?J():Je()),c),contentWidth:`workspace`",
58
+ "project selector shortcut final dropdown trigger anchor",
59
+ );
60
+ }
5
61
  if (text.includes("function Ti(e){let t=(0,Oi.c)(109),")) {
6
62
  return replaceOnce(
7
63
  text,
@@ -163,6 +219,14 @@ function patchHomeProjectDropdownProjectSelectorShortcut(text) {
163
219
 
164
220
  function patchRunCommandProjectSelectorShortcut(text) {
165
221
  const runtimeCommandEntries = "...(window.CodexPlus?.commands?.all?.()??[]).map(e=>[e.id,()=>window.CodexPlus?.commands?.run?.(e.id)])";
222
+ if (text.includes("Xi(`toggleSidebar`,r);")) {
223
+ return replaceOnce(
224
+ text,
225
+ "Xi(`toggleSidebar`,r);",
226
+ "Xi(`toggleSidebar`,r);for(let e of window.CodexPlus?.commands?.all?.()??[])Xi(e.id,()=>window.CodexPlus?.commands?.run?.(e.id));",
227
+ "codex plus runtime command dispatch anchor",
228
+ );
229
+ }
166
230
  if (text.includes("Jy(`toggleSidebar`,r);")) {
167
231
  return replaceOnce(
168
232
  text,
@@ -40,12 +40,13 @@
40
40
  }
41
41
 
42
42
  function trigger(element, variant, React) {
43
- return typeof React?.cloneElement === "function" &&
43
+ const cloneElement = typeof React?.cloneElement === "function" ? React.cloneElement : React?.default?.cloneElement;
44
+ return typeof cloneElement === "function" &&
44
45
  element != null &&
45
46
  typeof element === "object" &&
46
47
  "props" in element &&
47
48
  "type" in element
48
- ? React.cloneElement(element, {
49
+ ? cloneElement(element, {
49
50
  ...element.props,
50
51
  "data-codex-plus-project-selector-trigger": true,
51
52
  "data-codex-plus-project-selector-variant": variant,
@@ -182,16 +182,30 @@
182
182
  return true;
183
183
  }
184
184
 
185
+ function elementRect(element) {
186
+ const rect = element?.getBoundingClientRect?.();
187
+ return rect && rect.width > 0 && rect.height > 0 ? rect : null;
188
+ }
189
+
190
+ function visibleTriggerTarget(trigger) {
191
+ const rect = elementRect(trigger);
192
+ if (rect) return { element: trigger, rect };
193
+
194
+ const child = trigger?.querySelector?.("button,[role='button'],[tabindex]");
195
+ const childRect = elementRect(child);
196
+ return childRect ? { element: child, rect: childRect } : null;
197
+ }
198
+
185
199
  function visibleTriggerCandidates() {
186
- return Array.from(document.querySelectorAll(triggerSelector)).filter((trigger) => {
200
+ return Array.from(document.querySelectorAll(triggerSelector)).map((trigger) => {
187
201
  if (!(trigger instanceof HTMLElement)) return false;
188
202
  if (trigger.disabled || trigger.getAttribute("aria-disabled") === "true") return false;
189
- const rect = trigger.getBoundingClientRect?.();
190
- return rect && rect.width > 0 && rect.height > 0;
191
- });
203
+ const target = visibleTriggerTarget(trigger);
204
+ return target ? { trigger, target } : false;
205
+ }).filter(Boolean);
192
206
  }
193
207
 
194
- function triggerPriority(trigger) {
208
+ function triggerPriority({ trigger }) {
195
209
  const variant = trigger.getAttribute("data-codex-plus-project-selector-variant");
196
210
  if (variant === "default") return 0;
197
211
  if (variant == null || variant === "") return 1;
@@ -199,20 +213,21 @@
199
213
  }
200
214
 
201
215
  function projectSelectorTrigger() {
202
- const [trigger] = visibleTriggerCandidates().sort((left, right) => {
216
+ const [candidate] = visibleTriggerCandidates().sort((left, right) => {
203
217
  const priority = triggerPriority(left) - triggerPriority(right);
204
218
  if (priority !== 0) return priority;
205
- const leftRect = left.getBoundingClientRect();
206
- const rightRect = right.getBoundingClientRect();
219
+ const leftRect = left.target.rect;
220
+ const rightRect = right.target.rect;
207
221
  return rightRect.top - leftRect.top || rightRect.left - leftRect.left;
208
222
  });
209
- return trigger ?? null;
223
+ return candidate ?? null;
210
224
  }
211
225
 
212
226
  function focusProjectSelector() {
213
- const trigger = projectSelectorTrigger();
214
- if (trigger == null) return false;
215
- trigger.focus?.();
227
+ const candidate = projectSelectorTrigger();
228
+ if (candidate == null) return false;
229
+ const { trigger, target } = candidate;
230
+ target.element.focus?.();
216
231
  const dispatched = [
217
232
  dispatchMouseEvent(trigger, "pointerdown"),
218
233
  dispatchMouseEvent(trigger, "mousedown"),