clankerbend 0.1.1 → 0.1.3
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 +16 -2
- package/apps/sticky-notes/public/app.js +35 -2
- package/apps/vim-nav/public/app.js +35 -2
- package/cli.mjs +7 -4
- package/docs/launcher-profiles.md +32 -0
- package/docs/protocol.md +21 -10
- package/host/src/app-registry.js +2 -0
- package/host/src/codex-desktop-cdp-adapter.js +48 -6
- package/host/src/codex-desktop-renderer-bridge.js +636 -1
- package/host/src/index.js +156 -4
- package/launch/accounts.mjs +360 -0
- package/launch/codex-mux-adapter.mjs +175 -0
- package/launch/profiles.mjs +30 -6
- package/launch/runtime-paths.mjs +3 -0
- package/launch/test-accounts.mjs +71 -0
- package/package.json +1 -1
- package/server.mjs +14 -7
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(function installCodexVimNavigator() {
|
|
2
2
|
const VIM_NAV_APP_ID = "onewill.vim-nav";
|
|
3
|
-
const BRIDGE_VERSION =
|
|
3
|
+
const BRIDGE_VERSION = 116;
|
|
4
4
|
const RUNTIME_KEY = "__clankerbendRuntime";
|
|
5
5
|
const STYLE_ID = "codex-vim-nav-style";
|
|
6
6
|
const ANNOTATION_CLASS = "codex-vim-nav-annotation";
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
const MODE_BADGE_ID = "codex-vim-nav-mode-badge";
|
|
12
12
|
const HOST_UI_CLASS = "clankerbend-host-ui";
|
|
13
13
|
const SELECTION_MENU_ID = "clankerbend-selection-menu";
|
|
14
|
+
const ACCOUNT_SWITCHER_ID = "clankerbend-account-switcher";
|
|
14
15
|
const OVERLAY_ID = "clankerbend-anchored-overlay";
|
|
15
16
|
const COMPOSER_CHIPS_ID = "clankerbend-composer-chips";
|
|
16
17
|
const SELECTOR = [
|
|
@@ -67,6 +68,12 @@
|
|
|
67
68
|
activeTextSelection: null,
|
|
68
69
|
lastSelectionMenuAt: 0,
|
|
69
70
|
renderedOverlaySignature: null,
|
|
71
|
+
accountUiStatus: "",
|
|
72
|
+
accountAddOpen: false,
|
|
73
|
+
accountAddLabel: "",
|
|
74
|
+
accountManageOpen: false,
|
|
75
|
+
accountPendingAdoptId: null,
|
|
76
|
+
accountPendingDeleteId: null,
|
|
70
77
|
pendingComposerSubmission: null,
|
|
71
78
|
submittedComposerSubmissions: []
|
|
72
79
|
};
|
|
@@ -121,6 +128,7 @@
|
|
|
121
128
|
color: #241f12 !important;
|
|
122
129
|
}
|
|
123
130
|
#${OVERLAY_ID} button,
|
|
131
|
+
#${ACCOUNT_SWITCHER_ID} button,
|
|
124
132
|
#${COMPOSER_CHIPS_ID} button {
|
|
125
133
|
appearance: none !important;
|
|
126
134
|
border: 0 !important;
|
|
@@ -131,6 +139,202 @@
|
|
|
131
139
|
font: 650 12px/1 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
|
|
132
140
|
padding: 7px 8px !important;
|
|
133
141
|
}
|
|
142
|
+
#${ACCOUNT_SWITCHER_ID} {
|
|
143
|
+
display: grid !important;
|
|
144
|
+
gap: 7px !important;
|
|
145
|
+
min-width: 220px !important;
|
|
146
|
+
max-width: 320px !important;
|
|
147
|
+
border-top: 1px solid rgba(148, 163, 184, .24) !important;
|
|
148
|
+
border-bottom: 1px solid rgba(148, 163, 184, .18) !important;
|
|
149
|
+
margin: 5px 0 !important;
|
|
150
|
+
padding: 8px 4px !important;
|
|
151
|
+
color: inherit !important;
|
|
152
|
+
}
|
|
153
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-head {
|
|
154
|
+
display: flex !important;
|
|
155
|
+
align-items: center !important;
|
|
156
|
+
justify-content: space-between !important;
|
|
157
|
+
gap: 8px !important;
|
|
158
|
+
padding: 0 6px !important;
|
|
159
|
+
}
|
|
160
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-title {
|
|
161
|
+
color: currentColor !important;
|
|
162
|
+
font: 700 11px/1.2 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
|
|
163
|
+
letter-spacing: 0 !important;
|
|
164
|
+
}
|
|
165
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-status {
|
|
166
|
+
color: color-mix(in srgb, currentColor 62%, transparent) !important;
|
|
167
|
+
font: 500 11px/1.2 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
|
|
168
|
+
overflow: hidden !important;
|
|
169
|
+
text-overflow: ellipsis !important;
|
|
170
|
+
white-space: nowrap !important;
|
|
171
|
+
}
|
|
172
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-list {
|
|
173
|
+
display: grid !important;
|
|
174
|
+
gap: 2px !important;
|
|
175
|
+
}
|
|
176
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-row {
|
|
177
|
+
display: grid !important;
|
|
178
|
+
grid-template-columns: minmax(0, 1fr) auto !important;
|
|
179
|
+
align-items: center !important;
|
|
180
|
+
gap: 5px !important;
|
|
181
|
+
border-radius: 6px !important;
|
|
182
|
+
padding: 2px !important;
|
|
183
|
+
}
|
|
184
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-row.is-active {
|
|
185
|
+
background: rgba(143, 199, 212, .14) !important;
|
|
186
|
+
}
|
|
187
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-main {
|
|
188
|
+
display: grid !important;
|
|
189
|
+
min-width: 0 !important;
|
|
190
|
+
border-radius: 5px !important;
|
|
191
|
+
padding: 6px !important;
|
|
192
|
+
text-align: left !important;
|
|
193
|
+
color: inherit !important;
|
|
194
|
+
}
|
|
195
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-main:hover,
|
|
196
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-icon:hover {
|
|
197
|
+
background: rgba(148, 163, 184, .16) !important;
|
|
198
|
+
}
|
|
199
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-label,
|
|
200
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-meta {
|
|
201
|
+
overflow: hidden !important;
|
|
202
|
+
text-overflow: ellipsis !important;
|
|
203
|
+
white-space: nowrap !important;
|
|
204
|
+
}
|
|
205
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-label {
|
|
206
|
+
font: 650 12px/1.2 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
|
|
207
|
+
}
|
|
208
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-meta {
|
|
209
|
+
color: color-mix(in srgb, currentColor 58%, transparent) !important;
|
|
210
|
+
font: 500 11px/1.2 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
|
|
211
|
+
}
|
|
212
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-icon {
|
|
213
|
+
width: 28px !important;
|
|
214
|
+
height: 28px !important;
|
|
215
|
+
display: inline-grid !important;
|
|
216
|
+
place-items: center !important;
|
|
217
|
+
border-radius: 5px !important;
|
|
218
|
+
color: color-mix(in srgb, currentColor 76%, transparent) !important;
|
|
219
|
+
padding: 0 !important;
|
|
220
|
+
}
|
|
221
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-actions {
|
|
222
|
+
display: grid !important;
|
|
223
|
+
grid-template-columns: 1fr 1fr !important;
|
|
224
|
+
gap: 4px !important;
|
|
225
|
+
}
|
|
226
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-actions button {
|
|
227
|
+
border-radius: 5px !important;
|
|
228
|
+
background: rgba(148, 163, 184, .12) !important;
|
|
229
|
+
color: inherit !important;
|
|
230
|
+
min-width: 0 !important;
|
|
231
|
+
}
|
|
232
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-add-form {
|
|
233
|
+
display: grid !important;
|
|
234
|
+
gap: 6px !important;
|
|
235
|
+
border-radius: 6px !important;
|
|
236
|
+
background: rgba(148, 163, 184, .10) !important;
|
|
237
|
+
padding: 6px !important;
|
|
238
|
+
}
|
|
239
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-add-form input {
|
|
240
|
+
width: 100% !important;
|
|
241
|
+
min-width: 0 !important;
|
|
242
|
+
border: 1px solid rgba(148, 163, 184, .32) !important;
|
|
243
|
+
border-radius: 5px !important;
|
|
244
|
+
background: rgba(0, 0, 0, .18) !important;
|
|
245
|
+
color: inherit !important;
|
|
246
|
+
font: 500 12px/1.2 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
|
|
247
|
+
outline: none !important;
|
|
248
|
+
padding: 7px 8px !important;
|
|
249
|
+
}
|
|
250
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-add-form input:focus {
|
|
251
|
+
border-color: rgba(143, 199, 212, .76) !important;
|
|
252
|
+
box-shadow: 0 0 0 2px rgba(143, 199, 212, .18) !important;
|
|
253
|
+
}
|
|
254
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-add-actions {
|
|
255
|
+
display: grid !important;
|
|
256
|
+
grid-template-columns: 1fr 1fr !important;
|
|
257
|
+
gap: 4px !important;
|
|
258
|
+
}
|
|
259
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-add-actions button {
|
|
260
|
+
background: rgba(148, 163, 184, .14) !important;
|
|
261
|
+
}
|
|
262
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-manage {
|
|
263
|
+
display: grid !important;
|
|
264
|
+
gap: 7px !important;
|
|
265
|
+
border-top: 1px solid rgba(148, 163, 184, .18) !important;
|
|
266
|
+
padding-top: 7px !important;
|
|
267
|
+
}
|
|
268
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-manage-head {
|
|
269
|
+
display: flex !important;
|
|
270
|
+
align-items: baseline !important;
|
|
271
|
+
justify-content: space-between !important;
|
|
272
|
+
gap: 8px !important;
|
|
273
|
+
padding: 0 6px !important;
|
|
274
|
+
}
|
|
275
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-manage-head strong {
|
|
276
|
+
font: 700 11px/1.2 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
|
|
277
|
+
}
|
|
278
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-manage-head span {
|
|
279
|
+
color: color-mix(in srgb, currentColor 58%, transparent) !important;
|
|
280
|
+
font: 500 11px/1.2 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
|
|
281
|
+
}
|
|
282
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-manage-list {
|
|
283
|
+
display: grid !important;
|
|
284
|
+
gap: 6px !important;
|
|
285
|
+
max-height: 204px !important;
|
|
286
|
+
overflow-y: auto !important;
|
|
287
|
+
overscroll-behavior: contain !important;
|
|
288
|
+
padding-right: 2px !important;
|
|
289
|
+
}
|
|
290
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-manage-list::-webkit-scrollbar {
|
|
291
|
+
width: 8px !important;
|
|
292
|
+
}
|
|
293
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-manage-list::-webkit-scrollbar-thumb {
|
|
294
|
+
border-radius: 999px !important;
|
|
295
|
+
background: rgba(148, 163, 184, .34) !important;
|
|
296
|
+
}
|
|
297
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-manage-row {
|
|
298
|
+
display: grid !important;
|
|
299
|
+
grid-template-columns: minmax(0, 1fr) auto !important;
|
|
300
|
+
align-items: center !important;
|
|
301
|
+
gap: 7px !important;
|
|
302
|
+
border-radius: 6px !important;
|
|
303
|
+
background: rgba(148, 163, 184, .10) !important;
|
|
304
|
+
padding: 6px !important;
|
|
305
|
+
}
|
|
306
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-manage-title {
|
|
307
|
+
display: grid !important;
|
|
308
|
+
min-width: 0 !important;
|
|
309
|
+
}
|
|
310
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-manage-title strong,
|
|
311
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-manage-title span {
|
|
312
|
+
overflow: hidden !important;
|
|
313
|
+
text-overflow: ellipsis !important;
|
|
314
|
+
white-space: nowrap !important;
|
|
315
|
+
}
|
|
316
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-manage-title strong {
|
|
317
|
+
font: 650 12px/1.2 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
|
|
318
|
+
}
|
|
319
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-manage-title span {
|
|
320
|
+
color: color-mix(in srgb, currentColor 58%, transparent) !important;
|
|
321
|
+
font: 500 11px/1.2 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
|
|
322
|
+
}
|
|
323
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-manage-actions {
|
|
324
|
+
display: flex !important;
|
|
325
|
+
align-items: center !important;
|
|
326
|
+
justify-content: flex-end !important;
|
|
327
|
+
gap: 4px !important;
|
|
328
|
+
}
|
|
329
|
+
#${ACCOUNT_SWITCHER_ID} .clankerbend-account-manage-actions button {
|
|
330
|
+
background: rgba(148, 163, 184, .14) !important;
|
|
331
|
+
white-space: nowrap !important;
|
|
332
|
+
padding: 6px 7px !important;
|
|
333
|
+
}
|
|
334
|
+
#${ACCOUNT_SWITCHER_ID} button:disabled {
|
|
335
|
+
cursor: default !important;
|
|
336
|
+
opacity: .55 !important;
|
|
337
|
+
}
|
|
134
338
|
#${OVERLAY_ID} {
|
|
135
339
|
position: fixed !important;
|
|
136
340
|
z-index: 2147483004 !important;
|
|
@@ -261,6 +465,7 @@
|
|
|
261
465
|
font: 600 12px/1.25 ui-monospace, SFMono-Regular, Menlo, monospace !important;
|
|
262
466
|
letter-spacing: 0 !important;
|
|
263
467
|
user-select: none !important;
|
|
468
|
+
pointer-events: none !important;
|
|
264
469
|
}
|
|
265
470
|
#${MODE_BADGE_ID}.is-visible {
|
|
266
471
|
display: flex !important;
|
|
@@ -290,6 +495,7 @@
|
|
|
290
495
|
padding: 0 3px !important;
|
|
291
496
|
font: 700 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace !important;
|
|
292
497
|
cursor: pointer !important;
|
|
498
|
+
pointer-events: auto !important;
|
|
293
499
|
}
|
|
294
500
|
#${MODE_BADGE_ID} .codex-vim-nav-dismiss:hover {
|
|
295
501
|
color: #e8f6fa !important;
|
|
@@ -306,6 +512,7 @@
|
|
|
306
512
|
padding: 0 5px !important;
|
|
307
513
|
font: 700 11px/1.3 ui-monospace, SFMono-Regular, Menlo, monospace !important;
|
|
308
514
|
cursor: pointer !important;
|
|
515
|
+
pointer-events: auto !important;
|
|
309
516
|
}
|
|
310
517
|
#${MODE_BADGE_ID} .codex-vim-nav-help-toggle:hover {
|
|
311
518
|
border-color: rgba(143, 199, 212, .75) !important;
|
|
@@ -2131,6 +2338,8 @@
|
|
|
2131
2338
|
|
|
2132
2339
|
function applyHostState(hostState) {
|
|
2133
2340
|
state.hostState = hostState || null;
|
|
2341
|
+
settleAccountUiStatus();
|
|
2342
|
+
renderCodexAccountSwitcher();
|
|
2134
2343
|
renderSelectionMenu();
|
|
2135
2344
|
renderOverlay();
|
|
2136
2345
|
renderComposerContextChips();
|
|
@@ -2139,6 +2348,430 @@
|
|
|
2139
2348
|
return { ok: true };
|
|
2140
2349
|
}
|
|
2141
2350
|
|
|
2351
|
+
function settleAccountUiStatus() {
|
|
2352
|
+
if (state.accountUiStatus !== "Saving default...") return;
|
|
2353
|
+
if (state.hostState?.codexAccounts?.switching) return;
|
|
2354
|
+
state.accountUiStatus = "Default saved";
|
|
2355
|
+
setTimeout(() => {
|
|
2356
|
+
if (state.accountUiStatus !== "Default saved") return;
|
|
2357
|
+
state.accountUiStatus = "";
|
|
2358
|
+
renderCodexAccountSwitcher();
|
|
2359
|
+
}, 1400);
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2362
|
+
function codexAccountsState() {
|
|
2363
|
+
return state.hostState?.codexAccounts || null;
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
function visibleElement(el) {
|
|
2367
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
2368
|
+
if (!el.isConnected) return false;
|
|
2369
|
+
if (isHostUiElement(el)) return false;
|
|
2370
|
+
const rect = el.getBoundingClientRect();
|
|
2371
|
+
if (rect.width <= 0 || rect.height <= 0) return false;
|
|
2372
|
+
const style = getComputedStyle(el);
|
|
2373
|
+
return style.visibility !== "hidden" && style.display !== "none";
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
function normalizedText(el) {
|
|
2377
|
+
return String(el?.innerText || el?.textContent || "").replace(/\s+/g, " ").trim();
|
|
2378
|
+
}
|
|
2379
|
+
|
|
2380
|
+
function findLogoutMenuItem() {
|
|
2381
|
+
const candidates = Array.from(document.querySelectorAll("button,a,[role='menuitem'],[role='button']"))
|
|
2382
|
+
.filter(visibleElement)
|
|
2383
|
+
.map((el) => {
|
|
2384
|
+
const text = normalizedText(el);
|
|
2385
|
+
const label = [text, el.getAttribute("aria-label"), el.getAttribute("title")].filter(Boolean).join(" ");
|
|
2386
|
+
return { el, label };
|
|
2387
|
+
})
|
|
2388
|
+
.filter((candidate) => /\b(log\s*out|logout|sign\s*out)\b/i.test(candidate.label));
|
|
2389
|
+
return candidates
|
|
2390
|
+
.map((candidate) => candidate.el.closest?.("button,a,[role='menuitem']") || candidate.el)
|
|
2391
|
+
.find((el) => el instanceof HTMLElement && visibleElement(el)) || null;
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
function accountMenuInsertionParent(logoutItem) {
|
|
2395
|
+
if (!(logoutItem instanceof HTMLElement)) return null;
|
|
2396
|
+
for (let node = logoutItem.parentElement; node && node !== document.body && node !== document.documentElement; node = node.parentElement) {
|
|
2397
|
+
if (isHostUiElement(node)) return null;
|
|
2398
|
+
const rect = node.getBoundingClientRect();
|
|
2399
|
+
if (rect.width < 150 || rect.width > 520 || rect.height < 40 || rect.height > window.innerHeight * 0.9) continue;
|
|
2400
|
+
if (node.contains(logoutItem)) return node;
|
|
2401
|
+
}
|
|
2402
|
+
return logoutItem.parentElement;
|
|
2403
|
+
}
|
|
2404
|
+
|
|
2405
|
+
function accountMenuInsertionReference(parent, logoutItem) {
|
|
2406
|
+
if (!(parent instanceof HTMLElement) || !(logoutItem instanceof HTMLElement)) return null;
|
|
2407
|
+
let reference = logoutItem;
|
|
2408
|
+
while (reference?.parentElement && reference.parentElement !== parent) {
|
|
2409
|
+
reference = reference.parentElement;
|
|
2410
|
+
}
|
|
2411
|
+
return reference?.parentElement === parent ? reference : null;
|
|
2412
|
+
}
|
|
2413
|
+
|
|
2414
|
+
function accountUiSignature(accountsState) {
|
|
2415
|
+
const accounts = accountsState?.accounts || [];
|
|
2416
|
+
return JSON.stringify({
|
|
2417
|
+
active: accountsState?.activeAccountId || null,
|
|
2418
|
+
def: accountsState?.clankerbendDefaultAccountId || null,
|
|
2419
|
+
switching: Boolean(accountsState?.switching),
|
|
2420
|
+
status: state.accountUiStatus,
|
|
2421
|
+
addOpen: state.accountAddOpen,
|
|
2422
|
+
manageOpen: state.accountManageOpen,
|
|
2423
|
+
pendingAdopt: state.accountPendingAdoptId,
|
|
2424
|
+
pendingDelete: state.accountPendingDeleteId,
|
|
2425
|
+
accounts: accounts.map((account) => [
|
|
2426
|
+
account.id,
|
|
2427
|
+
account.kind,
|
|
2428
|
+
account.label,
|
|
2429
|
+
account.codexHome,
|
|
2430
|
+
account.auth?.authJson ? 1 : 0,
|
|
2431
|
+
account.backup ? 1 : 0
|
|
2432
|
+
])
|
|
2433
|
+
});
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2436
|
+
function renderCodexAccountSwitcher() {
|
|
2437
|
+
const accountsState = codexAccountsState();
|
|
2438
|
+
const logoutItem = findLogoutMenuItem();
|
|
2439
|
+
if (!accountsState?.available || !logoutItem) {
|
|
2440
|
+
document.getElementById(ACCOUNT_SWITCHER_ID)?.remove();
|
|
2441
|
+
return;
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
const parent = accountMenuInsertionParent(logoutItem);
|
|
2445
|
+
if (!(parent instanceof HTMLElement)) return;
|
|
2446
|
+
const reference = accountMenuInsertionReference(parent, logoutItem);
|
|
2447
|
+
if (!(reference instanceof HTMLElement)) return;
|
|
2448
|
+
let ui = document.getElementById(ACCOUNT_SWITCHER_ID);
|
|
2449
|
+
if (ui && ui.parentElement !== parent) {
|
|
2450
|
+
ui.remove();
|
|
2451
|
+
ui = null;
|
|
2452
|
+
}
|
|
2453
|
+
if (!ui) {
|
|
2454
|
+
ui = document.createElement("div");
|
|
2455
|
+
ui.id = ACCOUNT_SWITCHER_ID;
|
|
2456
|
+
ui.className = HOST_UI_CLASS;
|
|
2457
|
+
stopNativeMenuPropagation(ui);
|
|
2458
|
+
parent.insertBefore(ui, reference);
|
|
2459
|
+
} else if (ui.nextElementSibling !== reference) {
|
|
2460
|
+
parent.insertBefore(ui, reference);
|
|
2461
|
+
}
|
|
2462
|
+
|
|
2463
|
+
const signature = accountUiSignature(accountsState);
|
|
2464
|
+
if (ui.dataset.signature === signature) return;
|
|
2465
|
+
ui.dataset.signature = signature;
|
|
2466
|
+
ui.replaceChildren();
|
|
2467
|
+
|
|
2468
|
+
const head = document.createElement("div");
|
|
2469
|
+
head.className = "clankerbend-account-head";
|
|
2470
|
+
const title = document.createElement("div");
|
|
2471
|
+
title.className = "clankerbend-account-title";
|
|
2472
|
+
title.textContent = "ClankerID";
|
|
2473
|
+
const status = document.createElement("div");
|
|
2474
|
+
status.className = "clankerbend-account-status";
|
|
2475
|
+
status.textContent = state.accountUiStatus || (accountsState.switching ? "Switching..." : `${accountsState.accounts?.length || 0}/${accountsState.maxAccounts || 20}`);
|
|
2476
|
+
head.append(title, status);
|
|
2477
|
+
ui.appendChild(head);
|
|
2478
|
+
|
|
2479
|
+
if (state.accountManageOpen) {
|
|
2480
|
+
ui.appendChild(renderCodexAccountManage(accountsState));
|
|
2481
|
+
} else {
|
|
2482
|
+
const list = document.createElement("div");
|
|
2483
|
+
list.className = "clankerbend-account-list";
|
|
2484
|
+
for (const account of accountsState.accounts || []) {
|
|
2485
|
+
list.appendChild(renderCodexAccountRow(account, accountsState));
|
|
2486
|
+
}
|
|
2487
|
+
ui.appendChild(list);
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
const actions = document.createElement("div");
|
|
2491
|
+
actions.className = "clankerbend-account-actions";
|
|
2492
|
+
const add = document.createElement("button");
|
|
2493
|
+
add.type = "button";
|
|
2494
|
+
add.textContent = "Add account";
|
|
2495
|
+
add.disabled = Boolean(accountsState.switching) || (accountsState.accounts?.length || 0) >= (accountsState.maxAccounts || 20);
|
|
2496
|
+
bindInjectedActivation(add, () => {
|
|
2497
|
+
state.accountAddOpen = true;
|
|
2498
|
+
state.accountUiStatus = "";
|
|
2499
|
+
renderCodexAccountSwitcher();
|
|
2500
|
+
setTimeout(() => {
|
|
2501
|
+
document.querySelector(`#${ACCOUNT_SWITCHER_ID} input[data-clankerbend-account-label]`)?.focus?.({ preventScroll: true });
|
|
2502
|
+
}, 0);
|
|
2503
|
+
});
|
|
2504
|
+
const manage = document.createElement("button");
|
|
2505
|
+
manage.type = "button";
|
|
2506
|
+
manage.textContent = state.accountManageOpen ? "Done" : "Manage";
|
|
2507
|
+
bindInjectedActivation(manage, () => {
|
|
2508
|
+
state.accountManageOpen = !state.accountManageOpen;
|
|
2509
|
+
state.accountPendingAdoptId = null;
|
|
2510
|
+
state.accountPendingDeleteId = null;
|
|
2511
|
+
state.accountUiStatus = "";
|
|
2512
|
+
renderCodexAccountSwitcher();
|
|
2513
|
+
});
|
|
2514
|
+
actions.append(add, manage);
|
|
2515
|
+
ui.appendChild(actions);
|
|
2516
|
+
if (state.accountAddOpen) ui.appendChild(renderCodexAccountAddForm(accountsState));
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
function renderCodexAccountAddForm(accountsState) {
|
|
2520
|
+
const form = document.createElement("div");
|
|
2521
|
+
form.className = "clankerbend-account-add-form";
|
|
2522
|
+
|
|
2523
|
+
const input = document.createElement("input");
|
|
2524
|
+
input.type = "text";
|
|
2525
|
+
input.dataset.clankerbendAccountLabel = "true";
|
|
2526
|
+
input.placeholder = "Account label";
|
|
2527
|
+
input.value = state.accountAddLabel || "";
|
|
2528
|
+
input.autocomplete = "off";
|
|
2529
|
+
input.spellcheck = false;
|
|
2530
|
+
const stopInputEvent = (event) => {
|
|
2531
|
+
event.stopPropagation();
|
|
2532
|
+
event.stopImmediatePropagation?.();
|
|
2533
|
+
};
|
|
2534
|
+
input.addEventListener("pointerdown", stopInputEvent, true);
|
|
2535
|
+
input.addEventListener("mousedown", stopInputEvent, true);
|
|
2536
|
+
input.addEventListener("click", stopInputEvent, true);
|
|
2537
|
+
input.addEventListener("input", () => {
|
|
2538
|
+
state.accountAddLabel = input.value;
|
|
2539
|
+
});
|
|
2540
|
+
input.addEventListener("keydown", (event) => {
|
|
2541
|
+
event.stopPropagation();
|
|
2542
|
+
event.stopImmediatePropagation?.();
|
|
2543
|
+
if (event.key === "Enter") {
|
|
2544
|
+
event.preventDefault();
|
|
2545
|
+
submitCodexAccountAdd(input.value);
|
|
2546
|
+
} else if (event.key === "Escape") {
|
|
2547
|
+
event.preventDefault();
|
|
2548
|
+
cancelCodexAccountAdd();
|
|
2549
|
+
}
|
|
2550
|
+
}, true);
|
|
2551
|
+
|
|
2552
|
+
const buttons = document.createElement("div");
|
|
2553
|
+
buttons.className = "clankerbend-account-add-actions";
|
|
2554
|
+
const cancel = document.createElement("button");
|
|
2555
|
+
cancel.type = "button";
|
|
2556
|
+
cancel.textContent = "Cancel";
|
|
2557
|
+
bindInjectedActivation(cancel, cancelCodexAccountAdd);
|
|
2558
|
+
const create = document.createElement("button");
|
|
2559
|
+
create.type = "button";
|
|
2560
|
+
create.textContent = "Create";
|
|
2561
|
+
create.disabled = Boolean(accountsState.switching);
|
|
2562
|
+
bindInjectedActivation(create, () => submitCodexAccountAdd(input.value));
|
|
2563
|
+
buttons.append(cancel, create);
|
|
2564
|
+
form.append(input, buttons);
|
|
2565
|
+
return form;
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2568
|
+
function cancelCodexAccountAdd() {
|
|
2569
|
+
state.accountAddOpen = false;
|
|
2570
|
+
state.accountAddLabel = "";
|
|
2571
|
+
state.accountUiStatus = "";
|
|
2572
|
+
renderCodexAccountSwitcher();
|
|
2573
|
+
}
|
|
2574
|
+
|
|
2575
|
+
function submitCodexAccountAdd(rawLabel) {
|
|
2576
|
+
const label = String(rawLabel || "").trim();
|
|
2577
|
+
if (!label) {
|
|
2578
|
+
state.accountUiStatus = "Label required";
|
|
2579
|
+
renderCodexAccountSwitcher();
|
|
2580
|
+
setTimeout(() => {
|
|
2581
|
+
if (state.accountUiStatus !== "Label required") return;
|
|
2582
|
+
state.accountUiStatus = "";
|
|
2583
|
+
renderCodexAccountSwitcher();
|
|
2584
|
+
}, 1400);
|
|
2585
|
+
return;
|
|
2586
|
+
}
|
|
2587
|
+
state.accountAddOpen = false;
|
|
2588
|
+
state.accountAddLabel = "";
|
|
2589
|
+
state.accountUiStatus = "Creating...";
|
|
2590
|
+
renderCodexAccountSwitcher();
|
|
2591
|
+
enqueueHostEvent({ kind: "codexAccountCreateAndSwitch", label });
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
function renderCodexAccountManage(accountsState) {
|
|
2595
|
+
const wrap = document.createElement("div");
|
|
2596
|
+
wrap.className = "clankerbend-account-manage";
|
|
2597
|
+
const head = document.createElement("div");
|
|
2598
|
+
head.className = "clankerbend-account-manage-head";
|
|
2599
|
+
const title = document.createElement("strong");
|
|
2600
|
+
title.textContent = "Manage profiles";
|
|
2601
|
+
const count = document.createElement("span");
|
|
2602
|
+
const managed = (accountsState.accounts || []).filter((account) => account.kind === "managed");
|
|
2603
|
+
count.textContent = `${managed.length} managed`;
|
|
2604
|
+
head.append(title, count);
|
|
2605
|
+
wrap.appendChild(head);
|
|
2606
|
+
const list = document.createElement("div");
|
|
2607
|
+
list.className = "clankerbend-account-manage-list";
|
|
2608
|
+
if (!managed.length) {
|
|
2609
|
+
const row = document.createElement("div");
|
|
2610
|
+
row.className = "clankerbend-account-manage-row";
|
|
2611
|
+
const title = document.createElement("div");
|
|
2612
|
+
title.className = "clankerbend-account-manage-title";
|
|
2613
|
+
const strong = document.createElement("strong");
|
|
2614
|
+
strong.textContent = "No managed accounts";
|
|
2615
|
+
const hint = document.createElement("span");
|
|
2616
|
+
hint.textContent = "Create one with Add account.";
|
|
2617
|
+
title.append(strong, hint);
|
|
2618
|
+
row.appendChild(title);
|
|
2619
|
+
list.appendChild(row);
|
|
2620
|
+
wrap.appendChild(list);
|
|
2621
|
+
return wrap;
|
|
2622
|
+
}
|
|
2623
|
+
for (const account of managed) list.appendChild(renderCodexAccountManageRow(account, accountsState));
|
|
2624
|
+
wrap.appendChild(list);
|
|
2625
|
+
return wrap;
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2628
|
+
function renderCodexAccountManageRow(account, accountsState) {
|
|
2629
|
+
const row = document.createElement("div");
|
|
2630
|
+
row.className = "clankerbend-account-manage-row";
|
|
2631
|
+
row.title = accountCodexHomeTitle(account);
|
|
2632
|
+
const title = document.createElement("div");
|
|
2633
|
+
title.className = "clankerbend-account-manage-title";
|
|
2634
|
+
const strong = document.createElement("strong");
|
|
2635
|
+
strong.textContent = account.label || account.id;
|
|
2636
|
+
const hint = document.createElement("span");
|
|
2637
|
+
const pendingAdopt = state.accountPendingAdoptId === account.id;
|
|
2638
|
+
const pendingDelete = state.accountPendingDeleteId === account.id;
|
|
2639
|
+
hint.textContent = pendingAdopt
|
|
2640
|
+
? "Replace ~/.codex with this profile; current primary is backed up."
|
|
2641
|
+
: pendingDelete
|
|
2642
|
+
? "Hide this profile and move its files to archive storage."
|
|
2643
|
+
: [account.auth?.authJson ? "signed in" : "not signed in", account.backup ? "backup" : ""].filter(Boolean).join(" · ");
|
|
2644
|
+
title.append(strong, hint);
|
|
2645
|
+
|
|
2646
|
+
const actions = document.createElement("div");
|
|
2647
|
+
actions.className = "clankerbend-account-manage-actions";
|
|
2648
|
+
const adopt = document.createElement("button");
|
|
2649
|
+
adopt.type = "button";
|
|
2650
|
+
adopt.textContent = pendingAdopt ? "Replace ~/.codex" : "Make primary";
|
|
2651
|
+
adopt.title = "Make this the primary Codex home";
|
|
2652
|
+
adopt.disabled = Boolean(accountsState.switching);
|
|
2653
|
+
bindInjectedActivation(adopt, () => {
|
|
2654
|
+
if (!pendingAdopt) {
|
|
2655
|
+
state.accountPendingAdoptId = account.id;
|
|
2656
|
+
state.accountPendingDeleteId = null;
|
|
2657
|
+
state.accountUiStatus = "";
|
|
2658
|
+
renderCodexAccountSwitcher();
|
|
2659
|
+
return;
|
|
2660
|
+
}
|
|
2661
|
+
state.accountUiStatus = "Making primary...";
|
|
2662
|
+
state.accountPendingAdoptId = null;
|
|
2663
|
+
renderCodexAccountSwitcher();
|
|
2664
|
+
enqueueHostEvent({ kind: "codexAccountAdoptAsPrimary", accountId: account.id });
|
|
2665
|
+
});
|
|
2666
|
+
|
|
2667
|
+
const remove = document.createElement("button");
|
|
2668
|
+
remove.type = "button";
|
|
2669
|
+
remove.textContent = pendingDelete ? "Archive profile" : "Archive";
|
|
2670
|
+
remove.title = "Archive this managed profile";
|
|
2671
|
+
remove.disabled = Boolean(accountsState.switching) || account.id === accountsState.activeAccountId;
|
|
2672
|
+
bindInjectedActivation(remove, () => {
|
|
2673
|
+
if (!pendingDelete) {
|
|
2674
|
+
state.accountPendingDeleteId = account.id;
|
|
2675
|
+
state.accountPendingAdoptId = null;
|
|
2676
|
+
state.accountUiStatus = "";
|
|
2677
|
+
renderCodexAccountSwitcher();
|
|
2678
|
+
return;
|
|
2679
|
+
}
|
|
2680
|
+
state.accountUiStatus = "Archiving...";
|
|
2681
|
+
state.accountPendingDeleteId = null;
|
|
2682
|
+
renderCodexAccountSwitcher();
|
|
2683
|
+
enqueueHostEvent({ kind: "codexAccountDelete", accountId: account.id });
|
|
2684
|
+
});
|
|
2685
|
+
|
|
2686
|
+
actions.append(adopt, remove);
|
|
2687
|
+
row.append(title, actions);
|
|
2688
|
+
return row;
|
|
2689
|
+
}
|
|
2690
|
+
|
|
2691
|
+
function stopNativeMenuPropagation(el) {
|
|
2692
|
+
const stop = (event) => {
|
|
2693
|
+
event.stopPropagation();
|
|
2694
|
+
event.stopImmediatePropagation?.();
|
|
2695
|
+
};
|
|
2696
|
+
el.addEventListener("pointerdown", stop);
|
|
2697
|
+
el.addEventListener("mousedown", stop);
|
|
2698
|
+
el.addEventListener("touchstart", stop, { passive: false });
|
|
2699
|
+
el.addEventListener("click", stop);
|
|
2700
|
+
}
|
|
2701
|
+
|
|
2702
|
+
function bindInjectedActivation(button, handler) {
|
|
2703
|
+
let lastRunAt = 0;
|
|
2704
|
+
const run = (event) => {
|
|
2705
|
+
event.preventDefault();
|
|
2706
|
+
event.stopPropagation();
|
|
2707
|
+
event.stopImmediatePropagation?.();
|
|
2708
|
+
if (button.disabled) return;
|
|
2709
|
+
const now = Date.now();
|
|
2710
|
+
if (now - lastRunAt < 450) return;
|
|
2711
|
+
lastRunAt = now;
|
|
2712
|
+
handler(event);
|
|
2713
|
+
};
|
|
2714
|
+
button.addEventListener("pointerdown", run, true);
|
|
2715
|
+
button.addEventListener("mousedown", run, true);
|
|
2716
|
+
button.addEventListener("touchstart", run, { capture: true, passive: false });
|
|
2717
|
+
button.addEventListener("click", run, true);
|
|
2718
|
+
}
|
|
2719
|
+
|
|
2720
|
+
function renderCodexAccountRow(account, accountsState) {
|
|
2721
|
+
const row = document.createElement("div");
|
|
2722
|
+
row.className = `clankerbend-account-row${account.id === accountsState.activeAccountId ? " is-active" : ""}`;
|
|
2723
|
+
|
|
2724
|
+
const main = document.createElement("button");
|
|
2725
|
+
main.type = "button";
|
|
2726
|
+
main.className = "clankerbend-account-main";
|
|
2727
|
+
main.disabled = Boolean(accountsState.switching) || account.id === accountsState.activeAccountId;
|
|
2728
|
+
main.title = [
|
|
2729
|
+
account.id === accountsState.activeAccountId ? "Current Codex account profile" : `Switch to ${account.label || account.id}`,
|
|
2730
|
+
accountCodexHomeTitle(account)
|
|
2731
|
+
].filter(Boolean).join("\n");
|
|
2732
|
+
bindInjectedActivation(main, () => {
|
|
2733
|
+
if (account.id === accountsState.activeAccountId) return;
|
|
2734
|
+
state.accountUiStatus = "Switching...";
|
|
2735
|
+
renderCodexAccountSwitcher();
|
|
2736
|
+
enqueueHostEvent({ kind: "codexAccountSwitch", accountId: account.id });
|
|
2737
|
+
});
|
|
2738
|
+
|
|
2739
|
+
const label = document.createElement("span");
|
|
2740
|
+
label.className = "clankerbend-account-label";
|
|
2741
|
+
label.textContent = account.label || account.id;
|
|
2742
|
+
const meta = document.createElement("span");
|
|
2743
|
+
meta.className = "clankerbend-account-meta";
|
|
2744
|
+
meta.textContent = [
|
|
2745
|
+
account.kind,
|
|
2746
|
+
account.id === accountsState.clankerbendDefaultAccountId ? "default" : "",
|
|
2747
|
+
account.auth?.authJson ? "signed in" : "not signed in"
|
|
2748
|
+
].filter(Boolean).join(" · ");
|
|
2749
|
+
main.append(label, meta);
|
|
2750
|
+
|
|
2751
|
+
const def = document.createElement("button");
|
|
2752
|
+
def.type = "button";
|
|
2753
|
+
def.className = "clankerbend-account-icon";
|
|
2754
|
+
def.textContent = account.id === accountsState.clankerbendDefaultAccountId ? "✓" : "☆";
|
|
2755
|
+
def.setAttribute("aria-label", account.id === accountsState.clankerbendDefaultAccountId ? "Launches by default" : "Launch by default");
|
|
2756
|
+
def.title = [
|
|
2757
|
+
account.id === accountsState.clankerbendDefaultAccountId ? "Launches by default" : "Launch by default",
|
|
2758
|
+
accountCodexHomeTitle(account)
|
|
2759
|
+
].filter(Boolean).join("\n");
|
|
2760
|
+
def.disabled = Boolean(accountsState.switching) || account.id === accountsState.clankerbendDefaultAccountId;
|
|
2761
|
+
bindInjectedActivation(def, () => {
|
|
2762
|
+
state.accountUiStatus = "Saving default...";
|
|
2763
|
+
renderCodexAccountSwitcher();
|
|
2764
|
+
enqueueHostEvent({ kind: "codexAccountSetDefault", accountId: account.id });
|
|
2765
|
+
});
|
|
2766
|
+
|
|
2767
|
+
row.append(main, def);
|
|
2768
|
+
return row;
|
|
2769
|
+
}
|
|
2770
|
+
|
|
2771
|
+
function accountCodexHomeTitle(account) {
|
|
2772
|
+
return account?.codexHome ? `CODEX_HOME: ${account.codexHome}` : "";
|
|
2773
|
+
}
|
|
2774
|
+
|
|
2142
2775
|
function hostSelectionActions() {
|
|
2143
2776
|
return (state.hostState?.selectionActions || [])
|
|
2144
2777
|
.filter((action) => action?.enabled !== false && action.appliesTo === "text-selection");
|
|
@@ -3415,6 +4048,7 @@
|
|
|
3415
4048
|
const mutationObserver = new MutationObserver(() => {
|
|
3416
4049
|
state.version += 1;
|
|
3417
4050
|
scheduleAnnotationRefresh();
|
|
4051
|
+
renderCodexAccountSwitcher();
|
|
3418
4052
|
maskSubmittedComposerContexts();
|
|
3419
4053
|
});
|
|
3420
4054
|
window.addEventListener("resize", resizeHandler);
|
|
@@ -3428,6 +4062,7 @@
|
|
|
3428
4062
|
document.removeEventListener("mouseup", handlePossibleTextSelection, true);
|
|
3429
4063
|
document.removeEventListener("click", window.__clankerbendComposerSubmitClickHandler, true);
|
|
3430
4064
|
document.removeEventListener("keydown", window.__clankerbendComposerSubmitKeyHandler, true);
|
|
4065
|
+
document.getElementById(ACCOUNT_SWITCHER_ID)?.remove();
|
|
3431
4066
|
window.removeEventListener("resize", resizeHandler);
|
|
3432
4067
|
window.removeEventListener("blur", blurHandler);
|
|
3433
4068
|
mutationObserver.disconnect();
|