clay-server 5.3.0-beta.6 → 5.3.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/project-admin-overview.js +22 -11
- package/lib/project-background-tasks.js +76 -0
- package/lib/project-connection.js +1 -1
- package/lib/project-sessions.js +12 -5
- package/lib/project-split-views.js +12 -3
- package/lib/project-view-store.js +7 -5
- package/lib/project-worker-conversation.js +5 -0
- package/lib/public/app.js +7 -0
- package/lib/public/css/contextual-clay.css +2 -1
- package/lib/public/css/mate-conversation.css +1 -1
- package/lib/public/css/pending-message-queue.css +24 -19
- package/lib/public/css/project-admin-overview.css +42 -13
- package/lib/public/css/shared-browser.css +31 -8
- package/lib/public/css/split-view-layout.css +92 -7
- package/lib/public/css/tool-pane.css +4 -7
- package/lib/public/index.html +5 -5
- package/lib/public/modules/agent-message-bubbles.js +96 -0
- package/lib/public/modules/app-header.js +6 -0
- package/lib/public/modules/app-messages.js +26 -9
- package/lib/public/modules/background-task-previews.js +8 -0
- package/lib/public/modules/background-task-viewer.js +97 -0
- package/lib/public/modules/background-tasks-ui.js +34 -125
- package/lib/public/modules/chat-bubble-renderer.js +11 -5
- package/lib/public/modules/contextual-clay-geometry.js +7 -3
- package/lib/public/modules/pane-activation.js +5 -3
- package/lib/public/modules/pane-header-tooltips.js +32 -0
- package/lib/public/modules/pending-message-queue-drag.js +69 -0
- package/lib/public/modules/pending-message-queue.js +3 -33
- package/lib/public/modules/permission-control.js +6 -5
- package/lib/public/modules/project-admin-overview.js +125 -10
- package/lib/public/modules/project-grouping.js +9 -0
- package/lib/public/modules/server-settings-controls.js +33 -4
- package/lib/public/modules/session-folder-toolbar.js +1 -1
- package/lib/public/modules/session-tool-item.js +86 -0
- package/lib/public/modules/settings-select.js +2 -1
- package/lib/public/modules/shared-browser-tabs.js +37 -21
- package/lib/public/modules/sidebar-projects.js +1 -10
- package/lib/public/modules/split-session-empty-pane.js +47 -0
- package/lib/public/modules/split-view-create-menu.js +74 -65
- package/lib/public/modules/split-view-layout-scope.js +4 -0
- package/lib/public/modules/split-view-picker-actions.js +71 -0
- package/lib/public/modules/split-view-picker-render.js +81 -0
- package/lib/public/modules/split-view-presentation-renderer.js +8 -8
- package/lib/public/modules/split-view-worker-activity.js +2 -2
- package/lib/public/modules/split-view-worker-transcript.js +70 -33
- package/lib/public/modules/split-view-workers.js +6 -4
- package/lib/public/modules/terminal-viewport.js +14 -0
- package/lib/public/modules/terminal.js +3 -4
- package/lib/public/modules/theme-palette.js +177 -0
- package/lib/public/modules/theme.js +1 -174
- package/lib/public/modules/tool-pane-chrome.js +7 -1
- package/lib/public/modules/tools.js +5 -89
- package/lib/public/modules/tooltip.js +32 -6
- package/lib/public/modules/wireframe-workbench.js +2 -2
- package/lib/public/modules/worker-message-previews.js +39 -0
- package/lib/public/pane-assets/app-5W6KJXK5.js +1 -0
- package/lib/public/pane-assets/chunk-2IJUTAB6.js +2 -0
- package/lib/public/pane-assets/{chunk-BMHTBRMO.js → chunk-7OVNC5HT.js} +1 -1
- package/lib/public/pane-assets/chunk-BQN7B62H.js +3 -0
- package/lib/public/pane-assets/{chunk-2AIWCI6Y.js → chunk-EMLN2Y7L.js} +1 -1
- package/lib/public/pane-assets/{chunk-VKYBAVWT.js → chunk-ENPAHXLB.js} +1 -1
- package/lib/public/pane-assets/chunk-EPBGGZRT.js +1 -0
- package/lib/public/pane-assets/chunk-EYTE56IA.js +1 -0
- package/lib/public/pane-assets/{chunk-6NYCNWVF.js → chunk-FQTNR4ZS.js} +1 -1
- package/lib/public/pane-assets/chunk-L6LSA5WI.js +1 -0
- package/lib/public/pane-assets/chunk-M57UIGZV.js +1 -0
- package/lib/public/pane-assets/chunk-MGTFTEJP.js +1 -0
- package/lib/public/pane-assets/chunk-MMG6MYCN.js +1 -0
- package/lib/public/pane-assets/chunk-MZLMEXMW.js +1 -0
- package/lib/public/pane-assets/chunk-OG3E5SVA.js +166 -0
- package/lib/public/pane-assets/chunk-RGAFZ3T2.js +1 -0
- package/lib/public/pane-assets/chunk-RNDRURMF.js +1 -0
- package/lib/public/pane-assets/{chunk-PV4PCH2V.js → chunk-SJOT74VW.js} +1 -1
- package/lib/public/pane-assets/{chunk-LDIZLKB2.js → chunk-SJTGHIN5.js} +1 -1
- package/lib/public/pane-assets/chunk-SLMAFKI2.js +1 -0
- package/lib/public/pane-assets/chunk-U3I5KY3B.js +1 -0
- package/lib/public/pane-assets/{chunk-VNAV23CM.js → chunk-UPXU2IPC.js} +1 -1
- package/lib/public/pane-assets/{chunk-TMGSDFJI.js → chunk-VEBYD7TL.js} +1 -1
- package/lib/public/pane-assets/chunk-XBHYNXXT.js +1 -0
- package/lib/public/pane-assets/manifest.json +426 -414
- package/lib/public/pane-assets/style-BSKKK54Q.css +1 -0
- package/lib/sdk-message-processor.js +5 -2
- package/lib/server-storage.js +27 -3
- package/lib/server.js +4 -2
- package/lib/sessions.js +1 -1
- package/lib/storage-usage.js +29 -7
- package/lib/terminal-manager.js +2 -2
- package/lib/worker-message-previews.js +50 -0
- package/lib/ws-schema.js +1 -1
- package/lib/yoke/adapters/claude-worker.js +8 -0
- package/lib/yoke/adapters/claude.js +17 -2
- package/lib/yoke/claude-task-reader.js +94 -0
- package/lib/yoke/interface.js +1 -0
- package/package.json +1 -1
- package/lib/public/pane-assets/app-7UZ727NA.js +0 -1
- package/lib/public/pane-assets/chunk-6MIHIQ2W.js +0 -1
- package/lib/public/pane-assets/chunk-6UJ2ALOB.js +0 -1
- package/lib/public/pane-assets/chunk-6XK2OATZ.js +0 -1
- package/lib/public/pane-assets/chunk-BKIDR2ME.js +0 -1
- package/lib/public/pane-assets/chunk-GEFCFZEN.js +0 -1
- package/lib/public/pane-assets/chunk-GN2NQEVC.js +0 -2
- package/lib/public/pane-assets/chunk-IMLMBPT4.js +0 -166
- package/lib/public/pane-assets/chunk-K5S6AILM.js +0 -1
- package/lib/public/pane-assets/chunk-OWNNLZ36.js +0 -1
- package/lib/public/pane-assets/chunk-P2C3LOWL.js +0 -3
- package/lib/public/pane-assets/chunk-R4LMPTKU.js +0 -1
- package/lib/public/pane-assets/chunk-VWTHIQCE.js +0 -1
- package/lib/public/pane-assets/chunk-WM3K5AFH.js +0 -1
- package/lib/public/pane-assets/chunk-XOB3O327.js +0 -1
- package/lib/public/pane-assets/chunk-XOOUCFYT.js +0 -1
- package/lib/public/pane-assets/style-ZM7OOU3M.css +0 -1
|
@@ -405,13 +405,23 @@ body.split-view-resizing .split-pane-frame { pointer-events: none; }
|
|
|
405
405
|
.split-view-worker-popover-header strong { flex: 1; min-width: 0; font-size: 12px; }
|
|
406
406
|
.split-view-worker-popover-source { padding: 0 13px 10px; color: var(--text-dimmer); font-size: 10px; }
|
|
407
407
|
.split-view-worker-popover-body { flex: 1; min-height: 0; overflow: auto; padding: 14px; }
|
|
408
|
-
|
|
409
|
-
.
|
|
410
|
-
.
|
|
411
|
-
.
|
|
412
|
-
.
|
|
413
|
-
.
|
|
414
|
-
.
|
|
408
|
+
/* Use session anatomy unchanged; only transcript typography is reduced. */
|
|
409
|
+
.readonly-session-transcript { --content-width: 100%; font-size: 12px; }
|
|
410
|
+
.readonly-session-transcript .md-content,
|
|
411
|
+
.readonly-session-transcript .msg-user .bubble,
|
|
412
|
+
body.wide-view .readonly-session-transcript .msg-user .bubble,
|
|
413
|
+
.readonly-session-transcript .dm-bubble-name,
|
|
414
|
+
.readonly-session-transcript .tool-name,
|
|
415
|
+
.readonly-session-transcript .tool-desc,
|
|
416
|
+
.readonly-session-transcript .tool-group-label { font-size: 12px; }
|
|
417
|
+
.readonly-session-transcript .dm-bubble-time,
|
|
418
|
+
.readonly-session-transcript .tool-subtitle,
|
|
419
|
+
.readonly-session-transcript .tool-subtitle-text,
|
|
420
|
+
.readonly-session-transcript .clay-tool-evidence-label { font-size: 9.6px; }
|
|
421
|
+
.readonly-session-transcript .tool-result-block,
|
|
422
|
+
.readonly-session-transcript .tool-result-block pre { font-size: 10.4px; }
|
|
423
|
+
.readonly-session-transcript .clay-tool-technical-name,
|
|
424
|
+
.readonly-session-transcript .msg-copy-hint { font-size: 8.8px; }
|
|
415
425
|
.split-view-worker-popover-footer { padding: 8px 13px; border-top: 1px solid var(--border-subtle); color: var(--text-dimmer); font-size: 10px; }
|
|
416
426
|
.split-view-worker-page-status { display: block; margin-top: 3px; line-height: 1.4; }
|
|
417
427
|
.split-view-worker-popover-footer button { margin-left: 8px; padding: 4px 6px; font-size: 10px; }
|
|
@@ -496,3 +506,78 @@ body.is-dragging .split-pane-header, body.is-dragging .split-pane-drag-space { c
|
|
|
496
506
|
box-shadow: inset 0 0 0 1px var(--accent-20);
|
|
497
507
|
pointer-events: none;
|
|
498
508
|
}
|
|
509
|
+
|
|
510
|
+
/* Native tasks use the same compact conversation affordance as Split Workers. */
|
|
511
|
+
.background-task-tabs { border-bottom: 1px solid var(--border-subtle); }
|
|
512
|
+
.background-task-tabs .split-view-worker-name { max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
513
|
+
.background-task-viewer { top: 42px; right: 12px; width: min(640px, calc(100vw - 24px)); max-height: calc(100dvh - 58px); }
|
|
514
|
+
.background-task-viewer .split-view-worker-popover-body > pre { white-space: pre-wrap; overflow-wrap: anywhere; font-size: 12px; }
|
|
515
|
+
.background-task-viewer [hidden] { display: none; }
|
|
516
|
+
.background-task-ambient { opacity: .65; }
|
|
517
|
+
|
|
518
|
+
.agent-message-bubble {
|
|
519
|
+
position: fixed; z-index: 60; box-sizing: border-box; padding: 10px 12px;
|
|
520
|
+
border: 1px solid var(--border); border-radius: 9px; background: var(--bg);
|
|
521
|
+
color: var(--text); box-shadow: 0 5px 16px rgba(0, 0, 0, .16);
|
|
522
|
+
font-size: 12px; line-height: 1.55; white-space: pre-wrap; overflow-wrap: anywhere;
|
|
523
|
+
pointer-events: none; max-height: 180px;
|
|
524
|
+
}
|
|
525
|
+
.agent-message-bubble::before {
|
|
526
|
+
content: ''; position: absolute; top: -5px; left: var(--bubble-tip-x, 20px); width: 8px; height: 8px;
|
|
527
|
+
background: var(--bg); border-left: 1px solid var(--border); border-top: 1px solid var(--border); transform: rotate(45deg);
|
|
528
|
+
}
|
|
529
|
+
.agent-message-bubble[data-live="true"] { animation: agent-message-fade 7s linear forwards; }
|
|
530
|
+
.agent-message-bubble[data-held="true"] { opacity: 1 !important; }
|
|
531
|
+
@keyframes agent-message-fade { from { opacity: 1; } to { opacity: 0; } }
|
|
532
|
+
@media (prefers-reduced-motion: reduce) { .agent-message-bubble[data-live="true"] { animation: none; } }
|
|
533
|
+
|
|
534
|
+
.agent-message-bubble-text { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
|
|
535
|
+
|
|
536
|
+
/* Shared compact, anchored session insertion picker. */
|
|
537
|
+
.split-session-picker { padding: 5px; color: var(--text); }
|
|
538
|
+
.split-session-picker[data-stage="direction"] { width: min(212px, calc(100vw - 16px)); }
|
|
539
|
+
.split-session-picker[data-stage="sessions"] { width: min(360px, calc(100vw - 16px)); }
|
|
540
|
+
.split-session-directions { display: flex; flex-direction: column; gap: 2px; }
|
|
541
|
+
.split-session-directions button { display: flex; align-items: center; gap: 8px; height: 29px; padding: 4px 8px; text-align: left; font-size: 12px; }
|
|
542
|
+
.split-session-picker svg { width: 15px; height: 15px; flex-shrink: 0; }
|
|
543
|
+
.split-session-search { display: flex; align-items: center; gap: 7px; margin: 0 4px 5px; padding: 5px 7px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg); }
|
|
544
|
+
.split-session-search:focus-within { outline: 2px solid var(--accent); outline-offset: -1px; }
|
|
545
|
+
.split-session-search input { width: 100%; min-width: 0; border: 0; padding: 0; background: transparent; color: var(--text); font: inherit; font-size: 13px; outline: 0; }
|
|
546
|
+
.split-session-section { padding: 7px 8px 4px; font-size: 10px; color: var(--text-muted); }
|
|
547
|
+
.split-session-results { max-height: min(260px, 35dvh); overflow-y: auto; }
|
|
548
|
+
.split-session-picker .split-session-result { display: flex; align-items: center; width: 100%; min-height: 34px; gap: 8px; padding: 6px 8px; font-size: 12px; text-align: left; }
|
|
549
|
+
.split-session-result img { width: 16px; height: 16px; border-radius: 4px; flex-shrink: 0; }
|
|
550
|
+
.split-session-result-title { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
551
|
+
.split-session-result small { font-size: 10px; color: var(--text-muted); flex-shrink: 0; }
|
|
552
|
+
.split-session-move { display: block; overflow: hidden; text-overflow: ellipsis; }
|
|
553
|
+
.split-session-new { margin-top: 5px; border-top: 1px solid var(--border-subtle); }
|
|
554
|
+
.split-session-vendors { display: flex; flex-wrap: wrap; gap: 5px; padding: 4px 7px 7px; }
|
|
555
|
+
.split-session-vendors button { display: flex; align-items: center; justify-content: center; height: 30px; min-width: 30px; padding: 5px; border: 1px solid var(--border); border-radius: 6px; }
|
|
556
|
+
.split-session-vendors img { width: 20px; height: 20px; }
|
|
557
|
+
.split-session-empty { padding: 8px; font-size: 11px; color: var(--text-muted); }
|
|
558
|
+
.split-session-status { margin: 0; padding: 0 7px; font-size: 11px; color: var(--text-secondary); }
|
|
559
|
+
.split-session-hint { display: block; border-top: 1px solid var(--border-subtle); padding: 6px 7px 2px; font-size: 10px; color: var(--text-muted); }
|
|
560
|
+
|
|
561
|
+
.agent-message-bubble-text { white-space: normal; max-height: 55.8px; }
|
|
562
|
+
.agent-message-bubble-text p { margin: 0 0 4px; }
|
|
563
|
+
.agent-message-bubble-text > :last-child { margin-bottom: 0; }
|
|
564
|
+
.agent-message-bubble-text :is(ul, ol) { margin: 3px 0; padding-left: 18px; }
|
|
565
|
+
.agent-message-bubble-text code { font-size: 11px; background: var(--bg-alt); border-radius: 3px; padding: 1px 3px; }
|
|
566
|
+
.agent-message-bubble-text pre { margin: 4px 0; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
567
|
+
.agent-message-bubble-text pre code { padding: 0; }
|
|
568
|
+
.agent-message-bubble-text blockquote { margin: 4px 0; padding-left: 8px; border-left: 2px solid var(--border); }
|
|
569
|
+
|
|
570
|
+
/* A split reserves its destination before a session is selected. */
|
|
571
|
+
.split-session-empty-pane { position: absolute; display: flex; flex-direction: column; min-width: 0; min-height: 0; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; background: var(--bg); }
|
|
572
|
+
.split-session-empty-body { flex: 1; min-width: 0; min-height: 0; display: flex; align-items: safe center; justify-content: center; padding: 16px; overflow: auto; }
|
|
573
|
+
.split-session-empty-body > .split-session-picker { position: relative; flex: 0 1 360px; width: 360px; min-width: 0; max-width: 100%; max-height: 100%; margin: auto 0; display: flex; flex-direction: column; }
|
|
574
|
+
.split-session-empty-body .split-session-results { flex: 1 1 auto; min-height: 36px; max-height: 220px; }
|
|
575
|
+
.split-session-empty-body .split-view-arrange-header, .split-session-empty-body .split-session-search, .split-session-empty-body .split-session-section, .split-session-empty-body .split-session-vendors, .split-session-empty-body .split-session-status, .split-session-empty-body .split-session-hint { flex-shrink: 0; }
|
|
576
|
+
.split-session-reserved-divider { position: absolute; pointer-events: none; }
|
|
577
|
+
@media (max-width: 700px) { .split-session-empty-body { padding: 8px; } }
|
|
578
|
+
.split-session-empty-body > .split-session-picker[hidden] { display: none; }
|
|
579
|
+
|
|
580
|
+
.agent-message-bubble-meta { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; color: var(--text-muted); font-size: 10px; line-height: 16px; white-space: nowrap; }
|
|
581
|
+
.agent-message-bubble-avatar { width: 16px; height: 16px; border-radius: 4px; object-fit: contain; flex: none; }
|
|
582
|
+
.agent-message-bubble-vendor { overflow: hidden; text-overflow: ellipsis; }
|
|
583
|
+
.agent-message-bubble-time { margin-left: auto; flex: none; font-variant-numeric: tabular-nums; }
|
|
@@ -9,21 +9,18 @@
|
|
|
9
9
|
.tool-pane-header .workbench-layout-toggle { width: 24px !important; height: 24px !important; flex-basis: 24px !important; }
|
|
10
10
|
.tool-pane-header .workbench-layout-toggle svg { width: 13px !important; height: 13px !important; }
|
|
11
11
|
/* Tabs use the compact inset pills of the Split Worker row. */
|
|
12
|
-
.tool-pane-tab-row
|
|
12
|
+
.tool-pane-tab-row {
|
|
13
13
|
display: flex; align-items: center; gap: 5px; padding: 4px 11px; min-height: 30px; height: auto;
|
|
14
14
|
flex-shrink: 0; overflow-x: auto; background: var(--bg); border-bottom: 0; scrollbar-width: thin;
|
|
15
15
|
}
|
|
16
16
|
.tool-pane-tab-row .terminal-tabs, .tool-pane-tab-row .file-viewer-tabs { display: flex; gap: 5px; min-width: 0; overflow: auto; border: 0; }
|
|
17
|
-
.tool-pane-tab-row :is(.terminal-tab, .file-viewer-tab)
|
|
17
|
+
.tool-pane-tab-row :is(.terminal-tab, .file-viewer-tab) {
|
|
18
18
|
display: inline-flex; align-items: center; flex: 0 0 auto; gap: 5px; min-width: 0; max-width: 284px;
|
|
19
19
|
height: 22px; min-height: 22px; padding: 0 6px; border: 1px solid var(--border-subtle); border-radius: 5px;
|
|
20
20
|
background: var(--bg-alt); color: var(--text-muted); font-size: 11px; line-height: 20px;
|
|
21
21
|
}
|
|
22
|
-
.tool-pane-tab-row :is(.terminal-tab.active, .file-viewer-tab.active)
|
|
23
|
-
.tool-pane-
|
|
24
|
-
.tool-pane-header ~ .shared-browser-tabs .shared-browser-tab > [role="tab"] { min-width: 0; padding: 0 6px; height: 20px; border: 0; background: transparent; font-size: 11px; }
|
|
25
|
-
.tool-pane-header ~ .shared-browser-tabs .shared-browser-tab small { display: none; }
|
|
26
|
-
.tool-pane-tab-row :is(.terminal-tab-more, .file-viewer-tab-close), .tool-pane-header ~ .shared-browser-tabs .shared-browser-tab > button:not([role="tab"]) { width: 20px; height: 20px; padding: 0; border: 0; background: transparent; color: var(--text-dimmer); }
|
|
22
|
+
.tool-pane-tab-row :is(.terminal-tab.active, .file-viewer-tab.active) { border-color: var(--accent-20); background: var(--accent-bg); color: var(--text-secondary); }
|
|
23
|
+
.tool-pane-tab-row :is(.terminal-tab-more, .file-viewer-tab-close) { width: 20px; height: 20px; padding: 0; border: 0; background: transparent; color: var(--text-dimmer); }
|
|
27
24
|
.tool-pane-tab-row .terminal-tab-more svg { width: 11px; height: 11px; }
|
|
28
25
|
.tool-pane-tab-row .terminal-new-tab-btn { height: 22px; width: 22px; padding: 3px; flex-shrink: 0; }
|
|
29
26
|
@media (min-width: 1024px) {
|
package/lib/public/index.html
CHANGED
|
@@ -75,8 +75,8 @@ body { background: transparent; color: inherit; }
|
|
|
75
75
|
<button id="cmd-palette-btn" class="cmd-palette-searchbar" data-tip="Search conversations across all vendors. Ask Clay to find past work, reviews, decisions, and unfinished sessions." data-tip-suppress-when-open=".cmd-palette" aria-label="Search or ask Clay"><i data-lucide="search" aria-hidden="true"></i><span class="cmd-palette-searchbar-text">Search or ask Clay, like “What was I working on recently?”</span><kbd class="cmd-palette-searchbar-kbd"></kbd></button>
|
|
76
76
|
<div class="top-bar-actions">
|
|
77
77
|
<!-- Pill badges -->
|
|
78
|
-
<div id="skip-perms-pill" class="top-bar-pill pill-error hidden"><i data-lucide="shield-off"></i> <span>Skip perms</span></div>
|
|
79
|
-
<div id="client-count" class="top-bar-pill pill-accent hidden"><i data-lucide="users"></i> <span id="client-count-text"></span></div>
|
|
78
|
+
<div id="skip-perms-pill" class="top-bar-pill pill-error hidden" data-tip="Permission prompts are skipped"><i data-lucide="shield-off"></i> <span>Skip perms</span></div>
|
|
79
|
+
<div id="client-count" class="top-bar-pill pill-accent hidden" data-tip="Browsers connected to this project"><i data-lucide="users"></i> <span id="client-count-text"></span></div>
|
|
80
80
|
<!-- Right workbench controls: same nodes and handlers as before; app JS returns them to the title bar on mobile. -->
|
|
81
81
|
<button type="button" id="shared-browser-toggle-btn" class="hidden" title="Browser" aria-label="Browser" aria-controls="shared-browser-panel" aria-expanded="false" disabled><i data-lucide="globe"></i><span class="shared-browser-count hidden" aria-hidden="true"></span></button>
|
|
82
82
|
<button id="terminal-toggle-btn" title="Terminal"><i data-lucide="square-terminal"></i><span id="terminal-count" class="hidden"></span></button>
|
|
@@ -307,9 +307,9 @@ body { background: transparent; color: inherit; }
|
|
|
307
307
|
<button id="header-info-btn" type="button" title="Session info"><i data-lucide="info"></i></button>
|
|
308
308
|
<div id="header-full-access-btn" class="session-permission-control hidden" role="group" aria-label="Session permission mode">
|
|
309
309
|
<div class="session-permission-segmented">
|
|
310
|
-
<button type="button" data-permission-mode="default" aria-label="Ask permissions"
|
|
311
|
-
<button type="button" data-permission-mode="auto" aria-label="Auto permissions"
|
|
312
|
-
<button type="button" data-permission-mode="bypassPermissions" aria-label="YOLO permissions"
|
|
310
|
+
<button type="button" data-permission-mode="default" aria-label="Ask permissions" data-tip="Ask before tool actions that need permission. Previously allowed actions can run without asking again."><span class="permission-label">Ask</span><span class="permission-spinner" aria-hidden="true"></span></button>
|
|
311
|
+
<button type="button" data-permission-mode="auto" aria-label="Auto permissions" data-tip="Let the runtime assess tool actions and approve them automatically when allowed. Some actions may still need your permission."><span class="permission-label">Auto</span><span class="permission-spinner" aria-hidden="true"></span></button>
|
|
312
|
+
<button type="button" data-permission-mode="bypassPermissions" aria-label="YOLO permissions" data-tip="Run tools, file changes and commands without asking for permission. Clay access restrictions still apply."><span class="permission-label">YOLO</span><span class="permission-spinner" aria-hidden="true"></span></button>
|
|
313
313
|
</div>
|
|
314
314
|
<span class="session-permission-status hidden" aria-live="polite"></span>
|
|
315
315
|
</div>
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { dismissTooltip } from './tooltip.js';
|
|
2
|
+
import { renderMarkdown } from './markdown.js';
|
|
3
|
+
import { store } from './store.js';
|
|
4
|
+
|
|
5
|
+
var timer = null;
|
|
6
|
+
function context() { return JSON.stringify([store.get('myUserId') || null, store.get('currentSlug'), store.get('projectSplitViewSocketGeneration') || 0]); }
|
|
7
|
+
export function messagePreviewKey(kind, identities) { return JSON.stringify([context(), kind].concat(identities)); }
|
|
8
|
+
function element() { return document.getElementById('agent-message-bubble'); }
|
|
9
|
+
function anchor(key) { return Array.from(document.querySelectorAll('[data-message-preview]')).find(function (node) { return node.dataset.messagePreview === key && node.isConnected && node.getClientRects().length && !node.closest('[hidden], .split-pane-cached'); }); }
|
|
10
|
+
function remove() {
|
|
11
|
+
if (timer) clearTimeout(timer); timer = null;
|
|
12
|
+
var node = element(); if (node) node.remove();
|
|
13
|
+
document.querySelectorAll('[aria-describedby="agent-message-bubble"]').forEach(function (chip) { chip.removeAttribute('aria-describedby'); });
|
|
14
|
+
if (store.get('agentMessageBubble')) store.set({ agentMessageBubble: null });
|
|
15
|
+
}
|
|
16
|
+
function position() {
|
|
17
|
+
var state = store.get('agentMessageBubble'), node = element(), chip = state && anchor(state.key);
|
|
18
|
+
if (!chip || !node) { remove(); return; }
|
|
19
|
+
var rect = chip.getBoundingClientRect(), width = Math.min(360, innerWidth - 20);
|
|
20
|
+
var left = Math.max(10, Math.min(rect.left, innerWidth - width - 10));
|
|
21
|
+
node.style.width = width + 'px'; node.style.left = left + 'px'; node.style.top = Math.min(rect.bottom + 8, innerHeight - 70) + 'px';
|
|
22
|
+
node.style.setProperty('--bubble-tip-x', Math.max(16, Math.min(width - 16, rect.left + Math.min(rect.width / 2, 40) - left)) + 'px');
|
|
23
|
+
}
|
|
24
|
+
function renderMetadata(node, latest) {
|
|
25
|
+
var meta = node.querySelector('.agent-message-bubble-meta');
|
|
26
|
+
if (!meta) { meta = document.createElement('div'); meta.className = 'agent-message-bubble-meta'; node.prepend(meta); }
|
|
27
|
+
meta.textContent = '';
|
|
28
|
+
var vendor = (store.get('vendorInfo') || {})[latest.vendor] || {};
|
|
29
|
+
if (vendor.avatar) {
|
|
30
|
+
var avatar = document.createElement('img'); avatar.className = 'agent-message-bubble-avatar';
|
|
31
|
+
avatar.src = vendor.avatar; avatar.alt = ''; meta.appendChild(avatar);
|
|
32
|
+
}
|
|
33
|
+
var name = document.createElement('span'); name.className = 'agent-message-bubble-vendor';
|
|
34
|
+
name.textContent = vendor.displayName || 'Assistant'; meta.appendChild(name);
|
|
35
|
+
var time = document.createElement('time'); time.className = 'agent-message-bubble-time';
|
|
36
|
+
if (latest.timestamp && Number.isFinite(new Date(latest.timestamp).getTime())) {
|
|
37
|
+
var date = new Date(latest.timestamp); time.dateTime = date.toISOString();
|
|
38
|
+
time.textContent = date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
39
|
+
time.setAttribute('aria-label', 'Message time: ' + date.toLocaleString());
|
|
40
|
+
} else time.textContent = 'Time unavailable';
|
|
41
|
+
meta.appendChild(time);
|
|
42
|
+
}
|
|
43
|
+
function show(key, held, fresh) {
|
|
44
|
+
var latest = (store.get('agentMessagePreviews') || {})[key], chip = anchor(key);
|
|
45
|
+
if (!latest || !chip || store.get('splitViewWorkerConversation') || store.get('backgroundTaskViewer') || store.get('paneSuspended') || document.hidden || !store.get('connected')) return;
|
|
46
|
+
var old = store.get('agentMessageBubble'), expires = fresh ? Date.now() + 7000 : old && old.key === key ? old.expiresAt : 0;
|
|
47
|
+
var node = element();
|
|
48
|
+
if (!node || !old || old.key !== key || fresh) {
|
|
49
|
+
if (node) node.remove();
|
|
50
|
+
document.querySelectorAll('[aria-describedby="agent-message-bubble"]').forEach(function (other) { other.removeAttribute('aria-describedby'); });
|
|
51
|
+
node = document.createElement('div'); node.id = 'agent-message-bubble'; node.className = 'agent-message-bubble';
|
|
52
|
+
node.setAttribute('role', 'tooltip'); var text = document.createElement('div'); text.className = 'agent-message-bubble-text'; node.appendChild(text); document.body.appendChild(node);
|
|
53
|
+
}
|
|
54
|
+
renderMetadata(node, latest);
|
|
55
|
+
node.querySelector('.agent-message-bubble-text').innerHTML = DOMPurify.sanitize(renderMarkdown(latest.text), { ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'code', 'pre', 'ul', 'ol', 'li', 'blockquote', 'del', 's'], ALLOWED_ATTR: [], ALLOW_DATA_ATTR: false }); node.dataset.held = String(!!held); node.dataset.live = String(expires > Date.now());
|
|
56
|
+
dismissTooltip();
|
|
57
|
+
chip.setAttribute('aria-describedby', node.id);
|
|
58
|
+
store.set({ agentMessageBubble: { key: key, expiresAt: expires, held: !!held } }); position();
|
|
59
|
+
if (timer) clearTimeout(timer);
|
|
60
|
+
if (expires > Date.now()) timer = setTimeout(function () {
|
|
61
|
+
timer = null; var state = store.get('agentMessageBubble'); if (state && state.key === key && !state.held) remove();
|
|
62
|
+
}, Math.max(0, expires - Date.now()));
|
|
63
|
+
}
|
|
64
|
+
export function rememberAgentMessage(key, preview, live) {
|
|
65
|
+
if (!preview || typeof preview.id !== 'string' || typeof preview.text !== 'string' || !preview.text.trim()) return;
|
|
66
|
+
var all = Object.assign({}, store.get('agentMessagePreviews') || {}), previous = all[key];
|
|
67
|
+
var fresh = !previous || previous.id !== preview.id;
|
|
68
|
+
all[key] = { id: preview.id, text: preview.text.slice(0, 1200), vendor: preview.vendor || previous && previous.vendor || null, timestamp: typeof preview.timestamp === 'number' && Number.isFinite(preview.timestamp) && preview.timestamp > 0 ? preview.timestamp : previous && previous.id === preview.id ? previous.timestamp : null };
|
|
69
|
+
var keys = Object.keys(all); while (keys.length > 128) delete all[keys.shift()];
|
|
70
|
+
store.set({ agentMessagePreviews: all });
|
|
71
|
+
var state = store.get('agentMessageBubble'), chip = anchor(key);
|
|
72
|
+
var held = !!(chip && (chip.matches(':hover') || chip.contains(document.activeElement)));
|
|
73
|
+
if (live && fresh || held || state && state.key === key) show(key, held || !!(state && state.key === key && state.held), !!live && fresh);
|
|
74
|
+
}
|
|
75
|
+
export function bindAgentMessageBubble(chip, key) {
|
|
76
|
+
chip.dataset.messagePreview = key;
|
|
77
|
+
if (chip.dataset.previewBound) return;
|
|
78
|
+
chip.dataset.previewBound = 'true';
|
|
79
|
+
function enter() { show(chip.dataset.messagePreview, true, false); }
|
|
80
|
+
function leave() {
|
|
81
|
+
var state = store.get('agentMessageBubble'); if (!state || state.key !== chip.dataset.messagePreview || chip.matches(':hover') || chip.contains(document.activeElement)) return;
|
|
82
|
+
if (state.expiresAt <= Date.now()) remove(); else show(state.key, false, false);
|
|
83
|
+
}
|
|
84
|
+
chip.addEventListener('pointerenter', enter); chip.addEventListener('pointerleave', leave);
|
|
85
|
+
chip.addEventListener('focusin', enter); chip.addEventListener('focusout', leave);
|
|
86
|
+
var state = store.get('agentMessageBubble'); if (state && state.key === key) requestAnimationFrame(position);
|
|
87
|
+
}
|
|
88
|
+
store.subscribe(function (state, previous) {
|
|
89
|
+
if (state.splitViewWorkerConversation && !previous.splitViewWorkerConversation || state.backgroundTaskViewer && !previous.backgroundTaskViewer) remove();
|
|
90
|
+
if (state.sessionOriginId !== previous.sessionOriginId || state.currentSlug !== previous.currentSlug || state.myUserId !== previous.myUserId || state.projectSplitViewSocketGeneration !== previous.projectSplitViewSocketGeneration || state.connected === false && previous.connected !== false || state.projectSplitViewActiveId !== previous.projectSplitViewActiveId || state.paneSuspended === true && previous.paneSuspended !== true) {
|
|
91
|
+
remove(); if (Object.keys(state.agentMessagePreviews || {}).length) store.set({ agentMessagePreviews: {} });
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
document.addEventListener('keydown', function (event) { if (event.key === 'Escape') remove(); });
|
|
95
|
+
document.addEventListener('visibilitychange', function () { if (document.hidden) remove(); });
|
|
96
|
+
window.addEventListener('resize', position); window.addEventListener('scroll', function (event) { if (!element() || event.target === element()) return; position(); }, true);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { highlightCodeBlocks, renderMermaidBlocks } from './markdown.js';
|
|
1
2
|
import { beginProjectDebatePrepend, endProjectDebatePrepend } from './project-debate-workspace.js';
|
|
2
3
|
// app-header.js - Session rename, session info popover, progressive history loading
|
|
3
4
|
// Extracted from app.js (PR-34)
|
|
@@ -422,6 +423,11 @@ export function prependOlderHistory(items, meta) {
|
|
|
422
423
|
updateContextPanel();
|
|
423
424
|
updateUsagePanel();
|
|
424
425
|
|
|
426
|
+
// History prepend deliberately skipped per-message enhancements during replay.
|
|
427
|
+
// Run the same completed-replay pass after restoring the live render state.
|
|
428
|
+
highlightCodeBlocks(messagesEl);
|
|
429
|
+
renderMermaidBlocks(messagesEl);
|
|
430
|
+
|
|
425
431
|
// Fix scroll: restore anchor element to same visual position
|
|
426
432
|
if (anchorEl) {
|
|
427
433
|
var newTop = anchorEl.getBoundingClientRect().top;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { handleSplitSessionCreated, finishPickerCreation } from './split-view-picker-actions.js';
|
|
2
|
+
import { handleWorkerMessagePreview } from './worker-message-previews.js';
|
|
3
|
+
import { handleBackgroundTaskPreview } from './background-task-previews.js';
|
|
4
|
+
import { handleBackgroundTaskRead } from './background-task-viewer.js';
|
|
1
5
|
import { showCreatedWorkerPresentation } from './worker-created-presentation.js';
|
|
2
6
|
import { handleWorkerRemovalResult } from './split-view-worker-removal.js';
|
|
3
7
|
import { handleStudioDocumentMessage } from './clay-studio-document.js';
|
|
@@ -152,6 +156,7 @@ export function processMessage(msg) {
|
|
|
152
156
|
if (handleScheduledTaskMessage(msg)) return;
|
|
153
157
|
if (handleKnowledgeWorkbenchMessage(msg)) return;
|
|
154
158
|
if (handleSessionCreateMessage(msg)) return;
|
|
159
|
+
if (handleSplitSessionCreated(msg)) return;
|
|
155
160
|
if (msg && msg.type === "new_session_result" && handleNewSessionResult(msg)) return;
|
|
156
161
|
if (handleDefaultVendorMessage(msg)) return;
|
|
157
162
|
if (handleContextViewMessage(msg)) return;
|
|
@@ -281,14 +286,6 @@ export function processMessage(msg) {
|
|
|
281
286
|
if (messagesEl && msg.sessionId === store.get("activeSessionId") && msg.sessionOriginId === store.get("sessionOriginId")) { messagesEl.dataset.controlSessionId = String(msg.sessionId); messagesEl.dataset.controlSessionOrigin = msg.sessionOriginId; messagesEl.dataset.controlHistoryRevision = msg.controlHistoryRevision || ""; }
|
|
282
287
|
store.set({ replayingHistory: false });
|
|
283
288
|
resumeThinkingAfterReplay(store.get('sessionIsProcessing'));
|
|
284
|
-
// Batched syntax highlight + mermaid pass for the entire replayed
|
|
285
|
-
// transcript. Per-message highlights are skipped during replay
|
|
286
|
-
// (see markdown.js) to avoid cascading reflows that the sticky-
|
|
287
|
-
// bottom observer chases for several seconds on long sessions.
|
|
288
|
-
if (messagesEl) {
|
|
289
|
-
highlightCodeBlocks(messagesEl);
|
|
290
|
-
renderMermaidBlocks(messagesEl);
|
|
291
|
-
}
|
|
292
289
|
// Compact dead-session todo widgets (unfinished items will never
|
|
293
290
|
// resolve — the agent isn't coming back) so they don't anchor
|
|
294
291
|
// visual position mid-page on resume.
|
|
@@ -314,6 +311,15 @@ export function processMessage(msg) {
|
|
|
314
311
|
markAllToolsDone();
|
|
315
312
|
finalizeAssistantBlock();
|
|
316
313
|
}
|
|
314
|
+
// Enhance only after the final replay flush has finished writing Markdown.
|
|
315
|
+
// Batched syntax highlight + mermaid pass for the entire replayed
|
|
316
|
+
// transcript. Per-message highlights are skipped during replay
|
|
317
|
+
// (see markdown.js) to avoid cascading reflows that the sticky-
|
|
318
|
+
// bottom observer chases for several seconds on long sessions.
|
|
319
|
+
if (messagesEl) {
|
|
320
|
+
highlightCodeBlocks(messagesEl);
|
|
321
|
+
renderMermaidBlocks(messagesEl);
|
|
322
|
+
}
|
|
317
323
|
stopUrgentBlink();
|
|
318
324
|
// Clean up debate UI if debate is not active after replay
|
|
319
325
|
if (!isDebateActive()) {
|
|
@@ -790,6 +796,7 @@ export function processMessage(msg) {
|
|
|
790
796
|
renderMateSessionList(msg.sessions || []);
|
|
791
797
|
renderSessionList(msg.sessions || []);
|
|
792
798
|
store.set({ paneBindingSessions: msg.sessions || [] });
|
|
799
|
+
finishPickerCreation();
|
|
793
800
|
requestProjectSplitViews();
|
|
794
801
|
refreshPaneActivation();
|
|
795
802
|
syncGithubSessions(msg.sessions || []);
|
|
@@ -1467,8 +1474,18 @@ export function processMessage(msg) {
|
|
|
1467
1474
|
updateSubagentTaskStatus(msg.parentToolId, msg.patch);
|
|
1468
1475
|
break;
|
|
1469
1476
|
|
|
1477
|
+
case "split_worker_message_preview":
|
|
1478
|
+
handleWorkerMessagePreview(msg);
|
|
1479
|
+
break;
|
|
1480
|
+
case "background_task_preview":
|
|
1481
|
+
handleBackgroundTaskPreview(msg);
|
|
1482
|
+
break;
|
|
1483
|
+
case "background_task_read":
|
|
1484
|
+
handleBackgroundTaskRead(msg);
|
|
1485
|
+
break;
|
|
1486
|
+
|
|
1470
1487
|
case "active_background_tasks":
|
|
1471
|
-
store.set({ activeBackgroundTasks: Array.isArray(msg.tasks) ? msg.tasks : [] });
|
|
1488
|
+
store.set({ activeBackgroundTasks: Array.isArray(msg.tasks) ? msg.tasks : [], backgroundTaskCatalog: Array.isArray(msg.catalog) ? msg.catalog : msg.tasks || [] });
|
|
1472
1489
|
break;
|
|
1473
1490
|
|
|
1474
1491
|
case "result":
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { messagePreviewKey, rememberAgentMessage } from './agent-message-bubbles.js';
|
|
3
|
+
export function backgroundPreviewKey(task) { return messagePreviewKey('background', [store.get('sessionOriginId'), task.task_id, task.run_id || null]); }
|
|
4
|
+
export function handleBackgroundTaskPreview(message) {
|
|
5
|
+
if (!store.get('connected') || message.sessionOriginId !== store.get('sessionOriginId')) return;
|
|
6
|
+
var task = (store.get('backgroundTaskCatalog') || []).find(function (task) { return task.task_id === message.taskId && (task.run_id || null) === (message.runId || null); });
|
|
7
|
+
if (task) rememberAgentMessage(backgroundPreviewKey(task), message.preview, message.live === true);
|
|
8
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { getWs } from './ws-ref.js';
|
|
3
|
+
import { appendWorkerTranscript } from './split-view-worker-transcript.js';
|
|
4
|
+
import { captureWorkerDisclosures, restoreWorkerDisclosures, workerActivityNames } from './split-view-worker-activity.js';
|
|
5
|
+
import { iconHtml, refreshIcons } from './icons.js';
|
|
6
|
+
|
|
7
|
+
function panel() { return document.getElementById('background-task-viewer'); }
|
|
8
|
+
function current(state) {
|
|
9
|
+
return state && store.get('connected') && state.socketGeneration === store.get('projectSplitViewSocketGeneration') && state.origin === store.get('sessionOriginId') &&
|
|
10
|
+
state.slug === store.get('currentSlug') && state.owner === store.get('myUserId') &&
|
|
11
|
+
(store.get('backgroundTaskCatalog') || []).some(function (task) { return task.task_id === state.taskId && (task.run_id || null) === state.runId; });
|
|
12
|
+
}
|
|
13
|
+
export function closeBackgroundTask(focus) {
|
|
14
|
+
var state = store.get('backgroundTaskViewer');
|
|
15
|
+
if (state && state.timer) clearTimeout(state.timer);
|
|
16
|
+
if (state && state.timeout) clearTimeout(state.timeout);
|
|
17
|
+
store.set({ backgroundTaskViewer: null });
|
|
18
|
+
var element = panel(); if (element) element.remove();
|
|
19
|
+
if (focus && state) {
|
|
20
|
+
var chip = document.querySelector('[data-background-task="' + CSS.escape(state.taskId) + '"]'); if (chip) chip.focus();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function openBackgroundTask(task) {
|
|
24
|
+
closeBackgroundTask(false);
|
|
25
|
+
var state = { taskId: task.task_id, runId: task.run_id || null, title: task.description || 'Background task',
|
|
26
|
+
origin: store.get('sessionOriginId'), slug: store.get('currentSlug'), owner: store.get('myUserId'), socketGeneration: store.get('projectSplitViewSocketGeneration'), offset: 0 };
|
|
27
|
+
if (!current(state)) return;
|
|
28
|
+
store.set({ backgroundTaskViewer: state });
|
|
29
|
+
var element = document.createElement('section'); element.id = 'background-task-viewer'; element.className = 'split-view-worker-popover background-task-viewer';
|
|
30
|
+
element.setAttribute('role', 'dialog'); element.setAttribute('aria-label', 'Background task conversation');
|
|
31
|
+
element.innerHTML = '<header class="split-view-worker-popover-header"><strong></strong><button type="button" aria-label="Close background task">' + iconHtml('x') + '</button></header>' +
|
|
32
|
+
'<div class="split-view-worker-popover-source">Read-only · Native background task</div><div class="split-view-worker-popover-body readonly-session-transcript" tabindex="0"></div>' +
|
|
33
|
+
'<footer class="split-view-worker-popover-footer"><span role="status"></span><button type="button" data-page="previous">Previous</button><button type="button" data-page="next">Next</button><button type="button" data-retry>Refresh</button></footer>';
|
|
34
|
+
element.querySelector('strong').textContent = state.title;
|
|
35
|
+
element.querySelector('[aria-label="Close background task"]').addEventListener('click', function () { closeBackgroundTask(true); });
|
|
36
|
+
element.querySelector('[data-retry]').addEventListener('click', request);
|
|
37
|
+
element.querySelectorAll('[data-page]').forEach(function (button) { button.addEventListener('click', function () {
|
|
38
|
+
var live = store.get('backgroundTaskViewer'); if (!current(live) || live.pending) return;
|
|
39
|
+
store.set({ backgroundTaskViewer: Object.assign({}, live, { offset: button.dataset.page === 'next' ? live.offset + 100 : Math.max(0, live.offset - 100) }) }); request();
|
|
40
|
+
}); });
|
|
41
|
+
document.body.appendChild(element); refreshIcons(); request();
|
|
42
|
+
element.querySelector('button').focus();
|
|
43
|
+
}
|
|
44
|
+
function request() {
|
|
45
|
+
var state = store.get('backgroundTaskViewer');
|
|
46
|
+
if (!current(state)) { closeBackgroundTask(false); return; }
|
|
47
|
+
if (state.pending) return;
|
|
48
|
+
if (state.timer) clearTimeout(state.timer);
|
|
49
|
+
var id = crypto.randomUUID();
|
|
50
|
+
var timeout = setTimeout(function () {
|
|
51
|
+
var live = store.get('backgroundTaskViewer'); if (!live || live.requestId !== id) return;
|
|
52
|
+
store.set({ backgroundTaskViewer: Object.assign({}, live, { pending: false, timeout: null }) });
|
|
53
|
+
var element = panel(); if (element) element.querySelector('[role="status"]').textContent = 'Task read timed out. Use Refresh to retry.';
|
|
54
|
+
}, 18000);
|
|
55
|
+
store.set({ backgroundTaskViewer: Object.assign({}, state, { requestId: id, pending: true, timeout: timeout, timer: null }) });
|
|
56
|
+
var sent = getWs().send(JSON.stringify({ type: 'background_task_read', requestId: id, taskId: state.taskId, runId: state.runId, sessionOriginId: state.origin, offset: state.offset }));
|
|
57
|
+
if (sent === false) { clearTimeout(timeout); var live = store.get('backgroundTaskViewer'); if (live && live.requestId === id) { store.set({ backgroundTaskViewer: Object.assign({}, live, { pending: false, timeout: null }) }); panel().querySelector('[role="status"]').textContent = 'Task pane is not ready. Use Refresh to retry.'; } }
|
|
58
|
+
}
|
|
59
|
+
export function handleBackgroundTaskRead(message) {
|
|
60
|
+
var state = store.get('backgroundTaskViewer'), element = panel();
|
|
61
|
+
if (!element || !current(state) || message.requestId !== state.requestId || message.sessionOriginId !== state.origin ||
|
|
62
|
+
message.taskId !== state.taskId || (message.runId || null) !== state.runId) return;
|
|
63
|
+
clearTimeout(state.timeout);
|
|
64
|
+
var body = element.querySelector('.split-view-worker-popover-body'), footer = element.querySelector('[role="status"]');
|
|
65
|
+
var data = message.data;
|
|
66
|
+
if (message.error) { footer.textContent = message.error; state = Object.assign({}, state, { failures: (state.failures || 0) + 1 }); }
|
|
67
|
+
else if (data) {
|
|
68
|
+
var signature = JSON.stringify(data), changed = signature !== state.signature;
|
|
69
|
+
if (changed) {
|
|
70
|
+
var follow = body.scrollHeight - body.scrollTop - body.clientHeight < 48, top = body.scrollTop;
|
|
71
|
+
var disclosures = captureWorkerDisclosures(body); body.textContent = '';
|
|
72
|
+
if (data.kind === 'shell') { var pre = document.createElement('pre'); pre.textContent = data.output || 'No output yet.'; body.appendChild(pre); }
|
|
73
|
+
else appendWorkerTranscript(body, data.messages || [], { vendor: 'claude', userName: 'Parent agent', assistantName: 'Background agent' });
|
|
74
|
+
restoreWorkerDisclosures(body, disclosures);
|
|
75
|
+
body.scrollTop = follow ? body.scrollHeight : top;
|
|
76
|
+
}
|
|
77
|
+
footer.textContent = (data.active ? 'Running' : 'Finished') + (data.truncated ? ' · Last 8 KiB of output' : '') +
|
|
78
|
+
(data.kind === 'agent' && !data.messages.length ? ' · No transcript messages yet' : '');
|
|
79
|
+
element.querySelector('[data-page="previous"]').hidden = data.kind !== 'agent' || state.offset === 0;
|
|
80
|
+
element.querySelector('[data-page="next"]').hidden = !data.hasMore;
|
|
81
|
+
state = Object.assign({}, state, { signature: signature, failures: 0 });
|
|
82
|
+
}
|
|
83
|
+
var task = (store.get('backgroundTaskCatalog') || []).find(function (item) { return item.task_id === state.taskId; });
|
|
84
|
+
var timer = task && task.active !== false && (state.failures || 0) < 3 ? setTimeout(function () { if (!document.hidden) request(); else schedule(); }, 2000) : null;
|
|
85
|
+
store.set({ backgroundTaskViewer: Object.assign({}, state, { pending: false, timeout: null, timer: timer }) });
|
|
86
|
+
}
|
|
87
|
+
function schedule() {
|
|
88
|
+
var state = store.get('backgroundTaskViewer'); if (!current(state)) return;
|
|
89
|
+
var timer = setTimeout(function () { if (!document.hidden) request(); else schedule(); }, 2000);
|
|
90
|
+
store.set({ backgroundTaskViewer: Object.assign({}, state, { timer: timer }) });
|
|
91
|
+
}
|
|
92
|
+
store.subscribe(function (state, prev) {
|
|
93
|
+
if (state.backgroundTaskViewer && (state.sessionOriginId !== prev.sessionOriginId || state.currentSlug !== prev.currentSlug ||
|
|
94
|
+
state.myUserId !== prev.myUserId || state.projectSplitViewSocketGeneration !== prev.projectSplitViewSocketGeneration || !state.connected || state.backgroundTaskCatalog !== prev.backgroundTaskCatalog && !current(state.backgroundTaskViewer))) closeBackgroundTask(false);
|
|
95
|
+
});
|
|
96
|
+
document.addEventListener('keydown', function (event) { if (event.key === 'Escape' && panel()) { event.preventDefault(); closeBackgroundTask(true); } });
|
|
97
|
+
document.addEventListener('click', function (event) { var element = panel(); if (element && !element.contains(event.target) && !event.target.closest('[data-background-task]')) closeBackgroundTask(false); });
|