clankerbend 0.1.1 → 0.1.2
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 +15 -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 +15 -8
- package/host/src/app-registry.js +2 -0
- package/host/src/codex-desktop-cdp-adapter.js +23 -1
- package/host/src/codex-desktop-renderer-bridge.js +633 -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 = 115;
|
|
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;
|
|
@@ -2131,6 +2335,8 @@
|
|
|
2131
2335
|
|
|
2132
2336
|
function applyHostState(hostState) {
|
|
2133
2337
|
state.hostState = hostState || null;
|
|
2338
|
+
settleAccountUiStatus();
|
|
2339
|
+
renderCodexAccountSwitcher();
|
|
2134
2340
|
renderSelectionMenu();
|
|
2135
2341
|
renderOverlay();
|
|
2136
2342
|
renderComposerContextChips();
|
|
@@ -2139,6 +2345,430 @@
|
|
|
2139
2345
|
return { ok: true };
|
|
2140
2346
|
}
|
|
2141
2347
|
|
|
2348
|
+
function settleAccountUiStatus() {
|
|
2349
|
+
if (state.accountUiStatus !== "Saving default...") return;
|
|
2350
|
+
if (state.hostState?.codexAccounts?.switching) return;
|
|
2351
|
+
state.accountUiStatus = "Default saved";
|
|
2352
|
+
setTimeout(() => {
|
|
2353
|
+
if (state.accountUiStatus !== "Default saved") return;
|
|
2354
|
+
state.accountUiStatus = "";
|
|
2355
|
+
renderCodexAccountSwitcher();
|
|
2356
|
+
}, 1400);
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
function codexAccountsState() {
|
|
2360
|
+
return state.hostState?.codexAccounts || null;
|
|
2361
|
+
}
|
|
2362
|
+
|
|
2363
|
+
function visibleElement(el) {
|
|
2364
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
2365
|
+
if (!el.isConnected) return false;
|
|
2366
|
+
if (isHostUiElement(el)) return false;
|
|
2367
|
+
const rect = el.getBoundingClientRect();
|
|
2368
|
+
if (rect.width <= 0 || rect.height <= 0) return false;
|
|
2369
|
+
const style = getComputedStyle(el);
|
|
2370
|
+
return style.visibility !== "hidden" && style.display !== "none";
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
function normalizedText(el) {
|
|
2374
|
+
return String(el?.innerText || el?.textContent || "").replace(/\s+/g, " ").trim();
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
function findLogoutMenuItem() {
|
|
2378
|
+
const candidates = Array.from(document.querySelectorAll("button,a,[role='menuitem'],[role='button']"))
|
|
2379
|
+
.filter(visibleElement)
|
|
2380
|
+
.map((el) => {
|
|
2381
|
+
const text = normalizedText(el);
|
|
2382
|
+
const label = [text, el.getAttribute("aria-label"), el.getAttribute("title")].filter(Boolean).join(" ");
|
|
2383
|
+
return { el, label };
|
|
2384
|
+
})
|
|
2385
|
+
.filter((candidate) => /\b(log\s*out|logout|sign\s*out)\b/i.test(candidate.label));
|
|
2386
|
+
return candidates
|
|
2387
|
+
.map((candidate) => candidate.el.closest?.("button,a,[role='menuitem']") || candidate.el)
|
|
2388
|
+
.find((el) => el instanceof HTMLElement && visibleElement(el)) || null;
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
function accountMenuInsertionParent(logoutItem) {
|
|
2392
|
+
if (!(logoutItem instanceof HTMLElement)) return null;
|
|
2393
|
+
for (let node = logoutItem.parentElement; node && node !== document.body && node !== document.documentElement; node = node.parentElement) {
|
|
2394
|
+
if (isHostUiElement(node)) return null;
|
|
2395
|
+
const rect = node.getBoundingClientRect();
|
|
2396
|
+
if (rect.width < 150 || rect.width > 520 || rect.height < 40 || rect.height > window.innerHeight * 0.9) continue;
|
|
2397
|
+
if (node.contains(logoutItem)) return node;
|
|
2398
|
+
}
|
|
2399
|
+
return logoutItem.parentElement;
|
|
2400
|
+
}
|
|
2401
|
+
|
|
2402
|
+
function accountMenuInsertionReference(parent, logoutItem) {
|
|
2403
|
+
if (!(parent instanceof HTMLElement) || !(logoutItem instanceof HTMLElement)) return null;
|
|
2404
|
+
let reference = logoutItem;
|
|
2405
|
+
while (reference?.parentElement && reference.parentElement !== parent) {
|
|
2406
|
+
reference = reference.parentElement;
|
|
2407
|
+
}
|
|
2408
|
+
return reference?.parentElement === parent ? reference : null;
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
function accountUiSignature(accountsState) {
|
|
2412
|
+
const accounts = accountsState?.accounts || [];
|
|
2413
|
+
return JSON.stringify({
|
|
2414
|
+
active: accountsState?.activeAccountId || null,
|
|
2415
|
+
def: accountsState?.clankerbendDefaultAccountId || null,
|
|
2416
|
+
switching: Boolean(accountsState?.switching),
|
|
2417
|
+
status: state.accountUiStatus,
|
|
2418
|
+
addOpen: state.accountAddOpen,
|
|
2419
|
+
manageOpen: state.accountManageOpen,
|
|
2420
|
+
pendingAdopt: state.accountPendingAdoptId,
|
|
2421
|
+
pendingDelete: state.accountPendingDeleteId,
|
|
2422
|
+
accounts: accounts.map((account) => [
|
|
2423
|
+
account.id,
|
|
2424
|
+
account.kind,
|
|
2425
|
+
account.label,
|
|
2426
|
+
account.codexHome,
|
|
2427
|
+
account.auth?.authJson ? 1 : 0,
|
|
2428
|
+
account.backup ? 1 : 0
|
|
2429
|
+
])
|
|
2430
|
+
});
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2433
|
+
function renderCodexAccountSwitcher() {
|
|
2434
|
+
const accountsState = codexAccountsState();
|
|
2435
|
+
const logoutItem = findLogoutMenuItem();
|
|
2436
|
+
if (!accountsState?.available || !logoutItem) {
|
|
2437
|
+
document.getElementById(ACCOUNT_SWITCHER_ID)?.remove();
|
|
2438
|
+
return;
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2441
|
+
const parent = accountMenuInsertionParent(logoutItem);
|
|
2442
|
+
if (!(parent instanceof HTMLElement)) return;
|
|
2443
|
+
const reference = accountMenuInsertionReference(parent, logoutItem);
|
|
2444
|
+
if (!(reference instanceof HTMLElement)) return;
|
|
2445
|
+
let ui = document.getElementById(ACCOUNT_SWITCHER_ID);
|
|
2446
|
+
if (ui && ui.parentElement !== parent) {
|
|
2447
|
+
ui.remove();
|
|
2448
|
+
ui = null;
|
|
2449
|
+
}
|
|
2450
|
+
if (!ui) {
|
|
2451
|
+
ui = document.createElement("div");
|
|
2452
|
+
ui.id = ACCOUNT_SWITCHER_ID;
|
|
2453
|
+
ui.className = HOST_UI_CLASS;
|
|
2454
|
+
stopNativeMenuPropagation(ui);
|
|
2455
|
+
parent.insertBefore(ui, reference);
|
|
2456
|
+
} else if (ui.nextElementSibling !== reference) {
|
|
2457
|
+
parent.insertBefore(ui, reference);
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
const signature = accountUiSignature(accountsState);
|
|
2461
|
+
if (ui.dataset.signature === signature) return;
|
|
2462
|
+
ui.dataset.signature = signature;
|
|
2463
|
+
ui.replaceChildren();
|
|
2464
|
+
|
|
2465
|
+
const head = document.createElement("div");
|
|
2466
|
+
head.className = "clankerbend-account-head";
|
|
2467
|
+
const title = document.createElement("div");
|
|
2468
|
+
title.className = "clankerbend-account-title";
|
|
2469
|
+
title.textContent = "ClankerID";
|
|
2470
|
+
const status = document.createElement("div");
|
|
2471
|
+
status.className = "clankerbend-account-status";
|
|
2472
|
+
status.textContent = state.accountUiStatus || (accountsState.switching ? "Switching..." : `${accountsState.accounts?.length || 0}/${accountsState.maxAccounts || 20}`);
|
|
2473
|
+
head.append(title, status);
|
|
2474
|
+
ui.appendChild(head);
|
|
2475
|
+
|
|
2476
|
+
if (state.accountManageOpen) {
|
|
2477
|
+
ui.appendChild(renderCodexAccountManage(accountsState));
|
|
2478
|
+
} else {
|
|
2479
|
+
const list = document.createElement("div");
|
|
2480
|
+
list.className = "clankerbend-account-list";
|
|
2481
|
+
for (const account of accountsState.accounts || []) {
|
|
2482
|
+
list.appendChild(renderCodexAccountRow(account, accountsState));
|
|
2483
|
+
}
|
|
2484
|
+
ui.appendChild(list);
|
|
2485
|
+
}
|
|
2486
|
+
|
|
2487
|
+
const actions = document.createElement("div");
|
|
2488
|
+
actions.className = "clankerbend-account-actions";
|
|
2489
|
+
const add = document.createElement("button");
|
|
2490
|
+
add.type = "button";
|
|
2491
|
+
add.textContent = "Add account";
|
|
2492
|
+
add.disabled = Boolean(accountsState.switching) || (accountsState.accounts?.length || 0) >= (accountsState.maxAccounts || 20);
|
|
2493
|
+
bindInjectedActivation(add, () => {
|
|
2494
|
+
state.accountAddOpen = true;
|
|
2495
|
+
state.accountUiStatus = "";
|
|
2496
|
+
renderCodexAccountSwitcher();
|
|
2497
|
+
setTimeout(() => {
|
|
2498
|
+
document.querySelector(`#${ACCOUNT_SWITCHER_ID} input[data-clankerbend-account-label]`)?.focus?.({ preventScroll: true });
|
|
2499
|
+
}, 0);
|
|
2500
|
+
});
|
|
2501
|
+
const manage = document.createElement("button");
|
|
2502
|
+
manage.type = "button";
|
|
2503
|
+
manage.textContent = state.accountManageOpen ? "Done" : "Manage";
|
|
2504
|
+
bindInjectedActivation(manage, () => {
|
|
2505
|
+
state.accountManageOpen = !state.accountManageOpen;
|
|
2506
|
+
state.accountPendingAdoptId = null;
|
|
2507
|
+
state.accountPendingDeleteId = null;
|
|
2508
|
+
state.accountUiStatus = "";
|
|
2509
|
+
renderCodexAccountSwitcher();
|
|
2510
|
+
});
|
|
2511
|
+
actions.append(add, manage);
|
|
2512
|
+
ui.appendChild(actions);
|
|
2513
|
+
if (state.accountAddOpen) ui.appendChild(renderCodexAccountAddForm(accountsState));
|
|
2514
|
+
}
|
|
2515
|
+
|
|
2516
|
+
function renderCodexAccountAddForm(accountsState) {
|
|
2517
|
+
const form = document.createElement("div");
|
|
2518
|
+
form.className = "clankerbend-account-add-form";
|
|
2519
|
+
|
|
2520
|
+
const input = document.createElement("input");
|
|
2521
|
+
input.type = "text";
|
|
2522
|
+
input.dataset.clankerbendAccountLabel = "true";
|
|
2523
|
+
input.placeholder = "Account label";
|
|
2524
|
+
input.value = state.accountAddLabel || "";
|
|
2525
|
+
input.autocomplete = "off";
|
|
2526
|
+
input.spellcheck = false;
|
|
2527
|
+
const stopInputEvent = (event) => {
|
|
2528
|
+
event.stopPropagation();
|
|
2529
|
+
event.stopImmediatePropagation?.();
|
|
2530
|
+
};
|
|
2531
|
+
input.addEventListener("pointerdown", stopInputEvent, true);
|
|
2532
|
+
input.addEventListener("mousedown", stopInputEvent, true);
|
|
2533
|
+
input.addEventListener("click", stopInputEvent, true);
|
|
2534
|
+
input.addEventListener("input", () => {
|
|
2535
|
+
state.accountAddLabel = input.value;
|
|
2536
|
+
});
|
|
2537
|
+
input.addEventListener("keydown", (event) => {
|
|
2538
|
+
event.stopPropagation();
|
|
2539
|
+
event.stopImmediatePropagation?.();
|
|
2540
|
+
if (event.key === "Enter") {
|
|
2541
|
+
event.preventDefault();
|
|
2542
|
+
submitCodexAccountAdd(input.value);
|
|
2543
|
+
} else if (event.key === "Escape") {
|
|
2544
|
+
event.preventDefault();
|
|
2545
|
+
cancelCodexAccountAdd();
|
|
2546
|
+
}
|
|
2547
|
+
}, true);
|
|
2548
|
+
|
|
2549
|
+
const buttons = document.createElement("div");
|
|
2550
|
+
buttons.className = "clankerbend-account-add-actions";
|
|
2551
|
+
const cancel = document.createElement("button");
|
|
2552
|
+
cancel.type = "button";
|
|
2553
|
+
cancel.textContent = "Cancel";
|
|
2554
|
+
bindInjectedActivation(cancel, cancelCodexAccountAdd);
|
|
2555
|
+
const create = document.createElement("button");
|
|
2556
|
+
create.type = "button";
|
|
2557
|
+
create.textContent = "Create";
|
|
2558
|
+
create.disabled = Boolean(accountsState.switching);
|
|
2559
|
+
bindInjectedActivation(create, () => submitCodexAccountAdd(input.value));
|
|
2560
|
+
buttons.append(cancel, create);
|
|
2561
|
+
form.append(input, buttons);
|
|
2562
|
+
return form;
|
|
2563
|
+
}
|
|
2564
|
+
|
|
2565
|
+
function cancelCodexAccountAdd() {
|
|
2566
|
+
state.accountAddOpen = false;
|
|
2567
|
+
state.accountAddLabel = "";
|
|
2568
|
+
state.accountUiStatus = "";
|
|
2569
|
+
renderCodexAccountSwitcher();
|
|
2570
|
+
}
|
|
2571
|
+
|
|
2572
|
+
function submitCodexAccountAdd(rawLabel) {
|
|
2573
|
+
const label = String(rawLabel || "").trim();
|
|
2574
|
+
if (!label) {
|
|
2575
|
+
state.accountUiStatus = "Label required";
|
|
2576
|
+
renderCodexAccountSwitcher();
|
|
2577
|
+
setTimeout(() => {
|
|
2578
|
+
if (state.accountUiStatus !== "Label required") return;
|
|
2579
|
+
state.accountUiStatus = "";
|
|
2580
|
+
renderCodexAccountSwitcher();
|
|
2581
|
+
}, 1400);
|
|
2582
|
+
return;
|
|
2583
|
+
}
|
|
2584
|
+
state.accountAddOpen = false;
|
|
2585
|
+
state.accountAddLabel = "";
|
|
2586
|
+
state.accountUiStatus = "Creating...";
|
|
2587
|
+
renderCodexAccountSwitcher();
|
|
2588
|
+
enqueueHostEvent({ kind: "codexAccountCreateAndSwitch", label });
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
function renderCodexAccountManage(accountsState) {
|
|
2592
|
+
const wrap = document.createElement("div");
|
|
2593
|
+
wrap.className = "clankerbend-account-manage";
|
|
2594
|
+
const head = document.createElement("div");
|
|
2595
|
+
head.className = "clankerbend-account-manage-head";
|
|
2596
|
+
const title = document.createElement("strong");
|
|
2597
|
+
title.textContent = "Manage profiles";
|
|
2598
|
+
const count = document.createElement("span");
|
|
2599
|
+
const managed = (accountsState.accounts || []).filter((account) => account.kind === "managed");
|
|
2600
|
+
count.textContent = `${managed.length} managed`;
|
|
2601
|
+
head.append(title, count);
|
|
2602
|
+
wrap.appendChild(head);
|
|
2603
|
+
const list = document.createElement("div");
|
|
2604
|
+
list.className = "clankerbend-account-manage-list";
|
|
2605
|
+
if (!managed.length) {
|
|
2606
|
+
const row = document.createElement("div");
|
|
2607
|
+
row.className = "clankerbend-account-manage-row";
|
|
2608
|
+
const title = document.createElement("div");
|
|
2609
|
+
title.className = "clankerbend-account-manage-title";
|
|
2610
|
+
const strong = document.createElement("strong");
|
|
2611
|
+
strong.textContent = "No managed accounts";
|
|
2612
|
+
const hint = document.createElement("span");
|
|
2613
|
+
hint.textContent = "Create one with Add account.";
|
|
2614
|
+
title.append(strong, hint);
|
|
2615
|
+
row.appendChild(title);
|
|
2616
|
+
list.appendChild(row);
|
|
2617
|
+
wrap.appendChild(list);
|
|
2618
|
+
return wrap;
|
|
2619
|
+
}
|
|
2620
|
+
for (const account of managed) list.appendChild(renderCodexAccountManageRow(account, accountsState));
|
|
2621
|
+
wrap.appendChild(list);
|
|
2622
|
+
return wrap;
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2625
|
+
function renderCodexAccountManageRow(account, accountsState) {
|
|
2626
|
+
const row = document.createElement("div");
|
|
2627
|
+
row.className = "clankerbend-account-manage-row";
|
|
2628
|
+
row.title = accountCodexHomeTitle(account);
|
|
2629
|
+
const title = document.createElement("div");
|
|
2630
|
+
title.className = "clankerbend-account-manage-title";
|
|
2631
|
+
const strong = document.createElement("strong");
|
|
2632
|
+
strong.textContent = account.label || account.id;
|
|
2633
|
+
const hint = document.createElement("span");
|
|
2634
|
+
const pendingAdopt = state.accountPendingAdoptId === account.id;
|
|
2635
|
+
const pendingDelete = state.accountPendingDeleteId === account.id;
|
|
2636
|
+
hint.textContent = pendingAdopt
|
|
2637
|
+
? "Replace ~/.codex with this profile; current primary is backed up."
|
|
2638
|
+
: pendingDelete
|
|
2639
|
+
? "Hide this profile and move its files to archive storage."
|
|
2640
|
+
: [account.auth?.authJson ? "signed in" : "not signed in", account.backup ? "backup" : ""].filter(Boolean).join(" · ");
|
|
2641
|
+
title.append(strong, hint);
|
|
2642
|
+
|
|
2643
|
+
const actions = document.createElement("div");
|
|
2644
|
+
actions.className = "clankerbend-account-manage-actions";
|
|
2645
|
+
const adopt = document.createElement("button");
|
|
2646
|
+
adopt.type = "button";
|
|
2647
|
+
adopt.textContent = pendingAdopt ? "Replace ~/.codex" : "Make primary";
|
|
2648
|
+
adopt.title = "Make this the primary Codex home";
|
|
2649
|
+
adopt.disabled = Boolean(accountsState.switching);
|
|
2650
|
+
bindInjectedActivation(adopt, () => {
|
|
2651
|
+
if (!pendingAdopt) {
|
|
2652
|
+
state.accountPendingAdoptId = account.id;
|
|
2653
|
+
state.accountPendingDeleteId = null;
|
|
2654
|
+
state.accountUiStatus = "";
|
|
2655
|
+
renderCodexAccountSwitcher();
|
|
2656
|
+
return;
|
|
2657
|
+
}
|
|
2658
|
+
state.accountUiStatus = "Making primary...";
|
|
2659
|
+
state.accountPendingAdoptId = null;
|
|
2660
|
+
renderCodexAccountSwitcher();
|
|
2661
|
+
enqueueHostEvent({ kind: "codexAccountAdoptAsPrimary", accountId: account.id });
|
|
2662
|
+
});
|
|
2663
|
+
|
|
2664
|
+
const remove = document.createElement("button");
|
|
2665
|
+
remove.type = "button";
|
|
2666
|
+
remove.textContent = pendingDelete ? "Archive profile" : "Archive";
|
|
2667
|
+
remove.title = "Archive this managed profile";
|
|
2668
|
+
remove.disabled = Boolean(accountsState.switching) || account.id === accountsState.activeAccountId;
|
|
2669
|
+
bindInjectedActivation(remove, () => {
|
|
2670
|
+
if (!pendingDelete) {
|
|
2671
|
+
state.accountPendingDeleteId = account.id;
|
|
2672
|
+
state.accountPendingAdoptId = null;
|
|
2673
|
+
state.accountUiStatus = "";
|
|
2674
|
+
renderCodexAccountSwitcher();
|
|
2675
|
+
return;
|
|
2676
|
+
}
|
|
2677
|
+
state.accountUiStatus = "Archiving...";
|
|
2678
|
+
state.accountPendingDeleteId = null;
|
|
2679
|
+
renderCodexAccountSwitcher();
|
|
2680
|
+
enqueueHostEvent({ kind: "codexAccountDelete", accountId: account.id });
|
|
2681
|
+
});
|
|
2682
|
+
|
|
2683
|
+
actions.append(adopt, remove);
|
|
2684
|
+
row.append(title, actions);
|
|
2685
|
+
return row;
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2688
|
+
function stopNativeMenuPropagation(el) {
|
|
2689
|
+
const stop = (event) => {
|
|
2690
|
+
event.stopPropagation();
|
|
2691
|
+
event.stopImmediatePropagation?.();
|
|
2692
|
+
};
|
|
2693
|
+
el.addEventListener("pointerdown", stop);
|
|
2694
|
+
el.addEventListener("mousedown", stop);
|
|
2695
|
+
el.addEventListener("touchstart", stop, { passive: false });
|
|
2696
|
+
el.addEventListener("click", stop);
|
|
2697
|
+
}
|
|
2698
|
+
|
|
2699
|
+
function bindInjectedActivation(button, handler) {
|
|
2700
|
+
let lastRunAt = 0;
|
|
2701
|
+
const run = (event) => {
|
|
2702
|
+
event.preventDefault();
|
|
2703
|
+
event.stopPropagation();
|
|
2704
|
+
event.stopImmediatePropagation?.();
|
|
2705
|
+
if (button.disabled) return;
|
|
2706
|
+
const now = Date.now();
|
|
2707
|
+
if (now - lastRunAt < 450) return;
|
|
2708
|
+
lastRunAt = now;
|
|
2709
|
+
handler(event);
|
|
2710
|
+
};
|
|
2711
|
+
button.addEventListener("pointerdown", run, true);
|
|
2712
|
+
button.addEventListener("mousedown", run, true);
|
|
2713
|
+
button.addEventListener("touchstart", run, { capture: true, passive: false });
|
|
2714
|
+
button.addEventListener("click", run, true);
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
function renderCodexAccountRow(account, accountsState) {
|
|
2718
|
+
const row = document.createElement("div");
|
|
2719
|
+
row.className = `clankerbend-account-row${account.id === accountsState.activeAccountId ? " is-active" : ""}`;
|
|
2720
|
+
|
|
2721
|
+
const main = document.createElement("button");
|
|
2722
|
+
main.type = "button";
|
|
2723
|
+
main.className = "clankerbend-account-main";
|
|
2724
|
+
main.disabled = Boolean(accountsState.switching) || account.id === accountsState.activeAccountId;
|
|
2725
|
+
main.title = [
|
|
2726
|
+
account.id === accountsState.activeAccountId ? "Current Codex account profile" : `Switch to ${account.label || account.id}`,
|
|
2727
|
+
accountCodexHomeTitle(account)
|
|
2728
|
+
].filter(Boolean).join("\n");
|
|
2729
|
+
bindInjectedActivation(main, () => {
|
|
2730
|
+
if (account.id === accountsState.activeAccountId) return;
|
|
2731
|
+
state.accountUiStatus = "Switching...";
|
|
2732
|
+
renderCodexAccountSwitcher();
|
|
2733
|
+
enqueueHostEvent({ kind: "codexAccountSwitch", accountId: account.id });
|
|
2734
|
+
});
|
|
2735
|
+
|
|
2736
|
+
const label = document.createElement("span");
|
|
2737
|
+
label.className = "clankerbend-account-label";
|
|
2738
|
+
label.textContent = account.label || account.id;
|
|
2739
|
+
const meta = document.createElement("span");
|
|
2740
|
+
meta.className = "clankerbend-account-meta";
|
|
2741
|
+
meta.textContent = [
|
|
2742
|
+
account.kind,
|
|
2743
|
+
account.id === accountsState.clankerbendDefaultAccountId ? "default" : "",
|
|
2744
|
+
account.auth?.authJson ? "signed in" : "not signed in"
|
|
2745
|
+
].filter(Boolean).join(" · ");
|
|
2746
|
+
main.append(label, meta);
|
|
2747
|
+
|
|
2748
|
+
const def = document.createElement("button");
|
|
2749
|
+
def.type = "button";
|
|
2750
|
+
def.className = "clankerbend-account-icon";
|
|
2751
|
+
def.textContent = account.id === accountsState.clankerbendDefaultAccountId ? "✓" : "☆";
|
|
2752
|
+
def.setAttribute("aria-label", account.id === accountsState.clankerbendDefaultAccountId ? "Launches by default" : "Launch by default");
|
|
2753
|
+
def.title = [
|
|
2754
|
+
account.id === accountsState.clankerbendDefaultAccountId ? "Launches by default" : "Launch by default",
|
|
2755
|
+
accountCodexHomeTitle(account)
|
|
2756
|
+
].filter(Boolean).join("\n");
|
|
2757
|
+
def.disabled = Boolean(accountsState.switching) || account.id === accountsState.clankerbendDefaultAccountId;
|
|
2758
|
+
bindInjectedActivation(def, () => {
|
|
2759
|
+
state.accountUiStatus = "Saving default...";
|
|
2760
|
+
renderCodexAccountSwitcher();
|
|
2761
|
+
enqueueHostEvent({ kind: "codexAccountSetDefault", accountId: account.id });
|
|
2762
|
+
});
|
|
2763
|
+
|
|
2764
|
+
row.append(main, def);
|
|
2765
|
+
return row;
|
|
2766
|
+
}
|
|
2767
|
+
|
|
2768
|
+
function accountCodexHomeTitle(account) {
|
|
2769
|
+
return account?.codexHome ? `CODEX_HOME: ${account.codexHome}` : "";
|
|
2770
|
+
}
|
|
2771
|
+
|
|
2142
2772
|
function hostSelectionActions() {
|
|
2143
2773
|
return (state.hostState?.selectionActions || [])
|
|
2144
2774
|
.filter((action) => action?.enabled !== false && action.appliesTo === "text-selection");
|
|
@@ -3415,6 +4045,7 @@
|
|
|
3415
4045
|
const mutationObserver = new MutationObserver(() => {
|
|
3416
4046
|
state.version += 1;
|
|
3417
4047
|
scheduleAnnotationRefresh();
|
|
4048
|
+
renderCodexAccountSwitcher();
|
|
3418
4049
|
maskSubmittedComposerContexts();
|
|
3419
4050
|
});
|
|
3420
4051
|
window.addEventListener("resize", resizeHandler);
|
|
@@ -3428,6 +4059,7 @@
|
|
|
3428
4059
|
document.removeEventListener("mouseup", handlePossibleTextSelection, true);
|
|
3429
4060
|
document.removeEventListener("click", window.__clankerbendComposerSubmitClickHandler, true);
|
|
3430
4061
|
document.removeEventListener("keydown", window.__clankerbendComposerSubmitKeyHandler, true);
|
|
4062
|
+
document.getElementById(ACCOUNT_SWITCHER_ID)?.remove();
|
|
3431
4063
|
window.removeEventListener("resize", resizeHandler);
|
|
3432
4064
|
window.removeEventListener("blur", blurHandler);
|
|
3433
4065
|
mutationObserver.disconnect();
|