@polderlabs/bizar-omp 0.6.4 → 0.6.6

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.
Files changed (49) hide show
  1. package/README.md +16 -7
  2. package/dist/cli/agents-ink.d.ts +1 -1
  3. package/dist/cli/agents-ink.d.ts.map +1 -1
  4. package/dist/cli/agents-ink.js +8 -0
  5. package/dist/cli/agents-ink.js.map +1 -1
  6. package/dist/cli/agents.d.ts.map +1 -1
  7. package/dist/cli/agents.js.map +1 -1
  8. package/dist/cli/doctor.d.ts +4 -4
  9. package/dist/cli/omb-commands.d.ts.map +1 -1
  10. package/dist/cli/omb-commands.js +7 -0
  11. package/dist/cli/omb-commands.js.map +1 -1
  12. package/dist/cli/omb-update.d.ts +23 -0
  13. package/dist/cli/omb-update.d.ts.map +1 -0
  14. package/dist/cli/omb-update.js +145 -0
  15. package/dist/cli/omb-update.js.map +1 -0
  16. package/dist/omp/compatibility.d.ts +5 -5
  17. package/dist/omp/compatibility.d.ts.map +1 -1
  18. package/dist/omp/compatibility.js +2 -2
  19. package/dist/omp/compatibility.js.map +1 -1
  20. package/dist/omp/daemon-service.d.ts.map +1 -1
  21. package/dist/omp/daemon-service.js +338 -1
  22. package/dist/omp/daemon-service.js.map +1 -1
  23. package/dist/omp/daemon.d.ts +1 -1
  24. package/dist/omp/daemon.js +1 -1
  25. package/dist/omp/dashboard-html.d.ts.map +1 -1
  26. package/dist/omp/dashboard-html.js +225 -48
  27. package/dist/omp/dashboard-html.js.map +1 -1
  28. package/dist/omp/omb-sessions.d.ts +16 -0
  29. package/dist/omp/omb-sessions.d.ts.map +1 -1
  30. package/dist/omp/omb-sessions.js +92 -1
  31. package/dist/omp/omb-sessions.js.map +1 -1
  32. package/dist/omp/omb-settings.d.ts +13 -0
  33. package/dist/omp/omb-settings.d.ts.map +1 -1
  34. package/dist/omp/omb-settings.js +46 -2
  35. package/dist/omp/omb-settings.js.map +1 -1
  36. package/dist/omp/omp-provider-config.d.ts +26 -0
  37. package/dist/omp/omp-provider-config.d.ts.map +1 -0
  38. package/dist/omp/omp-provider-config.js +160 -0
  39. package/dist/omp/omp-provider-config.js.map +1 -0
  40. package/docs/compatibility/baseline.json +9 -8
  41. package/docs/compatibility/supported-surfaces.json +2 -1
  42. package/docs/releases/0.6.5.md +12 -0
  43. package/docs/releases/0.6.6.md +26 -0
  44. package/docs/releases/native-stable.md +1 -1
  45. package/docs/releases/support-matrix.md +1 -1
  46. package/package.json +4 -2
  47. package/skills/omp-native-development/SKILL.md +1 -1
  48. package/skills/omp-native-development/references/accuracy-and-versioning.md +2 -2
  49. package/skills/omp-native-development/references/source-manifest.json +3 -3
@@ -1,3 +1,8 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ const require = createRequire(import.meta.url);
4
+ const GSAP_SOURCE = readFileSync(require.resolve("gsap/dist/gsap.min.js"), "utf8")
5
+ .replace(/<\/script/gi, "<\\/script");
1
6
  /** Self-contained, authenticated browser dashboard. */
2
7
  export const DASHBOARD_PATH = "/dash";
