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
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { getWs } from './ws-ref.js';
|
|
3
|
+
import { livePaneBinding, samePaneBinding } from './pane-binding.js';
|
|
4
|
+
import { insertWindow } from './tool-pane-tree.js';
|
|
5
|
+
import { resize } from './split-view-layout-client.js';
|
|
6
|
+
import { repaintToolPanes, saveToolPaneTree, toolPaneContext } from './tool-pane-layout.js';
|
|
7
|
+
|
|
8
|
+
function receiptCurrent(drag) {
|
|
9
|
+
var current = store.get('toolPaneWindowGeometry');
|
|
10
|
+
return current && current.key === toolPaneContext() && store.get('connected') && current.key === drag.receipt.key && current.socket === getWs() && current.socket === drag.receipt.socket &&
|
|
11
|
+
current.scope === drag.receipt.scope && current.scope === JSON.stringify([store.get('myUserId'), store.get('currentSlug'), store.get('currentProjectOwnerId'), store.get('projectSplitViewSocketGeneration')]) && current.revision === drag.receipt.revision &&
|
|
12
|
+
JSON.stringify(current.windows.map(function (entry) { return entry.id; }).sort()) === JSON.stringify(drag.windows.map(function (entry) { return entry.id; }).sort()) && JSON.stringify(current.base) === JSON.stringify(drag.receipt.base) &&
|
|
13
|
+
current.host.clientWidth === drag.width && current.host.clientHeight === drag.height &&
|
|
14
|
+
drag.windows.every(function (entry) {
|
|
15
|
+
if (!entry.element.isConnected || entry.element.classList.contains('hidden')) return false;
|
|
16
|
+
if (!entry.frame) return entry.element.dataset.toolWindowId === entry.id;
|
|
17
|
+
var live = entry.frame.clayPane && entry.frame.clayPane.identity();
|
|
18
|
+
return entry.frame.contentDocument === entry.document && entry.frame.contentWindow === entry.window &&
|
|
19
|
+
live && samePaneBinding(live.binding, entry.binding) && livePaneBinding(entry.frame);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function capture(event, source, divider) {
|
|
23
|
+
var receipt = store.get('toolPaneWindowGeometry'), preference = store.get('workbenchLayoutPreference') || {};
|
|
24
|
+
if (!receipt || (!store.get('projectSplitViewActiveId') && (!preference.ready || preference.loading || preference.saving)) || store.get('projectSplitViewDrag') || store.get('projectSplitViewPending')) return null;
|
|
25
|
+
var drag = { pointerId: event.pointerId, receipt: receipt, source: source, divider: divider, startX: event.clientX, startY: event.clientY,
|
|
26
|
+
width: receipt.host.clientWidth, height: receipt.host.clientHeight, tree: receipt.tree, moved: false, windows: receipt.windows.map(function (item) {
|
|
27
|
+
var live = item.frame && item.frame.clayPane && item.frame.clayPane.identity();
|
|
28
|
+
return Object.assign({}, item, { rect: item.element.getBoundingClientRect(), document: item.frame && item.frame.contentDocument,
|
|
29
|
+
window: item.frame && item.frame.contentWindow, binding: live && live.binding, pointerEvents: item.frame && item.frame.style.pointerEvents });
|
|
30
|
+
}) };
|
|
31
|
+
return receiptCurrent(drag) ? drag : null;
|
|
32
|
+
}
|
|
33
|
+
function begin(drag) {
|
|
34
|
+
drag.moved = true;
|
|
35
|
+
drag.windows.forEach(function (item) { if (item.frame) item.frame.style.pointerEvents = 'none'; });
|
|
36
|
+
if (drag.source) drag.source.element.classList.add('tool-pane-drag-source');
|
|
37
|
+
document.body.classList.add('tool-pane-dragging'); drag.receipt.host.classList.add('split-view-drag-preview');
|
|
38
|
+
try { document.documentElement.setPointerCapture(drag.pointerId); } catch (error) {}
|
|
39
|
+
}
|
|
40
|
+
function pointerDown(event) {
|
|
41
|
+
if (event.button !== 0 || event.isPrimary === false || store.get('toolPaneDrag')) return;
|
|
42
|
+
var header = event.target.closest && event.target.closest('.tool-pane-window > .tool-pane-header, #split-host > .split-pane > .split-pane-header');
|
|
43
|
+
if (!header || event.target.closest('button, input, a, .tool-pane-title, .tool-pane-kind, .split-pane-title, .split-view-runtime, .split-worker-runtime')) return;
|
|
44
|
+
var receipt = store.get('toolPaneWindowGeometry');
|
|
45
|
+
if (!receipt || !receipt.windows.some(function (item) { return !!item.tool; })) return;
|
|
46
|
+
var source = receipt.windows.find(function (item) { return item.element === header.parentNode; });
|
|
47
|
+
var drag = source && capture(event, source);
|
|
48
|
+
if (!drag) return;
|
|
49
|
+
store.set({ toolPaneDrag: drag }); event.preventDefault(); event.stopImmediatePropagation();
|
|
50
|
+
}
|
|
51
|
+
function preview(drag, tree) {
|
|
52
|
+
drag.tree = tree;
|
|
53
|
+
store.set({ toolPaneLayoutPreview: { key: drag.receipt.key, base: drag.receipt.base, tree: tree, socket: drag.receipt.socket } });
|
|
54
|
+
repaintToolPanes();
|
|
55
|
+
}
|
|
56
|
+
function move(event) {
|
|
57
|
+
var drag = store.get('toolPaneDrag');
|
|
58
|
+
if (!drag || drag.pointerId !== event.pointerId) return;
|
|
59
|
+
if (!receiptCurrent(drag)) { finish(false); return; }
|
|
60
|
+
if (!drag.moved && Math.hypot(event.clientX - drag.startX, event.clientY - drag.startY) < 6) return;
|
|
61
|
+
if (!drag.moved) begin(drag);
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
if (drag.divider) {
|
|
64
|
+
var entry = drag.divider, horizontal = entry.axis === 'horizontal';
|
|
65
|
+
var delta = horizontal ? event.clientX - drag.startX : event.clientY - drag.startY;
|
|
66
|
+
var next = resize(drag.receipt.tree, entry.path, Math.max(.05, Math.min(.95, entry.ratio + delta / Math.max(1, entry.available))));
|
|
67
|
+
if (next) preview(drag, next);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
var target = drag.windows.find(function (entry) { var r = entry.rect; return event.clientX >= r.left && event.clientX <= r.right && event.clientY >= r.top && event.clientY <= r.bottom; });
|
|
71
|
+
var nextTree = drag.receipt.tree;
|
|
72
|
+
if (target && target.id !== drag.source.id) {
|
|
73
|
+
var r = target.rect, x = (event.clientX - r.left) / r.width, y = (event.clientY - r.top) / r.height;
|
|
74
|
+
var edge = Math.min(x, 1 - x, y, 1 - y), side = 'center';
|
|
75
|
+
if (edge < .25) side = edge === x ? 'left' : edge === 1 - x ? 'right' : edge === y ? 'top' : 'bottom';
|
|
76
|
+
nextTree = insertWindow(drag.receipt.tree, drag.source.id, target.id, side);
|
|
77
|
+
}
|
|
78
|
+
preview(drag, nextTree);
|
|
79
|
+
var ghost = document.getElementById('tool-pane-drag-ghost');
|
|
80
|
+
if (!ghost) { ghost = document.createElement('div'); ghost.id = 'tool-pane-drag-ghost'; ghost.className = 'split-view-drag-ghost'; document.body.appendChild(ghost); }
|
|
81
|
+
ghost.textContent = drag.source.element.querySelector('.tool-pane-title, .split-pane-title').textContent;
|
|
82
|
+
ghost.style.left = event.clientX + 12 + 'px'; ghost.style.top = event.clientY + 12 + 'px'; ghost.hidden = false;
|
|
83
|
+
}
|
|
84
|
+
async function finish(commit) {
|
|
85
|
+
var drag = store.get('toolPaneDrag');
|
|
86
|
+
if (!drag) return;
|
|
87
|
+
var allowed = commit && drag.moved && receiptCurrent(drag) && JSON.stringify(drag.tree) !== JSON.stringify(drag.receipt.tree);
|
|
88
|
+
store.set({ toolPaneDrag: null });
|
|
89
|
+
drag.windows.forEach(function (entry) { if (entry.frame && entry.frame.isConnected) entry.frame.style.pointerEvents = entry.pointerEvents; });
|
|
90
|
+
if (drag.source) drag.source.element.classList.remove('tool-pane-drag-source');
|
|
91
|
+
document.body.classList.remove('tool-pane-dragging');
|
|
92
|
+
var ghost = document.getElementById('tool-pane-drag-ghost'); if (ghost) ghost.hidden = true;
|
|
93
|
+
try { if (document.documentElement.hasPointerCapture(drag.pointerId)) document.documentElement.releasePointerCapture(drag.pointerId); } catch (error) {}
|
|
94
|
+
var saved = allowed ? await saveToolPaneTree(drag.receipt, drag.tree) : false;
|
|
95
|
+
if (!store.get('toolPaneDrag')) { store.set({ toolPaneLayoutPreview: null }); repaintToolPanes(); }
|
|
96
|
+
setTimeout(function () { if (!store.get('toolPaneDrag')) drag.receipt.host.classList.remove('split-view-drag-preview'); }, 320);
|
|
97
|
+
return saved;
|
|
98
|
+
}
|
|
99
|
+
function up(event) {
|
|
100
|
+
var drag = store.get('toolPaneDrag');
|
|
101
|
+
if (!drag || drag.pointerId !== event.pointerId) return;
|
|
102
|
+
if (drag.moved) move(event);
|
|
103
|
+
finish(true);
|
|
104
|
+
}
|
|
105
|
+
export function bindToolPaneDivider(divider, entry, tree) {
|
|
106
|
+
divider.addEventListener('pointerdown', function (event) {
|
|
107
|
+
if (event.button !== 0 || store.get('toolPaneDrag')) return;
|
|
108
|
+
var drag = capture(event, null, entry);
|
|
109
|
+
if (!drag) return;
|
|
110
|
+
store.set({ toolPaneDrag: drag }); begin(drag); event.preventDefault(); event.stopImmediatePropagation();
|
|
111
|
+
});
|
|
112
|
+
divider.addEventListener('keydown', function (event) {
|
|
113
|
+
var direction = ['ArrowLeft', 'ArrowUp'].indexOf(event.key) >= 0 ? -1 : ['ArrowRight', 'ArrowDown'].indexOf(event.key) >= 0 ? 1 : 0;
|
|
114
|
+
if (!direction) return;
|
|
115
|
+
var receipt = store.get('toolPaneWindowGeometry'), next = resize(tree, entry.path, Math.max(.05, Math.min(.95, entry.ratio + direction * .03)));
|
|
116
|
+
if (receipt && next) { event.preventDefault(); saveToolPaneTree(receipt, next); }
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
export function initToolPaneDrag() {
|
|
120
|
+
if (store.get('toolPaneDragInitialized')) return;
|
|
121
|
+
store.set({ toolPaneDragInitialized: true });
|
|
122
|
+
document.addEventListener('pointerdown', pointerDown, true);
|
|
123
|
+
document.addEventListener('pointermove', move, true); document.addEventListener('pointerup', up, true);
|
|
124
|
+
document.addEventListener('pointercancel', function () { finish(false); }, true);
|
|
125
|
+
document.addEventListener('keydown', function (event) { if (event.key === 'Escape' && store.get('toolPaneDrag')) { event.preventDefault(); finish(false); } }, true);
|
|
126
|
+
window.addEventListener('blur', function () { finish(false); });
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
store.subscribe(function (state, previous) {
|
|
130
|
+
if (state.toolPaneDrag && (state.myUserId !== previous.myUserId || state.currentSlug !== previous.currentSlug ||
|
|
131
|
+
state.currentProjectOwnerId !== previous.currentProjectOwnerId || state.splitPanes !== previous.splitPanes ||
|
|
132
|
+
state.connected !== previous.connected || state.projectSplitViewActiveId !== previous.projectSplitViewActiveId ||
|
|
133
|
+
state.projectSplitViewSocketGeneration !== previous.projectSplitViewSocketGeneration)) finish(false);
|
|
134
|
+
});
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { boundLegacyCoordinates } from './view-legacy-coordinates.js';
|
|
2
|
+
import { pairDisplayTree, pairBase } from './view-pair-presentation.js';
|
|
3
|
+
import { activeSavedView, displayTree, fromDisplayTree, copy, workspaceKey } from './view-workspace-state.js';
|
|
4
|
+
import { persistWorkspace } from './view-workspace-controller.js';
|
|
5
|
+
import { store } from './store.js';
|
|
6
|
+
import { getWs } from './ws-ref.js';
|
|
7
|
+
import { TOOL_PANES, toolWindowId, workspaceWindowTree } from './tool-pane-tree.js';
|
|
8
|
+
import { renderPresentationSplitView } from './split-view-presentations.js';
|
|
9
|
+
import { ensureWorkbenchLayoutPreference } from './workbench-layout-preference.js';
|
|
10
|
+
import { showToast } from './utils.js';
|
|
11
|
+
import { initToolPaneDrag, bindToolPaneDivider } from './tool-pane-drag.js';
|
|
12
|
+
|
|
13
|
+
export function toolPaneContext() {
|
|
14
|
+
var state = store.snap(), view = (state.projectSplitViews || []).find(function (item) { return item.id === state.projectSplitViewActiveId; });
|
|
15
|
+
var split = state.splitPanes;
|
|
16
|
+
var origin = view ? view.id : split && split.panes ? 'legacy-' + (split.groupId || split.panes.map(function (pane) { return pane.sessionId; }).join('-')) : state.projectSplitViewSingletonOrigin;
|
|
17
|
+
return origin && state.currentSlug && JSON.stringify([state.currentSlug, state.currentProjectOwnerId || null, origin]);
|
|
18
|
+
}
|
|
19
|
+
export function dockedToolPanes() {
|
|
20
|
+
if (window.innerWidth < 1024 || store.get('paneMode')) return [];
|
|
21
|
+
return (store.get('rightWorkbenchDockedOwners') || []).filter(function (name) {
|
|
22
|
+
var definition = TOOL_PANES[name], panel = definition && document.getElementById(definition.id);
|
|
23
|
+
return panel && !panel.classList.contains('hidden') && !panel.classList.contains('panel-fullscreen');
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export function toolPaneLayoutTree(view, base) {
|
|
27
|
+
initToolPaneDrag();
|
|
28
|
+
var owners = dockedToolPanes(), key = toolPaneContext(), preference = store.get('workbenchLayoutPreference') || {};
|
|
29
|
+
var draft = store.get('toolPaneLayoutPreview'), saved = preference.layouts && preference.layouts[key];
|
|
30
|
+
if (view && view.workspace) {
|
|
31
|
+
var legacy = store.get('savedWorkspaceLegacyLayout');
|
|
32
|
+
saved = legacy && legacy.key === workspaceKey(view) ? boundLegacyCoordinates(legacy.record, view.workspace) : { base: view.workspace.linkedPair ? pairBase(view.workspace.linkedPair, view.tree) : view.tree, tree: pairDisplayTree(view.workspace) };
|
|
33
|
+
}
|
|
34
|
+
if (draft && draft.key === key && JSON.stringify(draft.base) === JSON.stringify(base) && draft.socket === getWs()) return draft.tree;
|
|
35
|
+
return owners.length || saved ? workspaceWindowTree(base, saved, owners) : base;
|
|
36
|
+
}
|
|
37
|
+
export function paintToolPaneWindows(host, view, base, tree, geometry, inset) {
|
|
38
|
+
var owners = dockedToolPanes(), key = toolPaneContext(), main = document.getElementById('main-panels');
|
|
39
|
+
if (!main) return;
|
|
40
|
+
main.classList.toggle('view-session-focused', !!store.get('projectSplitViewFocusedOrigin'));
|
|
41
|
+
var hostRect = host.getBoundingClientRect(), mainRect = main.getBoundingClientRect();
|
|
42
|
+
var windows = Array.from(host.querySelectorAll(':scope > .split-pane:not(.split-pane-cached)')).map(function (pane) {
|
|
43
|
+
var frame = pane.querySelector('.split-pane-frame'); return { id: frame && frame.dataset.sessionOriginId, element: pane, frame: frame };
|
|
44
|
+
});
|
|
45
|
+
Object.keys(TOOL_PANES).forEach(function (name) {
|
|
46
|
+
var panel = document.getElementById(TOOL_PANES[name].id), rect = geometry.panes[toolWindowId(name)];
|
|
47
|
+
if (!panel) return;
|
|
48
|
+
if (!panel.dataset.toolPaneFullscreenObserved) {
|
|
49
|
+
panel.dataset.toolPaneFullscreenObserved = 'true';
|
|
50
|
+
var observer = new MutationObserver(function (records) {
|
|
51
|
+
if (records.some(function (record) { return (String(record.oldValue || '').split(/\s+/).indexOf('panel-fullscreen') >= 0) !== panel.classList.contains('panel-fullscreen'); })) requestAnimationFrame(repaintToolPanes);
|
|
52
|
+
});
|
|
53
|
+
observer.observe(panel, { attributes: true, attributeOldValue: true, attributeFilter: ['class'] });
|
|
54
|
+
panel.addEventListener('clay-tool-pane-dispose', function () { observer.disconnect(); }, { once: true });
|
|
55
|
+
}
|
|
56
|
+
var mounted = owners.indexOf(name) >= 0 && !!rect;
|
|
57
|
+
var wasMounted = panel.classList.contains('tool-pane-window');
|
|
58
|
+
panel.classList.toggle('tool-pane-window', mounted);
|
|
59
|
+
if (!mounted) {
|
|
60
|
+
if (wasMounted) ['left', 'top', 'width', 'height', 'right', 'maxHeight'].forEach(function (field) { panel.style[field] = ''; });
|
|
61
|
+
delete panel.dataset.toolWindowId; return;
|
|
62
|
+
}
|
|
63
|
+
panel.dataset.toolWindowId = toolWindowId(name);
|
|
64
|
+
panel.style.left = hostRect.left - mainRect.left + inset.left + rect.x + 'px';
|
|
65
|
+
panel.style.top = hostRect.top - mainRect.top + inset.top + rect.y + 'px';
|
|
66
|
+
panel.style.width = rect.width + 'px'; panel.style.height = rect.height + 'px'; panel.style.right = 'auto'; panel.style.maxHeight = 'none';
|
|
67
|
+
windows.push({ id: toolWindowId(name), element: panel, tool: name });
|
|
68
|
+
});
|
|
69
|
+
main.classList.toggle('has-tool-pane-windows', owners.length > 0);
|
|
70
|
+
if (Math.abs(host.clientWidth - hostRect.width) > 1) requestAnimationFrame(repaintToolPanes);
|
|
71
|
+
store.set({ toolPaneWindowGeometry: { key: key, base: base, tree: tree, view: view, windows: windows,
|
|
72
|
+
host: host, socket: getWs(), revision: view && view.workspace ? view.revision : (store.get('workbenchLayoutPreference') || {}).revision,
|
|
73
|
+
scope: JSON.stringify([store.get('myUserId'), store.get('currentSlug'), store.get('currentProjectOwnerId'), store.get('projectSplitViewSocketGeneration')]) } });
|
|
74
|
+
}
|
|
75
|
+
export function bindWorkspaceDivider(divider, entry, tree) { bindToolPaneDivider(divider, entry, tree); }
|
|
76
|
+
export function repaintToolPanes() { renderPresentationSplitView(); }
|
|
77
|
+
export async function saveToolPaneTree(receipt, tree) {
|
|
78
|
+
var live = store.get('toolPaneWindowGeometry');
|
|
79
|
+
if (!live || live.key !== toolPaneContext() || live.key !== receipt.key || live.socket !== receipt.socket || live.scope !== receipt.scope || live.revision !== receipt.revision || !store.get('connected')) return false;
|
|
80
|
+
var view = activeSavedView();
|
|
81
|
+
if (view) { var value = copy(view.workspace); value.tree = fromDisplayTree(value, tree); if (value.linkedPair) value.linkedPair.tree = copy(tree); return persistWorkspace(value); }
|
|
82
|
+
showToast('Wait for the saved View before changing pane positions.', 'warn');
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
store.subscribe(function (state, previous) {
|
|
86
|
+
if (state.paneMode) return;
|
|
87
|
+
var changed = state.rightWorkbenchDockedOwners !== previous.rightWorkbenchDockedOwners || state.workbenchLayoutPreference !== previous.workbenchLayoutPreference ||
|
|
88
|
+
state.toolPaneLayoutPreview !== previous.toolPaneLayoutPreview;
|
|
89
|
+
if (!changed) return;
|
|
90
|
+
if (!state.toolPaneRepaintQueued) {
|
|
91
|
+
store.set({ toolPaneRepaintQueued: true });
|
|
92
|
+
requestAnimationFrame(function () { store.set({ toolPaneRepaintQueued: false }); renderPresentationSplitView(); });
|
|
93
|
+
}
|
|
94
|
+
if ((state.rightWorkbenchDockedOwners || []).some(function (name) { return !!TOOL_PANES[name]; }) && !(state.workbenchLayoutPreference || {}).ready && !(state.workbenchLayoutPreference || {}).loading && !(state.workbenchLayoutPreference || {}).saving && !(state.workbenchLayoutPreference || {}).error) ensureWorkbenchLayoutPreference();
|
|
95
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ensureArrangeTrigger } from './split-view-presentation-renderer.js';
|
|
2
|
+
import { pairBase } from './view-pair-presentation.js';
|
|
3
|
+
import { activeSavedView } from './view-workspace-state.js';
|
|
4
|
+
// Legacy Driver/Worker pairs use the same display-only window geometry.
|
|
5
|
+
import { store } from './store.js';
|
|
6
|
+
import { rectangles } from './split-view-layout-client.js';
|
|
7
|
+
import { toolPaneLayoutTree, paintToolPaneWindows, bindWorkspaceDivider, dockedToolPanes } from './tool-pane-layout.js';
|
|
8
|
+
|
|
9
|
+
export function clearToolPaneWindows() {
|
|
10
|
+
document.querySelectorAll('.tool-pane-window').forEach(function (panel) {
|
|
11
|
+
panel.classList.remove('tool-pane-window'); delete panel.dataset.toolWindowId;
|
|
12
|
+
['left', 'top', 'width', 'height', 'right', 'maxHeight'].forEach(function (field) { panel.style[field] = ''; });
|
|
13
|
+
});
|
|
14
|
+
var main = document.getElementById('main-panels');
|
|
15
|
+
if (main) main.classList.remove('has-tool-pane-windows');
|
|
16
|
+
store.set({ toolPaneWindowGeometry: null });
|
|
17
|
+
}
|
|
18
|
+
export function paintLegacyToolPanes(host) {
|
|
19
|
+
var split = store.get('splitPanes');
|
|
20
|
+
if (!host || !split || !split.panes || split.panes.length < 2) return false;
|
|
21
|
+
var panes = Array.from(host.querySelectorAll(':scope > .split-pane:not(.split-pane-cached)'));
|
|
22
|
+
var ids = panes.map(function (pane) {
|
|
23
|
+
var frame = pane.querySelector('.split-pane-frame');
|
|
24
|
+
if (frame && !frame.dataset.toolPaneGeometryReadyBound) {
|
|
25
|
+
frame.dataset.toolPaneGeometryReadyBound = 'true';
|
|
26
|
+
frame.addEventListener('clay-pane-ready', function () { queueMicrotask(function () { if (frame.isConnected) paintLegacyToolPanes(host); }); });
|
|
27
|
+
}
|
|
28
|
+
return frame && frame.dataset.sessionOriginId;
|
|
29
|
+
});
|
|
30
|
+
if (ids.length < split.panes.length || ids.some(function (id) { return !id; }) || new Set(ids).size !== ids.length) return false;
|
|
31
|
+
var base = { id: ids[0] };
|
|
32
|
+
ids.slice(1).forEach(function (id) { base = { axis: 'horizontal', ratio: .5, left: base, right: { id: id } }; });
|
|
33
|
+
var view = activeSavedView() || { id: 'legacy-' + (split.groupId || ids.join('-')), activeSessionOriginId: ids[0] };
|
|
34
|
+
if (view.workspace && view.workspace.linkedPair) base = pairBase(view.workspace.linkedPair, view.tree);
|
|
35
|
+
host.classList.add('split-view-canvas'); host.dataset.positioned = 'true'; delete host.dataset.singlePane;
|
|
36
|
+
var style = getComputedStyle(host), left = parseFloat(style.paddingLeft) || 0, top = parseFloat(style.paddingTop) || 0;
|
|
37
|
+
var width = host.clientWidth - left - (parseFloat(style.paddingRight) || 0), height = host.clientHeight - top - (parseFloat(style.paddingBottom) || 0);
|
|
38
|
+
var tree = toolPaneLayoutTree(view, base), geometry = rectangles(tree, Math.max(0, width), Math.max(0, height), 7);
|
|
39
|
+
paintToolPaneWindows(host, view, base, tree, geometry, { left: left, top: top });
|
|
40
|
+
var focused = store.get('projectSplitViewFocusedOrigin');
|
|
41
|
+
panes.forEach(function (pane, index) {
|
|
42
|
+
var rect = geometry.panes[ids[index]];
|
|
43
|
+
pane.hidden = !!focused && ids[index] !== focused; pane.style.display = pane.hidden ? 'none' : '';
|
|
44
|
+
if (focused === ids[index]) rect = { x: 0, y: 0, width: width, height: height };
|
|
45
|
+
if (!rect) return;
|
|
46
|
+
if (view.sessions && view.sessions.some(function (s) { return s.sessionOriginId === ids[index]; })) ensureArrangeTrigger(pane.querySelector('.split-pane-header'));
|
|
47
|
+
pane.style.left = left + rect.x + 'px'; pane.style.top = top + rect.y + 'px';
|
|
48
|
+
pane.style.width = rect.width + 'px'; pane.style.height = rect.height + 'px';
|
|
49
|
+
});
|
|
50
|
+
host.querySelectorAll(':scope > .split-view-divider').forEach(function (divider) { divider.remove(); });
|
|
51
|
+
(focused ? [] : geometry.dividers).forEach(function (entry) {
|
|
52
|
+
var divider = document.createElement('div'); divider.className = 'split-view-divider'; divider.dataset.axis = entry.axis;
|
|
53
|
+
divider.setAttribute('role', 'separator'); divider.setAttribute('tabindex', '0'); divider.setAttribute('aria-label', 'Resize pane windows');
|
|
54
|
+
divider.setAttribute('aria-orientation', entry.axis === 'horizontal' ? 'vertical' : 'horizontal');
|
|
55
|
+
divider.style.left = left + entry.rect.x + 'px'; divider.style.top = top + entry.rect.y + 'px';
|
|
56
|
+
divider.style.width = entry.rect.width + 'px'; divider.style.height = entry.rect.height + 'px';
|
|
57
|
+
bindWorkspaceDivider(divider, entry, tree); host.appendChild(divider);
|
|
58
|
+
});
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { WORKBENCH_PANELS } from './workbench-panels.js';
|
|
2
|
+
import { listIds, remove, pairTree, swap } from './split-view-layout-client.js';
|
|
3
|
+
|
|
4
|
+
export var TOOL_PANES = { terminal: { id: 'terminal-container', label: 'Terminal', icon: 'terminal' },
|
|
5
|
+
browser: { id: 'shared-browser-panel', label: 'Browser', icon: 'globe' }, 'file-viewer': { id: 'file-viewer', label: 'Files', icon: 'file-text' } };
|
|
6
|
+
Object.assign(TOOL_PANES, WORKBENCH_PANELS);
|
|
7
|
+
export function toolWindowId(name) { return 'tool-' + name; }
|
|
8
|
+
export function isToolWindow(id) { return /^tool-file-[A-Za-z0-9_-]+$/.test(id) || Object.keys(TOOL_PANES).some(function (name) { return toolWindowId(name) === id; }); }
|
|
9
|
+
export function insertWindow(tree, source, target, side, ratio) {
|
|
10
|
+
if (source === target) return tree;
|
|
11
|
+
if (side === 'center' && isToolWindow(source) && isToolWindow(target)) return swap(tree, source, target).tree;
|
|
12
|
+
side = side === 'center' ? 'right' : side;
|
|
13
|
+
var base = remove(tree, source), found = false;
|
|
14
|
+
function walk(node) {
|
|
15
|
+
if (!node) return node;
|
|
16
|
+
if (node.id) {
|
|
17
|
+
if (node.id !== target) return node;
|
|
18
|
+
found = true;
|
|
19
|
+
var result = pairTree(target, source, side);
|
|
20
|
+
result.ratio = ratio || .5;
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
return { axis: node.axis, ratio: node.ratio, left: walk(node.left), right: walk(node.right) };
|
|
24
|
+
}
|
|
25
|
+
var next = walk(base);
|
|
26
|
+
return found && listIds(next).length <= 24 ? next : tree;
|
|
27
|
+
}
|
|
28
|
+
function stripUnknown(tree, allowed) {
|
|
29
|
+
var result = tree;
|
|
30
|
+
listIds(tree).forEach(function (id) { if (allowed.indexOf(id) < 0) result = remove(result, id); });
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
function placements(tree, output) {
|
|
34
|
+
if (!tree || tree.id) return;
|
|
35
|
+
var left = listIds(tree.left), right = listIds(tree.right);
|
|
36
|
+
if (tree.left.id && isToolWindow(tree.left.id)) output.push({ id: tree.left.id, target: right[0], side: tree.axis === 'horizontal' ? 'left' : 'top', ratio: tree.ratio });
|
|
37
|
+
else if (tree.right.id && isToolWindow(tree.right.id)) output.push({ id: tree.right.id, target: left[0], side: tree.axis === 'horizontal' ? 'right' : 'bottom', ratio: tree.ratio });
|
|
38
|
+
placements(tree.left, output); placements(tree.right, output);
|
|
39
|
+
}
|
|
40
|
+
export function workspaceWindowTree(base, saved, owners) {
|
|
41
|
+
var sessions = listIds(base), tools = owners.map(toolWindowId), allowed = sessions.concat(tools), tree = base;
|
|
42
|
+
if (saved && saved.tree && JSON.stringify(saved.base) === JSON.stringify(base)) tree = stripUnknown(saved.tree, allowed);
|
|
43
|
+
else if (saved && saved.tree) {
|
|
44
|
+
var attached = []; placements(saved.tree, attached);
|
|
45
|
+
attached.reverse().forEach(function (item) { if (tools.indexOf(item.id) >= 0 && listIds(tree).indexOf(item.target) >= 0) tree = insertWindow(tree, item.id, item.target, item.side, item.ratio); });
|
|
46
|
+
}
|
|
47
|
+
tools.forEach(function (id) {
|
|
48
|
+
if (listIds(tree).indexOf(id) < 0) tree = tree ? { axis: 'horizontal', ratio: .66, left: tree, right: { id: id } } : { id: id };
|
|
49
|
+
});
|
|
50
|
+
return tree;
|
|
51
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { createSessionToolItem, createSessionToolGroup, sessionToolGroupSummary } from './session-tool-item.js';
|
|
1
2
|
import { renderProductReview } from './product-review.js';
|
|
2
|
-
import { clayToolGroupSummary } from './clay-tool-presentation.js';
|
|
3
3
|
import { applyClayToolItem, applyClayToolResult, finishClayToolItem, findEndedClayTool, clayToolResultGroup, bindToolGroupDisclosure, syncToolGroupDisclosure, syncClayToolGroup } from './clay-tool-activity.js';
|
|
4
4
|
import { rememberMountedToolGroup, mountedToolGroup, transcriptToolGroups, popupChatRenderingEnabled, popupChatRenderingActive, syncPopupActivityGroup, syncPopupChatToolGroups as syncPopupGroups } from './popup-chat-rendering.js';
|
|
5
5
|
import { escapeHtml, copyToClipboard } from './utils.js';
|
|
@@ -80,61 +80,12 @@ function findToolGroup(groupId) {
|
|
|
80
80
|
return toolGroups[groupId] || null;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
function toolGroupSummary(group) {
|
|
84
|
-
var claySummary = clayToolGroupSummary(group, tools);
|
|
85
|
-
if (claySummary) return claySummary;
|
|
86
|
-
var names = group.toolNames;
|
|
87
|
-
var count = names.length;
|
|
88
|
-
var allDone = group.doneCount >= count;
|
|
89
|
-
|
|
90
|
-
// Count by tool name
|
|
91
|
-
var counts = {};
|
|
92
|
-
for (var i = 0; i < names.length; i++) {
|
|
93
|
-
counts[names[i]] = (counts[names[i]] || 0) + 1;
|
|
94
|
-
}
|
|
95
|
-
var uniqueNames = Object.keys(counts);
|
|
96
|
-
|
|
97
|
-
if (uniqueNames.length === 1) {
|
|
98
|
-
var name = uniqueNames[0];
|
|
99
|
-
var n = counts[name];
|
|
100
|
-
if (allDone) {
|
|
101
|
-
switch (name) {
|
|
102
|
-
case "Read": return "Read " + n + " file" + (n > 1 ? "s" : "");
|
|
103
|
-
case "Edit": return "Edited " + n + " file" + (n > 1 ? "s" : "");
|
|
104
|
-
case "Write": return "Wrote " + n + " file" + (n > 1 ? "s" : "");
|
|
105
|
-
case "Bash": return "Ran " + n + " command" + (n > 1 ? "s" : "");
|
|
106
|
-
case "Grep": return "Searched " + n + " pattern" + (n > 1 ? "s" : "");
|
|
107
|
-
case "Glob": return "Found " + n + " pattern" + (n > 1 ? "s" : "");
|
|
108
|
-
case "Task": return "Ran " + n + " task" + (n > 1 ? "s" : "");
|
|
109
|
-
case "WebSearch": return "Searched " + n + " quer" + (n > 1 ? "ies" : "y");
|
|
110
|
-
case "WebFetch": return "Fetched " + n + " URL" + (n > 1 ? "s" : "");
|
|
111
|
-
default: return "Ran " + n + " tool" + (n > 1 ? "s" : "");
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
switch (name) {
|
|
115
|
-
case "Read": return "Reading " + n + " file" + (n > 1 ? "s" : "") + "...";
|
|
116
|
-
case "Edit": return "Editing " + n + " file" + (n > 1 ? "s" : "") + "...";
|
|
117
|
-
case "Write": return "Writing " + n + " file" + (n > 1 ? "s" : "") + "...";
|
|
118
|
-
case "Bash": return "Running " + n + " command" + (n > 1 ? "s" : "") + "...";
|
|
119
|
-
case "Grep": return "Searching " + n + " pattern" + (n > 1 ? "s" : "") + "...";
|
|
120
|
-
case "Glob": return "Finding " + n + " pattern" + (n > 1 ? "s" : "") + "...";
|
|
121
|
-
case "Task": return "Running " + n + " task" + (n > 1 ? "s" : "") + "...";
|
|
122
|
-
case "WebSearch": return "Searching " + n + " quer" + (n > 1 ? "ies" : "y") + "...";
|
|
123
|
-
case "WebFetch": return "Fetching " + n + " URL" + (n > 1 ? "s" : "") + "...";
|
|
124
|
-
default: return "Running " + n + " tool" + (n > 1 ? "s" : "") + "...";
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Mixed tools
|
|
129
|
-
if (allDone) return "Ran " + count + " tools";
|
|
130
|
-
return "Running " + count + " tools...";
|
|
131
|
-
}
|
|
132
83
|
|
|
133
84
|
function updateToolGroupHeader(group) {
|
|
134
85
|
if (!group || !group.el) return;
|
|
135
86
|
var popupChat = group.popupChat && popupChatRenderingActive();
|
|
136
87
|
var label = group.el.querySelector(".tool-group-label");
|
|
137
|
-
var summary =
|
|
88
|
+
var summary = sessionToolGroupSummary(group, tools);
|
|
138
89
|
if (label && !popupChat) label.textContent = summary;
|
|
139
90
|
|
|
140
91
|
var allDone = group.doneCount >= group.toolCount;
|
|
@@ -1644,31 +1595,12 @@ export function createToolItem(id, name) {
|
|
|
1644
1595
|
// Group management: create new group or reuse existing open group
|
|
1645
1596
|
if (!currentToolGroup || currentToolGroup.closed) {
|
|
1646
1597
|
toolGroupCounter++;
|
|
1647
|
-
var groupEl = document.createElement("div");
|
|
1648
|
-
groupEl.className = "tool-group";
|
|
1649
1598
|
var identity = resolveProjectAssistantIdentity();
|
|
1650
1599
|
var isMateToolGroup = identity.kind === 'mate';
|
|
1651
|
-
|
|
1600
|
+
var groupEl = createSessionToolGroup(isMateToolGroup ? identity.avatarUrl : null);
|
|
1601
|
+
if (isMateToolGroup) groupEl.classList.add('mate-tool-group');
|
|
1652
1602
|
groupEl.dataset.groupId = "g" + toolGroupCounter;
|
|
1653
1603
|
|
|
1654
|
-
var toolGroupInner =
|
|
1655
|
-
'<div class="tool-group-header" style="display:none">' +
|
|
1656
|
-
'<span class="tool-group-chevron">' + iconHtml("chevron-right") + '</span>' +
|
|
1657
|
-
'<span class="tool-group-bullet"></span>' +
|
|
1658
|
-
'<span class="tool-group-label">Running...</span>' +
|
|
1659
|
-
'<span class="tool-group-status-icon">' + iconHtml("loader", "icon-spin") + '</span>' +
|
|
1660
|
-
'</div>' +
|
|
1661
|
-
'<div class="tool-group-items"></div>';
|
|
1662
|
-
|
|
1663
|
-
if (isMateToolGroup) {
|
|
1664
|
-
var mateAvatar = identity.avatarUrl;
|
|
1665
|
-
groupEl.innerHTML =
|
|
1666
|
-
'<img class="dm-bubble-avatar dm-bubble-avatar-mate" src="' + escapeHtml(mateAvatar) + '" alt="">' +
|
|
1667
|
-
'<div class="dm-bubble-content">' + toolGroupInner + '</div>';
|
|
1668
|
-
} else {
|
|
1669
|
-
groupEl.innerHTML = toolGroupInner;
|
|
1670
|
-
}
|
|
1671
|
-
|
|
1672
1604
|
applyProjectAssistantIdentity(groupEl, identity);
|
|
1673
1605
|
bindToolGroupDisclosure(groupEl, function () {
|
|
1674
1606
|
var group = mountedToolGroup(groupEl);
|
|
@@ -1694,23 +1626,7 @@ export function createToolItem(id, name) {
|
|
|
1694
1626
|
rememberMountedToolGroup(currentToolGroup);
|
|
1695
1627
|
}
|
|
1696
1628
|
|
|
1697
|
-
var el =
|
|
1698
|
-
el.className = "tool-item";
|
|
1699
|
-
el.dataset.toolId = id;
|
|
1700
|
-
el.innerHTML =
|
|
1701
|
-
'<div class="tool-header">' +
|
|
1702
|
-
'<span class="tool-chevron">' + iconHtml("chevron-right") + '</span>' +
|
|
1703
|
-
'<span class="tool-bullet"></span>' +
|
|
1704
|
-
'<span class="tool-name"></span>' +
|
|
1705
|
-
'<span class="tool-desc"></span>' +
|
|
1706
|
-
'<span class="tool-status-icon">' + iconHtml("loader", "icon-spin") + '</span>' +
|
|
1707
|
-
'</div>' +
|
|
1708
|
-
'<div class="tool-subtitle">' +
|
|
1709
|
-
'<span class="tool-connector">└</span>' +
|
|
1710
|
-
'<span class="tool-subtitle-text">Running...</span>' +
|
|
1711
|
-
'</div>';
|
|
1712
|
-
|
|
1713
|
-
el.querySelector(".tool-name").textContent = name;
|
|
1629
|
+
var el = createSessionToolItem(id, name);
|
|
1714
1630
|
|
|
1715
1631
|
tools[id] = { el: el, name: name, input: null, done: false, groupId: currentToolGroup.id };
|
|
1716
1632
|
applyClayToolItem(tools[id]);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { initPaneHeaderTooltips } from './pane-header-tooltips.js';
|
|
2
|
+
|
|
1
3
|
// --- Global JS tooltip system ---
|
|
2
4
|
// Unified tooltip using [data-tip] attribute.
|
|
3
5
|
// Usage: initTooltips() auto-binds, converts [title] to [data-tip].
|
|
@@ -9,13 +11,15 @@ var SHOW_DELAY = 120;
|
|
|
9
11
|
var currentTarget = null;
|
|
10
12
|
|
|
11
13
|
function initTooltips() {
|
|
14
|
+
if (tooltipEl) return;
|
|
12
15
|
// Create singleton tooltip element
|
|
13
16
|
tooltipEl = document.createElement("div");
|
|
14
|
-
tooltipEl.className = "tooltip";
|
|
17
|
+
tooltipEl.className = "tooltip"; tooltipEl.id = "clay-tooltip"; tooltipEl.setAttribute("role", "tooltip"); tooltipEl.setAttribute("aria-hidden", "true");
|
|
15
18
|
document.body.appendChild(tooltipEl);
|
|
16
19
|
|
|
17
20
|
// Convert existing title attributes to data-tip in target areas
|
|
18
21
|
convertTitles();
|
|
22
|
+
initPaneHeaderTooltips();
|
|
19
23
|
|
|
20
24
|
// Delegate hover events on document for [data-tip]
|
|
21
25
|
document.addEventListener("mouseover", function (e) {
|
|
@@ -34,6 +38,13 @@ function initTooltips() {
|
|
|
34
38
|
hideTooltip();
|
|
35
39
|
});
|
|
36
40
|
|
|
41
|
+
document.addEventListener("focusin", function (event) {
|
|
42
|
+
var target = event.target.closest("[data-tip], [data-tip-html]");
|
|
43
|
+
if (target) scheduleShow(target);
|
|
44
|
+
});
|
|
45
|
+
document.addEventListener("focusout", function () { cancelShow(); hideTooltip(); });
|
|
46
|
+
document.addEventListener("keydown", function (event) { if (event.key === "Escape") { cancelShow(); hideTooltip(); } });
|
|
47
|
+
window.addEventListener("blur", function () { cancelShow(); hideTooltip(); });
|
|
37
48
|
document.addEventListener("pointerdown", function () {
|
|
38
49
|
cancelShow();
|
|
39
50
|
hideTooltip();
|
|
@@ -58,8 +69,8 @@ function convertTitles() {
|
|
|
58
69
|
var el = els[i];
|
|
59
70
|
if (!el.getAttribute("data-tip")) {
|
|
60
71
|
el.setAttribute("data-tip", el.getAttribute("title"));
|
|
61
|
-
el.removeAttribute("title");
|
|
62
72
|
}
|
|
73
|
+
el.removeAttribute("title");
|
|
63
74
|
}
|
|
64
75
|
}
|
|
65
76
|
|
|
@@ -83,17 +94,23 @@ function cancelShow() {
|
|
|
83
94
|
}
|
|
84
95
|
|
|
85
96
|
function showTooltipAt(target) {
|
|
86
|
-
if (!tooltipEl) return;
|
|
97
|
+
if (!tooltipEl || !target.isConnected || !target.getClientRects().length) return;
|
|
98
|
+
// A message preview already describes this Worker/task tab.
|
|
99
|
+
if (target.getAttribute('aria-describedby') === 'agent-message-bubble' && document.getElementById('agent-message-bubble')) return;
|
|
87
100
|
// Suppress tooltip when an associated popover/picker is open
|
|
88
101
|
var suppressSel = target.getAttribute("data-tip-suppress-when-open");
|
|
89
102
|
if (suppressSel) {
|
|
90
103
|
var openEl = document.querySelector(suppressSel);
|
|
91
|
-
if (openEl && !openEl.classList.contains("hidden")) return;
|
|
104
|
+
if (openEl && !openEl.hidden && !openEl.classList.contains("hidden")) return;
|
|
92
105
|
}
|
|
93
106
|
var html = target.getAttribute("data-tip-html");
|
|
94
107
|
var text = target.getAttribute("data-tip");
|
|
95
108
|
if (!html && !text) return;
|
|
109
|
+
if (currentTarget && currentTarget !== target) hideTooltip();
|
|
96
110
|
currentTarget = target;
|
|
111
|
+
var described = (target.getAttribute('aria-describedby') || '').split(/\s+/).filter(Boolean);
|
|
112
|
+
if (described.indexOf('clay-tooltip') < 0) described.push('clay-tooltip');
|
|
113
|
+
target.setAttribute('aria-describedby', described.join(' '));
|
|
97
114
|
|
|
98
115
|
if (html) {
|
|
99
116
|
tooltipEl.innerHTML = html;
|
|
@@ -113,6 +130,7 @@ function showTooltipAt(target) {
|
|
|
113
130
|
tooltipEl.style.top = "-9999px";
|
|
114
131
|
tooltipEl.style.left = "0";
|
|
115
132
|
tooltipEl.style.right = "";
|
|
133
|
+
tooltipEl.setAttribute("aria-hidden", "false");
|
|
116
134
|
tooltipEl.classList.add("visible");
|
|
117
135
|
|
|
118
136
|
// Position after layout
|
|
@@ -144,9 +162,17 @@ function showTooltipAt(target) {
|
|
|
144
162
|
|
|
145
163
|
function hideTooltip() {
|
|
146
164
|
if (tooltipEl) {
|
|
147
|
-
tooltipEl.classList.remove("visible");
|
|
165
|
+
tooltipEl.classList.remove("visible"); tooltipEl.setAttribute("aria-hidden", "true");
|
|
166
|
+
}
|
|
167
|
+
if (currentTarget) {
|
|
168
|
+
var described = (currentTarget.getAttribute('aria-describedby') || '').split(/\s+/).filter(function (id) { return id && id !== 'clay-tooltip'; });
|
|
169
|
+
if (described.length) currentTarget.setAttribute('aria-describedby', described.join(' ')); else currentTarget.removeAttribute('aria-describedby');
|
|
148
170
|
}
|
|
149
171
|
currentTarget = null;
|
|
150
172
|
}
|
|
151
173
|
|
|
152
|
-
|
|
174
|
+
function pruneTooltip() { if (currentTarget && (!currentTarget.isConnected || !currentTarget.getClientRects().length)) { cancelShow(); hideTooltip(); } }
|
|
175
|
+
|
|
176
|
+
function dismissTooltip() { cancelShow(); hideTooltip(); }
|
|
177
|
+
|
|
178
|
+
export { initTooltips, registerTooltip, pruneTooltip, dismissTooltip };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { copy, mapWorkspaceTree, paneOwner } from './view-workspace-state.js';
|
|
2
|
+
import { listIds } from './split-view-layout-client.js';
|
|
3
|
+
// Coordinates never create resources. Resolve the old singleton Files slot only
|
|
4
|
+
// after a real file instance exists; leave other delayed slots intact.
|
|
5
|
+
export function boundLegacyCoordinates(record, value) {
|
|
6
|
+
var next = copy(record), mapping = {};
|
|
7
|
+
listIds(record.tree).forEach(function (id) { mapping[id] = id; });
|
|
8
|
+
var files = value.panes.filter(function (pane) { return pane.kind === 'file' && pane.open && pane.placement === 'docked'; });
|
|
9
|
+
if (files.length === 1) mapping['tool-file-viewer'] = 'tool-' + paneOwner(files[0]);
|
|
10
|
+
next.tree = mapWorkspaceTree(next.tree, mapping);
|
|
11
|
+
return next;
|
|
12
|
+
}
|
|
13
|
+
export function legacyCoordinatesReady(record, value) {
|
|
14
|
+
var ids = value.panes.filter(function (p) { return p.open && p.placement === 'docked'; }).map(function (p) { return 'tool-' + paneOwner(p); });
|
|
15
|
+
return listIds(boundLegacyCoordinates(record, value).tree).filter(function (id) { return id.indexOf('tool-') === 0; }).every(function (id) { return ids.indexOf(id) >= 0; });
|
|
16
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { getWs } from './ws-ref.js';
|
|
3
|
+
import { showToast } from './utils.js';
|
|
4
|
+
import { prepareViewMutation } from './view-workspace-state.js';
|
|
5
|
+
function contextKey(state) { return [state.currentSlug || '', state.currentProjectOwnerId || '', state.myUserId || '', state.projectSplitViewSocketGeneration || 0].join('|'); }
|
|
6
|
+
function projectReady(state) { return getWs() !== state.splitIdentityBlockedSocket && !state.paneMode && !state.dmMode && state.connected && state.authIdentityLoaded && state.currentSlug === state.activeProjectSlug && state.currentSlug === state.sessionListProjectSlug; }
|
|
7
|
+
function sendMessage(message) { var ws = getWs(); if (!ws || ws.readyState !== 1 || !projectReady(store.snap())) return false; try { ws.send(JSON.stringify(message)); return true; } catch (error) { return false; } }
|
|
8
|
+
export function mutation(type, body, meta) {
|
|
9
|
+
var state = store.snap();
|
|
10
|
+
if (!projectReady(state) || !state.projectSplitViewHydrated || state.projectSplitViewError || state.projectSplitViewPending) return false;
|
|
11
|
+
var requestId = "split-view-" + Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 10);
|
|
12
|
+
var ws = getWs();
|
|
13
|
+
var message = prepareViewMutation(type, Object.assign({ type: type, requestId: requestId, expectedRevision: state.projectSplitViewRevision }, body || {}));
|
|
14
|
+
if (!message) return false;
|
|
15
|
+
var pending = { requestId: requestId, type: type, socket: ws, contextKey: contextKey(state), expectedRevision: state.projectSplitViewRevision, meta: meta || null,
|
|
16
|
+
awaitingBroadcast: false, resultRevision: null };
|
|
17
|
+
store.set({ projectSplitViewPending: pending, projectSplitViewError: "" });
|
|
18
|
+
if (!sendMessage(message)) {
|
|
19
|
+
store.set({ projectSplitViewPending: null, projectSplitViewError: "Split view change could not be sent. Refresh before trying again." });
|
|
20
|
+
showToast("Split view change could not be sent. Refresh before trying again.", "error");
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
}
|