clay-server 5.3.0-beta.4 → 5.3.0-beta.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.
- package/lib/ask-user-request-identity.js +18 -1
- package/lib/clay-product-control-tools.js +2 -2
- package/lib/clay-product-control.js +1 -1
- package/lib/clay-screen-controls.js +1 -0
- package/lib/clay-split-view-awareness.js +5 -2
- package/lib/clay-split-view-controls.js +14 -7
- package/lib/clay-split-view-input.js +5 -4
- package/lib/clay-split-view-render-proof.js +14 -4
- package/lib/clay-view-workspace-input.js +71 -0
- package/lib/project-delegated-follow-up.js +18 -9
- package/lib/project-file-history.js +11 -9
- package/lib/project-file-watch.js +26 -7
- package/lib/project-filesystem.js +5 -4
- package/lib/project-shared-browser.js +7 -3
- package/lib/project-split-view-document.js +16 -2
- package/lib/project-split-views.js +15 -294
- package/lib/project-user-message.js +4 -3
- package/lib/project-vendor-login.js +2 -2
- package/lib/project-view-input.js +11 -0
- package/lib/project-view-pair.js +26 -0
- package/lib/project-view-store.js +346 -0
- package/lib/project-view-terminal-binding.js +16 -0
- package/lib/project-view-workspace.js +126 -0
- package/lib/project.js +1 -0
- package/lib/public/app.js +2 -0
- package/lib/public/css/issues.css +3 -0
- package/lib/public/css/pane.css +5 -1
- package/lib/public/css/right-workbench.css +25 -3
- package/lib/public/css/scheduled-tasks.css +7 -0
- package/lib/public/css/shared-browser.css +21 -0
- package/lib/public/css/split-view-layout.css +37 -3
- package/lib/public/css/sticky-notes.css +3 -2
- package/lib/public/css/tool-pane.css +127 -0
- package/lib/public/index.html +3 -3
- package/lib/public/modules/app-header.js +24 -2
- package/lib/public/modules/app-messages.js +1 -1
- package/lib/public/modules/clay-file-links.js +2 -1
- package/lib/public/modules/clay-session-links.js +1 -1
- package/lib/public/modules/clay-split-view-screen.js +6 -3
- package/lib/public/modules/clay-tool-pane-screen.js +35 -0
- package/lib/public/modules/file-history-snapshot.js +12 -0
- package/lib/public/modules/file-pane-entrypoints.js +55 -0
- package/lib/public/modules/file-pane-history.js +78 -0
- package/lib/public/modules/file-pane-instances.js +140 -0
- package/lib/public/modules/file-pane-links.js +41 -0
- package/lib/public/modules/file-pane-render.js +48 -0
- package/lib/public/modules/file-pane-review.js +26 -0
- package/lib/public/modules/file-pane-toolbar.js +54 -0
- package/lib/public/modules/file-viewer-source.js +70 -0
- package/lib/public/modules/filebrowser.js +44 -83
- package/lib/public/modules/git-panel.js +5 -0
- package/lib/public/modules/issues.js +3 -4
- package/lib/public/modules/linear-panel.js +2 -2
- package/lib/public/modules/mate-knowledge-workbench.js +10 -6
- package/lib/public/modules/notifications.js +4 -1
- package/lib/public/modules/pane-activation.js +5 -0
- package/lib/public/modules/pane-binding.js +5 -1
- package/lib/public/modules/pane-browser-bridge.js +20 -6
- package/lib/public/modules/pane-browser-transport.js +110 -0
- package/lib/public/modules/pane-header-runtime.js +18 -0
- package/lib/public/modules/plantuml-preview.js +12 -12
- package/lib/public/modules/right-workbench.js +116 -67
- package/lib/public/modules/scheduled-tasks.js +7 -6
- package/lib/public/modules/shared-browser.js +75 -9
- package/lib/public/modules/split-pane-renderer.js +7 -8
- package/lib/public/modules/split-pane-runtime.js +23 -0
- package/lib/public/modules/split-view-drag-preview.js +90 -0
- package/lib/public/modules/split-view-layout-client.js +1 -1
- package/lib/public/modules/split-view-presentation-drag.js +36 -13
- package/lib/public/modules/split-view-presentation-drop.js +21 -18
- package/lib/public/modules/split-view-presentation-renderer.js +15 -10
- package/lib/public/modules/split-view-presentations.js +25 -33
- package/lib/public/modules/split-view.js +17 -16
- package/lib/public/modules/sticky-notes-browser.js +2 -2
- package/lib/public/modules/sticky-notes.js +8 -10
- package/lib/public/modules/terminal.js +45 -13
- package/lib/public/modules/theme.js +1 -1
- package/lib/public/modules/tool-pane-chrome.js +82 -0
- package/lib/public/modules/tool-pane-drag.js +134 -0
- package/lib/public/modules/tool-pane-layout.js +95 -0
- package/lib/public/modules/tool-pane-legacy.js +60 -0
- package/lib/public/modules/tool-pane-tree.js +51 -0
- package/lib/public/modules/view-legacy-coordinates.js +16 -0
- package/lib/public/modules/view-mutations.js +24 -0
- package/lib/public/modules/view-pair-presentation.js +74 -0
- package/lib/public/modules/view-pair-validation.js +12 -0
- package/lib/public/modules/view-presentation-state.js +37 -0
- package/lib/public/modules/view-workspace-controller.js +210 -0
- package/lib/public/modules/view-workspace-state.js +103 -0
- package/lib/public/modules/view-workspace-validation.js +47 -0
- package/lib/public/modules/wireframe-workbench.js +11 -0
- package/lib/public/modules/workbench-layout-preference.js +12 -5
- package/lib/public/modules/workbench-panels.js +21 -0
- package/lib/public/modules/workspace-assignment-card.js +9 -6
- package/lib/public/pane-assets/{app-ED2VCGNQ.js → app-7UZ727NA.js} +1 -1
- package/lib/public/pane-assets/chunk-2AIWCI6Y.js +1 -0
- package/lib/public/pane-assets/chunk-6MIHIQ2W.js +1 -0
- package/lib/public/pane-assets/chunk-6NYCNWVF.js +1 -0
- package/lib/public/pane-assets/chunk-6QFLYOVY.js +1 -0
- package/lib/public/pane-assets/chunk-6UJ2ALOB.js +1 -0
- package/lib/public/pane-assets/chunk-6XK2OATZ.js +1 -0
- package/lib/public/pane-assets/chunk-BKIDR2ME.js +1 -0
- package/lib/public/pane-assets/chunk-BMHTBRMO.js +1 -0
- package/lib/public/pane-assets/chunk-GEFCFZEN.js +1 -0
- package/lib/public/pane-assets/chunk-GN2NQEVC.js +2 -0
- package/lib/public/pane-assets/chunk-IMLMBPT4.js +166 -0
- package/lib/public/pane-assets/chunk-JIV7O5GN.js +1 -0
- package/lib/public/pane-assets/chunk-K5S6AILM.js +1 -0
- package/lib/public/pane-assets/chunk-LDIZLKB2.js +1 -0
- package/lib/public/pane-assets/chunk-OWNNLZ36.js +1 -0
- package/lib/public/pane-assets/chunk-P2C3LOWL.js +3 -0
- package/lib/public/pane-assets/chunk-PV4PCH2V.js +2 -0
- package/lib/public/pane-assets/chunk-R4LMPTKU.js +1 -0
- package/lib/public/pane-assets/chunk-R7QVVFUG.js +1 -0
- package/lib/public/pane-assets/chunk-TMGSDFJI.js +1 -0
- package/lib/public/pane-assets/chunk-VKYBAVWT.js +1 -0
- package/lib/public/pane-assets/chunk-VNAV23CM.js +1 -0
- package/lib/public/pane-assets/chunk-VWTHIQCE.js +1 -0
- package/lib/public/pane-assets/chunk-VZ2OKDAO.js +1 -0
- package/lib/public/pane-assets/chunk-WM3K5AFH.js +1 -0
- package/lib/public/pane-assets/chunk-XOB3O327.js +1 -0
- package/lib/public/pane-assets/chunk-XOOUCFYT.js +1 -0
- package/lib/public/pane-assets/manifest.json +383 -336
- package/lib/public/pane-assets/style-ZM7OOU3M.css +1 -0
- package/lib/public/style.css +1 -0
- package/lib/server-workbench-layout-settings.js +3 -3
- package/lib/server.js +1 -0
- package/lib/split-view-layout.js +11 -7
- package/lib/users-workbench-layout-preferences.js +14 -4
- package/lib/workbench-window-layout.js +30 -0
- package/lib/workspace-assignment-service.js +56 -11
- package/lib/workspace-follow-up-queue.js +92 -0
- package/lib/workspace-query-mcp-server.js +1 -1
- package/package.json +1 -1
- package/lib/public/pane-assets/chunk-6D4DPE5X.js +0 -1
- package/lib/public/pane-assets/chunk-6SM2WQJX.js +0 -1
- package/lib/public/pane-assets/chunk-7H36SUZA.js +0 -2
- package/lib/public/pane-assets/chunk-DGOFGEUK.js +0 -1
- package/lib/public/pane-assets/chunk-I57URYSL.js +0 -1
- package/lib/public/pane-assets/chunk-IYFILQHZ.js +0 -2
- package/lib/public/pane-assets/chunk-IZ2ZANEY.js +0 -167
- package/lib/public/pane-assets/chunk-JAEQCN5S.js +0 -1
- package/lib/public/pane-assets/chunk-ST2Q62ET.js +0 -1
- package/lib/public/pane-assets/style-JVCOCG3W.css +0 -1
|
@@ -79,7 +79,6 @@
|
|
|
79
79
|
#split-host.split-view-canvas.visible {
|
|
80
80
|
display: flex;
|
|
81
81
|
padding: 7px;
|
|
82
|
-
background: var(--bg-alt);
|
|
83
82
|
box-sizing: border-box;
|
|
84
83
|
}
|
|
85
84
|
.split-view-tree, .split-view-branch, .split-view-leaf {
|
|
@@ -107,8 +106,10 @@
|
|
|
107
106
|
}
|
|
108
107
|
.split-view-canvas .split-pane-title {
|
|
109
108
|
min-width: 0;
|
|
110
|
-
flex: 1;
|
|
109
|
+
flex: 0 1 auto;
|
|
111
110
|
max-width: none;
|
|
111
|
+
padding: 0;
|
|
112
|
+
margin-left: 0;
|
|
112
113
|
font-size: 11px;
|
|
113
114
|
font-weight: 500;
|
|
114
115
|
overflow: hidden;
|
|
@@ -174,7 +175,7 @@ body.split-view-resizing .split-pane-frame { pointer-events: none; }
|
|
|
174
175
|
align-content: center;
|
|
175
176
|
gap: 4px;
|
|
176
177
|
}
|
|
177
|
-
.split-view-canvas .split-pane-title { flex: 1
|
|
178
|
+
.split-view-canvas .split-pane-title { flex: 0 1 auto; }
|
|
178
179
|
.split-view-canvas .split-view-runtime { flex: 0 1 auto; max-width: 55%; min-width: 48px; }
|
|
179
180
|
}
|
|
180
181
|
.split-view-workers {
|
|
@@ -462,3 +463,36 @@ body.split-view-resizing .split-pane-frame { pointer-events: none; }
|
|
|
462
463
|
|
|
463
464
|
/* Light reflects received WebSocket output, independent of processing state. */
|
|
464
465
|
.split-view-worker-dot.io { background: var(--brand-green); box-shadow: 0 0 5px var(--brand-green); }
|
|
466
|
+
|
|
467
|
+
/* Header whitespace is a handle; text and controls retain their own actions. */
|
|
468
|
+
.split-pane-drag-space { flex: 1 1 12px; min-width: 12px; align-self: stretch; cursor: grab; touch-action: none; }
|
|
469
|
+
.split-view-canvas .split-pane-header { cursor: grab; touch-action: none; }
|
|
470
|
+
.split-view-canvas .split-pane-header[data-pane-loading="true"] { cursor: default; }
|
|
471
|
+
body.is-dragging .split-pane-header, body.is-dragging .split-pane-drag-space { cursor: grabbing; }
|
|
472
|
+
.split-view-canvas.split-view-drag-preview > .split-pane,
|
|
473
|
+
.split-view-canvas.split-view-drag-preview > .split-view-divider,
|
|
474
|
+
.split-view-canvas.split-view-drag-preview > .split-view-drop-preview {
|
|
475
|
+
transition: left 280ms cubic-bezier(.2,.75,.25,1), top 280ms cubic-bezier(.2,.75,.25,1),
|
|
476
|
+
width 280ms cubic-bezier(.2,.75,.25,1), height 280ms cubic-bezier(.2,.75,.25,1);
|
|
477
|
+
}
|
|
478
|
+
.split-view-canvas.split-view-drag-preview > .split-view-divider { pointer-events: none; opacity: .35; }
|
|
479
|
+
.split-view-canvas.split-view-drag-preview > .split-view-drop-preview { background: transparent; }
|
|
480
|
+
@media (prefers-reduced-motion: reduce) {
|
|
481
|
+
.split-view-canvas.split-view-drag-preview > .split-pane,
|
|
482
|
+
.split-view-canvas.split-view-drag-preview > .split-view-divider,
|
|
483
|
+
.split-view-canvas.split-view-drag-preview > .split-view-drop-preview { transition: none; }
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
.split-view-canvas > .split-pane.split-pane-drag-source { opacity: .72; }
|
|
487
|
+
|
|
488
|
+
/* A shared tint marks the moving window without altering its content theme. */
|
|
489
|
+
:is(.split-pane-drag-source, .tool-pane-drag-source)::after {
|
|
490
|
+
content: "";
|
|
491
|
+
position: absolute;
|
|
492
|
+
inset: 0;
|
|
493
|
+
z-index: 20;
|
|
494
|
+
border-radius: inherit;
|
|
495
|
+
background: color-mix(in srgb, var(--accent) 16%, transparent);
|
|
496
|
+
box-shadow: inset 0 0 0 1px var(--accent-20);
|
|
497
|
+
pointer-events: none;
|
|
498
|
+
}
|
|
@@ -59,8 +59,8 @@
|
|
|
59
59
|
position: absolute;
|
|
60
60
|
top: -4px;
|
|
61
61
|
right: -4px;
|
|
62
|
-
background: var(--
|
|
63
|
-
color:
|
|
62
|
+
background: var(--brand-gold);
|
|
63
|
+
color: var(--brand-ink);
|
|
64
64
|
font-size: 9px;
|
|
65
65
|
font-weight: 700;
|
|
66
66
|
min-width: 14px;
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
justify-content: center;
|
|
72
72
|
padding: 0 3px;
|
|
73
73
|
font-family: "Roboto Mono", monospace;
|
|
74
|
+
pointer-events: none;
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
.sticky-notes-count.hidden { display: none; }
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/* All registered tool windows share the session pane's shell and density. */
|
|
2
|
+
.tool-pane-header { height: 33px; min-height: 33px; padding: 0 8px; gap: 5px; user-select: none; }
|
|
3
|
+
.tool-pane-header .tool-pane-title { flex: 0 1 auto; max-width: 55%; font-size: 11px; font-weight: 500; padding: 0; margin: 0; cursor: default; }
|
|
4
|
+
.tool-pane-kind { display: inline-flex; align-items: center; gap: 5px; flex-shrink: 0; }
|
|
5
|
+
.tool-pane-kind svg { width: 14px; height: 14px; color: var(--text-muted); }
|
|
6
|
+
.tool-pane-header :is(.terminal-header-actions, .file-viewer-window-actions, .shared-browser-window) { display: flex; align-items: center; margin-left: 0; gap: 2px; }
|
|
7
|
+
.tool-pane-header :is(.terminal-header-actions, .file-viewer-window-actions, .shared-browser-window, .workbench-pane-actions) button { width: 24px; height: 24px; padding: 4px; border-radius: 5px; }
|
|
8
|
+
.tool-pane-header button svg { width: 13px; height: 13px; }
|
|
9
|
+
.tool-pane-header .workbench-layout-toggle { width: 24px !important; height: 24px !important; flex-basis: 24px !important; }
|
|
10
|
+
.tool-pane-header .workbench-layout-toggle svg { width: 13px !important; height: 13px !important; }
|
|
11
|
+
/* Tabs use the compact inset pills of the Split Worker row. */
|
|
12
|
+
.tool-pane-tab-row, .tool-pane-header ~ .shared-browser-tabs {
|
|
13
|
+
display: flex; align-items: center; gap: 5px; padding: 4px 11px; min-height: 30px; height: auto;
|
|
14
|
+
flex-shrink: 0; overflow-x: auto; background: var(--bg); border-bottom: 0; scrollbar-width: thin;
|
|
15
|
+
}
|
|
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), .tool-pane-header ~ .shared-browser-tabs .shared-browser-tab {
|
|
18
|
+
display: inline-flex; align-items: center; flex: 0 0 auto; gap: 5px; min-width: 0; max-width: 284px;
|
|
19
|
+
height: 22px; min-height: 22px; padding: 0 6px; border: 1px solid var(--border-subtle); border-radius: 5px;
|
|
20
|
+
background: var(--bg-alt); color: var(--text-muted); font-size: 11px; line-height: 20px;
|
|
21
|
+
}
|
|
22
|
+
.tool-pane-tab-row :is(.terminal-tab.active, .file-viewer-tab.active), .tool-pane-header ~ .shared-browser-tabs .shared-browser-tab:has([aria-selected="true"]) { border-color: var(--accent-20); background: var(--accent-bg); color: var(--text-secondary); }
|
|
23
|
+
.tool-pane-header ~ .shared-browser-tabs .shared-browser-tab { padding: 0; gap: 0; }
|
|
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); }
|
|
27
|
+
.tool-pane-tab-row .terminal-tab-more svg { width: 11px; height: 11px; }
|
|
28
|
+
.tool-pane-tab-row .terminal-new-tab-btn { height: 22px; width: 22px; padding: 3px; flex-shrink: 0; }
|
|
29
|
+
@media (min-width: 1024px) {
|
|
30
|
+
#main-panels > :is(#terminal-container, #shared-browser-panel, #file-viewer, .file-instance, .workbench-pane-shell).tool-pane-window[data-workbench-placement="docked"]:not(.panel-fullscreen) {
|
|
31
|
+
position: absolute; inset: auto; flex: none; min-width: 0; max-width: none; margin: 0; align-self: auto;
|
|
32
|
+
z-index: 4; container-type: inline-size; box-sizing: border-box; border: 1px solid var(--border); border-radius: 9px;
|
|
33
|
+
background: var(--bg); box-shadow: none; animation: none;
|
|
34
|
+
transition: left 280ms cubic-bezier(.2,.75,.25,1), top 280ms cubic-bezier(.2,.75,.25,1), width 280ms cubic-bezier(.2,.75,.25,1), height 280ms cubic-bezier(.2,.75,.25,1);
|
|
35
|
+
}
|
|
36
|
+
.tool-pane-window > .tool-pane-header { cursor: grab; touch-action: none; }
|
|
37
|
+
.tool-pane-window .shared-browser-viewport { min-height: 0; }
|
|
38
|
+
.tool-pane-window [data-action="wide"], .tool-pane-window #file-viewer-wide { display: none; }
|
|
39
|
+
.tool-pane-drag-source { opacity: .72; }
|
|
40
|
+
body.tool-pane-dragging, body.tool-pane-dragging .tool-pane-header { cursor: grabbing; user-select: none; }
|
|
41
|
+
}
|
|
42
|
+
@media (prefers-reduced-motion: reduce) {
|
|
43
|
+
#main-panels > :is(#terminal-container, #shared-browser-panel, #file-viewer, .file-instance, .workbench-pane-shell).tool-pane-window[data-workbench-placement="docked"]:not(.panel-fullscreen) { transition: none; }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@container (max-width: 380px) {
|
|
47
|
+
.tool-pane-header { height: auto; min-height: 55px; padding: 4px 8px; flex-wrap: wrap; align-content: center; gap: 4px; }
|
|
48
|
+
.tool-pane-header .tool-pane-kind { max-width: 55%; }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.tool-pane-header ~ .shared-browser-header[hidden] { display: none; }
|
|
52
|
+
|
|
53
|
+
/* Expanded tool windows retain the same inset frame as a focused session. */
|
|
54
|
+
@media (min-width: 1024px) {
|
|
55
|
+
#main-panels > :is(#terminal-container, #shared-browser-panel, #file-viewer, .file-instance, .workbench-pane-shell).panel-fullscreen {
|
|
56
|
+
inset: 7px !important;
|
|
57
|
+
width: calc(100% - 14px) !important;
|
|
58
|
+
height: calc(100% - 14px) !important;
|
|
59
|
+
max-width: none;
|
|
60
|
+
max-height: none !important;
|
|
61
|
+
min-width: 0;
|
|
62
|
+
margin: 0;
|
|
63
|
+
box-sizing: border-box;
|
|
64
|
+
border: 1px solid var(--border);
|
|
65
|
+
border-radius: 9px;
|
|
66
|
+
container-type: inline-size;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* Independent Files panes use the same shell; only the content owns scrolling. */
|
|
71
|
+
.file-instance { position: absolute; right: 8px; top: 8px; bottom: 8px; width: min(50%, 720px); min-width: 300px; display: flex; flex-direction: column; overflow: hidden; z-index: 20; background: var(--bg); border: 1px solid var(--border); border-radius: 9px; box-shadow: 0 8px 28px #0002; }
|
|
72
|
+
.file-instance.hidden { display: none; }
|
|
73
|
+
.file-instance .tool-pane-header { height: 33px; min-height: 33px; flex-wrap: nowrap; }
|
|
74
|
+
.file-instance .tool-pane-title { max-width: none; flex-shrink: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
75
|
+
.file-instance button { color: var(--text-secondary); background: transparent; border: 1px solid transparent; border-radius: 5px; cursor: pointer; }
|
|
76
|
+
.file-instance button:hover { background: var(--bg-alt); }
|
|
77
|
+
.file-instance button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
|
|
78
|
+
.file-instance button[aria-pressed="true"] { color: var(--accent); background: var(--accent-bg); border-color: var(--accent-20); }
|
|
79
|
+
.file-instance button:disabled { opacity: .4; cursor: default; }
|
|
80
|
+
.file-instance-toolbar { display: flex; gap: 4px; padding: 4px 8px; border-bottom: 1px solid var(--border-subtle); flex-shrink: 0; }
|
|
81
|
+
.file-instance-toolbar button { font-size: 11px; height: 24px; padding: 2px 7px; }
|
|
82
|
+
.file-instance-toolbar button:last-child { margin-left: auto; }
|
|
83
|
+
.file-instance-toolbar svg { width: 14px; height: 14px; }
|
|
84
|
+
.file-instance-more { position: absolute; right: 8px; top: 65px; z-index: 30; min-width: 160px; padding: 4px; border: 1px solid var(--border); border-radius: 7px; background: var(--bg); box-shadow: 0 5px 20px #0003; }
|
|
85
|
+
.file-instance-more button { display: block; width: 100%; text-align: left; padding: 5px 8px; font-size: 12px; }
|
|
86
|
+
.file-instance-history { display: block; width: 100%; padding: 8px; text-align: left; }
|
|
87
|
+
.file-instance .file-viewer-body { flex: 1; min-height: 0; }
|
|
88
|
+
.file-instance .file-viewer-body > img { max-width: 100%; }
|
|
89
|
+
@media (max-width: 1023px) { .file-instance { position: fixed; inset: 0; width: 100%; min-width: 0; border-radius: 0; z-index: 300; } }
|
|
90
|
+
|
|
91
|
+
.file-instance-more.hidden { display: none; }
|
|
92
|
+
.file-instance.panel-fullscreen { z-index: 310; }
|
|
93
|
+
@media (max-width: 1023px) {
|
|
94
|
+
.file-instance { inset: 0 !important; width: 100% !important; height: 100% !important; max-height: none !important; }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* A focused conversation owns the canvas; the saved dock layout is retained. */
|
|
98
|
+
#main-panels.view-session-focused > .tool-pane-window:not(.panel-fullscreen) { visibility: hidden; pointer-events: none; }
|
|
99
|
+
|
|
100
|
+
/* Workbench content stays owned by its module; this shared shell owns geometry. */
|
|
101
|
+
.workbench-pane-actions { display: flex; align-items: center; gap: 2px; margin-left: 0; flex-shrink: 0; }
|
|
102
|
+
#main-panels > .workbench-pane-shell > .tool-pane-header { display: flex; flex-wrap: nowrap; height: 33px; min-height: 33px; padding: 0 8px; gap: 5px; }
|
|
103
|
+
.workbench-pane-shell > .tool-pane-header > [class$="-subtitle"] { display: none; }
|
|
104
|
+
.workbench-pane-shell > .tool-pane-header > .split-pane-drag-space { flex: 1; min-width: 12px; align-self: stretch; }
|
|
105
|
+
@media (min-width: 1024px) {
|
|
106
|
+
#main-panels > .workbench-pane-shell[data-workbench-placement="overlay"]:not(.panel-fullscreen) {
|
|
107
|
+
position: absolute; top: 8px; bottom: 8px; right: 8px; left: auto;
|
|
108
|
+
width: min(50%, 720px); min-width: 300px; max-width: none; margin: 0;
|
|
109
|
+
max-height: none; border: 1px solid var(--border); border-radius: 9px;
|
|
110
|
+
box-shadow: 0 8px 28px #0002; z-index: 20;
|
|
111
|
+
}
|
|
112
|
+
#main-panels > .workbench-pane-shell.tool-pane-window:not(.panel-fullscreen) [aria-label^="Widen"] { display: none; }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@media (min-width: 1024px) {
|
|
116
|
+
#main-panels > .workbench-pane-shell[data-workbench-placement="overlay"]:not(.panel-fullscreen):has([aria-label^="Widen"][aria-pressed="true"]) { width: 80%; }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
#main-panels > .workbench-pane-shell > .tool-pane-header .workbench-pane-actions { display: flex; align-items: center; gap: 2px; margin-left: 0; }
|
|
120
|
+
#main-panels > .workbench-pane-shell > .tool-pane-header .workbench-pane-actions button { min-width: 24px; width: 24px; height: 24px; padding: 4px; border: 0; border-radius: 5px; background: transparent; }
|
|
121
|
+
#main-panels > .workbench-pane-shell > .tool-pane-header button svg { width: 13px; height: 13px; }
|
|
122
|
+
#main-panels > .workbench-pane-shell > .tool-pane-header .tool-pane-title { flex: 0 1 auto; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: 11px; font-weight: 500; }
|
|
123
|
+
#main-panels > .workbench-pane-shell > .tool-pane-header .workbench-pane-actions button:hover { background: var(--sidebar-hover); color: var(--text); }
|
|
124
|
+
#main-panels > .workbench-pane-shell > .tool-pane-header .workbench-pane-actions button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
|
|
125
|
+
#main-panels > .workbench-pane-shell > .tool-pane-header .workbench-pane-actions button[aria-pressed="true"] { background: var(--accent-bg); color: var(--accent); }
|
|
126
|
+
|
|
127
|
+
.file-instance:not([data-workbench-placement="docked"]) [data-action="pin"] { display: none; }
|
package/lib/public/index.html
CHANGED
|
@@ -78,9 +78,9 @@ body { background: transparent; color: inherit; }
|
|
|
78
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
79
|
<div id="client-count" class="top-bar-pill pill-accent hidden"><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
|
-
<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></button>
|
|
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>
|
|
83
|
-
<button type="button" id="sticky-notes-toggle-btn" aria-label="Hide sticky notes" aria-pressed="false" title="Hide sticky notes" hidden><i data-lucide="sticky-note"></i></button>
|
|
83
|
+
<button type="button" id="sticky-notes-toggle-btn" aria-label="Hide sticky notes" aria-pressed="false" title="Hide sticky notes" hidden><i data-lucide="sticky-note"></i><span class="sticky-notes-count hidden" aria-hidden="true"></span></button>
|
|
84
84
|
<button id="notif-center-btn" title="Notifications"><i data-lucide="bell"></i><span id="notif-center-badge" class="notif-badge hidden">0</span></button>
|
|
85
85
|
<button id="server-settings-btn" title="Settings" aria-label="Settings" aria-expanded="false"><i data-lucide="settings"></i></button>
|
|
86
86
|
</div>
|
|
@@ -704,7 +704,7 @@ body { background: transparent; color: inherit; }
|
|
|
704
704
|
<button class="terminal-new-tab-btn" id="terminal-new-tab" title="New tab"><i data-lucide="plus"></i></button>
|
|
705
705
|
<div class="terminal-header-actions">
|
|
706
706
|
<button class="file-viewer-btn" id="terminal-fullscreen" title="Toggle fullscreen"><i data-lucide="maximize-2"></i></button>
|
|
707
|
-
<button class="file-viewer-btn" id="terminal-close" title="
|
|
707
|
+
<button class="file-viewer-btn" id="terminal-close" title="Close terminal pane" aria-label="Close terminal pane"><i data-lucide="x"></i></button>
|
|
708
708
|
</div>
|
|
709
709
|
</div>
|
|
710
710
|
<div id="terminal-toolbar" class="term-toolbar hidden">
|
|
@@ -83,6 +83,21 @@ function createHeaderWorkerHistoryControl(root, context) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
export function initHeader() {
|
|
86
|
+
store.subscribe(function (state, prev) {
|
|
87
|
+
if (!state.connected && prev.connected || state.panePendingBinding && state.panePendingBinding !== prev.panePendingBinding) {
|
|
88
|
+
store.set({ loadingMore: false });
|
|
89
|
+
}
|
|
90
|
+
if (state.paneReady && !prev.paneReady || !state.replayingHistory && prev.replayingHistory) {
|
|
91
|
+
// Replay can expose the sentinel before the pane accepts requests.
|
|
92
|
+
// Observe it again once the committed conversation is interactive.
|
|
93
|
+
var messages = getMessagesEl();
|
|
94
|
+
var sentinel = messages && messages.querySelector('.history-sentinel');
|
|
95
|
+
if (sentinel && historySentinelObserver) {
|
|
96
|
+
historySentinelObserver.unobserve(sentinel);
|
|
97
|
+
historySentinelObserver.observe(sentinel);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
});
|
|
86
101
|
var headerRenameBtn = document.getElementById("header-rename-btn");
|
|
87
102
|
var headerTitleEl = document.getElementById("header-title");
|
|
88
103
|
var headerInfoBtn = document.getElementById("header-info-btn");
|
|
@@ -339,12 +354,19 @@ export function updateHistorySentinel() {
|
|
|
339
354
|
export function requestMoreHistory() {
|
|
340
355
|
var ws = getWs();
|
|
341
356
|
var s = store.snap();
|
|
342
|
-
if (s.loadingMore || s.historyFrom <= 0 || !ws || !s.connected
|
|
357
|
+
if (s.loadingMore || s.historyFrom <= 0 || !ws || ws.readyState !== 1 || !s.connected ||
|
|
358
|
+
s.replayingHistory || s.historyReplayBatch || s.panePendingBinding || s.paneSuspended ||
|
|
359
|
+
s.paneCommittedBinding && !s.paneReady || s.deferredSessionSwitch) return;
|
|
343
360
|
store.set({ loadingMore: true });
|
|
344
361
|
var messagesEl = getMessagesEl();
|
|
345
362
|
var btn = messagesEl.querySelector(".load-more-btn");
|
|
346
363
|
if (btn) btn.classList.add("loading");
|
|
347
|
-
|
|
364
|
+
try {
|
|
365
|
+
ws.send(JSON.stringify({ type: "load_more_history", before: s.historyFrom, paneBinding: s.paneCommittedBinding || undefined }));
|
|
366
|
+
} catch (error) {
|
|
367
|
+
store.set({ loadingMore: false });
|
|
368
|
+
if (btn) btn.classList.remove("loading");
|
|
369
|
+
}
|
|
348
370
|
}
|
|
349
371
|
|
|
350
372
|
export function prependOlderHistory(items, meta) {
|
|
@@ -267,7 +267,7 @@ export function processMessage(msg) {
|
|
|
267
267
|
break;
|
|
268
268
|
|
|
269
269
|
case "history_meta":
|
|
270
|
-
store.set({ historyFrom: msg.from, historyTotal: msg.total, replayingHistory: true });
|
|
270
|
+
store.set({ historyFrom: msg.from, historyTotal: msg.total, loadingMore: false, replayingHistory: true });
|
|
271
271
|
updateHistorySentinel();
|
|
272
272
|
break;
|
|
273
273
|
|
|
@@ -63,10 +63,11 @@ export function renderLocalFileLink(href, title, text) {
|
|
|
63
63
|
var label = text || target.path;
|
|
64
64
|
var tooltip = target.path + (target.line ? ":" + target.line + (target.column ? ":" + target.column : "") : "");
|
|
65
65
|
var state = store.snap();
|
|
66
|
+
var fragment = target.href.indexOf('#') >= 0 && !target.line ? decode(target.href.slice(target.href.indexOf('#') + 1)) : '';
|
|
66
67
|
var projectAttr = state.currentSlug ? ' data-file-project-slug="' + escapeHtml(String(state.currentSlug)) + '"' : '';
|
|
67
68
|
var sessionAttr = state.activeSessionId != null ? ' data-file-session-id="' + escapeHtml(String(state.activeSessionId)) + '"' : '';
|
|
68
69
|
return '<button type="button" class="clay-file-link" data-file-path="' + escapeHtml(target.path) + '"' +
|
|
69
|
-
projectAttr + sessionAttr +
|
|
70
|
+
projectAttr + sessionAttr + (fragment ? ' data-file-heading="' + escapeHtml(fragment) + '"' : '') +
|
|
70
71
|
(target.line ? ' data-file-line="' + target.line + '"' : '') +
|
|
71
72
|
(target.column ? ' data-file-column="' + target.column + '"' : '') +
|
|
72
73
|
' title="' + escapeHtml(tooltip) + '" aria-label="Open file ' + escapeHtml(tooltip) + '">' +
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parseClaySessionReference, renderClaySessionLink } from './clay-log-links.js';
|
|
2
2
|
|
|
3
3
|
// The prefix is a display compatibility label, never a navigation target.
|
|
4
|
-
var citationPattern = /\[([A-Za-z0-9][A-Za-z0-9_-]{0,79})\/(session:[A-Za-z0-9_-]{24}(?:#entry-[0-9]+)?)(?:\s+[\u2014-]\s+([^\]\n]{1,80}))?\]/g;
|
|
4
|
+
var citationPattern = /\[([A-Za-z0-9][A-Za-z0-9_-]{0,79})\/(session:[A-Za-z0-9_-]{24}(?:#entry-[0-9]+)?)(?:\s+[\u2014\u00b7-]\s+([^\]\n]{1,80}))?\]/g;
|
|
5
5
|
function matches(text) {
|
|
6
6
|
var pattern = new RegExp(citationPattern.source, 'g'), results = [], match;
|
|
7
7
|
while ((match = pattern.exec(text))) if (parseClaySessionReference(match[2])) results.push({ start: match.index, end: pattern.lastIndex, prefix: match[1], ref: match[2], label: match[3] || '' });
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { toolPaneScreen, toolPaneRendering } from './clay-tool-pane-screen.js';
|
|
1
2
|
import { store } from './store.js';
|
|
2
3
|
import { getWs } from './ws-ref.js';
|
|
3
4
|
import { livePaneBinding } from './pane-binding.js';
|
|
@@ -13,6 +14,8 @@ export function splitViewScreenSnapshot() {
|
|
|
13
14
|
viewId: view ? view.id : null, revision: view ? view.revision : 0, tree: view ? view.tree : null,
|
|
14
15
|
focusOrigin: state.projectSplitViewFocusedOrigin || null, singletonOrigin: state.projectSplitViewSingletonOrigin || null,
|
|
15
16
|
activeSessionId: state.activeSessionId || null,
|
|
17
|
+
tools: toolPaneScreen(view, hidden),
|
|
18
|
+
expandedPaneId: (toolPaneScreen(view, hidden).find(function (tool) { return tool.expanded; }) || {}).instanceId || null,
|
|
16
19
|
panes: Array.from(document.querySelectorAll('.split-pane-frame')).slice(0, 32).map(function (frame) {
|
|
17
20
|
var live = frame.clayPane && frame.clayPane.identity(), binding = live && live.binding;
|
|
18
21
|
var ready = !!livePaneBinding(frame), cached = !!frame.closest('.split-pane-cached');
|
|
@@ -60,10 +63,10 @@ export function applySplitViewNavigation(target) {
|
|
|
60
63
|
}
|
|
61
64
|
function renderedTarget(snapshot, target) {
|
|
62
65
|
var exact = snapshot && !snapshot.hidden && snapshot.viewId === target.view.id && snapshot.revision === target.view.revision &&
|
|
63
|
-
snapshot.focusOrigin === target.focusOrigin && JSON.stringify(snapshot.tree) === JSON.stringify(target.view.tree);
|
|
64
|
-
var expected = target.view.sessions.filter(function (session) { return !target.focusOrigin || session.sessionOriginId === target.focusOrigin; });
|
|
66
|
+
snapshot.focusOrigin === target.focusOrigin && (snapshot.expandedPaneId || null) === (target.view.workspace && target.view.workspace.presentation && target.view.workspace.presentation.expandedPaneId || null) && JSON.stringify(snapshot.tree) === JSON.stringify(target.view.tree);
|
|
67
|
+
var expected = target.view.sessions.filter(function (session) { return snapshot && !snapshot.expandedPaneId && (!target.focusOrigin || session.sessionOriginId === target.focusOrigin); });
|
|
65
68
|
var shown = snapshot ? snapshot.panes.filter(function (pane) { return pane.visible; }) : [];
|
|
66
|
-
return !!(exact && shown.length === expected.length && expected.every(function (session) { return shown.some(function (pane) { return pane.origin === session.sessionOriginId && pane.sessionId === session.id && pane.ready && !pane.cached; }); }));
|
|
69
|
+
return !!(exact && toolPaneRendering(snapshot, target.view) && shown.length === expected.length && expected.every(function (session) { return shown.some(function (pane) { return pane.origin === session.sessionOriginId && pane.sessionId === session.id && pane.ready && !pane.cached; }); }));
|
|
67
70
|
}
|
|
68
71
|
export function observeSplitViewRendering(msg, socket) {
|
|
69
72
|
var target = msg.target;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { TOOL_PANES } from './tool-pane-tree.js';
|
|
2
|
+
import { store } from './store.js';
|
|
3
|
+
import { visible } from './clay-navigation-render.js';
|
|
4
|
+
import { paneOwner } from './view-workspace-state.js';
|
|
5
|
+
import { filePaneState } from './file-pane-instances.js';
|
|
6
|
+
export function toolPaneScreen(view, hidden) {
|
|
7
|
+
if (!view || !view.workspace) return [];
|
|
8
|
+
var run = store.get('savedWorkspaceRuntime');
|
|
9
|
+
return view.workspace.panes.filter(function (pane) { return pane.kind !== 'session'; }).slice(0, 16).map(function (pane) {
|
|
10
|
+
var name = paneOwner(pane), panel = document.getElementById(pane.kind === 'file' ? name : TOOL_PANES[name] && TOOL_PANES[name].id);
|
|
11
|
+
var file = pane.kind === 'file' ? filePaneState(pane.instanceId) : null;
|
|
12
|
+
var unavailable = !!(run && run.unavailable[name] || file && file.error);
|
|
13
|
+
var exact = false;
|
|
14
|
+
if (pane.kind === 'workbench') exact = !!(panel && !panel.classList.contains('hidden') && (store.get('rightWorkbenchOwner') === name || (store.get('rightWorkbenchDockedOwners') || []).indexOf(name) >= 0));
|
|
15
|
+
else if (pane.kind === 'file') exact = !!(file && file.resource && file.resource.path === pane.resource.path && file.resource.mode === pane.resource.mode && file.resource.rendered === pane.resource.rendered && (file.content != null || file.binary));
|
|
16
|
+
else if (pane.kind === 'terminal') {
|
|
17
|
+
var terminal = store.get('savedViewTerminalResource');
|
|
18
|
+
exact = !!(terminal && terminal.id === pane.resource.id && terminal.identity === pane.resource.identity && terminal.runtimeId === pane.resource.runtimeId && (store.get('clayControlTerminalViews') || []).some(function (item) { return item.id === terminal.id && item.identity === terminal.identity && !item.exited; }));
|
|
19
|
+
} else {
|
|
20
|
+
var browser = store.get('sharedBrowser');
|
|
21
|
+
exact = !!(browser && browser.id === pane.resource.id && browser.phase !== 'ended' && store.get('controlRuntimeId') === pane.resource.runtimeId && store.get('sessionOriginId') === pane.resource.sessionOriginId);
|
|
22
|
+
}
|
|
23
|
+
var ready = !!(pane.open && panel && !unavailable && exact);
|
|
24
|
+
var shown = !!(!hidden && pane.open && panel && visible(panel));
|
|
25
|
+
var rect = shown ? panel.getBoundingClientRect() : null;
|
|
26
|
+
function pixel(value) { return Math.max(-100000, Math.min(100000, Math.round(value))); }
|
|
27
|
+
return { instanceId: pane.instanceId, kind: pane.kind, placement: panel && panel.dataset && ['docked', 'overlay'].indexOf(panel.dataset.workbenchPlacement) >= 0 ? panel.dataset.workbenchPlacement : null, expanded: !!(pane.open && panel && panel.classList && panel.classList.contains('panel-fullscreen')), visible: shown, ready: ready, unavailable: unavailable,
|
|
28
|
+
geometry: rect ? { x: pixel(rect.x), y: pixel(rect.y), width: Math.max(0, pixel(rect.width)), height: Math.max(0, pixel(rect.height)) } : null };
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
export function toolPaneRendering(snapshot, view) {
|
|
32
|
+
var expected = view.workspace ? view.workspace.panes.filter(function (pane) { return pane.kind !== 'session' && pane.open && (!snapshot.focusOrigin || snapshot.expandedPaneId || pane.placement === 'overlay') && (!view.workspace.presentation || !view.workspace.presentation.expandedPaneId || view.workspace.presentation.expandedPaneId === pane.instanceId); }) : [];
|
|
33
|
+
var tools = snapshot.tools || [];
|
|
34
|
+
return expected.every(function (pane) { return tools.some(function (tool) { return tool.instanceId === pane.instanceId && tool.kind === pane.kind && tool.placement === pane.placement && !!tool.expanded === !!(view.workspace.presentation && view.workspace.presentation.expandedPaneId === pane.instanceId) && tool.visible && tool.ready && !tool.unavailable; }); }) && tools.filter(function (tool) { return tool.visible; }).length === expected.length;
|
|
35
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Session tool input is historical evidence. The current disk file is not.
|
|
2
|
+
export function historicalSnapshot(entry, before) {
|
|
3
|
+
if (!entry || entry.source === 'git') return undefined;
|
|
4
|
+
var full = before ? entry.beforeContent : entry.afterContent;
|
|
5
|
+
if (typeof full === 'string') return full;
|
|
6
|
+
if (entry.toolName === 'Write' || entry.isFullWrite) return before ? undefined : typeof entry.content === 'string' ? entry.content : undefined;
|
|
7
|
+
var excerpt = before ? entry.old_string : entry.new_string;
|
|
8
|
+
return typeof excerpt === 'string' ? excerpt : undefined;
|
|
9
|
+
}
|
|
10
|
+
export function historicalScope(entry) {
|
|
11
|
+
return entry && entry.source !== 'git' && entry.toolName === 'Edit' && typeof entry.afterContent !== 'string' ? 'Recorded changed excerpt; a full-file snapshot was not recorded.' : '';
|
|
12
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { recordToolExpansion } from './view-presentation-state.js';
|
|
2
|
+
import { registerStudioPaint } from './clay-studio-render.js';
|
|
3
|
+
import { bindFileReview } from './file-pane-review.js';
|
|
4
|
+
// Compatibility entrypoints resolve an actual pane in the authenticated current View.
|
|
5
|
+
import { store } from './store.js';
|
|
6
|
+
import { activeSavedView } from './view-workspace-state.js';
|
|
7
|
+
import { filePaneStates, filePaneState, openIndependentFile, updateFilePaneState, repaintFilePane, actOnFilePane, closeFilePane, mountFilePane } from './file-pane-instances.js';
|
|
8
|
+
import { claimRightWorkbench } from './right-workbench.js';
|
|
9
|
+
import { presentedDocument } from './clay-studio-document.js';
|
|
10
|
+
import { showToast } from './utils.js';
|
|
11
|
+
|
|
12
|
+
export function independentFilesActive() { return !!activeSavedView() && !store.get('paneMode'); }
|
|
13
|
+
export function fileEntryContext() { var s = store.snap(); return JSON.stringify([s.currentSlug, s.myUserId, s.currentProjectOwnerId, s.projectSplitViewActiveId, s.projectSplitViewSocketGeneration]); }
|
|
14
|
+
export function normalizedFilePath(path) {
|
|
15
|
+
var cwd = String(store.get('cwd') || '').replace(/\/$/, '');
|
|
16
|
+
return cwd && path.indexOf(cwd + '/') === 0 ? path.slice(cwd.length + 1) : path;
|
|
17
|
+
}
|
|
18
|
+
export function activeFilePane() {
|
|
19
|
+
var id = store.get('activeFilePaneId'), state = filePaneState(id);
|
|
20
|
+
if (state) return state;
|
|
21
|
+
var ids = Object.keys(filePaneStates()).reverse();
|
|
22
|
+
for (var i = 0; i < ids.length; i++) { state = filePaneState(ids[i]); if (state) return state; }
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
export function closeActiveFilePane() { var state = activeFilePane(); if (state) closeFilePane(state.instanceId); }
|
|
26
|
+
export function expandActiveFilePane(enabled) { var state = activeFilePane(); if (state) { updateFilePaneState(state.instanceId, { expanded: !!enabled }); repaintFilePane(state.instanceId); recordToolExpansion('file-' + state.instanceId, enabled); } }
|
|
27
|
+
export function reopenIndependentFile() {
|
|
28
|
+
var last = filePaneStates()[store.get('activeFilePaneId')];
|
|
29
|
+
if (last && !last.open && last.context === fileEntryContext()) { mountFilePane(last); return true; }
|
|
30
|
+
var state = activeFilePane();
|
|
31
|
+
if (state) { claimRightWorkbench('file-' + state.instanceId); return true; }
|
|
32
|
+
var closed = Object.keys(filePaneStates()).reverse().map(function (id) { return filePaneStates()[id]; }).find(function (item) { return item.context === fileEntryContext(); });
|
|
33
|
+
if (!closed) return false;
|
|
34
|
+
mountFilePane(closed); return true;
|
|
35
|
+
}
|
|
36
|
+
export function refreshIndependentFile(path) {
|
|
37
|
+
path = normalizedFilePath(path);
|
|
38
|
+
Object.keys(filePaneStates()).forEach(function (id) { var state = filePaneState(id); if (state && state.resource.path === path && !state.historyVisible && !state.diff) actOnFilePane(id, 'refresh'); });
|
|
39
|
+
}
|
|
40
|
+
export function presentIndependentFile(msg, diff, review) {
|
|
41
|
+
// Source-frame receipts do not grant access to a same-named parent-project file.
|
|
42
|
+
var source = presentedDocument(msg.path);
|
|
43
|
+
if (source && source.descriptor.sourceSlug !== store.get('currentSlug')) { showToast('Source-project document presentation is unavailable in this View.', 'warn'); return false; }
|
|
44
|
+
if (!openIndependentFile(msg.path, { rendered: !diff, diff: diff })) return false;
|
|
45
|
+
var path = normalizedFilePath(msg.path);
|
|
46
|
+
Object.keys(filePaneStates()).forEach(function (id) {
|
|
47
|
+
var state = filePaneState(id);
|
|
48
|
+
if (!state || state.resource.mode !== 'follow' || state.resource.path !== path) return;
|
|
49
|
+
bindFileReview(state, review);
|
|
50
|
+
updateFilePaneState(id, { suppliedSnapshotRequestId: state.requestId, content: msg.content || '', size: msg.size || (msg.content || '').length, binary: !!msg.binary, diff: diff || null }); repaintFilePane(id);
|
|
51
|
+
var panel = document.getElementById('file-' + id);
|
|
52
|
+
registerStudioPaint(msg, panel.querySelector('.tool-pane-header'), null, panel.querySelector('.file-viewer-body'));
|
|
53
|
+
});
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { historicalSnapshot, historicalScope } from './file-history-snapshot.js';
|
|
2
|
+
import { filePaneState, updateFilePaneState, sendFilePaneRequest, repaintFilePane } from './file-pane-instances.js';
|
|
3
|
+
import { renderSplitDiff, renderUnifiedDiff, reconstructPatchSources } from './diff.js';
|
|
4
|
+
import { renderCodeWithLineNumbers, mapExtToLanguage } from './file-viewer-source.js';
|
|
5
|
+
import { navigateToEdit } from './filebrowser.js';
|
|
6
|
+
|
|
7
|
+
function control(label, action) { var b = document.createElement('button'); b.type = 'button'; b.textContent = label; b.addEventListener('click', action); return b; }
|
|
8
|
+
function current(state) { var live = filePaneState(state.instanceId); return live && live.requestId === state.requestId; }
|
|
9
|
+
function change(state, patch) { if (!current(state)) return; updateFilePaneState(state.instanceId, patch); repaintFilePane(state.instanceId); }
|
|
10
|
+
function entryContent(state, entry, before) { return entry.source === 'git' ? (state.historyContents || {})[entry.hash] : historicalSnapshot(entry, before); }
|
|
11
|
+
function requestEntry(state, entry) { if (entry.source === 'git') sendFilePaneRequest(state, 'fs_file_at', { hash: entry.hash }); }
|
|
12
|
+
export function paintFileHistory(body, state) {
|
|
13
|
+
body.replaceChildren();
|
|
14
|
+
var target = state.historyTarget;
|
|
15
|
+
if (target) {
|
|
16
|
+
var bar = document.createElement('div'); bar.className = 'file-history-view-bar';
|
|
17
|
+
bar.appendChild(control('Back to timeline', function () { change(state, { historyTarget: null }); }));
|
|
18
|
+
['Split', 'Unified', 'Source'].forEach(function (mode) { bar.appendChild(control(mode, function () { change(state, { historyMode: mode.toLowerCase() }); })); });
|
|
19
|
+
body.appendChild(bar);
|
|
20
|
+
var content = document.createElement('div'); body.appendChild(content);
|
|
21
|
+
var before, after;
|
|
22
|
+
if (target.kind === 'git') {
|
|
23
|
+
if (state.historyDiffHash !== target.entry.hash) { content.textContent = 'Loading revision…'; return; }
|
|
24
|
+
if (state.historyError) { content.textContent = state.historyError; return; }
|
|
25
|
+
var parsed = reconstructPatchSources(state.historyDiff || ''); before = parsed.oldStr; after = parsed.newStr;
|
|
26
|
+
if (state.historyMode === 'source') after = entryContent(state, target.entry);
|
|
27
|
+
} else if (target.kind === 'compare') { before = entryContent(state, target.entries[0], true); after = entryContent(state, target.entries[1]); }
|
|
28
|
+
else { before = historicalSnapshot(target.entry, true); after = historicalSnapshot(target.entry, false); }
|
|
29
|
+
var entries = target.entries || [target.entry], historical = entries.some(function (entry) { return entry.source !== 'git'; });
|
|
30
|
+
var note = entries.map(historicalScope).filter(Boolean).join(' ');
|
|
31
|
+
if (note) { var notice = document.createElement('p'); notice.textContent = note; body.insertBefore(notice, content); }
|
|
32
|
+
if (after === undefined || before === undefined && state.historyMode !== 'source') {
|
|
33
|
+
content.textContent = state.historyError || (historical ? 'This historical snapshot was not recorded. Use Source to inspect the recorded content, when available.' : 'Loading revision…'); return;
|
|
34
|
+
}
|
|
35
|
+
var ext = state.resource.path.split('.').pop();
|
|
36
|
+
if (state.historyMode === 'source') renderCodeWithLineNumbers(content, after, ext);
|
|
37
|
+
else content.appendChild((state.historyMode === 'unified' ? renderUnifiedDiff : renderSplitDiff)(before, after, mapExtToLanguage(ext)));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (!state.entries) { body.textContent = 'Loading history…'; return; }
|
|
41
|
+
if (!state.entries.length) { body.textContent = 'No file history'; return; }
|
|
42
|
+
var selected = state.historySelected || [], comparison = document.createElement('div'); comparison.className = 'file-history-view-bar';
|
|
43
|
+
comparison.textContent = 'Compare revisions (' + selected.length + '/2)';
|
|
44
|
+
var compare = control('Compare', function () {
|
|
45
|
+
if (!current(state) || selected.length !== 2) return;
|
|
46
|
+
change(state, { historyTarget: { kind: 'compare', entries: selected }, historyError: null });
|
|
47
|
+
selected.forEach(function (entry) { requestEntry(state, entry); });
|
|
48
|
+
}); compare.disabled = selected.length !== 2; comparison.appendChild(compare); body.appendChild(comparison);
|
|
49
|
+
state.entries.forEach(function (entry) {
|
|
50
|
+
var row = document.createElement('div'); row.className = 'file-history-entry';
|
|
51
|
+
var title = document.createElement('div'); title.className = 'file-history-title'; title.textContent = entry.message || entry.assistantSnippet || entry.toolName || 'Edit'; row.appendChild(title);
|
|
52
|
+
var meta = document.createElement('div'); meta.className = 'file-history-meta'; meta.textContent = entry.source === 'git' ? entry.hash.slice(0, 7) + ' · ' + (entry.author || '') : entry.sessionTitle || 'Conversation'; row.appendChild(meta);
|
|
53
|
+
if (entry.source !== 'git' && entry.toolName === 'Edit') row.appendChild(renderUnifiedDiff(entry.old_string || '', entry.new_string || '', mapExtToLanguage(state.resource.path.split('.').pop())));
|
|
54
|
+
var select = control(selected.indexOf(entry) >= 0 ? 'Deselect' : 'Select for comparison', function () {
|
|
55
|
+
var next = selected.filter(function (item) { return item !== entry; });
|
|
56
|
+
if (next.length === selected.length) next = next.slice(-1).concat(entry);
|
|
57
|
+
change(state, { historySelected: next });
|
|
58
|
+
}); row.appendChild(select);
|
|
59
|
+
row.appendChild(control('View change', function () {
|
|
60
|
+
change(state, { historyTarget: { kind: entry.source === 'git' ? 'git' : 'session', entry: entry }, historyError: null });
|
|
61
|
+
if (entry.source === 'git') { sendFilePaneRequest(state, 'fs_git_diff', { hash: entry.hash }); requestEntry(state, entry); }
|
|
62
|
+
}));
|
|
63
|
+
if (entry.source === 'session') row.appendChild(control('Go to conversation', function () { if (current(state)) navigateToEdit(entry); }));
|
|
64
|
+
body.appendChild(row);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
export function receiveFileHistory(state, msg) {
|
|
68
|
+
if (msg.type === 'fs_file_history_result') updateFilePaneState(state.instanceId, { entries: msg.entries || [], historyError: msg.error || null });
|
|
69
|
+
else if (msg.type === 'fs_git_diff_result') {
|
|
70
|
+
var target = state.historyTarget;
|
|
71
|
+
if (!target || target.kind !== 'git' || target.entry.hash !== msg.hash) return;
|
|
72
|
+
updateFilePaneState(state.instanceId, { historyDiff: msg.diff, historyDiffHash: msg.hash, historyError: msg.error || null });
|
|
73
|
+
} else if (msg.type === 'fs_file_at_result') {
|
|
74
|
+
var contents = Object.assign({}, state.historyContents || {}); contents[msg.hash] = msg.content;
|
|
75
|
+
updateFilePaneState(state.instanceId, { historyContents: contents, historyError: msg.error || null });
|
|
76
|
+
} else return;
|
|
77
|
+
if (state.historyVisible) repaintFilePane(state.instanceId);
|
|
78
|
+
}
|