clay-server 5.3.0-beta.5 → 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/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-admin-overview.js +22 -11
- package/lib/project-background-tasks.js +76 -0
- package/lib/project-connection.js +1 -1
- 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-sessions.js +12 -5
- package/lib/project-shared-browser.js +7 -3
- package/lib/project-split-view-document.js +16 -2
- package/lib/project-split-views.js +27 -297
- 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 +348 -0
- package/lib/project-view-terminal-binding.js +16 -0
- package/lib/project-view-workspace.js +126 -0
- package/lib/project-worker-conversation.js +5 -0
- package/lib/project.js +1 -0
- package/lib/public/app.js +9 -0
- package/lib/public/css/contextual-clay.css +2 -1
- package/lib/public/css/issues.css +3 -0
- package/lib/public/css/mate-conversation.css +1 -1
- package/lib/public/css/pane.css +5 -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/right-workbench.css +0 -1
- package/lib/public/css/scheduled-tasks.css +7 -0
- package/lib/public/css/shared-browser.css +31 -8
- package/lib/public/css/split-view-layout.css +129 -10
- package/lib/public/css/tool-pane.css +124 -0
- package/lib/public/index.html +6 -6
- 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/clay-file-links.js +2 -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/contextual-clay-geometry.js +7 -3
- 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 +43 -82
- 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 +7 -3
- package/lib/public/modules/pane-binding.js +5 -1
- package/lib/public/modules/pane-browser-bridge.js +6 -2
- package/lib/public/modules/pane-browser-transport.js +110 -0
- 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/plantuml-preview.js +12 -12
- package/lib/public/modules/project-admin-overview.js +125 -10
- package/lib/public/modules/project-grouping.js +9 -0
- package/lib/public/modules/right-workbench.js +34 -15
- package/lib/public/modules/scheduled-tasks.js +7 -6
- 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/shared-browser.js +69 -9
- package/lib/public/modules/sidebar-projects.js +1 -10
- package/lib/public/modules/split-pane-renderer.js +1 -0
- 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-drag-preview.js +90 -0
- package/lib/public/modules/split-view-layout-client.js +1 -1
- 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-drag.js +36 -13
- package/lib/public/modules/split-view-presentation-drop.js +21 -18
- package/lib/public/modules/split-view-presentation-renderer.js +22 -17
- package/lib/public/modules/split-view-presentations.js +25 -33
- 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/split-view.js +9 -2
- package/lib/public/modules/sticky-notes-browser.js +2 -2
- package/lib/public/modules/terminal-viewport.js +14 -0
- package/lib/public/modules/terminal.js +49 -23
- package/lib/public/modules/theme-palette.js +177 -0
- package/lib/public/modules/theme.js +2 -180
- package/lib/public/modules/tool-pane-chrome.js +88 -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/tools.js +5 -89
- package/lib/public/modules/tooltip.js +32 -6
- 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 +13 -2
- package/lib/public/modules/workbench-layout-preference.js +12 -5
- package/lib/public/modules/workbench-panels.js +21 -0
- 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-6QFLYOVY.js +1 -0
- package/lib/public/pane-assets/chunk-7OVNC5HT.js +1 -0
- package/lib/public/pane-assets/chunk-BQN7B62H.js +3 -0
- package/lib/public/pane-assets/chunk-EMLN2Y7L.js +1 -0
- package/lib/public/pane-assets/chunk-ENPAHXLB.js +1 -0
- 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-FQTNR4ZS.js +1 -0
- package/lib/public/pane-assets/chunk-JIV7O5GN.js +1 -0
- 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-R7QVVFUG.js +1 -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-SJOT74VW.js +2 -0
- package/lib/public/pane-assets/chunk-SJTGHIN5.js +1 -0
- 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-UPXU2IPC.js +1 -0
- package/lib/public/pane-assets/chunk-VEBYD7TL.js +1 -0
- package/lib/public/pane-assets/chunk-VZ2OKDAO.js +1 -0
- package/lib/public/pane-assets/chunk-XBHYNXXT.js +1 -0
- package/lib/public/pane-assets/manifest.json +438 -381
- package/lib/public/pane-assets/style-BSKKK54Q.css +1 -0
- package/lib/public/style.css +1 -0
- package/lib/sdk-message-processor.js +5 -2
- package/lib/server-storage.js +27 -3
- package/lib/server-workbench-layout-settings.js +3 -3
- package/lib/server.js +4 -2
- package/lib/sessions.js +1 -1
- package/lib/split-view-layout.js +11 -7
- package/lib/storage-usage.js +29 -7
- package/lib/terminal-manager.js +2 -2
- package/lib/users-workbench-layout-preferences.js +14 -4
- package/lib/workbench-window-layout.js +30 -0
- 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-6H5S2Y3Z.js +0 -1
- package/lib/public/pane-assets/chunk-3QV5SDC4.js +0 -1
- package/lib/public/pane-assets/chunk-4FM3U3DW.js +0 -1
- package/lib/public/pane-assets/chunk-7GZH3FZY.js +0 -1
- package/lib/public/pane-assets/chunk-AGJTF5WQ.js +0 -1
- 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-NPKRTUNG.js +0 -2
- package/lib/public/pane-assets/chunk-VGA2Y3D2.js +0 -2
- package/lib/public/pane-assets/chunk-WKO3T75E.js +0 -167
- package/lib/public/pane-assets/style-TSRDU54Z.css +0 -1
|
@@ -5,13 +5,25 @@ import { checkComposerDeparture } from './session-rich-drafts.js';
|
|
|
5
5
|
import { showToast } from './utils.js';
|
|
6
6
|
import { listIds, place, swap, pairTree } from './split-view-layout-client.js';
|
|
7
7
|
|
|
8
|
+
// Preview and persistence must resolve the same tree for every drop region.
|
|
9
|
+
export function presentationDropTree(drag, target, sourceView, targetView) {
|
|
10
|
+
if (!target || target.folderId || drag.origin === target.origin) return null;
|
|
11
|
+
var sameView = sourceView && targetView && sourceView.id === targetView.id;
|
|
12
|
+
if (target.sidebar && sameView) return null;
|
|
13
|
+
var side = target.side === "center" ? (sameView ? "center" : "right") : target.side;
|
|
14
|
+
if (!targetView) return pairTree(target.origin, drag.origin, side);
|
|
15
|
+
var result = side === "center" ? swap(targetView.tree, drag.origin, target.origin) : place(targetView.tree, drag.origin, target.origin, side, 0.5);
|
|
16
|
+
return result.ok ? result.tree : null;
|
|
17
|
+
}
|
|
18
|
+
|
|
8
19
|
export function applyPresentationDrop(drag, target, mutation, checkMountedPaneDrafts) {
|
|
9
20
|
var views = store.get('projectSplitViews') || [], sourceView = views.find(function (view) { return view.id === drag.sourceViewId; }), targetView = views.find(function (view) { return view.id === target.viewId; });
|
|
10
21
|
var sessions = getCachedSessions() || [], sourceSession = exactDriverForOrigin(drag.origin, sessions), targetSession = exactDriverForOrigin(target.origin, sessions);
|
|
11
22
|
if (!sourceSession || !targetSession || sourceSession.id !== drag.sessionId) return;
|
|
12
23
|
var sameView = sourceView && targetView && sourceView.id === targetView.id;
|
|
13
24
|
if (target.sidebar && sameView) return;
|
|
14
|
-
var
|
|
25
|
+
var plannedTree = presentationDropTree(drag, target, sourceView, targetView);
|
|
26
|
+
if (!plannedTree) return false;
|
|
15
27
|
function checkDeparture() {
|
|
16
28
|
try {
|
|
17
29
|
if (sourceView && sourceView.id === store.get('projectSplitViewActiveId')) checkMountedPaneDrafts();
|
|
@@ -22,36 +34,27 @@ export function applyPresentationDrop(drag, target, mutation, checkMountedPaneDr
|
|
|
22
34
|
if (!targetView) {
|
|
23
35
|
if (sourceView) {
|
|
24
36
|
if (!checkDeparture()) return;
|
|
25
|
-
var newTree =
|
|
26
|
-
mutation("project_split_view_move", { sourceViewId: sourceView.id, sourceSessionOriginId: drag.origin,
|
|
37
|
+
var newTree = plannedTree;
|
|
38
|
+
return mutation("project_split_view_move", { sourceViewId: sourceView.id, sourceSessionOriginId: drag.origin,
|
|
27
39
|
targetSessionOriginId: target.origin, targetSessionOrigins: listIds(newTree), tree: newTree, layout: "balanced",
|
|
28
40
|
activeSessionOriginId: target.origin, name: "Split view" }, { activateCreatedView: true });
|
|
29
41
|
} else {
|
|
30
42
|
if (!checkDeparture()) return;
|
|
31
|
-
var createdTree =
|
|
32
|
-
mutation("project_split_view_create", { name: "Split view", sessionOrigins: listIds(createdTree), tree: createdTree,
|
|
43
|
+
var createdTree = plannedTree;
|
|
44
|
+
return mutation("project_split_view_create", { name: "Split view", sessionOrigins: listIds(createdTree), tree: createdTree,
|
|
33
45
|
layout: "balanced", activeSessionOriginId: target.origin }, { activateCreatedView: true });
|
|
34
46
|
}
|
|
35
|
-
return;
|
|
36
47
|
}
|
|
37
48
|
if (sourceView && targetView.id !== sourceView.id) {
|
|
38
49
|
if (targetView.sessions.length >= 8) { showToast("Eight sessions are already open. Close a pane to add another.", "warn"); return; }
|
|
39
|
-
var movedTree =
|
|
40
|
-
|
|
41
|
-
mutation("project_split_view_move", { sourceViewId: sourceView.id, sourceSessionOriginId: drag.origin,
|
|
50
|
+
var movedTree = { tree: plannedTree };
|
|
51
|
+
return mutation("project_split_view_move", { sourceViewId: sourceView.id, sourceSessionOriginId: drag.origin,
|
|
42
52
|
targetViewId: targetView.id, targetSessionOrigins: listIds(movedTree.tree), tree: movedTree.tree,
|
|
43
53
|
layout: targetView.layout, activeSessionOriginId: targetView.activeSessionOriginId }, target.sidebar ? { activateCreatedView: true, createdViewId: targetView.id } : null);
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
var nextTree;
|
|
47
|
-
if (side === "center" && sourceView) nextTree = swap(targetView.tree, drag.origin, target.origin).tree;
|
|
48
|
-
else {
|
|
49
|
-
var placed = place(targetView.tree, drag.origin, target.origin, side, 0.5);
|
|
50
|
-
if (!placed.ok) return;
|
|
51
|
-
nextTree = placed.tree;
|
|
52
54
|
}
|
|
55
|
+
var nextTree = plannedTree;
|
|
53
56
|
var origins = listIds(nextTree);
|
|
54
57
|
if (origins.length > 8) { showToast("Eight sessions are already open. Close a pane to add another.", "warn"); return; }
|
|
55
|
-
mutation("project_split_view_update", { id: targetView.id, sessionOrigins: origins, tree: nextTree,
|
|
58
|
+
return mutation("project_split_view_update", { id: targetView.id, sessionOrigins: origins, tree: nextTree,
|
|
56
59
|
layout: targetView.layout, activeSessionOriginId: targetView.activeSessionOriginId }, target.sidebar ? { activateCreatedView: true, createdViewId: targetView.id } : null);
|
|
57
60
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { paintLegacyToolPanes, clearToolPaneWindows } from './tool-pane-legacy.js';
|
|
2
|
+
import { toolPaneLayoutTree, paintToolPaneWindows, bindWorkspaceDivider, dockedToolPanes } from './tool-pane-layout.js';
|
|
3
|
+
import { afterPaneMotion } from './split-view-drag-preview.js';
|
|
4
|
+
import { layoutScope, currentLayoutScope, presentationTree, bindingMatches } from './split-view-layout-scope.js';
|
|
2
5
|
// Flat, stable pane presentation for persisted binary trees. Geometry is
|
|
3
6
|
// applied to existing direct-child .split-pane nodes; iframes are never moved
|
|
4
7
|
// below a nested layout wrapper.
|
|
@@ -14,6 +17,7 @@ import { syncPaneHeaderState } from './split-pane-header-state.js';
|
|
|
14
17
|
import { rectangles, resize, validateTree, listIds } from './split-view-layout-client.js';
|
|
15
18
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
16
19
|
import { appendLayoutOption } from './split-view-layout-icons.js';
|
|
20
|
+
import { reserveEmptySplit, clearEmptySplit } from './split-session-empty-pane.js';
|
|
17
21
|
|
|
18
22
|
var host = null;
|
|
19
23
|
var app = null;
|
|
@@ -21,7 +25,6 @@ var menuTrigger = null;
|
|
|
21
25
|
var resizeObserver = null;
|
|
22
26
|
var geometryToken = 0;
|
|
23
27
|
var stickyStatusOrigins = null;
|
|
24
|
-
|
|
25
28
|
function relocateStickyStatus(enabled) {
|
|
26
29
|
var ids = ['todo-sticky', 'ralph-sticky', 'debate-sticky'];
|
|
27
30
|
var main = document.getElementById('main-panels'), appElement = document.getElementById('app');
|
|
@@ -41,7 +44,6 @@ function relocateStickyStatus(enabled) {
|
|
|
41
44
|
});
|
|
42
45
|
}
|
|
43
46
|
}
|
|
44
|
-
|
|
45
47
|
function dispatch(name, detail) {
|
|
46
48
|
document.dispatchEvent(new CustomEvent(name, { detail: detail }));
|
|
47
49
|
}
|
|
@@ -50,6 +52,7 @@ export function initPresentationRenderer(hostElement) {
|
|
|
50
52
|
host = hostElement;
|
|
51
53
|
app = document.getElementById("app");
|
|
52
54
|
ensureArrangeMenu();
|
|
55
|
+
document.addEventListener('clay-split-picker-layout', renderCurrentGeometry);
|
|
53
56
|
document.addEventListener("click", onClick, true);
|
|
54
57
|
document.addEventListener("keydown", onKeyDown, true);
|
|
55
58
|
document.addEventListener("pointermove", onPointerMove, true);
|
|
@@ -67,6 +70,7 @@ export function initPresentationRenderer(hostElement) {
|
|
|
67
70
|
|
|
68
71
|
export function hidePresentationView() {
|
|
69
72
|
if (!host) return;
|
|
73
|
+
clearEmptySplit(host);
|
|
70
74
|
var legacySplit = store.get('splitPanes');
|
|
71
75
|
if (legacySplit) releaseRetainedPanes(host);
|
|
72
76
|
else clearRetainedPanes(host);
|
|
@@ -84,6 +88,7 @@ export function hidePresentationView() {
|
|
|
84
88
|
delete host.dataset.positioned;
|
|
85
89
|
delete host.dataset.singlePane;
|
|
86
90
|
if (!legacySplit) {
|
|
91
|
+
clearToolPaneWindows();
|
|
87
92
|
host.classList.remove("visible");
|
|
88
93
|
if (app) app.classList.remove("split-native-hidden");
|
|
89
94
|
}
|
|
@@ -98,7 +103,7 @@ export function hidePresentationView() {
|
|
|
98
103
|
if (legacySplit) {
|
|
99
104
|
Array.from(host.querySelectorAll(':scope > .split-view-divider, :scope > .split-view-drop-preview')).forEach(function (item) { item.remove(); });
|
|
100
105
|
Array.from(host.querySelectorAll('.split-view-arrange-trigger')).forEach(function (trigger) { trigger.remove(); });
|
|
101
|
-
closePresentationArrange(false);
|
|
106
|
+
closePresentationArrange(false); paintLegacyToolPanes(host);
|
|
102
107
|
return;
|
|
103
108
|
}
|
|
104
109
|
var frames = host.querySelectorAll('.split-pane-frame');
|
|
@@ -169,8 +174,9 @@ function currentSingletonGeometry() {
|
|
|
169
174
|
}
|
|
170
175
|
|
|
171
176
|
function renderCurrentGeometry() {
|
|
177
|
+
if (paintLegacyToolPanes(host)) return;
|
|
172
178
|
var view = currentView(), tree = activeTree();
|
|
173
|
-
if (view && tree) { applyGeometry(view, tree); return; }
|
|
179
|
+
if (view && (tree || !view.sessions.length)) { applyGeometry(view, tree); return; }
|
|
174
180
|
var singleton = currentSingletonGeometry();
|
|
175
181
|
if (singleton) applyGeometry(singleton.view, { id: singleton.origin }, function () {
|
|
176
182
|
var current = currentSingletonGeometry();
|
|
@@ -211,12 +217,15 @@ function applyGeometry(view, tree, stillCurrent) {
|
|
|
211
217
|
});
|
|
212
218
|
var style = window.getComputedStyle(host), left = parseFloat(style.paddingLeft) || 0, top = parseFloat(style.paddingTop) || 0;
|
|
213
219
|
var right = parseFloat(style.paddingRight) || 0, bottom = parseFloat(style.paddingBottom) || 0;
|
|
214
|
-
var
|
|
220
|
+
var windowTree = toolPaneLayoutTree(view, tree);
|
|
221
|
+
var geometry = rectangles(windowTree, Math.max(0, host.clientWidth - left - right), Math.max(0, host.clientHeight - top - bottom), 7);
|
|
222
|
+
paintToolPaneWindows(host, view, tree, windowTree, geometry, { left: left, top: top });
|
|
223
|
+
var reserving = reserveEmptySplit(host, geometry, { left: left, top: top });
|
|
215
224
|
var focused = store.get('projectSplitViewFocusedOrigin');
|
|
216
225
|
var panes = host.querySelectorAll(':scope > .split-pane');
|
|
217
226
|
for (var i = 0; i < panes.length; i++) {
|
|
218
227
|
var frame = panes[i].querySelector('.split-pane-frame'), origin = panes[i].dataset.presentationOrigin || frame && frame.dataset.sessionOriginId;
|
|
219
|
-
var rect = host.dataset.singlePane === 'true' && panes.length === 1 ?
|
|
228
|
+
var rect = !reserving && host.dataset.singlePane === 'true' && panes.length === 1 && !dockedToolPanes().length ?
|
|
220
229
|
{ x: 0, y: 0, width: Math.max(0, host.clientWidth - left - right), height: Math.max(0, host.clientHeight - top - bottom) } : geometry.panes[origin];
|
|
221
230
|
if (!rect) continue;
|
|
222
231
|
if (focused && geometry.panes[focused]) {
|
|
@@ -239,13 +248,15 @@ function applyGeometry(view, tree, stillCurrent) {
|
|
|
239
248
|
divider.setAttribute("role", "separator"); divider.setAttribute("tabindex", "0");
|
|
240
249
|
divider.setAttribute("aria-orientation", entry.axis === "horizontal" ? "vertical" : "horizontal");
|
|
241
250
|
divider.setAttribute("aria-label", "Resize Split view panels");
|
|
251
|
+
if (reserving) { divider.inert = true; divider.tabIndex = -1; divider.setAttribute('aria-disabled', 'true'); }
|
|
242
252
|
divider.style.left = (left + entry.rect.x) + "px"; divider.style.top = (top + entry.rect.y) + "px";
|
|
243
253
|
divider.style.width = entry.rect.width + "px"; divider.style.height = entry.rect.height + "px";
|
|
244
|
-
|
|
254
|
+
if (dockedToolPanes().length) bindWorkspaceDivider(divider, entry, windowTree);
|
|
255
|
+
else bindDivider(divider, entry, tree, listIds(tree), scope); host.appendChild(divider);
|
|
245
256
|
if (focusedDividerPath && divider.dataset.path === focusedDividerPath) divider.focus();
|
|
246
257
|
}
|
|
247
258
|
ensureDropPreview();
|
|
248
|
-
|
|
259
|
+
afterPaneMotion(host, function () {
|
|
249
260
|
if (resizeToken !== 'split-view-layout-' + geometryToken || (stillCurrent ? !stillCurrent() : !currentLayoutScope(scope))) return;
|
|
250
261
|
resizeFrames.forEach(function (item) {
|
|
251
262
|
var live = item.frame.clayPane && item.frame.clayPane.identity();
|
|
@@ -254,17 +265,11 @@ function applyGeometry(view, tree, stillCurrent) {
|
|
|
254
265
|
});
|
|
255
266
|
});
|
|
256
267
|
}
|
|
257
|
-
|
|
258
|
-
function bindingMatches(left, right) {
|
|
259
|
-
return !!left && !!right && left.sessionId === right.sessionId && left.sessionOriginId === right.sessionOriginId &&
|
|
260
|
-
left.generation === right.generation && left.requestId === right.requestId;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
function ensureArrangeTrigger(header) {
|
|
268
|
+
export function ensureArrangeTrigger(header) {
|
|
264
269
|
if (!header) return;
|
|
265
270
|
if (!header.querySelector('[data-action="arrange"]')) {
|
|
266
271
|
var trigger = document.createElement("button"); trigger.type = "button"; trigger.className = "split-view-arrange-trigger";
|
|
267
|
-
trigger.dataset.action = "arrange"; trigger.setAttribute("aria-label", "
|
|
272
|
+
trigger.dataset.action = "arrange"; trigger.setAttribute("aria-label", "Split view options");
|
|
268
273
|
trigger.setAttribute("aria-expanded", "false"); trigger.setAttribute("aria-controls", "split-view-arrange-menu");
|
|
269
274
|
trigger.innerHTML = iconHtml("columns-2");
|
|
270
275
|
var close = header.querySelector(".split-pane-close"); header.insertBefore(trigger, close || null);
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import { workspaceChanged } from './view-workspace-state.js';
|
|
2
|
+
import { restoreViewPair } from './view-pair-presentation.js';
|
|
3
|
+
import { repaintLegacySplit } from './split-view.js';
|
|
4
|
+
import { validWorkspaceView } from './view-workspace-validation.js';
|
|
5
|
+
import { mutation } from './view-mutations.js';
|
|
6
|
+
import { initSavedWorkspace } from './view-workspace-controller.js';
|
|
1
7
|
import { preserveReconnectingPresentation } from './split-pane-retention.js';
|
|
2
8
|
import { layoutScope, currentLayoutScope, presentationTree } from './split-view-layout-scope.js';
|
|
3
9
|
import { validPresentationClose } from './split-view-presentation-close.js';
|
|
@@ -99,7 +105,7 @@ export function handleProjectSplitViews(msg) {
|
|
|
99
105
|
var invalidView = false;
|
|
100
106
|
msg.views.forEach(function (view) {
|
|
101
107
|
var ids = view && Array.isArray(view.sessions) ? view.sessions.map(function (session) { return session.sessionOriginId; }) : [];
|
|
102
|
-
if (ids.length
|
|
108
|
+
if (ids.length > 8 || viewSessions(view).length !== ids.length || !validateTree(view.tree, ids).ok || !validWorkspaceView(view)) invalidView = true;
|
|
103
109
|
else nextViews.push(view);
|
|
104
110
|
});
|
|
105
111
|
if (invalidView) {
|
|
@@ -115,7 +121,7 @@ export function handleProjectSplitViews(msg) {
|
|
|
115
121
|
if (state.projectSplitViewDrag || state.projectSplitViewDividerDrag) cleanupTransientInteractions(state);
|
|
116
122
|
var activeId = state.projectSplitViewActiveId;
|
|
117
123
|
if (!viewExists(nextViews, activeId)) {
|
|
118
|
-
var restored = restorablePresentation(state, nextViews, getCachedSessions());
|
|
124
|
+
var restored = nextViews.find(function (view) { return view.id === msg.activeViewId && !view.sessions.length; }) || restorablePresentation(state, nextViews, getCachedSessions());
|
|
119
125
|
if (restored) activeId = restored.id;
|
|
120
126
|
}
|
|
121
127
|
var removedActive = activeId && !viewExists(nextViews, activeId) ? activeId : null;
|
|
@@ -126,7 +132,7 @@ export function handleProjectSplitViews(msg) {
|
|
|
126
132
|
var survivor = exactDriverForOrigin(completedPending.meta.activeOrigin, getCachedSessions());
|
|
127
133
|
if (survivor) standaloneTarget = { id: survivor.id, origin: survivor.sessionOriginId, previousSessionId: state.activeSessionId, contextKey: contextKey(state) };
|
|
128
134
|
}
|
|
129
|
-
store.set({ projectSplitViews: nextViews, projectSplitViewRevision: msg.revision, projectSplitViewHydrated: true,
|
|
135
|
+
store.set({ projectSplitViews: nextViews, projectSplitViewRevision: msg.revision, projectSplitViewSavedActiveId: msg.activeViewId || null, projectSplitViewHydrated: true,
|
|
130
136
|
projectSplitViewError: msg.error || "", projectSplitViewStandaloneTarget: standaloneTarget, projectSplitViewActiveId: viewExists(nextViews, activeId) ? activeId : null,
|
|
131
137
|
projectSplitViewFocusedOrigin: viewExists(nextViews, activeId) ? state.projectSplitViewFocusedOrigin : null,
|
|
132
138
|
projectSplitViewPending: readyPending, projectSplitViewPreviewTree: readyPending ? state.projectSplitViewPreviewTree : null,
|
|
@@ -182,22 +188,6 @@ export function organizePresentationView(id, revision, folderId, favorite) {
|
|
|
182
188
|
if (!view || view.revision !== revision) return false;
|
|
183
189
|
return mutation('project_split_view_organize', { id: id, expectedViewRevision: revision, projectSlug: store.get('currentSlug'), folderId: folderId, favorite: favorite });
|
|
184
190
|
}
|
|
185
|
-
function mutation(type, body, meta) {
|
|
186
|
-
var state = store.snap();
|
|
187
|
-
if (!projectReady(state) || !state.projectSplitViewHydrated || state.projectSplitViewError || state.projectSplitViewPending) return false;
|
|
188
|
-
var requestId = "split-view-" + Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 10);
|
|
189
|
-
var ws = getWs();
|
|
190
|
-
var message = Object.assign({ type: type, requestId: requestId, expectedRevision: state.projectSplitViewRevision }, body || {});
|
|
191
|
-
var pending = { requestId: requestId, type: type, socket: ws, contextKey: contextKey(state), expectedRevision: state.projectSplitViewRevision, meta: meta || null,
|
|
192
|
-
awaitingBroadcast: false, resultRevision: null };
|
|
193
|
-
store.set({ projectSplitViewPending: pending, projectSplitViewError: "" });
|
|
194
|
-
if (!sendMessage(message)) {
|
|
195
|
-
store.set({ projectSplitViewPending: null, projectSplitViewError: "Split view change could not be sent. Refresh before trying again." });
|
|
196
|
-
showToast("Split view change could not be sent. Refresh before trying again.", "error");
|
|
197
|
-
return false;
|
|
198
|
-
}
|
|
199
|
-
return true;
|
|
200
|
-
}
|
|
201
191
|
export function setActiveView(id, preferredOrigin) {
|
|
202
192
|
var state = store.snap(), view = viewExists(state.projectSplitViews, id);
|
|
203
193
|
if (!view) return false;
|
|
@@ -235,6 +225,7 @@ export function switchToStandalone(sessionId) {
|
|
|
235
225
|
return true;
|
|
236
226
|
}
|
|
237
227
|
function renderActive() {
|
|
228
|
+
if (store.get('splitPanes')) { repaintLegacySplit(); return; }
|
|
238
229
|
if (!host || !nativeApp || preserveReconnectingPresentation(host)) return;
|
|
239
230
|
var state = store.snap(), view = viewExists(state.projectSplitViews, state.projectSplitViewActiveId);
|
|
240
231
|
if (state.splitIdentityBlockedSocket === getWs()) { hidePresentationView(); return; }
|
|
@@ -246,9 +237,11 @@ function renderActive() {
|
|
|
246
237
|
}
|
|
247
238
|
var singleton = currentSingletonPresentation(state, getCachedSessions());
|
|
248
239
|
if (singleton) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
240
|
+
if (state.projectSplitViewHydrated && !state.projectSplitViewPending && !state.projectSplitViewError) mutation("project_split_view_create", { name: "View", sessionOrigins: [singleton.origin], tree: { id: singleton.origin }, layout: "balanced", activeSessionOriginId: singleton.origin }, { activateCreatedView: true });
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
if (state.projectSplitViewHydrated && projectReady(state) && !(getCachedSessions() || []).length && !state.projectSplitViewPending && !state.projectSplitViewError) {
|
|
244
|
+
mutation('project_split_view_create', { name: 'View', sessionOrigins: [], tree: null, layout: 'balanced', activeSessionOriginId: null }, { activateCreatedView: true });
|
|
252
245
|
return;
|
|
253
246
|
}
|
|
254
247
|
if (state.projectSplitViewSingletonOrigin) store.set({ projectSplitViewSingletonOrigin: null });
|
|
@@ -257,10 +250,11 @@ function renderActive() {
|
|
|
257
250
|
return;
|
|
258
251
|
}
|
|
259
252
|
if (state.projectSplitViewSingletonOrigin) store.set({ projectSplitViewSingletonOrigin: null });
|
|
253
|
+
if (restoreViewPair(view)) return;
|
|
260
254
|
var sessions = viewSessions(view);
|
|
261
255
|
var tree = presentationTree(view);
|
|
262
256
|
var ids = listIds(tree);
|
|
263
|
-
if (sessions.length !== view.sessions.length || ids.length
|
|
257
|
+
if (sessions.length !== view.sessions.length || ids.length > 8 || !validateTree(tree, ids).ok) return;
|
|
264
258
|
if (stickyContainer && mainPanels) mainPanels.appendChild(stickyContainer);
|
|
265
259
|
renderPresentationView(view, tree, sessions);
|
|
266
260
|
}
|
|
@@ -424,14 +418,9 @@ function onClosePane(event) {
|
|
|
424
418
|
if (!session || !view.sessions.some(function (item) { return item.sessionOriginId === session.sessionOriginId; })) return;
|
|
425
419
|
try { checkMountedPaneDrafts(); } catch (error) { showToast(error.message, "warn"); return; }
|
|
426
420
|
var origins = view.sessions.map(function (item) { return item.sessionOriginId; }).filter(function (origin) { return origin !== session.sessionOriginId; });
|
|
427
|
-
if (origins.length < 2) {
|
|
428
|
-
var remaining = view.sessions.filter(function (item) { return item.sessionOriginId !== session.sessionOriginId; })[0];
|
|
429
|
-
if (remaining) mutation("project_split_view_separate", { id: view.id }, { separateViewId: view.id, activeOrigin: remaining.sessionOriginId });
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
421
|
var nextTree = remove(view.tree, session.sessionOriginId);
|
|
433
|
-
if (!
|
|
434
|
-
var nextActive = view.activeSessionOriginId === session.sessionOriginId ? origins[0] : view.activeSessionOriginId;
|
|
422
|
+
if (!validateTree(nextTree, origins).ok) return;
|
|
423
|
+
var nextActive = view.activeSessionOriginId === session.sessionOriginId ? origins[0] || null : view.activeSessionOriginId;
|
|
435
424
|
mutation("project_split_view_update", { id: view.id, sessionOrigins: origins, tree: nextTree,
|
|
436
425
|
layout: view.layout, activeSessionOriginId: nextActive }, { closedOrigin: session.sessionOriginId, activeOrigin: nextActive });
|
|
437
426
|
}
|
|
@@ -442,13 +431,15 @@ function onFocus(event) {
|
|
|
442
431
|
if (event.detail && event.detail.focused && !source) return;
|
|
443
432
|
store.set({ projectSplitViewFocusedOrigin: event.detail && event.detail.focused ? source.sessionOriginId : null });
|
|
444
433
|
renderActive();
|
|
434
|
+
if (!store.get('savedWorkspaceApplying')) workspaceChanged();
|
|
445
435
|
}
|
|
446
436
|
function init(hostElement) {
|
|
447
437
|
host = hostElement; nativeApp = document.getElementById("app"); mainPanels = document.getElementById("main-panels");
|
|
448
438
|
stickyContainer = document.getElementById("sticky-notes-container");
|
|
449
439
|
if (stickyContainer) { stickyHome = stickyContainer.parentNode; stickyAnchor = stickyContainer.nextSibling; }
|
|
450
440
|
initPresentationRenderer(host);
|
|
451
|
-
|
|
441
|
+
initSavedWorkspace();
|
|
442
|
+
initPresentationDrag(host, function (drag, target) { return applyPresentationDrop(drag, target, mutation, checkMountedPaneDrafts); });
|
|
452
443
|
initSplitViewCreateMenu();
|
|
453
444
|
document.addEventListener('clay-split-view-create', function (event) { handleSplitViewCreateEvent(event, mutation, checkMountedPaneDrafts); });
|
|
454
445
|
document.addEventListener("click", onClick, true);
|
|
@@ -476,7 +467,7 @@ function init(hostElement) {
|
|
|
476
467
|
state.paneBindingSessions !== previous.paneBindingSessions || state.sessionActivatedProjectSlug !== previous.sessionActivatedProjectSlug) renderActive();
|
|
477
468
|
if (state.activeSessionId !== previous.activeSessionId && state.projectSplitViewActiveId) {
|
|
478
469
|
var activeView = viewExists(state.projectSplitViews, state.projectSplitViewActiveId);
|
|
479
|
-
if (activeView && !activeView.sessions.some(function (session) { return session.id === state.activeSessionId; })) {
|
|
470
|
+
if (activeView && activeView.sessions.length && !activeView.sessions.some(function (session) { return session.id === state.activeSessionId; })) {
|
|
480
471
|
store.set({ projectSplitViewActiveId: null, projectSplitViewPreviewTree: null });
|
|
481
472
|
}
|
|
482
473
|
}
|
|
@@ -493,6 +484,7 @@ export function applyReviewedSplitView(target) {
|
|
|
493
484
|
var view = viewExists(store.get('projectSplitViews'), target.view.id);
|
|
494
485
|
if (!projectReady(store.snap()) || store.get('projectSplitViewPending') || !view || JSON.stringify(view) !== JSON.stringify(target.view)) return false;
|
|
495
486
|
if (!setActiveView(view.id, view.activeSessionOriginId)) return false;
|
|
496
|
-
|
|
487
|
+
store.set({ savedWorkspaceApplying: true });
|
|
488
|
+
try { onFocus({ detail: { focused: !!target.focusOrigin, sessionOriginId: target.focusOrigin } }); } finally { store.set({ savedWorkspaceApplying: false }); }
|
|
497
489
|
return true;
|
|
498
490
|
}
|
|
@@ -22,14 +22,14 @@ export function workerActivityLabel(message, names) {
|
|
|
22
22
|
}
|
|
23
23
|
export function captureWorkerDisclosures(body) {
|
|
24
24
|
return {
|
|
25
|
-
open: Array.from(body.querySelectorAll('details[open]')).map(function (node) { return node.dataset.disclosureKey; }),
|
|
25
|
+
open: Array.from(body.querySelectorAll('details[open], [data-disclosure-key][aria-expanded="true"]')).map(function (node) { return node.dataset.disclosureKey; }),
|
|
26
26
|
focus: document.activeElement && body.contains(document.activeElement) && (document.activeElement.dataset.disclosureKey || document.activeElement.parentElement && document.activeElement.parentElement.dataset.disclosureKey)
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
29
|
export function restoreWorkerDisclosures(body, saved) {
|
|
30
30
|
Array.from(body.querySelectorAll('[data-disclosure-key]')).forEach(function (node) {
|
|
31
31
|
var key = node.dataset.disclosureKey;
|
|
32
|
-
if (saved.open.indexOf(key) >= 0) node.open = true;
|
|
32
|
+
if (saved.open.indexOf(key) >= 0) { if (node.tagName === 'DETAILS') node.open = true; else if (node.getAttribute('aria-expanded') !== 'true') node.click(); }
|
|
33
33
|
if (saved.focus === key) {
|
|
34
34
|
var target = node.tagName === 'DETAILS' ? node.querySelector('summary') : node;
|
|
35
35
|
if (target) target.focus({ preventScroll: true });
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { workerActivityNames } from './split-view-worker-activity.js';
|
|
2
|
+
import { createAssistantBubble, createUserBubble, renderAssistantBubbleText } from './chat-bubble-renderer.js';
|
|
3
|
+
import { createSessionToolItem, createSessionToolGroup, sessionToolGroupSummary } from './session-tool-item.js';
|
|
4
|
+
import { applyClayToolItem, bindToolGroupDisclosure } from './clay-tool-activity.js';
|
|
5
|
+
import { iconHtml } from './icons.js';
|
|
6
|
+
import { VENDOR_AVATARS } from './chat-render-runtime.js';
|
|
3
7
|
|
|
4
8
|
// Reassemble bounded record fragments and contiguous streaming deltas before
|
|
5
9
|
// parsing Markdown, including fences and lists that cross a page boundary.
|
|
@@ -17,38 +21,71 @@ export function workerTranscriptRows(messages) {
|
|
|
17
21
|
return rows;
|
|
18
22
|
}
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
function inputFor(message) {
|
|
25
|
+
if (message.recordType !== 'tool_use' && message.recordType !== 'tool_executing') return null;
|
|
26
|
+
try { var start = message.text.indexOf('{'); return start >= 0 ? JSON.parse(message.text.slice(start)) : null; } catch (error) { return null; }
|
|
27
|
+
}
|
|
28
|
+
export function appendWorkerMessage(body, message, names, options) {
|
|
29
|
+
options = options || {};
|
|
21
30
|
if (message.role === 'tool') {
|
|
22
|
-
var
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
var
|
|
27
|
-
|
|
28
|
-
var
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
var name = message.toolName || names && names[message.toolId] && names[message.toolId].name || 'Tool activity';
|
|
32
|
+
var el = createSessionToolItem(message.toolId, name);
|
|
33
|
+
el.classList.add('worker-readonly-message', 'worker-tool-message'); el.dataset.recordIndex = String(message.recordIndex);
|
|
34
|
+
el.dataset.phase = message.isError ? 'failed' : message.recordType === 'tool_result' ? 'returned' : 'recorded';
|
|
35
|
+
var input = inputFor(message), output = message.resultText;
|
|
36
|
+
var tool = { el: el, name: name, input: input, clayRawResult: output, clayOutcome: output !== undefined ? { phase: message.isError ? 'error' : 'done' } : { phase: 'returned', status: 'result_unavailable' } };
|
|
37
|
+
var header = el.querySelector('.tool-header'); header.dataset.disclosureKey = 'record-' + message.recordIndex;
|
|
38
|
+
if (!applyClayToolItem(tool)) {
|
|
39
|
+
var terminal = message.isError ? 'Failed' : output !== undefined || message.recordType === 'tool_result' ? 'Result returned' : 'Call recorded';
|
|
40
|
+
el.querySelector('.tool-desc').textContent = input && (input.file_path || input.command || input.pattern) || '';
|
|
41
|
+
el.querySelector('.tool-subtitle-text').textContent = terminal;
|
|
42
|
+
el.querySelector('.tool-status-icon').innerHTML = iconHtml(message.isError ? 'alert-triangle' : output !== undefined ? 'check' : 'circle-help');
|
|
43
|
+
el.classList.toggle('done', output !== undefined); el.classList.toggle('error', !!message.isError);
|
|
44
|
+
var block = document.createElement('div'); block.className = 'tool-result-block collapsed';
|
|
45
|
+
var pre = document.createElement('pre'); pre.textContent = output !== undefined ? output : message.text; block.appendChild(pre); el.appendChild(block);
|
|
46
|
+
header.setAttribute('role', 'button'); header.tabIndex = 0; header.setAttribute('aria-expanded', 'false');
|
|
47
|
+
function toggle() { var expanded = el.classList.toggle('expanded'); block.classList.toggle('collapsed', !expanded); header.setAttribute('aria-expanded', String(expanded)); }
|
|
48
|
+
header.addEventListener('click', toggle); header.addEventListener('keydown', function (event) { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); toggle(); } });
|
|
49
|
+
}
|
|
50
|
+
body.appendChild(el); return;
|
|
34
51
|
}
|
|
35
|
-
var
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
var
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// Keep the shared Markdown parser and its sanitization, with a narrower
|
|
43
|
-
// read-only prose vocabulary for this secondary history surface.
|
|
44
|
-
content.innerHTML = DOMPurify.sanitize(renderMarkdown(message.text), {
|
|
45
|
-
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'del', 's', 'blockquote', 'ul', 'ol', 'li', 'pre', 'code', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'a', 'wbr'],
|
|
46
|
-
ALLOWED_ATTR: ['href', 'title', 'target', 'rel', 'start'], ALLOW_DATA_ATTR: false
|
|
47
|
-
});
|
|
48
|
-
article.append(author, content); body.appendChild(article);
|
|
52
|
+
var user = message.role === 'user';
|
|
53
|
+
var label = message.authorLabel || (user ? options.userName || 'Driver' : options.assistantName || 'Worker');
|
|
54
|
+
var config = { tagName: 'article', name: label, avatarUrl: user ? options.userAvatar || '/icon-192.png' : VENDOR_AVATARS[options.vendor || 'claude'], text: message.text };
|
|
55
|
+
var article = user ? createUserBubble(config) : createAssistantBubble(config);
|
|
56
|
+
article.classList.add('worker-readonly-message'); article.dataset.recordIndex = String(message.recordIndex);
|
|
57
|
+
if (!user) renderAssistantBubbleText(article, message.text, true, { readOnly: true });
|
|
58
|
+
body.appendChild(article);
|
|
49
59
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
60
|
+
export function appendWorkerTranscript(body, messages, options) {
|
|
61
|
+
var rows = workerTranscriptRows(messages), names = workerActivityNames(rows), consumed = new Set(), visible = [];
|
|
62
|
+
rows.forEach(function (message, index) {
|
|
63
|
+
if (consumed.has(index)) return;
|
|
64
|
+
var row = message;
|
|
65
|
+
if (message.role === 'tool' && names[message.toolId] && names[message.toolId].index === message.recordIndex) {
|
|
66
|
+
var later = rows.slice(index + 1), executing = later.find(function (item) { return item.toolId === message.toolId && item.recordType === 'tool_executing'; });
|
|
67
|
+
var result = later.find(function (item) { return item.toolId === message.toolId && item.recordType === 'tool_result'; });
|
|
68
|
+
row = Object.assign({}, message, executing ? { text: executing.text, recordType: executing.recordType } : {}, result ? { resultText: result.text, isError: result.isError } : {});
|
|
69
|
+
[executing, result].forEach(function (item) { if (item) consumed.add(rows.indexOf(item)); });
|
|
70
|
+
}
|
|
71
|
+
visible.push(row);
|
|
72
|
+
});
|
|
73
|
+
var group = null, groupRows = [];
|
|
74
|
+
function finish() {
|
|
75
|
+
if (!group) return;
|
|
76
|
+
var header = group.querySelector('.tool-group-header'), done = groupRows.every(function (row) { return row.resultText !== undefined || row.recordType === 'tool_result'; });
|
|
77
|
+
var meta = { toolNames: groupRows.map(function (row) { return row.toolName || names[row.toolId] && names[row.toolId].name || 'Tool'; }), doneCount: done ? groupRows.length : 0 };
|
|
78
|
+
header.style.display = groupRows.length > 1 ? '' : 'none';
|
|
79
|
+
header.dataset.disclosureKey = 'group-' + groupRows[0].recordIndex;
|
|
80
|
+
group.querySelector('.tool-group-label').textContent = done ? sessionToolGroupSummary(meta, {}) : groupRows.length + ' tool calls recorded';
|
|
81
|
+
group.classList.toggle('done', done); group.classList.toggle('collapsed', groupRows.length > 1);
|
|
82
|
+
group.querySelector('.tool-group-status-icon').innerHTML = iconHtml(done ? 'check' : 'circle-help');
|
|
83
|
+
bindToolGroupDisclosure(group, function () {}); group = null; groupRows = [];
|
|
84
|
+
}
|
|
85
|
+
visible.forEach(function (row) {
|
|
86
|
+
if (row.role !== 'tool') { finish(); appendWorkerMessage(body, row, names, options); return; }
|
|
87
|
+
if (!group) { group = createSessionToolGroup(); body.appendChild(group); }
|
|
88
|
+
groupRows.push(row); appendWorkerMessage(group.querySelector('.tool-group-items'), row, names, options);
|
|
89
|
+
});
|
|
90
|
+
finish();
|
|
54
91
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { syncWorkerMessagePreviews } from './worker-message-previews.js';
|
|
1
2
|
import { appendWorkerRemoval, reconcileWorkerRemoval } from './split-view-worker-removal.js';
|
|
2
3
|
import { workerCursorKey, workerCursorAdvances, shouldLoadWorkerPage, captureWorkerAnchor, restoreWorkerAnchor } from './split-view-worker-scroll.js';
|
|
3
4
|
import { appendWorkerTranscript } from './split-view-worker-transcript.js';
|
|
@@ -189,7 +190,7 @@ function open(chip, driver, worker, takeFocus) {
|
|
|
189
190
|
if (existing && existing.driverOriginId === driver.sessionOriginId && existing.workerOriginId === worker.sessionOriginId && existing.generation === worker.workerGeneration) { close(true); return; }
|
|
190
191
|
if (existing) close(false);
|
|
191
192
|
var state = { requestId: null, driverOriginId: driver.sessionOriginId, workerOriginId: worker.sessionOriginId,
|
|
192
|
-
generation: worker.workerGeneration, driverTitle: driver.title || "Driver session", workerTitle: worker.title || "Worker",
|
|
193
|
+
generation: worker.workerGeneration, workerVendor: worker.vendor, driverTitle: driver.title || "Driver session", workerTitle: worker.title || "Worker",
|
|
193
194
|
sourceBinding: chip.closest('.split-pane').querySelector('.split-pane-frame').clayPane.identity().binding,
|
|
194
195
|
status: "loading", messages: [], hasMore: false, nextBefore: null, append: false, inFlight: false, initialPage: true };
|
|
195
196
|
store.set({ splitViewWorkerConversation: state }); ensurePopover(); renderContent();
|
|
@@ -217,7 +218,7 @@ function ensurePopover() {
|
|
|
217
218
|
var closeButton = document.createElement("button"); closeButton.type = "button"; closeButton.setAttribute("aria-label", "Close Worker conversation"); closeButton.innerHTML = iconHtml("x");
|
|
218
219
|
closeButton.addEventListener("click", function () { close(true); }); header.appendChild(closeButton); element.appendChild(header);
|
|
219
220
|
var source = document.createElement("div"); source.className = "split-view-worker-popover-source"; element.appendChild(source);
|
|
220
|
-
var body = document.createElement("div"); body.className = "split-view-worker-popover-body"; body.tabIndex = 0; body.setAttribute("aria-label", "Read-only Worker transcript"); element.appendChild(body);
|
|
221
|
+
var body = document.createElement("div"); body.className = "split-view-worker-popover-body readonly-session-transcript"; body.tabIndex = 0; body.setAttribute("aria-label", "Read-only Worker transcript"); element.appendChild(body);
|
|
221
222
|
body.addEventListener('scroll', function () { loadEarlier(body, true); }, { passive: true });
|
|
222
223
|
body.addEventListener('wheel', function (event) { if (event.deltaY < 0) loadEarlier(body, true); }, { passive: true });
|
|
223
224
|
var footer = document.createElement("footer"); footer.className = "split-view-worker-popover-footer"; element.appendChild(footer);
|
|
@@ -241,7 +242,7 @@ function renderContent() {
|
|
|
241
242
|
if (state.status === "loading" && !state.messages.length) body.textContent = "Loading conversation…";
|
|
242
243
|
else if (state.status === "error" && !state.messages.length) body.textContent = state.error || "Conversation is unavailable.";
|
|
243
244
|
else if (!state.messages.length) body.textContent = "No recorded conversation.";
|
|
244
|
-
appendWorkerTranscript(body, state.messages);
|
|
245
|
+
appendWorkerTranscript(body, state.messages, { vendor: state.workerVendor, assistantName: state.workerTitle });
|
|
245
246
|
restoreWorkerDisclosures(body, disclosures);
|
|
246
247
|
if ((state.initialPage || state.liveRefresh && followTail) && state.status === 'ready') body.scrollTop = body.scrollHeight;
|
|
247
248
|
else if (anchor) restoreWorkerAnchor(body, anchor);
|
|
@@ -370,7 +371,7 @@ export function renderPaneWorkers(container, driver) {
|
|
|
370
371
|
data.items.forEach(function (worker) {
|
|
371
372
|
var pill = document.createElement("span"); pill.className = "split-view-worker-pill";
|
|
372
373
|
var button = document.createElement("button"); button.type = "button"; button.className = "split-view-worker-chip";
|
|
373
|
-
button.dataset.workerOrigin = worker.sessionOriginId; button.dataset.workerGeneration = String(worker.workerGeneration);
|
|
374
|
+
button.dataset.workerParent = driver.sessionOriginId; button.dataset.workerOrigin = worker.sessionOriginId; button.dataset.workerGeneration = String(worker.workerGeneration);
|
|
374
375
|
var current = data.currentIds.has(worker.id);
|
|
375
376
|
var modelLabel = worker.model || "Default model";
|
|
376
377
|
button.title = (VENDOR_NAMES[worker.vendor] || worker.vendor || "Unknown vendor") + " · " + modelLabel + " · Split Worker #" + worker.id +
|
|
@@ -419,6 +420,7 @@ export function renderPaneWorkers(container, driver) {
|
|
|
419
420
|
root.appendChild(controls);
|
|
420
421
|
}
|
|
421
422
|
introduceWorkerViewer(container, driver);
|
|
423
|
+
syncWorkerMessagePreviews();
|
|
422
424
|
var conversation = store.get('splitViewWorkerConversation');
|
|
423
425
|
if (conversation) ensurePopover();
|
|
424
426
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { syncPairView, pairPresentationPanes, pairViewCapacity } from './view-pair-presentation.js';
|
|
2
|
+
import { paintLegacyToolPanes } from './tool-pane-legacy.js';
|
|
1
3
|
import { adoptDocumentSource } from './clay-studio-document.js';
|
|
2
4
|
import { trustedStudioFrame } from './clay-studio-render.js';
|
|
3
5
|
import { handlePaneBrowserMessage } from './pane-browser-bridge.js';
|
|
@@ -185,7 +187,8 @@ function renderSplit(split) {
|
|
|
185
187
|
singleton || split && split.panes && split.panes.length >= 2 || !!state.projectSplitViewActiveId);
|
|
186
188
|
// The exact singleton presentation is verified before the redundant cache catches up.
|
|
187
189
|
if (singleton) { renderPresentationSplitView(); return; }
|
|
188
|
-
|
|
190
|
+
var savedPair = split && split.panes && syncPairView();
|
|
191
|
+
if (split && split.panes && !savedPair && store.get('projectSplitViewActiveId')) {
|
|
189
192
|
store.set({ projectSplitViewActiveId: null, projectSplitViewFocusedOrigin: null, projectSplitViewPreviewTree: null });
|
|
190
193
|
}
|
|
191
194
|
var expectedPresentationContext = [store.get('currentSlug') || "", store.get('currentProjectOwnerId') || "", store.get('myUserId') || "", store.get('projectSplitViewSocketGeneration') || 0].join("|");
|
|
@@ -213,8 +216,10 @@ function renderSplit(split) {
|
|
|
213
216
|
placeStickyNotesOverlay(true);
|
|
214
217
|
nativeApp.classList.add("split-native-hidden");
|
|
215
218
|
host.classList.add("visible");
|
|
219
|
+
if (savedPair) panes = pairPresentationPanes(panes);
|
|
216
220
|
reconcileSplitPanes(host, panes, function (pane) { return createSplitPane(pane, closePane); }, updateSplitPaneMetadata);
|
|
217
221
|
syncPairChrome(host, split);
|
|
222
|
+
paintLegacyToolPanes(host);
|
|
218
223
|
refreshIcons();
|
|
219
224
|
}
|
|
220
225
|
|
|
@@ -246,7 +251,7 @@ function openSplit(side, draggedId) {
|
|
|
246
251
|
|
|
247
252
|
export function openGroup(group) {
|
|
248
253
|
if (!canLeavePanes()) return false;
|
|
249
|
-
if (!group || !Array.isArray(group.members) || group.members.length < 2) return false;
|
|
254
|
+
if (!group || !Array.isArray(group.members) || group.members.length < 2 || !pairViewCapacity(group)) return false;
|
|
250
255
|
var memberIds = splitGroupMemberIds(group);
|
|
251
256
|
if (memberIds.length !== group.members.length) return false;
|
|
252
257
|
for (var mi = 0; mi < memberIds.length; mi++) if (!sessionById(memberIds[mi])) return false;
|
|
@@ -491,3 +496,5 @@ export function initSplitView() {
|
|
|
491
496
|
initSplitViewPresentations(host);
|
|
492
497
|
renderSplit(store.get('splitPanes'));
|
|
493
498
|
}
|
|
499
|
+
|
|
500
|
+
export function repaintLegacySplit() { if (store.get('splitPanes')) renderSplit(store.get('splitPanes')); }
|
|
@@ -14,7 +14,7 @@ import { enhanceClayLogLinks } from './clay-log-links.js';
|
|
|
14
14
|
import { closeHumanNote } from './sticky-notes-actions.js';
|
|
15
15
|
import { send, getNoteUiState } from './sticky-notes-shared.js';
|
|
16
16
|
import { listNoteData, focusNoteOnCanvas, hideNotes, showNotes } from './sticky-notes.js';
|
|
17
|
-
import { claimRightWorkbench, registerRightWorkbench, releaseRightWorkbench } from './right-workbench.js';
|
|
17
|
+
import { handlesWorkbenchKey, claimRightWorkbench, registerRightWorkbench, releaseRightWorkbench } from './right-workbench.js';
|
|
18
18
|
|
|
19
19
|
var panel = null;
|
|
20
20
|
var gridEl = null;
|
|
@@ -427,7 +427,7 @@ export function isNotesBrowserOpen() {
|
|
|
427
427
|
export function initNotesBrowser() {
|
|
428
428
|
registerRightWorkbench("notes-browser", closeNotesBrowser);
|
|
429
429
|
document.addEventListener("keydown", function (e) {
|
|
430
|
-
if (e.key !== "Escape") return;
|
|
430
|
+
if (e.key !== "Escape" || !handlesWorkbenchKey("notes-browser", e)) return;
|
|
431
431
|
if (deleteDialog && !deleteDialog.classList.contains("hidden")) return;
|
|
432
432
|
if (!store.get('notesBrowserOpen')) return;
|
|
433
433
|
closeNotesBrowser();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Attachment snapshots describe the previous PTY size, not the current pane.
|
|
2
|
+
export function hasTerminalViewport(tab) {
|
|
3
|
+
var rect = tab && tab.bodyEl && tab.bodyEl.getBoundingClientRect();
|
|
4
|
+
return !!(rect && rect.width > 0 && rect.height > 0);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function applyTerminalSize(tab, message, active, refit) {
|
|
8
|
+
if (!tab || !tab.xterm || !(message.cols > 0 && message.rows > 0)) return;
|
|
9
|
+
if (message.initial === true && active) {
|
|
10
|
+
if (hasTerminalViewport(tab)) refit();
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
tab.xterm.resize(message.cols, message.rows);
|
|
14
|
+
}
|