3
8
  export function dashboardPageHtml() {
@@ -135,6 +140,7 @@ export function dashboardPageHtml() {
135
140
  .card {
136
141
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
137
142
  padding: 12px 16px; box-shadow: var(--shadow);
143
+ min-width: 0;
138
144
  }
139
145
  .card.invert { background: var(--panel-invert); border-color: var(--panel-invert); color: var(--text-invert); }
140
146
  .card.invert .label { color: color-mix(in srgb, var(--text-invert) 62%, transparent); }
@@ -200,35 +206,105 @@ export function dashboardPageHtml() {
200
206
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
201
207
  }
202
208
  .conversation-actions { display: flex; gap: 8px; flex: none; }
203
- .console-wrap { position: relative; flex: 1; min-height: 0; display: flex; }
204
- .console {
205
- margin: 0; flex: 1; min-height: 0; overflow: auto; padding: 16px 24px;
206
- font-family: var(--mono); font-size: 12px; line-height: 1.5; tab-size: 2;
207
- white-space: pre-wrap; word-break: break-word;
208
- }
209
- .console-state { display: block; color: var(--muted); font-size: 12px; }
210
- .console-state strong {
211
- display: block; margin-bottom: 8px; color: var(--text);
212
- font-size: 10px; letter-spacing: var(--track); text-transform: uppercase;
213
- }
209
+ .chat-stage { position: relative; flex: 1; min-height: 0; overflow: hidden; }
210
+ .message-scroll {
211
+ height: 100%; overflow-y: auto; overscroll-behavior: contain;
212
+ padding: 34px clamp(20px, 5vw, 72px) 210px; scroll-padding-bottom: 210px;
213
+ }
214
+ .message-stream { width: min(880px, 100%); margin: 0 auto; display: flex; flex-direction: column; gap: 28px; }
215
+ .message-state {
216
+ margin: auto; width: min(480px, 100%); padding: 34px 28px; text-align: center;
217
+ color: var(--muted); border: 1px dashed var(--line); border-radius: var(--radius);
218
+ }
219
+ .message-state strong { display: block; margin-bottom: 8px; color: var(--text); font-size: 13px; }
220
+ .message { display: grid; grid-template-columns: 34px minmax(0, 1fr); gap: 12px; align-items: start; max-width: 88%; }
221
+ .message.user { grid-template-columns: minmax(0, 1fr) 34px; align-self: flex-end; max-width: 76%; }
222
+ .message.user .message-avatar { grid-column: 2; }
223
+ .message.user .message-content { grid-column: 1; grid-row: 1; }
224
+ .message-avatar {
225
+ width: 32px; height: 32px; display: grid; place-items: center; border: 1px solid var(--line);
226
+ border-radius: 10px; background: var(--panel); color: var(--muted); font-size: 9px; letter-spacing: 0.08em;
227
+ }
228
+ .message.assistant .message-avatar { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 48%, var(--line)); }
229
+ .message-content { min-width: 0; }
230
+ .message-meta { display: flex; align-items: center; gap: 10px; margin: 0 2px 7px; color: var(--muted); font-size: 10px; }
231
+ .message.user .message-meta { justify-content: flex-end; }
232
+ .message-bubble {
233
+ color: var(--text); font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
234
+ font-size: 14px; line-height: 1.66; overflow-wrap: anywhere;
235
+ }
236
+ .message.user .message-bubble {
237
+ padding: 13px 16px; border: 1px solid color-mix(in srgb, var(--accent) 42%, var(--line));
238
+ border-radius: 18px 18px 5px 18px; background: color-mix(in srgb, var(--accent) 11%, var(--panel));
239
+ }
240
+ .message-status { align-self:center; display:flex; align-items:center; gap:9px; max-width:min(620px,92%); padding:8px 12px; border:1px solid var(--line); border-radius:999px; background:color-mix(in srgb,var(--panel) 92%,transparent); color:var(--muted); font:11px/1.35 var(--mono); }
241
+ .message-status .status-icon { width:8px; height:8px; flex:none; border-radius:50%; background:var(--ok); }
242
+ .message-status[data-state="running"] .status-icon,.message-status[data-state="thinking"] .status-icon { background:var(--accent); box-shadow:0 0 10px color-mix(in srgb,var(--accent) 70%,transparent); animation:status-pulse 1.2s ease-in-out infinite; }
243
+ .message-status[data-state="failed"] { border-color:color-mix(in srgb,var(--err) 60%,var(--line)); color:var(--err); }
244
+ .message-status[data-state="failed"] .status-icon { background:var(--err); }
245
+ .message-status time { margin-left:auto; font-size:9px; opacity:.72; }
246
+ @keyframes status-pulse { 0%,100% { transform:scale(.75); opacity:.5; } 50% { transform:scale(1.15); opacity:1; } }
247
+ .message-bubble p { margin: 0 0 12px; }
248
+ .message-bubble p:last-child { margin-bottom: 0; }
249
+ .message-bubble h1, .message-bubble h2, .message-bubble h3 { margin: 18px 0 8px; font: inherit; font-weight: 750; }
250
+ .message-bubble h1:first-child, .message-bubble h2:first-child, .message-bubble h3:first-child { margin-top: 0; }
251
+ .message-bubble ul, .message-bubble ol { margin: 10px 0; padding-left: 22px; }
252
+ .message-bubble li + li { margin-top: 5px; }
253
+ .message-bubble code { padding: 2px 5px; border-radius: 5px; background: var(--panel-2); font: 0.88em/1.5 var(--mono); }
254
+ .message-bubble pre { margin: 0; padding: 16px; overflow-x: auto; background: #090a0d; color: #e7e9ee; }
255
+ .message-bubble pre code { padding: 0; background: transparent; font-size: 12px; }
256
+ .message-bubble blockquote { margin: 12px 0; padding-left: 14px; border-left: 2px solid var(--accent); color: var(--muted); }
257
+ .message-bubble hr { border:0; border-top:1px solid var(--line); margin:18px 0; }
258
+ .message-bubble a { text-decoration-thickness:1px; text-underline-offset:3px; }
259
+ .md-table-wrap { width:100%; margin:14px 0; overflow-x:auto; border:1px solid var(--line); border-radius:10px; }
260
+ .message-bubble table { width:100%; border-collapse:collapse; min-width:420px; font:12px/1.5 var(--mono); }
261
+ .message-bubble th,.message-bubble td { padding:9px 11px; border-right:1px solid var(--line); border-bottom:1px solid var(--line); text-align:left; vertical-align:top; }
262
+ .message-bubble th { background:var(--panel-2); color:var(--text); font-weight:700; }
263
+ .message-bubble tr:last-child td { border-bottom:0; }
264
+ .message-bubble th:last-child,.message-bubble td:last-child { border-right:0; }
265
+ .code-block { margin:12px 0; overflow:hidden; border:1px solid var(--line); border-radius:12px; background:#090a0d; }
266
+ .code-head { display:flex; align-items:center; justify-content:space-between; padding:7px 10px; border-bottom:1px solid #232833; color:#9299a8; font:10px/1.3 var(--mono); text-transform:uppercase; letter-spacing:.1em; }
267
+ .copy-code { border:0; background:transparent; color:#c7ccd6; padding:4px 7px; font:10px/1 var(--mono); cursor:pointer; }
268
+ .activity-strip {
269
+ position: absolute; left: 50%; bottom: 152px; transform: translateX(-50%); z-index: 3;
270
+ width: min(620px, calc(100% - 40px)); display: flex; align-items: center; gap: 10px;
271
+ padding: 9px 13px; border: 1px solid var(--line); border-radius: 12px;
272
+ background: color-mix(in srgb, var(--panel) 94%, transparent); color: var(--muted);
273
+ font-size: 11px; box-shadow: var(--shadow-overlay); backdrop-filter: blur(12px);
274
+ }
275
+ .activity-mark { width: 9px; height: 9px; border-radius: 50%; background: var(--accent); flex: none; }
276
+ .activity-strip[data-idle="true"] { box-shadow: none; background: var(--panel); }
277
+ .activity-strip[data-idle="true"] .activity-mark { background: var(--muted); }
214
278
  .follow-button {
215
- position: absolute; right: 16px; bottom: 16px; display: inline-flex; align-items: center;
279
+ position: absolute; right: 24px; bottom: 154px; z-index: 4; display: inline-flex; align-items: center;
216
280
  border: 1px solid var(--accent); background: var(--panel); color: var(--accent);
217
281
  border-radius: 999px; padding: 8px 13px; font-family: var(--mono); font-size: 10px;
218
282
  letter-spacing: var(--track); text-transform: uppercase; cursor: pointer;
219
283
  box-shadow: var(--shadow-overlay);
220
284
  }
221
- .composer { display: grid; gap: 10px; padding: 14px 24px 16px; border-top: 1px solid var(--line); }
222
- .composer-box { display: flex; gap: 8px; align-items: flex-end; }
223
- .composer-box textarea { flex: 1; min-height: 48px; max-height: 168px; }
224
- .composer-meta {
225
- display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
226
- font-size: 10px; letter-spacing: var(--track); text-transform: uppercase; color: var(--muted);
227
- }
228
- .composer-runtime { display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap; }
229
- .composer-runtime .field { display: grid; gap: 3px; }
230
- .composer-runtime .field-label { font-size: 9px; }
231
- .composer-runtime select { width: auto; min-width: 132px; padding: 6px 9px; font-size: 11px; }
285
+ .composer {
286
+ position: absolute; left: 50%; bottom: 20px; z-index: 5; transform: translateX(-50%);
287
+ width: min(920px, calc(100% - 40px)); display: grid; gap: 6px; padding: 12px;
288
+ border: 1px solid color-mix(in srgb, var(--accent) 34%, var(--line)); border-radius: 22px;
289
+ background: color-mix(in srgb, var(--panel) 96%, transparent); box-shadow: 0 18px 54px rgba(0,0,0,.32);
290
+ backdrop-filter: blur(14px);
291
+ }
292
+ .composer-box { display: flex; gap: 8px; align-items: flex-end; min-width: 0; max-width: 100%; }
293
+ .composer-box textarea {
294
+ flex: 1; min-height: 64px; max-height: 180px; padding: 10px 10px 14px; resize: none;
295
+ border: 0; border-radius: 14px; background: transparent; font-family: ui-sans-serif, system-ui, sans-serif;
296
+ font-size: 15px; line-height: 1.45;
297
+ }
298
+ .composer-box textarea:focus { outline: 0; }
299
+ .composer-send { width: 48px; height: 48px; padding: 0; border-radius: 14px; flex: none; font-size: 15px; letter-spacing: 0; }
300
+ .composer-meta { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:0 8px 2px; font-size:10px; color:var(--muted); min-width:0; }
301
+ .composer-tools { display:flex; align-items:center; gap:8px; min-width:0; }
302
+ .composer-tool { width:auto; min-width:0; max-width:210px; padding:7px 9px; border:0; background:transparent; color:var(--muted); font-size:11px; }
303
+ .composer-tool:hover { color:var(--text); background:var(--panel-2); }
304
+ .composer-plus { width:34px; height:34px; border-radius:50%; border:1px solid var(--line); background:transparent; color:var(--text); font-size:20px; line-height:1; }
305
+ .composer-menu { position:absolute; left:12px; bottom:54px; width:220px; padding:7px; border:1px solid var(--line); border-radius:12px; background:var(--panel); box-shadow:var(--shadow-overlay); }
306
+ .composer-menu button { width:100%; border:0; border-radius:8px; padding:10px; text-align:left; background:transparent; color:var(--text); }
307
+ .composer-menu button:hover { background:var(--panel-2); }
232
308
  #inspector { flex: 1; min-height: 0; overflow-y: auto; padding: 16px 24px 24px; }
233
309
  .inspector-section { margin-bottom: 24px; }
234
310
  .inspector-section h2 {
@@ -254,6 +330,23 @@ export function dashboardPageHtml() {
254
330
  .dialog-head h2 { margin: 4px 0 0; font-size: 15px; font-weight: 650; }
255
331
  .dialog-body { display: grid; gap: 16px; padding: 16px 24px; max-height: 68vh; overflow-y: auto; }
256
332
  .dialog-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 8px 24px 16px; }
333
+ .settings-dialog { width:min(980px,94vw); height:min(760px,90vh); }
334
+ .settings-shell { display:grid; grid-template-columns:230px minmax(0,1fr); height:100%; min-height:0; }
335
+ .settings-rail { padding:16px 12px; border-right:1px solid var(--line); display:flex; flex-direction:column; gap:10px; overflow:auto; }
336
+ .settings-close { align-self:flex-start; }
337
+ .settings-tabs { display:grid; gap:4px; }
338
+ .settings-tab { min-height:40px; border:0; border-radius:9px; padding:9px 10px; text-align:left; background:transparent; color:var(--muted); }
339
+ .settings-tab:hover,.settings-tab.active { color:var(--text); background:var(--panel-2); }
340
+ .settings-main { min-width:0; min-height:0; display:grid; grid-template-rows:auto minmax(0,1fr) auto; }
341
+ .settings-main .dialog-head { padding:20px 24px 14px; border-bottom:1px solid var(--line); }
342
+ .settings-panel { display:none; }
343
+ .settings-panel.active { display:grid; gap:16px; }
344
+ .settings-section { display:grid; gap:12px; }
345
+ .settings-section-title { margin:0; padding-bottom:8px; border-bottom:1px solid var(--line); font-size:11px; }
346
+ .provider-grid { display:grid; grid-template-columns:minmax(0,1fr) minmax(300px,.9fr); gap:16px; }
347
+ .provider-card { display:grid; grid-template-columns:minmax(0,1fr) auto; gap:12px; align-items:center; }
348
+ .provider-editor { display:grid; gap:12px; padding:16px; }
349
+ .inline-fields { display:grid; grid-template-columns:1fr 1fr; gap:10px; }
257
350
  .field { display: grid; gap: 8px; }
258
351
  .field-label { font-size: 10px; letter-spacing: var(--track); text-transform: uppercase; color: var(--muted); }
259
352
  .field-help, .runtime-note { margin: 0; font-size: 11px; color: var(--muted); }
@@ -298,12 +391,38 @@ export function dashboardPageHtml() {
298
391
  letter-spacing: var(--track); text-transform: uppercase;
299
392
  }
300
393
  .mobile-nav button.active { color: var(--accent); }
394
+ .primary-nav { display:flex; gap:4px; margin-left:24px; }
395
+ .primary-nav button { min-height:36px; border:0; border-bottom:2px solid transparent; background:transparent; color:var(--muted); padding:8px 10px; font-size:11px; letter-spacing:var(--track); text-transform:uppercase; }
396
+ .primary-nav button.active { color:var(--text); border-color:var(--accent); }
397
+ .overview { overflow:auto; padding:24px 28px 48px; }
398
+ .overview-grid { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:12px; margin:18px 0 24px; }
399
+ .metric { min-height:112px; display:flex; flex-direction:column; justify-content:space-between; }
400
+ .metric-value { font-size:30px; line-height:1; font-weight:700; }
401
+ .overview-columns { display:grid; grid-template-columns:minmax(0,1.6fr) minmax(240px,1fr); gap:18px; }
402
+ .overview-stack { display:grid; gap:18px; min-width:0; }
403
+ .overview-actions { display:flex; gap:8px; flex-wrap:wrap; margin-top:16px; }
404
+ .workspace-summary { display:grid; gap:0; }
405
+ .overview-list { display:grid; gap:8px; }
406
+ .overview-row { display:flex; justify-content:space-between; gap:12px; padding:10px 0; border-bottom:1px solid var(--line); flex-wrap:wrap; }
407
+ .overview-row > * { min-width: 0; }
408
+ .provider-order-list { display:grid; gap:8px; }
409
+ .provider-order-row { display:grid; grid-template-columns:minmax(0,1fr) auto; align-items:center; gap:12px; padding:10px 12px; }
410
+ .provider-order-actions { display:flex; gap:6px; }
411
+ .provider-order-actions .btn { min-width:38px; padding:7px 9px; }
412
+ .utility-menu { position:absolute; right:28px; top:64px; width:min(320px,calc(100vw - 32px)); padding:8px; background:var(--panel); border:1px solid var(--line); border-radius:var(--radius); box-shadow:var(--shadow-overlay); z-index:10; }
413
+ .utility-menu button { display:block; width:100%; text-align:left; border:0; background:transparent; color:var(--text); padding:12px; border-radius:8px; }
414
+ .utility-menu button:hover { background:var(--panel-2); }
415
+ body[data-page="overview"] .panes { display:none; }
416
+ body[data-page="chat"] .overview { display:none; }
301
417
  footer {
302
418
  display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
303
419
  padding: 10px 28px; border-top: 1px solid var(--line);
304
420
  color: var(--muted); font-size: 10px; letter-spacing: var(--track); text-transform: uppercase;
305
421
  }
306
422
  @media (max-width: 1099px) {
423
+ .bar { flex-wrap: wrap; row-gap: 10px; padding: 14px 20px; }
424
+ .overview { padding: 20px 20px 40px; }
425
+ .overview-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
307
426
  .panes { grid-template-columns: minmax(224px, 288px) minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) auto; }
308
427
  .detail-pane {
309
428
  grid-column: 1 / -1; border-left: 0; border-top: 1px solid var(--line);
@@ -311,6 +430,17 @@ export function dashboardPageHtml() {
311
430
  }
312
431
  }
313
432
  @media (max-width: 759px) {
433
+ .primary-nav { order:3; width:100%; margin:0; justify-content:space-between; }
434
+ .primary-nav button { flex:1; min-height:44px; }
435
+ .overview { padding:16px 16px calc(90px + env(safe-area-inset-bottom,0px)); }
436
+ .overview-grid, .overview-columns { grid-template-columns:1fr; gap:10px; }
437
+ .settings-shell { grid-template-columns:1fr; grid-template-rows:auto minmax(0,1fr); }
438
+ .settings-rail { border-right:0; border-bottom:1px solid var(--line); padding:10px 12px; }
439
+ .settings-close { position:absolute; right:10px; top:10px; z-index:2; }
440
+ .settings-rail .field { padding-right:46px; }
441
+ .settings-tabs { display:flex; overflow-x:auto; }
442
+ .settings-tab { flex:none; min-height:44px; }
443
+ .provider-grid,.inline-fields { grid-template-columns:1fr; }
314
444
  .panes { grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); }
315
445
  .pane { display: none; }
316
446
  .pane + .pane { border-left: 0; }
@@ -322,8 +452,17 @@ export function dashboardPageHtml() {
322
452
  .bar { flex-wrap: wrap; padding: 14px 16px; }
323
453
  .bar-right { flex-wrap: wrap; justify-content: flex-end; }
324
454
  .brand-sub { display: none; }
325
- .conversation-head, .composer, #inspector { padding-left: 16px; padding-right: 16px; }
326
- .console { padding: 16px; }
455
+ .conversation-head, #inspector { padding-left: 16px; padding-right: 16px; }
456
+ .conversation-actions #attach-top { display: none; }
457
+ .message-scroll { padding: 22px 14px 224px; scroll-padding-bottom: 224px; }
458
+ .message-stream { gap: 22px; }
459
+ .message, .message.user { max-width: 94%; }
460
+ .message { grid-template-columns: 30px minmax(0,1fr); gap: 9px; }
461
+ .message.user { grid-template-columns: minmax(0,1fr) 30px; }
462
+ .message-avatar { width: 30px; height: 30px; }
463
+ .activity-strip { bottom: 168px; width: calc(100% - 28px); }
464
+ .follow-button { right: 14px; bottom: 170px; }
465
+ .composer { bottom: calc(10px + env(safe-area-inset-bottom, 0px)); width: calc(100% - 20px); }
327
466
  .composer-runtime { width: 100%; }
328
467
  .composer-runtime .field { flex: 1; min-width: 116px; }
329
468
  .composer-runtime select { width: 100%; min-width: 0; }
@@ -341,12 +480,13 @@ export function dashboardPageHtml() {
341
480
  @media (prefers-reduced-motion: reduce) { *, *::before, *::after { scroll-behavior: auto !important; transition: none !important; } }
342
481
  </style>
343
482
  </head>
344
- <body data-mobile-view="sessions">
483
+ <body data-mobile-view="sessions" data-page="overview">
345
484
  <div class="app">
346
- <header class="bar"><div class="brand"><span class="brand-dot" aria-hidden="true"></span><span class="brand-name">Bizar OMP</span><span class="brand-sub">OMB DASHBOARD</span></div><div class="bar-right"><span class="chip" id="daemon-state"><span class="state-dot"></span><span>Connecting to local daemon</span></span><button class="btn ghost" id="theme-toggle" type="button">Light theme</button><button class="btn ghost" id="settings-open" type="button" title="Settings">Settings</button><button class="btn primary" id="new-session-top" type="button">New session</button></div></header>
485
+ <header class="bar"><div class="brand"><span class="brand-dot" aria-hidden="true"></span><span class="brand-name">Bizar OMP</span><span class="brand-sub">OMB DASHBOARD</span></div><nav class="primary-nav" aria-label="Dashboard navigation"><button data-page="overview" class="active" type="button">Overview</button><button data-page="chat" type="button">Chat</button></nav><div class="bar-right"><span class="chip" id="daemon-state"><span class="state-dot"></span><span>Connecting to local daemon</span></span><button class="btn ghost" id="theme-toggle" type="button">Light theme</button><button class="btn ghost" id="utility-open" type="button" aria-expanded="false" aria-controls="utility-menu">Menu</button><button class="btn primary" id="new-session-top" type="button">New session</button></div><div class="utility-menu hidden" id="utility-menu" role="menu"><button type="button" role="menuitem" data-page="overview">Overview</button><button type="button" role="menuitem" data-page="chat">Conversation</button><button type="button" role="menuitem" id="settings-open">Settings</button><button type="button" role="menuitem" id="utility-refresh">Refresh status</button></div></header>
486
+ <section class="overview" aria-label="Overview"><div class="label">Operator overview</div><h1 style="margin:6px 0 0">Local runtime</h1><p id="overview-status" class="field-help" role="status">Loading the daemon snapshot.</p><div class="overview-actions"><button class="btn primary" type="button" data-overview-action="new">New session</button><button class="btn" type="button" data-overview-action="chat">Open chat</button><button class="btn" type="button" data-overview-action="settings">Settings</button><button class="btn ghost" type="button" data-overview-action="refresh">Refresh</button></div><div class="overview-grid" id="overview-metrics"><div class="card metric">Loading overview.</div></div><div class="overview-columns"><div class="overview-stack"><section class="card"><div class="label">Current focus</div><div class="overview-list" id="focus-summary"><p class="field-help">Loading selected session.</p></div></section><section class="card"><div class="label">Workspace activity</div><div class="workspace-summary" id="workspace-summary"><p class="field-help">Loading workspaces.</p></div></section><section class="card"><div class="label">Recent sessions</div><div class="overview-list" id="recent-sessions"><p class="field-help">Loading sessions.</p></div></section></div><div class="overview-stack"><section class="card"><div class="label">Needs attention</div><div class="overview-list" id="attention-list"><p class="field-help">Loading sessions.</p></div></section><section class="card"><div class="label">OMP runtime</div><div class="overview-list" id="omp-summary"><p class="field-help">Loading OMP status.</p></div></section></div></div></section>
347
487
  <main class="panes">
348
488
  <aside class="pane rail" aria-label="Sessions"><div class="pane-head"><span class="label">Workspace</span><span class="pane-title">Sessions <span class="count" id="session-count">0</span></span></div><div class="search-wrap"><input class="search" id="session-search" type="search" placeholder="Find a session" aria-label="Find a session" /></div><div class="filters" id="filters" aria-label="Filter sessions"></div><div class="session-list" id="session-list"><div class="empty"><strong>Loading sessions</strong>Reading the local daemon snapshot.</div></div></aside>
349
- <section class="pane console-pane" aria-label="Session chat"><header class="conversation-head"><div class="conversation-identity"><div class="state-label" id="session-state"><span class="state-dot"></span><span>No session selected</span></div><h1 class="conversation-title" id="conversation-title">Choose a session</h1></div><div class="conversation-actions"><button class="btn ghost" id="console-refresh" type="button" disabled>Refresh</button><button class="btn" id="attach-top" type="button" disabled>Open terminal</button></div></header><div class="console-wrap"><pre class="console" id="console" tabindex="0" aria-label="Live session console"><span class="console-state"><span><strong>No session open</strong>Select a session to read its live console and send a message.</span></span></pre><button class="follow-button hidden" id="console-follow" type="button">Jump to latest</button></div><form class="composer" id="composer"><div class="composer-box"><textarea id="composer-input" placeholder="Send a message to the selected session" aria-label="Message" disabled></textarea><button class="btn primary" id="composer-send" type="submit" disabled>Send</button></div><div class="composer-meta"><span id="composer-status" role="status">Select a live session to begin.</span><div class="composer-runtime" role="group" aria-label="Launch defaults"><label class="field"><span class="field-label">Model</span><select class="field-select" id="composer-model" aria-describedby="composer-runtime-hint"><option value="">OMP default</option></select></label><label class="field"><span class="field-label">Effort</span><select class="field-select" id="composer-thinking" aria-describedby="composer-runtime-hint"><option value="">OMP default</option><option>off</option><option>minimal</option><option>low</option><option>medium</option><option>high</option><option>xhigh</option><option>max</option><option>auto</option></select></label><label class="field"><span class="field-label">Permissions</span><select class="field-select" id="composer-permission" aria-describedby="composer-runtime-hint"><option value="">OMP default</option><option value="always-ask">Always ask</option><option value="write">Allow workspace writes</option><option value="yolo">Auto approve all</option></select></label></div><span>Ctrl Enter to send</span></div><p class="runtime-note" id="composer-runtime-hint">Launch defaults for sessions the dashboard starts: model, effort, and permission mode. Sessions that are already running keep their own runtime.</p></form></section>
489
+ <section class="pane console-pane" aria-label="Session chat"><header class="conversation-head"><div class="conversation-identity"><div class="state-label" id="session-state"><span class="state-dot"></span><span>No session selected</span></div><h2 class="conversation-title" id="conversation-title">Choose a session</h2></div><div class="conversation-actions"><button class="btn ghost" id="console-refresh" type="button" disabled>Refresh messages</button><button class="btn" id="attach-top" type="button" disabled>Open terminal</button></div></header><div class="chat-stage"><div class="message-scroll" id="message-scroll" tabindex="0" aria-label="Conversation messages"><div class="message-stream" id="message-stream"><div class="message-state"><strong>No session open</strong>Select a session to read its conversation and send a message.</div></div></div><div class="activity-strip" id="agent-activity" data-idle="true" role="status" aria-live="polite"><span class="activity-mark" aria-hidden="true"></span><span id="activity-label">Waiting for a session</span></div><button class="follow-button hidden" id="console-follow" type="button">Jump to latest</button><form class="composer" id="composer"><div class="composer-box"><textarea id="composer-input" rows="2" placeholder="Work on anything" aria-label="Message" disabled></textarea><button class="btn primary composer-send" id="composer-send" type="submit" aria-label="Send message" disabled>↑</button></div><div class="composer-meta"><div class="composer-tools"><button class="composer-plus" id="composer-menu-open" type="button" aria-label="Composer actions" aria-expanded="false">+</button><select class="composer-tool" id="workspace-select" aria-label="Workspace filter"><option value="all">All workspaces</option></select><select class="composer-tool" id="composer-model" aria-label="Default model"><option value="">OMP default</option></select><select class="composer-tool" id="composer-thinking" aria-label="Default effort"><option value="">Effort</option></select></div><span id="composer-status" role="status">Select a live session to begin.</span></div><div class="composer-menu hidden" id="composer-menu"><button type="button" data-composer-action="new">New session</button><button type="button" data-composer-action="terminal">Open selected in terminal</button><button type="button" data-composer-action="settings">Open settings</button></div><select class="hidden" id="composer-permission" aria-label="Default permissions"><option value="">OMP default</option><option value="always-ask">Always ask</option><option value="write">Allow workspace writes</option><option value="yolo">Auto approve all</option></select></form></div></section>
350
490
  <aside class="pane detail-pane" aria-label="Session details"><div class="pane-head"><span class="label">Selected session</span><span class="pane-title">Details</span></div><div id="inspector"><div class="empty"><strong>Nothing selected</strong>Session identity and controls appear here.</div></div></aside>
351
491
  </main>
352
492
  <nav class="mobile-nav" aria-label="Dashboard views"><button type="button" data-view="sessions" class="active">Sessions</button><button type="button" data-view="chat">Chat</button><button type="button" data-view="details">Details</button></nav>
@@ -355,11 +495,18 @@ export function dashboardPageHtml() {
355
495
  <dialog id="create-dialog" aria-labelledby="create-title"><form method="dialog"><div class="dialog-head"><div><div class="label">Launch</div><h2 id="create-title">New session</h2></div><button class="btn icon ghost" value="cancel" aria-label="Close">×</button></div><div class="dialog-body"><label class="field"><span class="field-label">Project directory</span><input class="field-input" id="create-cwd" list="workspace-list" placeholder="/path/to/project" autocomplete="off" required /><span class="field-help">Must be an existing directory on this machine.</span></label><datalist id="workspace-list"></datalist><label class="field"><span class="field-label">First message</span><textarea class="field-textarea" id="create-task" placeholder="Describe the work for this session"></textarea></label><label class="field"><span class="field-label">Session label</span><input class="field-input" id="create-id" placeholder="Optional, for example review" autocomplete="off" /></label><section class="runtime-grid" aria-labelledby="runtime-title"><div class="runtime-heading"><div class="label">Session runtime</div><strong id="runtime-title">Model and permissions</strong></div><label class="field"><span class="field-label">Model</span><select class="field-select" id="create-model"><option value="">OMP default</option></select></label><label class="field"><span class="field-label">Thinking</span><select class="field-select" id="create-thinking"><option value="">OMP default</option><option>off</option><option>minimal</option><option>low</option><option>medium</option><option>high</option><option>xhigh</option><option>max</option><option>auto</option></select></label><label class="field"><span class="field-label">Permissions</span><select class="field-select" id="create-permission"><option value="">OMP default</option><option value="always-ask">Always ask</option><option value="write">Allow workspace writes</option><option value="yolo">Auto approve all</option></select></label><div class="runtime-note" id="model-status">Loading models from OMP.</div></section></div><div class="dialog-foot"><button class="btn" value="cancel">Cancel</button><button class="btn primary" value="create">Create session</button></div></form></dialog>
356
496
  <dialog id="rename-dialog" aria-labelledby="rename-title"><form method="dialog"><div class="dialog-head"><div><div class="label">Identity</div><h2 id="rename-title">Rename session</h2></div><button class="btn icon ghost" value="cancel" aria-label="Close">×</button></div><div class="dialog-body"><label class="field"><span class="field-label">Session label</span><input class="field-input" id="rename-id" autocomplete="off" required /></label></div><div class="dialog-foot"><button class="btn" value="cancel">Cancel</button><button class="btn primary" value="rename">Rename</button></div></form></dialog>
357
497
  <dialog id="stop-dialog" aria-labelledby="stop-title"><form method="dialog"><div class="dialog-head"><div><div class="label">Destructive action</div><h2 id="stop-title">Stop session</h2></div><button class="btn icon ghost" value="cancel" aria-label="Close">×</button></div><div class="dialog-body"><p id="stop-text" style="margin:0">This ends the tmux session and its agent work.</p></div><div class="dialog-foot"><button class="btn" value="cancel">Keep running</button><button class="btn danger" value="stop">Stop session</button></div></form></dialog>
358
- <dialog id="settings-dialog" aria-labelledby="settings-title"><form method="dialog"><div class="dialog-head"><div><div class="label">Configuration</div><h2 id="settings-title">Dashboard settings</h2></div><button class="btn icon ghost" value="cancel" aria-label="Close">×</button></div><div class="dialog-body"><label class="field"><span class="field-label">Projects folder</span><input class="field-input" id="setting-projects" placeholder="/path/to/projects" autocomplete="off" /><span class="field-help">Pre-fills the project directory when creating a session.</span></label><label class="card setting-row"><span><strong>Attach latest session</strong>Reconnect plain omb launches to the latest live session.</span><span class="switch"><input id="setting-attach" type="checkbox" /><span class="switch-track"></span></span></label><label class="card setting-row"><span><strong>Mouse forwarding</strong>Enable scrolling and pointer events in OMP tmux sessions.</span><span class="switch"><input id="setting-mouse" type="checkbox" /><span class="switch-track"></span></span></label><label class="card setting-row"><span><strong>Show stopped sessions</strong>Include recent stopped work in the session rail.</span><span class="switch"><input id="setting-stopped" type="checkbox" /><span class="switch-track"></span></span></label><label class="card setting-row"><span><strong>System tray</strong>Keep native quick access available when supported.</span><span class="switch"><input id="setting-tray" type="checkbox" /><span class="switch-track"></span></span></label><div class="field-help" id="settings-status" role="status">Changes are stored in your Bizar OMP configuration.</div></div><div class="dialog-foot"><button class="btn" value="cancel">Cancel</button><button class="btn primary" value="save">Save settings</button></div></form></dialog>
498
+ <dialog class="settings-dialog" id="settings-dialog" aria-labelledby="settings-title"><form method="dialog" class="settings-shell"><aside class="settings-rail"><button class="btn icon ghost settings-close" value="cancel" aria-label="Close settings">×</button><label class="field"><span class="field-label">Find settings</span><input id="settings-search" type="search" placeholder="Search settings" autocomplete="off" /></label><nav class="settings-tabs" aria-label="Settings sections"><button class="settings-tab active" type="button" data-settings-tab="general">General</button><button class="settings-tab" type="button" data-settings-tab="runtime">Runtime</button><button class="settings-tab" type="button" data-settings-tab="omp">OMP config</button><button class="settings-tab" type="button" data-settings-tab="providers">Providers</button><button class="settings-tab" type="button" data-settings-tab="security">Security</button></nav></aside><div class="settings-main"><div class="dialog-head"><div><div class="label">Configuration</div><h2 id="settings-title">Settings</h2></div></div><div class="dialog-body" id="settings-content">
499
+ <section class="settings-panel active" data-settings-panel="general"><div class="settings-section"><h3 class="settings-section-title">General</h3><label class="field"><span class="field-label">Projects folder</span><input class="field-input" id="setting-projects" placeholder="/path/to/projects" autocomplete="off" /><span class="field-help">Default workspace for new sessions.</span></label><label class="field"><span class="field-label">Appearance</span><select id="setting-theme"><option value="dark">Dark</option><option value="light">Light</option><option value="system">System</option></select></label><label class="card setting-row"><span><strong>Attach latest session</strong>Reconnect plain omb launches to the latest live session.</span><span class="switch"><input id="setting-attach" type="checkbox" /><span class="switch-track"></span></span></label><label class="card setting-row"><span><strong>Mouse forwarding</strong>Enable scrolling and pointer events in OMP tmux sessions.</span><span class="switch"><input id="setting-mouse" type="checkbox" /><span class="switch-track"></span></span></label><label class="card setting-row"><span><strong>Show stopped sessions</strong>Include recent stopped work in session lists.</span><span class="switch"><input id="setting-stopped" type="checkbox" /><span class="switch-track"></span></span></label><label class="card setting-row"><span><strong>System tray</strong>Keep native quick access available when supported.</span><span class="switch"><input id="setting-tray" type="checkbox" /><span class="switch-track"></span></span></label></div></section>
500
+ <section class="settings-panel" data-settings-panel="runtime"><div class="settings-section"><h3 class="settings-section-title">New session defaults</h3><label class="field"><span class="field-label">Model</span><select id="setting-model"><option value="">OMP default</option></select></label><label class="field"><span class="field-label">Thinking effort</span><select id="setting-thinking"><option value="">OMP default</option></select></label><label class="field"><span class="field-label">Permissions</span><select id="setting-permission"><option value="">OMP default</option><option value="always-ask">Always ask</option><option value="write">Allow workspace writes</option><option value="yolo">Auto approve all</option></select></label><p class="field-help">These defaults apply only to sessions created from the dashboard.</p></div></section>
501
+ <section class="settings-panel" data-settings-panel="omp"><div class="settings-section"><h3 class="settings-section-title">Native OMP configuration</h3><label class="field"><span class="field-label">Filter settings</span><input class="field-input" id="omp-config-filter" type="search" placeholder="Name, type, or description" autocomplete="off" /></label><p class="field-help" id="omp-config-status" role="status">Loading typed configuration.</p><div id="omp-config-list"></div></div></section>
502
+ <section class="settings-panel" data-settings-panel="providers"><div class="settings-section"><h3 class="settings-section-title">Providers</h3><p class="field-help" id="providers-status" role="status">Loading provider state.</p><div class="provider-grid"><div><div id="providers-list"></div><section class="field" aria-labelledby="provider-order-title"><span class="field-label" id="provider-order-title">Routing order</span><div class="provider-order-list" id="provider-order-list"></div><div class="provider-order-actions"><button class="btn" id="provider-order-save" type="button">Save order</button><button class="btn ghost" id="provider-order-reset" type="button">Use default</button></div></section></div><div class="card provider-editor" id="provider-editor"><div><div class="label">Custom provider</div><strong id="provider-editor-title">Add provider</strong></div><label class="field"><span class="field-label">Provider id</span><input id="provider-id" placeholder="company-openai" required pattern="[A-Za-z0-9._-]+" /></label><label class="field"><span class="field-label">API endpoint</span><input id="provider-url" type="url" placeholder="https://api.example.com/v1" required /></label><div class="inline-fields"><label class="field"><span class="field-label">API format</span><select id="provider-api"></select></label><label class="field"><span class="field-label">Authentication</span><select id="provider-auth"><option value="apiKey">API key</option><option value="none">None</option><option value="oauth">OAuth</option></select></label></div><label class="field"><span class="field-label">Model ids</span><textarea id="provider-models" placeholder="model-a&#10;model-b" required></textarea><span class="field-help">One model id per line.</span></label><label class="field"><span class="field-label">API key</span><input id="provider-key" type="password" autocomplete="new-password" placeholder="Leave blank to preserve stored key" /><span class="field-help" id="provider-key-state">Keys are write-only and never returned.</span></label><label class="setting-row"><span><strong>Clear stored API key</strong>Remove the existing key when saving.</span><span class="switch"><input id="provider-key-clear" type="checkbox" /><span class="switch-track"></span></span></label><div class="provider-order-actions"><button class="btn primary" id="provider-save" type="button">Save provider</button><button class="btn ghost" id="provider-editor-reset" type="button">New provider</button><button class="btn danger hidden" id="provider-delete" type="button">Delete</button></div></div></div></div></section>
503
+ <section class="settings-panel" data-settings-panel="security"><div class="settings-section"><h3 class="settings-section-title">Security and authentication</h3><div class="card"><strong>Local dashboard boundary</strong><p class="field-help">The server binds to loopback, exchanges a one-time access code for an in-memory token, and never returns provider keys or credential values.</p></div><p class="field-help" id="auth-status" role="status">Auth status is read from OMP. Credentials remain hidden.</p><div class="provider-order-actions"><button class="btn" type="button" data-auth-action="status">Refresh auth status</button><button class="btn" type="button" data-auth-action="list">List auth methods</button></div><p class="field-help">Interactive login, import, migration, and logout remain terminal-only so the dashboard cannot capture credential prompts.</p></div><div class="settings-section"><h3 class="settings-section-title">Collab and live URLs</h3><p class="field-help" id="collab-status" role="status">Loading active collab hosts.</p><div id="collab-list"></div><div class="provider-order-actions"><button class="btn" type="button" data-collab-action="list">Refresh collab hosts</button></div><p class="field-help">Collab relay, share, and stream endpoints can be configured from the native OMP configuration tab; this card shows currently advertised sessions.</p></div></section>
504
+ </div><div class="dialog-foot"><span class="field-help" id="settings-status" role="status">Changes are stored in Bizar OMP and OMP configuration.</span><button class="btn" value="cancel">Close</button><button class="btn primary" id="settings-save" type="button">Save dashboard settings</button></div></div></form></dialog>
359
505
  <div class="toast hidden" id="toast" role="status" aria-live="polite"></div>
360
506
  <script>
507
+ ${GSAP_SOURCE}
361
508
  (function(){"use strict";
362
- var token="",sessions=[],models=[],selectedName="",filter="all",query="",projectsFolder="",runtimeDefaults={model:"",thinking:"",approvalMode:""},refreshTimer,consoleTimer,consoleSessionName="",consoleBusy=false,consoleText="",followConsole=true,THEME_KEY="bizar.dash.theme",SELECTED_KEY="bizar.dash.selected";
509
+ var token="",sessions=[],models=[],messages=[],pendingMessages=[],providers=[],selectedName="",filter="all",query="",workspaceFilter="all",projectsFolder="",runtimeDefaults={model:"",thinking:"",approvalMode:""},providerOrder=[],refreshTimer,consoleTimer,consoleSessionName="",consoleBusy=false,followConsole=true,lastActivityLabel="",renderedMessageIds=new Set(),reducedMotion=matchMedia("(prefers-reduced-motion: reduce)").matches,THEME_KEY="bizar.dash.theme",SELECTED_KEY="bizar.dash.selected";
363
510
  function byId(id){return document.getElementById(id)}
364
511
  function esc(value){return String(value==null?"":value).replace(/[&<>"']/g,function(c){if(c==="&")return"&amp;";if(c==="<")return"&lt;";if(c===">")return"&gt;";if(c.charCodeAt(0)===34)return"&quot;";return"&#39;"})}
365
512
  function err(error){return error&&error.message?error.message:"Unexpected error"}
@@ -375,41 +522,71 @@ function title(s){return s.displayName||s.id||s.project||s.name}
375
522
  function available(s){return Boolean(s&&s.state!=="stopped"&&s.hosted!==false)}
376
523
  function tone(state){return state==="working"||state==="connected"?"dot-ok":state==="error"?"dot-err":state==="waiting"||state==="disconnected"?"dot-warn":"dot-muted"}
377
524
  function mobile(view){document.body.setAttribute("data-mobile-view",view);document.querySelectorAll("[data-view]").forEach(function(b){b.classList.toggle("active",b.dataset.view===view)})}
378
- function visible(){var needle=query.toLowerCase();return sessions.filter(function(s){return(filter==="all"||s.state===filter)&&(!needle||[s.displayName,s.name,s.id,s.project,s.cwd,s.state].join(" ").toLowerCase().includes(needle))})}
525
+ function workspaceOf(s){return s.cwd||s.project||"Unknown workspace"}
526
+ function visible(){var needle=query.toLowerCase();return sessions.filter(function(s){return(filter==="all"||s.state===filter)&&(workspaceFilter==="all"||workspaceOf(s)===workspaceFilter)&&(!needle||[s.displayName,s.name,s.id,s.project,s.cwd,s.state].join(" ").toLowerCase().includes(needle))})}
527
+ function renderWorkspaceSelect(){var select=byId("workspace-select"),values=[...new Set(sessions.map(workspaceOf))].sort();select.innerHTML='<option value="all">All workspaces</option>'+values.map(function(value){return'<option value="'+esc(value)+'">'+esc(value.split("/").filter(Boolean).pop()||value)+'</option>'}).join("");if(workspaceFilter!=="all"&&values.indexOf(workspaceFilter)===-1)workspaceFilter="all";select.value=workspaceFilter;byId("workspace-list").innerHTML=values.map(function(value){return'<option value="'+esc(value)+'"></option>'}).join("")}
379
528
  function renderFilters(){var counts={};sessions.forEach(function(s){counts[s.state]=(counts[s.state]||0)+1});var keys=["all"].concat(Object.keys(counts).sort());byId("filters").innerHTML=keys.map(function(key){var count=key==="all"?sessions.length:counts[key];return'<button type="button" class="filter'+(filter===key?' active':'')+'" data-filter="'+esc(key)+'">'+esc(key)+" "+count+"</button>"}).join("")}
380
529
  function renderSessions(){var list=visible(),target=byId("session-list");byId("session-count").textContent=String(sessions.length);if(!list.length){target.innerHTML='<div class="empty"><strong>'+(sessions.length?"No matching sessions":"No sessions yet")+'</strong>'+(sessions.length?"Change the search or state filter.":"Create a session to start work from the dashboard.")+"</div>";return}var groups={};list.forEach(function(s){var key=s.cwd||s.project||"Unknown workspace";(groups[key]=groups[key]||[]).push(s)});target.innerHTML=Object.keys(groups).sort().map(function(workspace){return'<div class="workspace-label" title="'+esc(workspace)+'">'+esc(workspace)+"</div>"+groups[workspace].map(function(s){return'<button type="button" class="card session-row'+(s.name===selectedName?' selected':'')+'" data-session="'+esc(s.name)+'"><span class="state-dot '+tone(s.state)+'"></span><span><span class="session-name">'+esc(title(s))+'</span><span class="session-meta"><span>'+esc(s.state)+'</span><span>·</span><span>'+esc(age(s.lastAttachedAt||s.createdAt))+"</span></span></span></button>"}).join("")}).join("")}
381
530
  function renderInspector(){var s=selected(),target=byId("inspector");if(!s){target.innerHTML='<div class="empty"><strong>Nothing selected</strong>Session identity and controls appear here.</div>';return}var activity=s.activity&&s.activity.label?s.activity.label:"No structured activity";target.innerHTML='<section class="inspector-section"><h2>Session</h2><dl class="detail-list"><div><dt>Status</dt><dd><span class="state-label"><span class="state-dot '+tone(s.state)+'"></span><span>'+esc(s.state)+'</span></span></dd></div><div><dt>Activity</dt><dd>'+esc(activity)+'</dd></div><div><dt>Workspace</dt><dd class="mono">'+esc(s.cwd||s.project||"Unknown workspace")+'</dd></div><div><dt>Session id</dt><dd class="mono">'+esc(s.name)+'</dd></div><div><dt>Created</dt><dd>'+esc(date(s.createdAt))+'</dd></div><div><dt>Last attached</dt><dd>'+esc(date(s.lastAttachedAt))+'</dd></div></dl></section><section class="inspector-section"><h2>Actions</h2><div class="action-stack"><button class="btn" type="button" data-action="attach"'+(available(s)?'':' disabled')+'>Open in terminal</button><button class="btn" type="button" data-action="rename"'+(available(s)?'':' disabled')+'>Rename session</button><button class="btn danger" type="button" data-action="stop"'+(available(s)?'':' disabled')+'>Stop session</button></div></section>'}
382
- function consoleState(head,detail){consoleText="";byId("console").innerHTML='<span class="console-state"><span><strong>'+esc(head)+"</strong>"+esc(detail)+"</span></span>";byId("console-follow").classList.add("hidden")}
383
- function renderConversation(){var s=selected(),ok=available(s);byId("conversation-title").textContent=s?title(s):"Choose a session";byId("session-state").innerHTML='<span class="state-dot '+(s?tone(s.state):'dot-muted')+'"></span><span>'+esc(s?s.state:"No session selected")+'</span>';byId("console-refresh").disabled=!ok;byId("attach-top").disabled=!ok;byId("composer-input").disabled=!ok;byId("composer-send").disabled=!ok;byId("composer-status").textContent=ok?"Input is sent directly to the active tmux pane.":s?"This session has no live console.":"Select a live session to begin.";if(!s)consoleState("No session open","Select a session to read its live console and send a message.");else if(!ok)consoleState("Console unavailable","This session is stopped or no longer has a Bizar-owned tmux host.")}
384
- function render(){renderFilters();renderSessions();renderConversation();renderInspector()}
531
+ function safeHref(value){try{var url=new URL(value);return url.protocol==="http:"||url.protocol==="https:"?url.href:""}catch{return""}}
532
+ function inlineMarkdown(value){var source=esc(value);source=source.replace(/\\[([^\\]]+)\\]\\((https?:\\/\\/[^\\s)]+)\\)/g,function(_,label,url){var href=safeHref(url);return href?'<a href="'+esc(href)+'" target="_blank" rel="noreferrer">'+label+"</a>":label});return source.replace(/\\x60([^\\x60]+)\\x60/g,"<code>$1</code>").replace(/\\*\\*([^*]+)\\*\\*/g,"<strong>$1</strong>").replace(/__([^_]+)__/g,"<strong>$1</strong>").replace(/\\*([^*]+)\\*/g,"<em>$1</em>")}
533
+ function markdown(value){var lines=String(value||"").split(/\\r?\\n/),out=[],code=[],paragraph=[],inCode=false,list="",language="",skipUntil=0;function closeList(){if(list){out.push("</"+list+">");list=""}}function flushParagraph(){if(paragraph.length){out.push("<p>"+paragraph.map(inlineMarkdown).join("<br>")+"</p>");paragraph=[]}}function flushCode(){var label=language||"code",content=esc(code.join("\\n"));out.push('<div class="code-block"><div class="code-head"><span>'+esc(label)+'</span><button class="copy-code" type="button">Copy</button></div><pre><code>'+content+"</code></pre></div>");code=[];language=""}function cells(line){var value=line.trim().replace(/^\\|/,"").replace(/\\|$/,"");return value.split("|").map(function(cell){return cell.trim()})}function tableAt(index){var header=cells(lines[index]),divider=cells(lines[index+1]||"");if(header.length<2||header.length!==divider.length||!divider.every(function(cell){return /^:?-{3,}:?$/.test(cell)}))return 0;closeList();flushParagraph();var align=divider.map(function(cell){return cell[0]===":"&&cell[cell.length-1]===":"?"center":cell[cell.length-1]===":"?"right":"left"}),rows=[],cursor=index+2;while(cursor<lines.length&&lines[cursor].includes("|")&&lines[cursor].trim()){var row=cells(lines[cursor]);while(row.length<header.length)row.push("");rows.push(row.slice(0,header.length));cursor++}out.push('<div class="md-table-wrap"><table><thead><tr>'+header.map(function(cell,column){return'<th style="text-align:'+align[column]+'">'+inlineMarkdown(cell)+"</th>"}).join("")+"</tr></thead><tbody>"+rows.map(function(row){return"<tr>"+row.map(function(cell,column){return'<td style="text-align:'+align[column]+'">'+inlineMarkdown(cell)+"</td>"}).join("")+"</tr>"}).join("")+"</tbody></table></div>");return cursor}lines.forEach(function(line,index){if(index<skipUntil)return;var fence=line.match(/^\\x60\\x60\\x60\\s*([^\\s]*)/);if(fence){closeList();flushParagraph();if(inCode)flushCode();else language=fence[1]||"";inCode=!inCode;return}if(inCode){code.push(line);return}if(line.includes("|")){var next=tableAt(index);if(next){skipUntil=next;return}}if(!line.trim()){closeList();flushParagraph();return}var heading=line.match(/^(#{1,3})\\s+(.+)$/);if(heading){closeList();flushParagraph();var level=heading[1].length;out.push("<h"+level+">"+inlineMarkdown(heading[2])+"</h"+level+">");return}if(/^\\s*([-*_])(?:\\s*\\1){2,}\\s*$/.test(line)){closeList();flushParagraph();out.push("<hr>");return}var bullet=line.match(/^\\s*[-*]\\s+(.+)$/),ordered=line.match(/^\\s*\\d+[.)]\\s+(.+)$/);if(bullet||ordered){flushParagraph();var nextList=ordered?"ol":"ul";if(list!==nextList){closeList();list=nextList;out.push("<"+list+">")}out.push("<li>"+inlineMarkdown((bullet||ordered)[1])+"</li>");return}closeList();if(/^>\\s?/.test(line)){flushParagraph();out.push("<blockquote>"+inlineMarkdown(line.replace(/^>\\s?/,""))+"</blockquote>")}else paragraph.push(line)});closeList();flushParagraph();if(inCode||code.length)flushCode();return out.join("")}
534
+ function messageState(head,detail){messages=[];pendingMessages=[];renderedMessageIds.clear();byId("message-stream").innerHTML='<div class="message-state"><strong>'+esc(head)+"</strong>"+esc(detail)+"</div>";byId("console-follow").classList.add("hidden")}
535
+ function messageTime(value){return value?new Date(value).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}):"Sending"}
536
+ function messageKey(item){return item.role+"\\n"+String(item.text||"").replace(/\\r\\n/g,"\\n").trim()}
537
+ function renderMessages(animate){var node=byId("message-scroll"),target=byId("message-stream"),confirmed=new Set(messages.map(messageKey)),optimistic=pendingMessages.filter(function(item){return!confirmed.has(messageKey(item))}),all=messages.concat(optimistic),wasNear=followConsole||node.scrollHeight-node.scrollTop-node.clientHeight<100;if(!all.length){target.innerHTML='<div class="message-state"><strong>No messages yet</strong>Send the first message to start this session conversation.</div>';return}target.innerHTML=all.map(function(item){if(item.role==="status")return'<div class="message-status" data-state="'+esc(item.state||"completed")+'" data-message-id="'+esc(item.id)+'"><span class="status-icon" aria-hidden="true"></span><span>'+esc(item.text)+'</span><time>'+esc(messageTime(item.timestamp))+'</time></div>';var user=item.role==="user",pending=item.pending?' · sending':'';return'<article class="message '+item.role+'" data-message-id="'+esc(item.id)+'"><div class="message-avatar" aria-hidden="true">'+(user?'YOU':'OMP')+'</div><div class="message-content"><div class="message-meta"><strong>'+(user?'You':'Assistant')+'</strong><time>'+esc(messageTime(item.timestamp))+esc(pending)+'</time></div><div class="message-bubble">'+markdown(item.text)+'</div></div></article>'}).join("");if(animate&&!reducedMotion&&window.gsap){var fresh=Array.from(target.querySelectorAll("[data-message-id]")).filter(function(element){return!renderedMessageIds.has(element.dataset.messageId)});if(fresh.length)window.gsap.fromTo(fresh,{autoAlpha:0,y:12},{autoAlpha:1,y:0,duration:.28,stagger:.045,ease:"power2.out",clearProps:"transform,opacity"})}all.forEach(function(item){renderedMessageIds.add(item.id)});if(wasNear){node.scrollTop=node.scrollHeight;followConsole=true;byId("console-follow").classList.add("hidden")}}
538
+ function setActivity(s){var activity=s&&s.activity,label=activity&&activity.label?activity.label:s?s.state==="stopped"?"Session stopped":s.state==="disconnected"?"Session detached":"Ready for your message":"Waiting for a session",idle=!s||!activity||activity.awaitingInput||!activity.running,n=byId("agent-activity");byId("activity-label").textContent=label;n.dataset.idle=String(idle);if(label!==lastActivityLabel&&!reducedMotion&&window.gsap){window.gsap.killTweensOf(n);window.gsap.fromTo(n,{autoAlpha:.55},{autoAlpha:1,duration:.3,ease:"power2.out",clearProps:"opacity,visibility"});if(!idle)window.gsap.fromTo(n.querySelector(".activity-mark"),{scale:.72},{scale:1.35,duration:.22,repeat:1,yoyo:true,ease:"power2.inOut",clearProps:"transform"})}lastActivityLabel=label}
539
+ function renderConversation(){var s=selected(),ok=available(s);byId("conversation-title").textContent=s?title(s):"Choose a session";byId("session-state").innerHTML='<span class="state-dot '+(s?tone(s.state):'dot-muted')+'"></span><span>'+esc(s?s.state:"No session selected")+'</span>';byId("console-refresh").disabled=!s;byId("attach-top").disabled=!ok;byId("composer-input").disabled=!ok;byId("composer-send").disabled=!ok;byId("composer-status").textContent=ok?"Messages go directly to this OMP session.":s?"History is readable, but this session cannot accept input.":"Select a live session to begin.";setActivity(s);if(!s)messageState("No session open","Select a session to read its conversation and send a message.")}
540
+ function page(name){var onMobile=window.matchMedia&&window.matchMedia("(max-width: 759px)").matches;if(onMobile){var view=name==="overview"?"sessions":name;if(document.body.getAttribute("data-mobile-view")!==view)mobile(view)}document.body.setAttribute("data-page",name);document.querySelectorAll("[data-page]").forEach(function(button){button.classList.toggle("active",button.dataset.page===name)});if(name==="chat")startConversation();history.replaceState({},"",location.pathname+"#"+name)}
541
+ function render(){renderWorkspaceSelect();renderFilters();renderSessions();renderConversation();renderInspector()}
385
542
  function daemonState(body){var n=byId("daemon-state"),dot=n.querySelector(".state-dot");dot.className="state-dot "+(body.scanError||body.stale?"dot-warn":"dot-ok");n.querySelector("span:last-child").textContent=body.scanError?"Scan issue: "+body.scanError:body.stale?"Session data is stale":"Local daemon live · updated "+age(body.collectedAt)}
386
- function refresh(){return apiJson("/sessions").then(function(body){sessions=Array.isArray(body.sessions)?body.sessions:[];if(!selectedName||!sessions.some(function(s){return s.name===selectedName})){var remembered=localStorage.getItem(SELECTED_KEY)||"";selectedName=sessions.some(function(s){return s.name===remembered})?remembered:(sessions[0]?sessions[0].name:"")}daemonState(body);render();if(selectedName)startConsole()}).catch(function(error){var n=byId("daemon-state");n.querySelector(".state-dot").className="state-dot dot-warn";n.querySelector("span:last-child").textContent="Daemon unavailable";toast("Session refresh failed: "+err(error),true)})}
387
- function loadConsole(force){var s=selected();if(!available(s)||consoleBusy)return Promise.resolve();consoleBusy=true;if(force||!consoleText)consoleState("Loading console","Capturing the active tmux pane.");return apiJson("/sessions/"+encodeURIComponent(s.name)+"/console").then(function(body){var node=byId("console"),wasNear=followConsole||node.scrollHeight-node.scrollTop-node.clientHeight<80,next=typeof body.text==="string"?body.text:"";consoleText=next;if(next)node.textContent=next;else consoleState("Console is quiet","Waiting for output from this session.");if(wasNear&&next){node.scrollTop=node.scrollHeight;followConsole=true;byId("console-follow").classList.add("hidden")}}).catch(function(error){consoleState("Could not read console",err(error))}).finally(function(){consoleBusy=false})}
388
- function startConsole(){var s=selected();if(!available(s)){clearInterval(consoleTimer);consoleSessionName="";return}if(consoleSessionName===s.name&&consoleTimer)return;clearInterval(consoleTimer);consoleSessionName=s.name;void loadConsole(true);consoleTimer=setInterval(function(){void loadConsole(false)},1800)}
389
- function choose(name){if(selectedName===name){mobile("chat");return}selectedName=name;localStorage.setItem(SELECTED_KEY,name);consoleText="";render();startConsole();mobile("chat");byId("composer-input").focus()}
543
+ function overview(){return apiJson("/overview").then(function(body){var total=body.sessions&&body.sessions.total||0,states=body.sessions&&body.sessions.states||{},modelCount=body.models&&body.models.total||0,providerCount=body.models&&body.models.providers||0;byId("overview-status").textContent=body.stale?"Session snapshot is stale. Refresh to retry.":body.daemon&&body.daemon.ompAvailable?"Daemon and OMP are available.":"OMP information is unavailable.";byId("overview-metrics").innerHTML=[["Sessions",total],["Working",states.working||0],["Waiting",states.waiting||0],["Workspaces",body.sessions&&body.sessions.workspaces||0]].map(function(item){return'<div class="card metric"><span class="label">'+esc(item[0])+'</span><strong class="metric-value">'+esc(item[1])+'</strong></div>'}).join("");var attention=body.sessions&&Array.isArray(body.sessions.attention)?body.sessions.attention:[],focus=selected()||sessions[0];byId("attention-list").innerHTML=attention.length?attention.map(function(s){return'<button class="overview-row" style="width:100%;border:0;background:transparent;color:inherit" type="button" data-session="'+esc(s.name)+'"><span>'+esc(title(s))+'</span><span class="state-label"><span class="state-dot '+tone(s.state)+'"></span>'+esc(s.state)+'</span></button>'}).join(""):'<p class="field-help">No sessions currently need attention.</p>';byId("focus-summary").innerHTML=focus?'<div class="overview-row"><span><strong>'+esc(title(focus))+'</strong><span class="field-help">'+esc(workspaceOf(focus))+'</span></span><span class="state-label"><span class="state-dot '+tone(focus.state)+'"></span>'+esc(focus.state)+'</span></div><button class="btn primary" type="button" data-session="'+esc(focus.name)+'">Continue in chat</button>':'<p class="field-help">No session selected. Create one to begin.</p>';var workspaceMap={};sessions.forEach(function(s){var key=workspaceOf(s),item=workspaceMap[key]||(workspaceMap[key]={total:0,working:0});item.total++;if(s.state==="working")item.working++});byId("workspace-summary").innerHTML=Object.keys(workspaceMap).length?Object.keys(workspaceMap).sort().slice(0,8).map(function(key){var item=workspaceMap[key];return'<button class="overview-row" style="width:100%;border-width:0 0 1px;background:transparent;color:inherit" type="button" data-workspace="'+esc(key)+'"><span>'+esc(key.split("/").filter(Boolean).pop()||key)+'</span><span class="field-help">'+item.total+' sessions · '+item.working+' working</span></button>'}).join(""):'<p class="field-help">Workspace activity appears when sessions exist.</p>';byId("recent-sessions").innerHTML=sessions.length?sessions.slice().sort(function(a,b){return epochMs(b.lastAttachedAt||b.createdAt)-epochMs(a.lastAttachedAt||a.createdAt)}).slice(0,6).map(function(s){return'<button class="overview-row" style="width:100%;border-width:0 0 1px;background:transparent;color:inherit" type="button" data-session="'+esc(s.name)+'"><span>'+esc(title(s))+'</span><span class="field-help">'+esc(age(s.lastAttachedAt||s.createdAt))+'</span></button>'}).join(""):'<p class="field-help">No recent sessions.</p>';byId("omp-summary").innerHTML='<div class="overview-row"><span>Models</span><strong>'+esc(modelCount)+'</strong></div><div class="overview-row"><span>Providers</span><strong>'+esc(providerCount)+'</strong></div><div class="overview-row"><span>Daemon</span><strong>'+esc(body.daemon&&body.daemon.healthy?"Healthy":"Check status")+'</strong></div><div class="overview-row"><span>OMP catalog</span><strong>'+esc(body.daemon&&body.daemon.ompAvailable?"Available":"Unavailable")+'</strong></div><div class="overview-row"><span>Last scan</span><strong>'+esc(age(body.collectedAt))+'</strong></div>';if(!reducedMotion&&window.gsap)window.gsap.fromTo("#overview-metrics .metric",{autoAlpha:0,y:8},{autoAlpha:1,y:0,duration:.24,stagger:.04,ease:"power2.out",clearProps:"transform,opacity"})}).catch(function(error){byId("overview-status").textContent="Could not load overview: "+err(error)})}
544
+ function configInput(setting){var value=setting.value===undefined?"":(typeof setting.value==="string"?setting.value:JSON.stringify(setting.value));if(setting.redacted)return'<div class="overview-row"><span><strong>'+esc(setting.key)+'</strong><span class="field-help">'+esc(setting.description||"Sensitive OMP value")+'</span></span><span class="field-help">Hidden</span></div>';var control=setting.type==="boolean"?'<select data-config-value="'+esc(setting.key)+'"><option value="true"'+(value==="true"?' selected':'')+'>Enabled</option><option value="false"'+(value==="false"?' selected':'')+'>Disabled</option></select>':setting.type==="enum"&&setting.options?'<select data-config-value="'+esc(setting.key)+'">'+setting.options.map(function(option){return'<option value="'+esc(option)+'"'+(option===value?' selected':'')+'>'+esc(option)+'</option>'}).join("")+'</select>':setting.type==="array"||setting.type==="record"?'<textarea data-config-value="'+esc(setting.key)+'" aria-label="'+esc(setting.key)+'">'+esc(value)+'</textarea>':'<input data-config-value="'+esc(setting.key)+'" value="'+esc(value)+'" aria-label="'+esc(setting.key)+'" />';return'<div class="overview-row"><label style="flex:1"><strong>'+esc(setting.key)+'</strong><span class="field-help">'+esc(setting.description||setting.type)+'</span>'+control+'</label><span><button type="button" class="btn" data-config-save="'+esc(setting.key)+'" '+(setting.writable?'':'disabled')+'>Save</button><button type="button" class="btn ghost" data-config-reset="'+esc(setting.key)+'" '+(setting.writable?'':'disabled')+'>Reset</button></span></div>'}
545
+ function decodeConfigValue(setting,input){if(setting.type==="boolean")return input.value==="true";if(setting.type==="number")return Number(input.value);if(setting.type==="array"||setting.type==="record")return JSON.parse(input.value);return input.value}
546
+ function renderConfigSettings(settings){var list=byId("omp-config-list");list._settings=settings;list.innerHTML=settings.length?settings.map(configInput).join(""):'<p class="field-help">OMP returned no browser-safe settings.</p>'}
547
+ function loadOmpConfig(){byId("omp-config-status").textContent="Loading typed configuration.";return apiJson("/omp/config").then(function(body){var settings=Array.isArray(body.settings)?body.settings:[],list=byId("omp-config-list");list._allSettings=settings;renderConfigSettings(settings);byId("omp-config-status").textContent="Values are read from OMP. Redacted values cannot be read or edited here."}).catch(function(error){byId("omp-config-status").textContent="Could not load OMP configuration: "+err(error)})}
548
+ function saveConfig(key,reset){var list=byId("omp-config-list"),setting=(list._settings||[]).find(function(item){return item.key===key}),input=document.querySelector('[data-config-value="'+CSS.escape(key)+'"]');if(!setting||(!reset&&!input))return;var payload={key:key};try{if(!reset)payload.value=decodeConfigValue(setting,input)}catch(error){toast("Enter valid "+setting.type+" JSON.",true);return}post(reset?"/omp/config/reset":"/omp/config",payload,reset?"POST":"PATCH").then(loadOmpConfig).then(function(){toast(reset?"OMP setting reset":"OMP setting saved")}).catch(function(error){toast("OMP setting was not changed: "+err(error),true)})}
549
+ function renderProviderOrder(){var target=byId("provider-order-list");if(!target)return;target.innerHTML=providerOrder.length?providerOrder.map(function(id,index){return'<div class="overview-row"><span class="mono">'+esc(id)+'</span><span><button type="button" class="btn ghost" data-provider-move="up" data-provider-index="'+index+'" '+(index?'':'disabled')+'>Up</button><button type="button" class="btn ghost" data-provider-move="down" data-provider-index="'+index+'" '+(index===providerOrder.length-1?'disabled':'')+'>Down</button></span></div>'}).join(""):'<p class="field-help">No explicit provider order is configured.</p>'}
550
+ function resetProviderEditor(){byId("provider-editor-title").textContent="Add provider";byId("provider-id").value="";byId("provider-id").disabled=false;byId("provider-url").value="";byId("provider-api").value="openai-responses";byId("provider-auth").value="apiKey";byId("provider-models").value="";byId("provider-key").value="";byId("provider-key-clear").checked=false;byId("provider-key-state").textContent="Keys are write-only and never returned.";byId("provider-delete").classList.add("hidden")}
551
+ function editProvider(id){var item=providers.find(function(provider){return provider.id===id}),custom=item&&item.custom;if(!custom)return;byId("provider-editor-title").textContent="Edit "+id;byId("provider-id").value=id;byId("provider-id").disabled=true;byId("provider-url").value=custom.baseUrl||"";byId("provider-api").value=custom.api||"openai-responses";byId("provider-auth").value=custom.auth||"apiKey";byId("provider-models").value=(custom.models||[]).join("\\n");byId("provider-key").value="";byId("provider-key-clear").checked=false;byId("provider-key-state").textContent=custom.hasApiKey?"A key is stored. Leave blank to preserve it.":"No key is stored.";byId("provider-delete").classList.remove("hidden")}
552
+ function loadProviders(){byId("providers-status").textContent="Loading provider state.";return apiJson("/omp/providers").then(function(body){providers=Array.isArray(body.providers)?body.providers:[];var apis=Array.isArray(body.providerApis)?body.providerApis:[];byId("provider-api").innerHTML=apis.map(function(api){return'<option value="'+esc(api)+'">'+esc(api)+'</option>'}).join("");providerOrder=Array.isArray(body.modelProviderOrder)&&body.modelProviderOrder.length?body.modelProviderOrder.slice():providers.map(function(provider){return provider.id});byId("providers-list").innerHTML=providers.length?providers.map(function(provider){return'<div class="card provider-card"><span><strong>'+esc(provider.id)+'</strong><span class="field-help">'+esc(provider.models)+' models · '+esc(provider.enabled?"enabled":"disabled")+(provider.custom?' · custom':'')+'</span></span><span class="provider-order-actions">'+(provider.custom?'<button type="button" class="btn ghost" data-provider-edit="'+esc(provider.id)+'">Edit</button>':'')+'<button type="button" class="btn" data-provider="'+esc(provider.id)+'" data-provider-operation="'+(provider.enabled?'disable':'enable')+'">'+(provider.enabled?'Disable':'Enable')+'</button></span></div>'}).join(""):'<p class="field-help">OMP returned no providers.</p>';renderProviderOrder();if(!byId("provider-api").value&&apis[0])byId("provider-api").value=apis[0];byId("providers-status").textContent="Routing and custom providers are read directly from OMP."}).catch(function(error){byId("providers-status").textContent="Could not load providers: "+err(error)})}
553
+ function saveCustomProvider(){var id=byId("provider-id").value.trim(),url=byId("provider-url").value.trim(),models=byId("provider-models").value.split(/\\r?\\n|,/).map(function(value){return value.trim()}).filter(Boolean);if(!id||!url||!models.length){toast("Provider id, endpoint, and at least one model are required.",true);return}var payload={baseUrl:url,api:byId("provider-api").value,auth:byId("provider-auth").value,models:models,clearApiKey:byId("provider-key-clear").checked};if(byId("provider-key").value)payload.apiKey=byId("provider-key").value;post("/omp/providers/"+encodeURIComponent(id),payload,"PUT").then(function(){toast("Custom provider saved to OMP");return loadProviders()}).then(function(){editProvider(id);return loadModels()}).catch(function(error){toast("Provider was not saved: "+err(error),true)})}
554
+ function deleteProvider(){var id=byId("provider-id").value;if(!id||!confirm("Delete custom provider "+id+" from OMP?"))return;apiJson("/omp/providers/"+encodeURIComponent(id),{method:"DELETE"}).then(function(){toast("Custom provider deleted");resetProviderEditor();return loadProviders()}).then(loadModels).catch(function(error){toast("Provider was not deleted: "+err(error),true)})}
555
+ function saveProviderOrder(order){return post("/omp/providers",{operation:"order",order:order},"PATCH").then(function(){providerOrder=order.slice();renderProviderOrder();toast("OMP provider order saved")}).catch(function(error){toast("Provider order was not changed: "+err(error),true)})}
556
+ function authAction(action){var confirm=action!=="status"&&action!=="list";byId("auth-status").textContent="Running OMP auth-broker "+action+"…";return post("/omp/provider-action",{action:action,confirm:confirm}).then(function(body){byId("auth-status").textContent=body.status==="handoff-required"?"Open an OMP terminal to finish this interactive action.":"OMP auth-broker action completed.";toast(byId("auth-status").textContent)}).catch(function(error){byId("auth-status").textContent="Auth action failed: "+err(error);toast(byId("auth-status").textContent,true)})}
557
+ function loadCollabHosts(){var status=byId("collab-status"),target=byId("collab-list");if(!status||!target)return;status.textContent="Loading active collab hosts.";return apiJson("/omp/collab").then(function(body){var hosts=Array.isArray(body&&body.hosts)?body.hosts:[];if(!hosts.length){target.innerHTML='<p class="field-help">No active collab sessions on this host. Mint a share link with omp collab link in a terminal.</p>';status.textContent="No active collab hosts.";return}target.innerHTML=hosts.map(collabRow).join("");status.textContent=hosts.length+" active collab host"+(hosts.length===1?"":"s")+"."}).catch(function(error){status.textContent="Could not load collab hosts: "+err(error);target.innerHTML=""})}
558
+ function collabRow(host){var instanceId=String(host&&host.instanceId||""),pid=host&&host.pid,model=(host&&host.model)||{},provider=String(model.provider||""),modelId=String(model.id||""),cwd=String(host&&host.cwd||""),sessionName=String(host&&host.sessionName||""),participants=Number(host&&host.participants)||0,relay=Boolean(host&&host.relayConnected),access=String(host&&host.access||""),sep=String.fromCharCode(92),cut=Math.max(cwd.lastIndexOf("/"),cwd.lastIndexOf(sep)),basename=cut>=0?cwd.slice(cut+1):cwd,label=modelId||instanceId||String(pid||"host"),hostId=instanceId||String(pid||"");return '<div class="overview-row"><div><span class="mono">'+esc(provider||label)+'</span><p class="field-help">'+esc(label)+(basename?' &middot; '+esc(basename):'')+(sessionName?' &middot; '+esc(sessionName):'')+' &middot; '+participants+' participant'+(participants===1?'':'s')+(access?' &middot; '+esc(access):'')+'</p></div>'+'<div><span class="state-dot '+(relay?'dot-ok':'dot-warn')+'" title="'+(relay?'relay connected':'relay disconnected')+'"></span>'+'<button class="btn ghost" type="button" data-collab-copy="'+esc(hostId)+'">Copy id</button></div></div>'}
559
+ function refresh(){return apiJson("/sessions").then(function(body){sessions=Array.isArray(body.sessions)?body.sessions:[];if(!selectedName||!sessions.some(function(s){return s.name===selectedName})){var remembered=localStorage.getItem(SELECTED_KEY)||"";selectedName=sessions.some(function(s){return s.name===remembered})?remembered:(sessions[0]?sessions[0].name:"")}daemonState(body);render();if(selectedName)startConversation()}).catch(function(error){var n=byId("daemon-state");n.querySelector(".state-dot").className="state-dot dot-warn";n.querySelector("span:last-child").textContent="Daemon unavailable";toast("Session refresh failed: "+err(error),true)})}
560
+ function loadMessages(force){var s=selected();if(!s||consoleBusy)return Promise.resolve();var requestedName=s.name;consoleBusy=true;if(force&&!messages.length)byId("message-stream").innerHTML='<div class="message-state"><strong>Loading conversation</strong>Reading recent messages from the OMP session journal.</div>';return apiJson("/sessions/"+encodeURIComponent(requestedName)+"/messages").then(function(body){if(selectedName!==requestedName)return;var next=Array.isArray(body.messages)?body.messages:[],keys=new Set(next.map(messageKey));pendingMessages=pendingMessages.filter(function(pending){return!keys.has(messageKey(pending))});messages=next;renderMessages(true)}).catch(function(error){if(selectedName===requestedName&&!messages.length)byId("message-stream").innerHTML='<div class="message-state"><strong>Messages unavailable</strong>'+esc(err(error))+' Refresh to try again.</div>'}).finally(function(){consoleBusy=false})}
561
+ function startConversation(){var s=selected();if(!s){clearInterval(consoleTimer);consoleSessionName="";return}if(consoleSessionName===s.name&&consoleTimer)return;clearInterval(consoleTimer);consoleSessionName=s.name;void loadMessages(true);if(available(s))consoleTimer=setInterval(function(){void loadMessages(false)},1600)}
562
+ function choose(name){if(selectedName===name){mobile("chat");page("chat");return}selectedName=name;localStorage.setItem(SELECTED_KEY,name);messages=[];pendingMessages=[];renderedMessageIds.clear();render();startConversation();mobile("chat");page("chat");byId("composer-input").focus()}
390
563
  function attach(){var s=selected();if(!s)return;post("/sessions/"+encodeURIComponent(s.name)+"/attach",{}).then(function(body){if(body.spawned)toast("Opened "+body.terminal);else if(body.command){if(navigator.clipboard&&navigator.clipboard.writeText)navigator.clipboard.writeText(body.command).catch(function(){});toast("No terminal emulator found. Attach command copied when browser access allowed.",true)}}).catch(function(error){toast("Could not open terminal: "+err(error),true)})}
391
564
  function modelOptions(select,value){var custom=value&&!models.some(function(model){return model.selector===value})?'<option value="'+esc(value)+'">'+esc(value)+"</option>":"";select.innerHTML='<option value="">OMP default</option>'+custom+models.map(function(model){return'<option value="'+esc(model.selector)+'">'+esc(model.name+" · "+model.provider)+'</option>'}).join("");select.value=value||""}
392
565
  var THINKING_LEVELS=["off","minimal","low","medium","high","xhigh","max","auto"],SETTINGS_FIELD={model:"defaultModel",thinking:"defaultThinking",approvalMode:"defaultApprovalMode"};
393
566
  function thinkingLevels(selector){var known=models.find(function(model){return model.selector===selector});return known&&Array.isArray(known.thinking)&&known.thinking.length?known.thinking:THINKING_LEVELS}
394
567
  function thinkingOptions(select,selector,level){var levels=thinkingLevels(selector),custom=level&&levels.indexOf(level)===-1?'<option value="'+esc(level)+'">'+esc(level)+"</option>":"";select.innerHTML='<option value="">OMP default</option>'+custom+levels.map(function(item){return'<option value="'+esc(item)+'">'+esc(item)+"</option>"}).join("");select.value=level||""}
395
- function seedRuntime(){modelOptions(byId("composer-model"),runtimeDefaults.model);modelOptions(byId("create-model"),runtimeDefaults.model);thinkingOptions(byId("composer-thinking"),runtimeDefaults.model,runtimeDefaults.thinking);thinkingOptions(byId("create-thinking"),runtimeDefaults.model,runtimeDefaults.thinking);byId("composer-permission").value=runtimeDefaults.approvalMode;byId("create-permission").value=runtimeDefaults.approvalMode}
568
+ function seedRuntime(){modelOptions(byId("composer-model"),runtimeDefaults.model);modelOptions(byId("create-model"),runtimeDefaults.model);modelOptions(byId("setting-model"),runtimeDefaults.model);thinkingOptions(byId("composer-thinking"),runtimeDefaults.model,runtimeDefaults.thinking);thinkingOptions(byId("create-thinking"),runtimeDefaults.model,runtimeDefaults.thinking);thinkingOptions(byId("setting-thinking"),runtimeDefaults.model,runtimeDefaults.thinking);byId("composer-permission").value=runtimeDefaults.approvalMode;byId("create-permission").value=runtimeDefaults.approvalMode;byId("setting-permission").value=runtimeDefaults.approvalMode}
396
569
  function saveRuntime(key,value){if(value===runtimeDefaults[key])return;var payload={};payload[SETTINGS_FIELD[key]]=value;post("/settings",payload,"PATCH").then(loadSettings).then(function(){toast("Launch default saved")}).catch(function(error){seedRuntime();toast("Could not save the launch default: "+err(error),true)})}
397
570
  function loadModels(){return apiJson("/models").then(function(body){models=Array.isArray(body.models)?body.models:[];seedRuntime();byId("model-status").textContent=models.length?models.length+" available models loaded from OMP.":"OMP returned no available models."}).catch(function(error){byId("model-status").textContent="Model catalog unavailable. OMP default remains available.";models=[];seedRuntime()})}
398
571
  function openCreate(){byId("create-cwd").value=projectsFolder;byId("create-task").value="";byId("create-id").value="";seedRuntime();byId("create-dialog").showModal();requestAnimationFrame(function(){byId("create-cwd").focus()})}
399
572
  function openRename(){var s=selected();if(!s)return;byId("rename-id").value=s.id||"";byId("rename-dialog").showModal();requestAnimationFrame(function(){byId("rename-id").select()})}
400
573
  function openStop(){var s=selected();if(!s)return;byId("stop-text").textContent="Stop "+title(s)+"? This ends its tmux session and agent work.";byId("stop-dialog").showModal()}
401
- function loadSettings(){return apiJson("/settings").then(function(body){var s=body.settings||{};projectsFolder=s.projectsFolder||"";runtimeDefaults={model:s.defaultModel||"",thinking:s.defaultThinking||"",approvalMode:s.defaultApprovalMode||""};byId("setting-projects").value=projectsFolder;seedRuntime();byId("setting-attach").checked=Boolean(s.autoAttachLatest);byId("setting-mouse").checked=Boolean(s.mouseScrolling);byId("setting-stopped").checked=Boolean(s.showStoppedSessions);byId("setting-tray").checked=Boolean(s.trayEnabled);byId("settings-status").textContent="Changes are stored in your Bizar OMP configuration."})}
402
- function openSettings(){byId("settings-dialog").showModal();byId("settings-status").textContent="Loading settings.";loadSettings().catch(function(error){byId("settings-status").textContent="Could not load settings: "+err(error)})}
403
- function theme(value,remember){document.documentElement.setAttribute("data-theme",value);byId("theme-toggle").textContent=value==="dark"?"Light theme":"Dark theme";if(remember)localStorage.setItem(THEME_KEY,value)}
404
- document.addEventListener("click",function(event){var target=event.target.closest("button");if(!target)return;if(target.dataset.session)choose(target.dataset.session);if(target.dataset.filter){filter=target.dataset.filter;renderFilters();renderSessions()}if(target.dataset.view)mobile(target.dataset.view);if(target.dataset.action==="attach")attach();if(target.dataset.action==="rename")openRename();if(target.dataset.action==="stop")openStop()});
405
- byId("session-search").addEventListener("input",function(event){query=event.target.value.trim();renderSessions()});byId("new-session-top").addEventListener("click",openCreate);byId("settings-open").addEventListener("click",openSettings);byId("attach-top").addEventListener("click",attach);byId("console-refresh").addEventListener("click",function(){void loadConsole(true)});byId("theme-toggle").addEventListener("click",function(){theme(document.documentElement.getAttribute("data-theme")==="dark"?"light":"dark",true)});byId("composer-model").addEventListener("change",function(event){saveRuntime("model",event.target.value)});byId("composer-thinking").addEventListener("change",function(event){saveRuntime("thinking",event.target.value)});byId("composer-permission").addEventListener("change",function(event){saveRuntime("approvalMode",event.target.value)});
406
- byId("console").addEventListener("scroll",function(event){var n=event.target;followConsole=n.scrollHeight-n.scrollTop-n.clientHeight<80;byId("console-follow").classList.toggle("hidden",followConsole)});byId("console-follow").addEventListener("click",function(){var n=byId("console");n.scrollTop=n.scrollHeight;followConsole=true;byId("console-follow").classList.add("hidden")});byId("composer-input").addEventListener("keydown",function(event){if(event.key==="Enter"&&(event.ctrlKey||event.metaKey)){event.preventDefault();byId("composer").requestSubmit()}});
407
- byId("composer").addEventListener("submit",function(event){event.preventDefault();var s=selected(),input=byId("composer-input"),message=input.value;if(!s||!message.trim())return;byId("composer-send").disabled=true;byId("composer-status").textContent="Sending to "+title(s)+".";post("/sessions/"+encodeURIComponent(s.name)+"/console",{message:message}).then(function(){input.value="";byId("composer-status").textContent="Sent. Waiting for session output.";followConsole=true;return new Promise(function(resolve){setTimeout(resolve,180)})}).then(function(){return loadConsole(false)}).catch(function(error){byId("composer-status").textContent="Send failed. Your draft is still here.";toast("Message was not sent: "+err(error),true)}).finally(function(){byId("composer-send").disabled=!available(selected())})});
574
+ function loadSettings(){return apiJson("/settings").then(function(body){var s=body.settings||{};projectsFolder=s.projectsFolder||"";runtimeDefaults={model:s.defaultModel||"",thinking:s.defaultThinking||"",approvalMode:s.defaultApprovalMode||""};byId("setting-projects").value=projectsFolder;seedRuntime();byId("setting-attach").checked=Boolean(s.autoAttachLatest);byId("setting-mouse").checked=Boolean(s.mouseScrolling);byId("setting-stopped").checked=Boolean(s.showStoppedSessions);byId("setting-tray").checked=Boolean(s.trayEnabled);byId("setting-theme").value=localStorage.getItem(THEME_KEY)||"system";byId("settings-status").textContent="Changes are stored in Bizar OMP and OMP configuration."})}
575
+ function settingsTab(name){document.querySelectorAll("[data-settings-tab]").forEach(function(tab){tab.classList.toggle("active",tab.dataset.settingsTab===name)});document.querySelectorAll("[data-settings-panel]").forEach(function(panel){panel.classList.toggle("active",panel.dataset.settingsPanel===name)});if(name==="omp")void loadOmpConfig();if(name==="providers")void loadProviders();if(name==="security")void loadCollabHosts()}
576
+ function openSettings(tab){if(!byId("settings-dialog").open)byId("settings-dialog").showModal();settingsTab(tab||"general");byId("settings-status").textContent="Loading settings.";Promise.all([loadSettings(),tab==="providers"?loadProviders():Promise.resolve(),tab==="omp"?loadOmpConfig():Promise.resolve()]).catch(function(error){byId("settings-status").textContent="Could not load settings: "+err(error)})}
577
+ function saveDashboardSettings(){var payload={projectsFolder:byId("setting-projects").value.trim(),autoAttachLatest:byId("setting-attach").checked,mouseScrolling:byId("setting-mouse").checked,showStoppedSessions:byId("setting-stopped").checked,trayEnabled:byId("setting-tray").checked,defaultModel:byId("setting-model").value,defaultThinking:byId("setting-thinking").value,defaultApprovalMode:byId("setting-permission").value};post("/settings",payload,"PATCH").then(function(){var selectedTheme=byId("setting-theme").value;theme(selectedTheme,true);return loadSettings()}).then(function(){toast("Dashboard settings saved");return refresh()}).catch(function(error){toast("Could not save settings: "+err(error),true)})}
578
+ function theme(value,remember){var effective=value==="system"?(matchMedia("(prefers-color-scheme: light)").matches?"light":"dark"):value;document.documentElement.setAttribute("data-theme",effective);byId("theme-toggle").textContent=effective==="dark"?"Light theme":"Dark theme";if(remember)localStorage.setItem(THEME_KEY,value)}
579
+ document.addEventListener("click",function(event){var copy=event.target.closest(".copy-code");if(copy){var code=copy.closest(".code-block").querySelector("code").textContent;navigator.clipboard.writeText(code).then(function(){copy.textContent="Copied";setTimeout(function(){copy.textContent="Copy"},1200)}).catch(function(){toast("Code could not be copied.",true)});return}var target=event.target.closest("button");if(!target)return;if(target.dataset.session)choose(target.dataset.session);if(target.dataset.workspace){workspaceFilter=target.dataset.workspace;render();page("chat")}if(target.dataset.filter){filter=target.dataset.filter;renderFilters();renderSessions()}if(target.dataset.view)mobile(target.dataset.view);if(target.dataset.page){page(target.dataset.page);byId("utility-menu").classList.add("hidden");byId("utility-open").setAttribute("aria-expanded","false")}if(target.dataset.action==="attach")attach();if(target.dataset.action==="rename")openRename();if(target.dataset.action==="stop")openStop();if(target.dataset.configSave)saveConfig(target.dataset.configSave,false);if(target.dataset.configReset)saveConfig(target.dataset.configReset,true);if(target.dataset.settingsTab)settingsTab(target.dataset.settingsTab);if(target.dataset.providerEdit)editProvider(target.dataset.providerEdit);if(target.dataset.provider){post("/omp/providers",{operation:target.dataset.providerOperation,provider:target.dataset.provider},"PATCH").then(loadProviders).then(function(){toast("Provider routing updated")}).catch(function(error){toast("Provider was not changed: "+err(error),true)})}if(target.dataset.providerMove){var index=Number(target.dataset.providerIndex),next=index+(target.dataset.providerMove==="up"?-1:1);if(Number.isInteger(index)&&next>=0&&next<providerOrder.length){var moved=providerOrder[index];providerOrder[index]=providerOrder[next];providerOrder[next]=moved;renderProviderOrder()}}if(target.id==="provider-order-save")saveProviderOrder(providerOrder);if(target.id==="provider-order-reset")saveProviderOrder([]);if(target.id==="provider-save")saveCustomProvider();if(target.id==="provider-editor-reset")resetProviderEditor();if(target.id==="provider-delete")deleteProvider();if(target.id==="settings-save")saveDashboardSettings();if(target.dataset.authAction)authAction(target.dataset.authAction);if(target.dataset.overviewAction==="new")openCreate();if(target.dataset.overviewAction==="chat")page("chat");if(target.dataset.overviewAction==="settings")openSettings("general");if(target.dataset.overviewAction==="refresh"){void refresh();void overview()}if(target.dataset.composerAction==="new")openCreate();if(target.dataset.composerAction==="terminal")attach();if(target.dataset.composerAction==="settings")openSettings("runtime");if(target.dataset.composerAction)byId("composer-menu").classList.add("hidden");if(target.dataset.authAction)authAction(target.dataset.authAction);if(target.dataset.collabAction)void loadCollabHosts();if(target.dataset.collabCopy){navigator.clipboard.writeText(target.dataset.collabCopy).then(function(){toast("Collab id copied")}).catch(function(){toast("Could not copy collab id",true)});return}});
580
+ byId("omp-config-filter").addEventListener("input",function(event){var needle=event.target.value.trim().toLowerCase(),all=byId("omp-config-list")._allSettings||[];renderConfigSettings(all.filter(function(setting){return!needle||[setting.key,setting.type,setting.description||""].join(" ").toLowerCase().indexOf(needle)!==-1}))});
581
+ byId("settings-search").addEventListener("input",function(event){var needle=event.target.value.trim().toLowerCase(),match="";document.querySelectorAll("[data-settings-panel]").forEach(function(panel){var hit=!needle||panel.textContent.toLowerCase().includes(needle);if(hit&&!match)match=panel.dataset.settingsPanel});if(match)settingsTab(match)});
582
+ byId("session-search").addEventListener("input",function(event){query=event.target.value.trim();renderSessions()});byId("workspace-select").addEventListener("change",function(event){workspaceFilter=event.target.value;var list=visible();if(list.length&&!list.some(function(s){return s.name===selectedName}))choose(list[0].name);else renderSessions()});byId("new-session-top").addEventListener("click",openCreate);byId("settings-open").addEventListener("click",function(){openSettings("general")});byId("utility-open").addEventListener("click",function(){var menu=byId("utility-menu"),open=menu.classList.toggle("hidden");byId("utility-open").setAttribute("aria-expanded",String(!open))});byId("utility-refresh").addEventListener("click",function(){void refresh();void overview()});byId("composer-menu-open").addEventListener("click",function(){var menu=byId("composer-menu"),hidden=menu.classList.toggle("hidden");this.setAttribute("aria-expanded",String(!hidden))});document.addEventListener("keydown",function(event){if(event.key==="Escape"){byId("utility-menu").classList.add("hidden");byId("composer-menu").classList.add("hidden");byId("utility-open").setAttribute("aria-expanded","false")}});byId("attach-top").addEventListener("click",attach);byId("console-refresh").addEventListener("click",function(){void loadMessages(true)});byId("theme-toggle").addEventListener("click",function(){theme(document.documentElement.getAttribute("data-theme")==="dark"?"light":"dark",true)});byId("composer-model").addEventListener("change",function(event){saveRuntime("model",event.target.value)});byId("composer-thinking").addEventListener("change",function(event){saveRuntime("thinking",event.target.value)});byId("composer-permission").addEventListener("change",function(event){saveRuntime("approvalMode",event.target.value)});
583
+ byId("message-scroll").addEventListener("scroll",function(event){var n=event.target;followConsole=n.scrollHeight-n.scrollTop-n.clientHeight<100;byId("console-follow").classList.toggle("hidden",followConsole)});byId("console-follow").addEventListener("click",function(){var n=byId("message-scroll");n.scrollTo({top:n.scrollHeight,behavior:reducedMotion?"auto":"smooth"});followConsole=true;byId("console-follow").classList.add("hidden")});byId("composer-input").addEventListener("keydown",function(event){if(event.key==="Enter"&&(event.ctrlKey||event.metaKey)){event.preventDefault();byId("composer").requestSubmit()}});
584
+ byId("composer-input").addEventListener("input",function(event){var input=event.target;input.style.height="auto";input.style.height=Math.min(input.scrollHeight,160)+"px"});byId("composer-input").addEventListener("focus",function(){if(!reducedMotion&&window.gsap)window.gsap.to(byId("composer"),{boxShadow:"0 22px 64px rgba(0,0,0,.42)",duration:.2,ease:"power2.out"})});byId("composer-input").addEventListener("blur",function(){if(!reducedMotion&&window.gsap)window.gsap.to(byId("composer"),{boxShadow:"0 18px 54px rgba(0,0,0,.32)",duration:.2,ease:"power2.out",clearProps:"boxShadow"})});
585
+ byId("composer").addEventListener("submit",function(event){event.preventDefault();var s=selected(),input=byId("composer-input"),message=input.value;if(!s||!message.trim())return;byId("composer-send").disabled=true;byId("composer-status").textContent="Sending to "+title(s)+".";post("/sessions/"+encodeURIComponent(s.name)+"/console",{message:message}).then(function(body){input.value="";input.style.height="auto";pendingMessages.push({id:"pending-"+(body.sentAt||Date.now()),role:"user",text:message,timestamp:body.sentAt||Date.now(),pending:true});byId("composer-status").textContent="Sent. The agent is receiving your message.";followConsole=true;renderMessages(true);void refresh();return new Promise(function(resolve){setTimeout(resolve,220)})}).then(function(){return loadMessages(false)}).catch(function(error){byId("composer-status").textContent="Send failed. Your draft is still here.";toast("Message was not sent: "+err(error),true)}).finally(function(){byId("composer-send").disabled=!available(selected())})});
408
586
  byId("create-dialog").addEventListener("close",function(event){if(event.target.returnValue!=="create")return;var cwd=byId("create-cwd").value.trim();if(!cwd)return;post("/sessions/create",{cwd:cwd,task:byId("create-task").value.trim(),sessionId:byId("create-id").value.trim(),model:byId("create-model").value,thinking:byId("create-thinking").value,approvalMode:byId("create-permission").value}).then(function(body){selectedName=body.name||"";toast("Session created in "+cwd);return refresh()}).then(function(){if(selectedName)choose(selectedName)}).catch(function(error){toast("Could not create session: "+err(error),true)})});
409
587
  byId("rename-dialog").addEventListener("close",function(event){var s=selected();if(event.target.returnValue!=="rename"||!s)return;var next=byId("rename-id").value.trim();if(!next)return;post("/sessions/"+encodeURIComponent(s.name)+"/rename",{sessionId:next}).then(function(){selectedName="";toast("Session renamed to "+next);return refresh()}).catch(function(error){toast("Could not rename session: "+err(error),true)})});
410
588
  byId("stop-dialog").addEventListener("close",function(event){var s=selected();if(event.target.returnValue!=="stop"||!s)return;post("/sessions/"+encodeURIComponent(s.name)+"/delete",{}).then(function(){toast("Stopped "+title(s));selectedName="";return refresh()}).catch(function(error){toast("Could not stop session: "+err(error),true)})});
411
- byId("settings-dialog").addEventListener("close",function(event){if(event.target.returnValue!=="save")return;var payload={projectsFolder:byId("setting-projects").value.trim(),autoAttachLatest:byId("setting-attach").checked,mouseScrolling:byId("setting-mouse").checked,showStoppedSessions:byId("setting-stopped").checked,trayEnabled:byId("setting-tray").checked};post("/settings",payload,"PATCH").then(function(){return loadSettings()}).then(function(){toast("Dashboard settings saved");return refresh()}).catch(function(error){toast("Could not save settings: "+err(error),true)})});
412
- function bootstrap(){var remembered=localStorage.getItem(THEME_KEY);theme(remembered==="light"||remembered==="dark"?remembered:matchMedia("(prefers-color-scheme: light)").matches?"light":"dark",false);var code=new URL(location.href).searchParams.get("oc"),auth=code?fetch("/dash/session",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({code:code})}).then(function(response){return response.json().then(function(body){if(!response.ok)throw new Error(body.error||"Access code rejected");return body})}):Promise.reject(new Error("Open the dashboard with omb dashboard --open to obtain a fresh access code."));auth.then(function(body){token=body.token;history.replaceState({},"",location.pathname);return loadSettings()}).then(loadModels).then(refresh).then(function(){refreshTimer=setInterval(function(){void refresh()},5000)}).catch(function(error){byId("session-list").innerHTML='<div class="notice"><strong>Dashboard access failed</strong>'+esc(err(error))+"</div>";consoleState("Dashboard locked",err(error));var n=byId("daemon-state");n.querySelector(".state-dot").className="state-dot dot-warn";n.querySelector("span:last-child").textContent="Authentication required"})}
589
+ function bootstrap(){var remembered=localStorage.getItem(THEME_KEY)||"system";theme(remembered,false);resetProviderEditor();var code=new URL(location.href).searchParams.get("oc"),auth=code?fetch("/dash/session",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({code:code})}).then(function(response){return response.json().then(function(body){if(!response.ok)throw new Error(body.error||"Access code rejected");return body})}):Promise.reject(new Error("Open the dashboard with omb dashboard --open to obtain a fresh access code."));auth.then(function(body){token=body.token;history.replaceState({},"",location.pathname);return loadSettings()}).then(loadModels).then(refresh).then(overview).then(function(){refreshTimer=setInterval(function(){void refresh();void overview()},5000)}).catch(function(error){byId("session-list").innerHTML='<div class="notice"><strong>Dashboard access failed</strong>'+esc(err(error))+"</div>";messageState("Dashboard locked",err(error));var n=byId("daemon-state");n.querySelector(".state-dot").className="state-dot dot-warn";n.querySelector("span:last-child").textContent="Authentication required"})}
413
590
  window.addEventListener("beforeunload",function(){clearInterval(refreshTimer);clearInterval(consoleTimer)});bootstrap();
414
591
  })();
415
592
  </script>