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,32 @@
|
|
|
1
|
+
// Pane chrome uses the existing JS tooltip service, including lazy workbenches.
|
|
2
|
+
import { registerTooltip, pruneTooltip } from './tooltip.js';
|
|
3
|
+
|
|
4
|
+
var HEADERS = '.split-pane-header, .tool-pane-header, .file-viewer-tabbar, .terminal-header, .shared-browser-header, .split-view-workers, #top-bar .top-bar-actions';
|
|
5
|
+
function sync(header) {
|
|
6
|
+
header.querySelectorAll('[title], button, [role="button"], a[aria-label]').forEach(function (element) {
|
|
7
|
+
var label = element.getAttribute('title') || element.getAttribute('aria-label') || element.getAttribute('data-tip');
|
|
8
|
+
if (!label || !label.trim()) return;
|
|
9
|
+
if (element.matches('button, [role="button"], a') && (!element.hasAttribute('aria-label') || element.dataset.tooltipGeneratedLabel === 'true' && element.hasAttribute('title'))) {
|
|
10
|
+
element.dataset.tooltipGeneratedLabel = 'true';
|
|
11
|
+
if (element.getAttribute('aria-label') !== label) element.setAttribute('aria-label', label);
|
|
12
|
+
}
|
|
13
|
+
if (element.getAttribute('data-tip') !== label || element.hasAttribute('title')) registerTooltip(element, label);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
export function initPaneHeaderTooltips() {
|
|
17
|
+
document.querySelectorAll(HEADERS).forEach(sync);
|
|
18
|
+
var observer = new MutationObserver(function (records) {
|
|
19
|
+
pruneTooltip();
|
|
20
|
+
var headers = new Set();
|
|
21
|
+
records.forEach(function (record) {
|
|
22
|
+
var parent = record.target.nodeType === 1 && record.target.closest(HEADERS); if (parent) headers.add(parent);
|
|
23
|
+
(record.addedNodes || []).forEach(function (node) {
|
|
24
|
+
if (node.nodeType !== 1) return;
|
|
25
|
+
if (node.matches(HEADERS)) headers.add(node);
|
|
26
|
+
node.querySelectorAll(HEADERS).forEach(function (header) { headers.add(header); });
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
headers.forEach(function (header) { if (header.isConnected) sync(header); });
|
|
30
|
+
});
|
|
31
|
+
observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['title', 'aria-label'] });
|
|
32
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { queueContext, sameQueueContext } from './pending-message-queue-recovery.js';
|
|
3
|
+
|
|
4
|
+
function validDrag(drag) {
|
|
5
|
+
var queue = store.get('pendingMessageQueue') || {};
|
|
6
|
+
return drag && queueContext(store.snap()).enabled && sameQueueContext(drag.context, queueContext(store.snap())) && queue.revision === drag.revision;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function paintPendingQueueDrag(element) {
|
|
10
|
+
var drag = store.get('pendingMessageQueueDrag');
|
|
11
|
+
element.classList.toggle('is-reordering', !!drag && drag.moved);
|
|
12
|
+
element.querySelectorAll('[data-pending-id]').forEach(function (row) {
|
|
13
|
+
row.classList.toggle('is-dragging', !!drag && drag.moved && row.dataset.pendingId === drag.id);
|
|
14
|
+
row.classList.toggle('is-drop-target', !!drag && drag.moved && row.dataset.pendingId === drag.targetId);
|
|
15
|
+
row.classList.toggle('is-drop-after', !!drag && drag.after && row.dataset.pendingId === drag.targetId);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function bindPendingQueueDrag(element, reorder) {
|
|
20
|
+
function cleanupDrag(event) {
|
|
21
|
+
var drag = store.get('pendingMessageQueueDrag');
|
|
22
|
+
if (!drag || (event && drag.pointerId !== event.pointerId)) return null;
|
|
23
|
+
store.set({ pendingMessageQueueDrag: null });
|
|
24
|
+
if (element.hasPointerCapture(drag.pointerId)) element.releasePointerCapture(drag.pointerId);
|
|
25
|
+
paintPendingQueueDrag(element);
|
|
26
|
+
return drag;
|
|
27
|
+
}
|
|
28
|
+
element.addEventListener('pointerdown', function (event) {
|
|
29
|
+
var row = event.target.closest('[data-pending-id]');
|
|
30
|
+
var handle = row && row.querySelector('[data-action="drag"]');
|
|
31
|
+
if (event.target.closest('textarea, input, a, [contenteditable="true"]') || (event.target.closest('button') && !event.target.closest('[data-action="drag"]'))) return;
|
|
32
|
+
if (!handle || handle.disabled || !event.isPrimary || event.button !== 0) return;
|
|
33
|
+
if (store.get('pendingMessageQueueEdit') && store.get('pendingMessageQueueEdit').id === row.dataset.pendingId) return;
|
|
34
|
+
event.preventDefault();
|
|
35
|
+
handle.focus({ preventScroll: true });
|
|
36
|
+
// Rows can be replaced by a live queue refresh during this gesture.
|
|
37
|
+
element.setPointerCapture(event.pointerId);
|
|
38
|
+
store.set({ pendingMessageQueueDrag: { id: row.dataset.pendingId, pointerId: event.pointerId, targetId: null, context: queueContext(store.snap()), revision: (store.get('pendingMessageQueue') || {}).revision, startX: event.clientX, startY: event.clientY, moved: false } });
|
|
39
|
+
});
|
|
40
|
+
element.addEventListener('pointermove', function (event) {
|
|
41
|
+
var drag = store.get('pendingMessageQueueDrag');
|
|
42
|
+
if (!drag || drag.pointerId !== event.pointerId) return;
|
|
43
|
+
if (!validDrag(drag)) { cleanupDrag(event); return; }
|
|
44
|
+
var moved = drag.moved || Math.abs(event.clientX - drag.startX) + Math.abs(event.clientY - drag.startY) > 4;
|
|
45
|
+
var hit = document.elementFromPoint(event.clientX, event.clientY);
|
|
46
|
+
var row = hit && hit.closest('[data-pending-id]');
|
|
47
|
+
var targetId = row && element.contains(row) && row.querySelector('button[data-action="drag"]:not(:disabled)') && row.dataset.pendingId !== drag.id ? row.dataset.pendingId : null;
|
|
48
|
+
var rows = Array.from(element.querySelectorAll('[data-pending-id]'));
|
|
49
|
+
var source = rows.findIndex(function (candidate) { return candidate.dataset.pendingId === drag.id; });
|
|
50
|
+
store.set({ pendingMessageQueueDrag: Object.assign({}, drag, { moved: moved, targetId: targetId, after: targetId && rows.indexOf(row) > source }) });
|
|
51
|
+
paintPendingQueueDrag(element);
|
|
52
|
+
});
|
|
53
|
+
element.addEventListener('pointerup', function (event) {
|
|
54
|
+
var drag = cleanupDrag(event);
|
|
55
|
+
if (validDrag(drag) && drag.moved && drag.targetId) reorder(drag.id, 0, drag.targetId);
|
|
56
|
+
});
|
|
57
|
+
element.addEventListener('pointercancel', cleanupDrag);
|
|
58
|
+
element.addEventListener('lostpointercapture', cleanupDrag);
|
|
59
|
+
element.addEventListener('keydown', function (event) { if (event.key === 'Escape') cleanupDrag(); });
|
|
60
|
+
window.addEventListener('blur', function () { cleanupDrag(); });
|
|
61
|
+
store.subscribe(function (state, previous) {
|
|
62
|
+
var drag = state.pendingMessageQueueDrag;
|
|
63
|
+
if (drag && !validDrag(drag)) cleanupDrag();
|
|
64
|
+
else if (!drag && previous.pendingMessageQueueDrag) {
|
|
65
|
+
if (element.hasPointerCapture(previous.pendingMessageQueueDrag.pointerId)) element.releasePointerCapture(previous.pendingMessageQueueDrag.pointerId);
|
|
66
|
+
paintPendingQueueDrag(element);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
@@ -5,6 +5,7 @@ import { refreshIcons } from './icons.js';
|
|
|
5
5
|
import { activeQueueItems, hasFutureScheduledPending, moveQueueId, matchesQueueContext, canApplyQueueRevision } from './pending-message-queue-model.js';
|
|
6
6
|
import { emptyQueue, queueContext, sameQueueContext, queueScopeChanged, queueReconnected, queueCanRender, createQueueHydrationRecovery, canonicalQueue } from './pending-message-queue-recovery.js';
|
|
7
7
|
import { imageSrc, imageDraft, openImagePreview, chooseImages } from './pending-message-image-editor.js';
|
|
8
|
+
import { bindPendingQueueDrag, paintPendingQueueDrag } from './pending-message-queue-drag.js';
|
|
8
9
|
import { setScheduleBtnDisabled } from './scheduled-message-state.js';
|
|
9
10
|
|
|
10
11
|
export { hasFutureScheduledPending };
|
|
@@ -391,6 +392,7 @@ function render() {
|
|
|
391
392
|
html += '</div>';
|
|
392
393
|
queueEl.innerHTML = html;
|
|
393
394
|
refreshIcons();
|
|
395
|
+
paintPendingQueueDrag(queueEl);
|
|
394
396
|
restoreFocus(focus);
|
|
395
397
|
}
|
|
396
398
|
|
|
@@ -426,39 +428,7 @@ function bindEvents() {
|
|
|
426
428
|
var row = handle.closest("[data-pending-id]");
|
|
427
429
|
reorder(row.dataset.pendingId, event.key === "ArrowUp" ? -1 : 1, null);
|
|
428
430
|
});
|
|
429
|
-
queueEl
|
|
430
|
-
var handle = event.target.closest('[data-action="drag"]');
|
|
431
|
-
if (!handle) return;
|
|
432
|
-
var row = handle.closest("[data-pending-id]");
|
|
433
|
-
handle.setPointerCapture(event.pointerId);
|
|
434
|
-
store.set({ pendingMessageQueueDrag: { id: row.dataset.pendingId, pointerId: event.pointerId, targetId: null } });
|
|
435
|
-
row.classList.add("is-dragging");
|
|
436
|
-
});
|
|
437
|
-
queueEl.addEventListener("pointermove", function (event) {
|
|
438
|
-
var drag = store.get('pendingMessageQueueDrag');
|
|
439
|
-
if (!drag || drag.pointerId !== event.pointerId) return;
|
|
440
|
-
var target = document.elementFromPoint(event.clientX, event.clientY);
|
|
441
|
-
var row = target && target.closest("[data-pending-id]");
|
|
442
|
-
var targetId = row && row.dataset.pendingId !== drag.id ? row.dataset.pendingId : null;
|
|
443
|
-
queueEl.querySelectorAll(".is-drop-target").forEach(function (node) { node.classList.remove("is-drop-target"); });
|
|
444
|
-
if (targetId && ownedPendingIds().indexOf(targetId) >= 0) row.classList.add("is-drop-target");
|
|
445
|
-
store.set({ pendingMessageQueueDrag: Object.assign({}, drag, { targetId: targetId }) });
|
|
446
|
-
});
|
|
447
|
-
function cleanupDrag(event) {
|
|
448
|
-
var drag = store.get('pendingMessageQueueDrag');
|
|
449
|
-
if (!drag || drag.pointerId !== event.pointerId) return null;
|
|
450
|
-
store.set({ pendingMessageQueueDrag: null });
|
|
451
|
-
queueEl.querySelectorAll(".is-dragging, .is-drop-target").forEach(function (node) { node.classList.remove("is-dragging", "is-drop-target"); });
|
|
452
|
-
return drag;
|
|
453
|
-
}
|
|
454
|
-
function finishDrag(event) {
|
|
455
|
-
var drag = cleanupDrag(event);
|
|
456
|
-
if (!drag) return;
|
|
457
|
-
if (drag.targetId && ownedPendingIds().indexOf(drag.targetId) >= 0) reorder(drag.id, 0, drag.targetId);
|
|
458
|
-
}
|
|
459
|
-
queueEl.addEventListener("pointerup", finishDrag);
|
|
460
|
-
queueEl.addEventListener("pointercancel", cleanupDrag);
|
|
461
|
-
queueEl.addEventListener("lostpointercapture", cleanupDrag);
|
|
431
|
+
bindPendingQueueDrag(queueEl, reorder);
|
|
462
432
|
}
|
|
463
433
|
|
|
464
434
|
export function initPendingMessageQueue() {
|
|
@@ -46,9 +46,9 @@ export function createPermissionControl(className, ariaLabel) {
|
|
|
46
46
|
control.setAttribute("role", "group");
|
|
47
47
|
control.setAttribute("aria-label", ariaLabel || "Session permission mode");
|
|
48
48
|
control.innerHTML = '<div class="session-permission-segmented">' +
|
|
49
|
-
'<button type="button" data-permission-mode="default" aria-label="Ask permissions"
|
|
50
|
-
'<button type="button" data-permission-mode="auto" aria-label="Auto permissions"
|
|
51
|
-
'<button type="button" data-permission-mode="bypassPermissions" aria-label="YOLO permissions"
|
|
49
|
+
'<button type="button" data-permission-mode="default" aria-label="Ask permissions" data-tip="Ask before tool actions that need permission. Previously allowed actions can run without asking again."><span class="permission-label">Ask</span><span class="permission-spinner" aria-hidden="true"></span></button>' +
|
|
50
|
+
'<button type="button" data-permission-mode="auto" aria-label="Auto permissions" data-tip="Let the runtime assess tool actions and approve them automatically when allowed. Some actions may still need your permission."><span class="permission-label">Auto</span><span class="permission-spinner" aria-hidden="true"></span></button>' +
|
|
51
|
+
'<button type="button" data-permission-mode="bypassPermissions" aria-label="YOLO permissions" data-tip="Run tools, file changes and commands without asking for permission. Clay access restrictions still apply."><span class="permission-label">YOLO</span><span class="permission-spinner" aria-hidden="true"></span></button>' +
|
|
52
52
|
'</div><span class="session-permission-status" aria-live="polite"></span>';
|
|
53
53
|
control.querySelector(".session-permission-status").setAttribute("aria-atomic", "true");
|
|
54
54
|
return control;
|
|
@@ -102,7 +102,7 @@ export function renderPermissionControl(control, state) {
|
|
|
102
102
|
control.classList.toggle("permission-globally-forced", globallyForced);
|
|
103
103
|
control.classList.toggle("permission-runtime-fallback", state.permissionMode === "auto" && !!state.effectivePermissionMode && state.effectivePermissionMode !== "auto");
|
|
104
104
|
control.setAttribute("aria-busy", pending ? "true" : "false");
|
|
105
|
-
var
|
|
105
|
+
var buttonDescriptions = {"default": "Ask before tool actions that need permission. Previously allowed actions can run without asking again.", "auto": "Let the runtime assess tool actions and approve them automatically when allowed. Some actions may still need your permission.", "bypassPermissions": "Run tools, file changes and commands without asking for permission. Clay access restrictions still apply."};
|
|
106
106
|
var buttons = control.querySelectorAll("[data-permission-mode]");
|
|
107
107
|
for (var i = 0; i < buttons.length; i++) {
|
|
108
108
|
var mode = buttons[i].dataset.permissionMode;
|
|
@@ -115,7 +115,8 @@ export function renderPermissionControl(control, state) {
|
|
|
115
115
|
buttons[i].classList.toggle("pending", requested);
|
|
116
116
|
buttons[i].setAttribute("aria-pressed", selected ? "true" : "false");
|
|
117
117
|
buttons[i].setAttribute("aria-busy", requested ? "true" : "false");
|
|
118
|
-
buttons[i].
|
|
118
|
+
buttons[i].setAttribute("data-tip", (unsupported ? "Auto is unavailable for this session. " : "") + buttonDescriptions[mode]);
|
|
119
|
+
buttons[i].removeAttribute("title");
|
|
119
120
|
}
|
|
120
121
|
var status = control.querySelector(".session-permission-status");
|
|
121
122
|
if (status) {
|
|
@@ -6,17 +6,17 @@ export function isPlantUmlFile(path) {
|
|
|
6
6
|
return /\.(puml|plantuml|pu|uml|salt)$/i.test(String(path || ""));
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export function cancelPlantUmlPreview() {
|
|
10
|
-
var job = store.get('plantUmlPreviewJob');
|
|
9
|
+
export function cancelPlantUmlPreview(instanceId) {
|
|
10
|
+
var job = store.get(instanceId ? 'plantUmlPreviewJob:' + instanceId : 'plantUmlPreviewJob');
|
|
11
11
|
if (!job) return;
|
|
12
12
|
clearTimeout(job.timer);
|
|
13
13
|
job.observer.disconnect();
|
|
14
14
|
job.controller.abort();
|
|
15
|
-
|
|
15
|
+
var cleared = {}; cleared[instanceId ? 'plantUmlPreviewJob:' + instanceId : 'plantUmlPreviewJob'] = null; store.set(cleared);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export function renderPlantUmlPreview(body, source, path) {
|
|
19
|
-
cancelPlantUmlPreview();
|
|
18
|
+
export function renderPlantUmlPreview(body, source, path, instanceId) {
|
|
19
|
+
cancelPlantUmlPreview(instanceId);
|
|
20
20
|
body.innerHTML = "";
|
|
21
21
|
var preview = document.createElement("div");
|
|
22
22
|
preview.className = "file-viewer-plantuml-preview";
|
|
@@ -36,22 +36,22 @@ export function renderPlantUmlPreview(body, source, path) {
|
|
|
36
36
|
|
|
37
37
|
var job = { controller: new AbortController(), observer: null, timer: null };
|
|
38
38
|
job.observer = new MutationObserver(function () {
|
|
39
|
-
if (!preview.isConnected && store.get('plantUmlPreviewJob') === job) cancelPlantUmlPreview();
|
|
39
|
+
if (!preview.isConnected && store.get(instanceId ? 'plantUmlPreviewJob:' + instanceId : 'plantUmlPreviewJob') === job) cancelPlantUmlPreview(instanceId);
|
|
40
40
|
});
|
|
41
41
|
job.observer.observe(body, { childList: true });
|
|
42
42
|
job.timer = setTimeout(function () {
|
|
43
|
-
if (store.get('plantUmlPreviewJob') !== job) return;
|
|
43
|
+
if (store.get(instanceId ? 'plantUmlPreviewJob:' + instanceId : 'plantUmlPreviewJob') !== job) return;
|
|
44
44
|
showError("The diagram preview timed out.");
|
|
45
|
-
cancelPlantUmlPreview();
|
|
45
|
+
cancelPlantUmlPreview(instanceId);
|
|
46
46
|
}, 20000);
|
|
47
|
-
|
|
47
|
+
var patch = {}; patch[instanceId ? 'plantUmlPreviewJob:' + instanceId : 'plantUmlPreviewJob'] = job; store.set(patch);
|
|
48
48
|
fetch("api/file/plantuml?path=" + encodeURIComponent(path), { signal: job.controller.signal, cache: "no-store" }).then(function (response) {
|
|
49
49
|
return response.text().then(function (text) {
|
|
50
50
|
if (!response.ok) throw new Error(text || "Unable to render this diagram.");
|
|
51
51
|
return text;
|
|
52
52
|
});
|
|
53
53
|
}).then(function (svg) {
|
|
54
|
-
if (store.get('plantUmlPreviewJob') !== job || !preview.isConnected) return;
|
|
54
|
+
if (store.get(instanceId ? 'plantUmlPreviewJob:' + instanceId : 'plantUmlPreviewJob') !== job || !preview.isConnected) return;
|
|
55
55
|
if (svg.length > 8 * 1024 * 1024 || !/<svg[\s>]/i.test(svg)) throw new Error("The renderer returned an invalid diagram.");
|
|
56
56
|
var image = document.createElement("img");
|
|
57
57
|
image.alt = "Diagram preview of " + path;
|
|
@@ -69,8 +69,8 @@ export function renderPlantUmlPreview(body, source, path) {
|
|
|
69
69
|
mountWireframeViewport(viewport, image, toolbar);
|
|
70
70
|
|
|
71
71
|
}).catch(function (error) {
|
|
72
|
-
if (store.get('plantUmlPreviewJob') === job && error.name !== "AbortError") showError(error.message);
|
|
72
|
+
if (store.get(instanceId ? 'plantUmlPreviewJob:' + instanceId : 'plantUmlPreviewJob') === job && error.name !== "AbortError") showError(error.message);
|
|
73
73
|
}).finally(function () {
|
|
74
|
-
if (store.get('plantUmlPreviewJob') === job) cancelPlantUmlPreview();
|
|
74
|
+
if (store.get(instanceId ? 'plantUmlPreviewJob:' + instanceId : 'plantUmlPreviewJob') === job) cancelPlantUmlPreview(instanceId);
|
|
75
75
|
});
|
|
76
76
|
}
|
|
@@ -1,21 +1,136 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Restricted projects use public shell primitives without opening a project socket.
|
|
2
|
+
import { createStore, store } from './store.js';
|
|
3
|
+
import { groupProjects, getProjectAbbrev } from './project-grouping.js';
|
|
4
|
+
import { computeVars, validateTheme, defaultDarkFallback, defaultLightFallback } from './theme-palette.js';
|
|
5
|
+
import { initTooltips } from './tooltip.js';
|
|
6
|
+
|
|
7
|
+
var data = JSON.parse(document.getElementById('project-admin-data').textContent);
|
|
8
|
+
createStore({ adminOverview: data, adminUsage: null, adminUsageLoading: false, adminUsageTimer: null, adminUsageGeneration: 0, adminRailCollapsed: {}, adminThemes: null });
|
|
9
|
+
|
|
10
|
+
function applyAppearance() {
|
|
11
|
+
var seed = window.__clayAppearanceSeed;
|
|
12
|
+
var mode = seed && seed.configured && seed.mode || (matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark');
|
|
13
|
+
var id = seed && (seed[mode + 'Theme'] || seed.theme);
|
|
14
|
+
var themes = store.get('adminThemes') || {};
|
|
15
|
+
var theme = themes[id] && themes[id].variant === mode ? themes[id] : (mode === 'light' ? defaultLightFallback : defaultDarkFallback);
|
|
16
|
+
if (!validateTheme(theme)) return;
|
|
17
|
+
var vars = computeVars(theme);
|
|
18
|
+
Object.keys(vars).forEach(function (name) { document.documentElement.style.setProperty(name, vars[name]); });
|
|
19
|
+
document.documentElement.classList.toggle('light-theme', theme.variant === 'light');
|
|
20
|
+
document.documentElement.classList.toggle('dark-theme', theme.variant !== 'light');
|
|
21
|
+
}
|
|
22
|
+
applyAppearance();
|
|
23
|
+
fetch('/api/themes', { credentials: 'same-origin' }).then(function (response) { if (!response.ok) throw new Error('Themes unavailable'); return response.json(); }).then(function (themes) {
|
|
24
|
+
var valid = {};
|
|
25
|
+
Object.entries(Object.assign({}, themes.bundled, themes.custom)).forEach(function (entry) { if (validateTheme(entry[1])) valid[entry[0]] = entry[1]; });
|
|
26
|
+
store.set({ adminThemes: valid }); applyAppearance();
|
|
27
|
+
}).catch(function () {});
|
|
28
|
+
matchMedia('(prefers-color-scheme: light)').addEventListener('change', applyAppearance);
|
|
29
|
+
|
|
30
|
+
function projectLink(project, worktree) {
|
|
31
|
+
var link = document.createElement('a');
|
|
32
|
+
link.className = (worktree ? 'icon-strip-wt-item' : 'icon-strip-item') + (project.slug === data.project.slug ? ' active' : '');
|
|
33
|
+
link.href = '/p/' + encodeURIComponent(project.slug) + '/';
|
|
34
|
+
link.dataset.slug = project.slug;
|
|
35
|
+
link.dataset.tip = project.title + (project.contentAccess === false ? ' · Administrative overview' : '');
|
|
36
|
+
link.setAttribute('aria-label', link.dataset.tip);
|
|
37
|
+
if (project.slug === data.project.slug) link.setAttribute('aria-current', 'page');
|
|
38
|
+
var label = document.createElement('span');
|
|
39
|
+
label.className = worktree ? 'wt-branch-abbrev' : 'icon-strip-name';
|
|
40
|
+
if (project.icon) label.classList.add('project-emoji');
|
|
41
|
+
label.textContent = project.icon || getProjectAbbrev(project.title);
|
|
42
|
+
link.appendChild(label);
|
|
43
|
+
return link;
|
|
44
|
+
}
|
|
45
|
+
function renderNavigation() {
|
|
46
|
+
var container = document.getElementById('icon-strip-projects');
|
|
47
|
+
var grouped = groupProjects(data.projects);
|
|
48
|
+
grouped.parents.forEach(function (project) {
|
|
49
|
+
var children = grouped.wtByParent[project.slug] || [];
|
|
50
|
+
var link = projectLink(project, false);
|
|
51
|
+
if (!children.length) { container.appendChild(link); return; }
|
|
52
|
+
var group = document.createElement('div');
|
|
53
|
+
group.className = 'icon-strip-group'; group.dataset.parentSlug = project.slug;
|
|
54
|
+
var header = document.createElement('div'); header.className = 'project-admin-rail-header';
|
|
55
|
+
header.appendChild(link); link.classList.add('folder-header');
|
|
56
|
+
var toggle = document.createElement('button');
|
|
57
|
+
toggle.type = 'button'; toggle.className = 'icon-strip-group-chevron'; toggle.dataset.tip = 'Toggle worktrees';
|
|
58
|
+
toggle.setAttribute('aria-label', 'Toggle ' + project.title + ' worktrees'); toggle.setAttribute('aria-expanded', 'true');
|
|
59
|
+
var list = document.createElement('div'); list.className = 'icon-strip-group-items'; list.id = 'worktrees-' + project.slug;
|
|
60
|
+
toggle.setAttribute('aria-controls', list.id);
|
|
61
|
+
toggle.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M6 3v12a6 6 0 0 0 12 0V6M6 7h0"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="6" r="3"/></svg>';
|
|
62
|
+
toggle.addEventListener('click', function () {
|
|
63
|
+
var collapsed = group.classList.toggle('collapsed'); list.inert = collapsed;
|
|
64
|
+
toggle.setAttribute('aria-expanded', String(!collapsed));
|
|
65
|
+
var state = Object.assign({}, store.get('adminRailCollapsed')); state[project.slug] = collapsed; store.set({ adminRailCollapsed: state });
|
|
66
|
+
});
|
|
67
|
+
children.forEach(function (child) { list.appendChild(projectLink(child, true)); });
|
|
68
|
+
header.appendChild(toggle); group.append(header, list); container.appendChild(group);
|
|
69
|
+
});
|
|
70
|
+
var parent = data.projects.find(function (project) { return project.slug === data.project.parentSlug; });
|
|
71
|
+
var label = document.getElementById('project-admin-parent');
|
|
72
|
+
if (parent) { label.hidden = false; label.textContent = 'Worktree of ' + parent.title; }
|
|
73
|
+
}
|
|
74
|
+
renderNavigation();
|
|
75
|
+
initTooltips();
|
|
76
|
+
|
|
2
77
|
var tabs = Array.from(document.querySelectorAll('[role="tab"]'));
|
|
3
78
|
function select(tab) {
|
|
4
79
|
tabs.forEach(function (item) {
|
|
5
80
|
var active = item === tab;
|
|
6
|
-
item.setAttribute(
|
|
7
|
-
item.
|
|
8
|
-
document.getElementById(item.getAttribute("aria-controls")).hidden = !active;
|
|
81
|
+
item.setAttribute('aria-selected', String(active)); item.tabIndex = active ? 0 : -1;
|
|
82
|
+
document.getElementById(item.getAttribute('aria-controls')).hidden = !active;
|
|
9
83
|
});
|
|
10
84
|
}
|
|
11
85
|
tabs.forEach(function (tab, index) {
|
|
12
|
-
tab.addEventListener(
|
|
13
|
-
tab.addEventListener(
|
|
14
|
-
if ([
|
|
86
|
+
tab.addEventListener('click', function () { select(tab); });
|
|
87
|
+
tab.addEventListener('keydown', function (event) {
|
|
88
|
+
if (['ArrowLeft', 'ArrowRight', 'Home', 'End'].indexOf(event.key) === -1) return;
|
|
15
89
|
event.preventDefault();
|
|
16
|
-
var target = event.key ===
|
|
17
|
-
select(target);
|
|
18
|
-
target.focus();
|
|
90
|
+
var target = event.key === 'Home' ? tabs[0] : event.key === 'End' ? tabs[tabs.length - 1] : tabs[(index + (event.key === 'ArrowRight' ? 1 : tabs.length - 1)) % tabs.length];
|
|
91
|
+
select(target); target.focus();
|
|
19
92
|
});
|
|
20
93
|
});
|
|
21
94
|
select(tabs[0]);
|
|
95
|
+
|
|
96
|
+
function size(bytes) {
|
|
97
|
+
if (!Number.isFinite(bytes) || bytes < 0) return 'Unavailable';
|
|
98
|
+
var units = ['B', 'KiB', 'MiB', 'GiB', 'TiB'], index = 0;
|
|
99
|
+
while (bytes >= 1024 && index < units.length - 1) { bytes /= 1024; index++; }
|
|
100
|
+
return bytes.toFixed(index ? 1 : 0) + ' ' + units[index];
|
|
101
|
+
}
|
|
102
|
+
function clearUsage() {
|
|
103
|
+
['bytes', 'files', 'free', 'capacity'].forEach(function (field) { document.getElementById('usage-' + field).textContent = '—'; });
|
|
104
|
+
store.set({ adminUsage: null });
|
|
105
|
+
}
|
|
106
|
+
async function loadUsage(refresh) {
|
|
107
|
+
if (store.get('adminUsageLoading')) return;
|
|
108
|
+
clearTimeout(store.get('adminUsageTimer'));
|
|
109
|
+
var generation = store.get('adminUsageGeneration');
|
|
110
|
+
store.set({ adminUsageLoading: true });
|
|
111
|
+
var button = document.getElementById('project-admin-refresh'), status = document.getElementById('usage-status');
|
|
112
|
+
button.disabled = true; document.getElementById('project-admin-usage').setAttribute('aria-busy', 'true');
|
|
113
|
+
try {
|
|
114
|
+
var response = await fetch('/api/server/storage/project/' + encodeURIComponent(data.project.slug) + (refresh ? '?refresh=1' : ''), { credentials: 'same-origin' });
|
|
115
|
+
var result = await response.json();
|
|
116
|
+
if (generation !== store.get('adminUsageGeneration')) return;
|
|
117
|
+
if (!response.ok) { if ([401, 403, 404, 409].indexOf(response.status) >= 0) clearUsage(); throw new Error(result.error || 'Storage measurement failed.'); }
|
|
118
|
+
if (result.slug !== data.project.slug) throw new Error('Storage response belongs to another project.');
|
|
119
|
+
store.set({ adminUsage: result });
|
|
120
|
+
var usage = result.usage;
|
|
121
|
+
if (usage && usage.available) {
|
|
122
|
+
document.getElementById('usage-bytes').textContent = (result.partial || result.scanning ? 'At least ' : '') + size(usage.bytes);
|
|
123
|
+
document.getElementById('usage-files').textContent = (result.partial || result.scanning ? 'At least ' : '') + Number(usage.files).toLocaleString();
|
|
124
|
+
document.getElementById('usage-free').textContent = size(usage.availableBytes);
|
|
125
|
+
document.getElementById('usage-capacity').textContent = size(usage.capacityBytes);
|
|
126
|
+
} else clearUsage();
|
|
127
|
+
status.textContent = result.scanning ? 'Scan in progress. Totals are not final.' : !usage || !usage.available ? 'Storage is unavailable: the directory is missing, unreadable or shared by multiple registrations.' : (result.partial ? 'Partial scan; some paths could not be measured. ' : '') + 'Measured ' + new Date(result.checkedAt).toLocaleString() + '. Cached for up to 5 minutes.';
|
|
128
|
+
if (result.scanning && !document.hidden) store.set({ adminUsageTimer: setTimeout(function () { loadUsage(false); }, 1500) });
|
|
129
|
+
} catch (error) { if (generation === store.get('adminUsageGeneration')) status.textContent = error.message + (store.get('adminUsage') ? ' Previous measurement retained.' : ''); }
|
|
130
|
+
finally { if (generation === store.get('adminUsageGeneration')) { store.set({ adminUsageLoading: false }); button.disabled = false; document.getElementById('project-admin-usage').setAttribute('aria-busy', 'false'); } }
|
|
131
|
+
}
|
|
132
|
+
document.getElementById('project-admin-refresh').addEventListener('click', function () { loadUsage(true); });
|
|
133
|
+
document.addEventListener('visibilitychange', function () { if (document.hidden) clearTimeout(store.get('adminUsageTimer')); else if (store.get('adminUsage') && store.get('adminUsage').scanning) loadUsage(false); });
|
|
134
|
+
window.addEventListener('pagehide', function () { clearTimeout(store.get('adminUsageTimer')); store.set({ adminUsageGeneration: store.get('adminUsageGeneration') + 1 }); });
|
|
135
|
+
window.addEventListener('pageshow', function (event) { if (event.persisted) { clearUsage(); store.set({ adminUsageLoading: false }); loadUsage(false); } });
|
|
136
|
+
loadUsage(false);
|
|
@@ -20,3 +20,12 @@ export function groupProjects(projects) {
|
|
|
20
20
|
}
|
|
21
21
|
return { parents: parents, wtByParent: wtByParent };
|
|
22
22
|
}
|
|
23
|
+
|
|
24
|
+
export function getProjectAbbrev(name) {
|
|
25
|
+
if (!name) return "?";
|
|
26
|
+
var words = name.replace(/[^a-zA-Z0-9\s]/g, "").trim().split(/\s+/);
|
|
27
|
+
if (words.length >= 2) {
|
|
28
|
+
return (words[0][0] + words[1][0]).toUpperCase();
|
|
29
|
+
}
|
|
30
|
+
return name.substring(0, 2).toUpperCase();
|
|
31
|
+
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
import { WORKBENCH_PANELS } from './workbench-panels.js';
|
|
2
|
+
import { activeSavedView, workspaceChanged } from './view-workspace-state.js';
|
|
1
3
|
// Shared arbitration for docked background panes and one foreground overlay.
|
|
2
4
|
|
|
5
|
+
import { renderToolPaneChrome } from './tool-pane-chrome.js';
|
|
3
6
|
import { store } from './store.js';
|
|
4
7
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
5
8
|
import { showToast } from './utils.js';
|
|
6
9
|
|
|
7
10
|
var closers = {};
|
|
8
|
-
var panelIds = { terminal: 'terminal-container', browser: 'shared-browser-panel', 'file-viewer': 'file-viewer'
|
|
9
|
-
|
|
10
|
-
'notes-browser': 'notes-browser', 'scheduled-tasks': 'scheduled-tasks-panel', debates: 'debates-workbench',
|
|
11
|
-
'mcp-skills': 'mcp-skills-workbench', 'mate-knowledge': 'mate-knowledge-workbench', wireframe: 'wireframe-panel' };
|
|
11
|
+
var panelIds = { terminal: 'terminal-container', browser: 'shared-browser-panel', 'file-viewer': 'file-viewer' };
|
|
12
|
+
Object.keys(WORKBENCH_PANELS).forEach(function (name) { panelIds[name] = WORKBENCH_PANELS[name].id; });
|
|
12
13
|
|
|
13
14
|
// Pane startup does not need account settings for a closed workbench.
|
|
14
15
|
function ensureLayoutPreference() {
|
|
@@ -34,6 +35,7 @@ export function rightWorkbenchRevisionFor(name) {
|
|
|
34
35
|
function publish(owner, docked, revisions) {
|
|
35
36
|
store.set({ rightWorkbenchOwner: owner, rightWorkbenchDockedOwners: docked,
|
|
36
37
|
rightWorkbenchPanelRevisions: revisions, rightWorkbenchRevision: (store.get('rightWorkbenchRevision') || 0) + 1 });
|
|
38
|
+
workspaceChanged();
|
|
37
39
|
}
|
|
38
40
|
function setDocked(name, docked) {
|
|
39
41
|
if (!openOwner(name)) return;
|
|
@@ -53,16 +55,18 @@ function renderLayoutControls() {
|
|
|
53
55
|
var panel = document.getElementById(panelIds[name]);
|
|
54
56
|
if (!panel) return;
|
|
55
57
|
var docked = owners.indexOf(name) !== -1;
|
|
58
|
+
renderToolPaneChrome(panel, name);
|
|
56
59
|
panel.dataset.workbenchPlacement = docked ? 'docked' : 'overlay';
|
|
57
60
|
panel.dataset.workbenchActive = store.get('rightWorkbenchOwner') === name ? 'true' : 'false';
|
|
58
61
|
if (!openOwner(name)) return;
|
|
59
|
-
var header = panel.querySelector(':scope > header, :scope > .file-viewer-header .file-viewer-tabbar, :scope > .terminal-header');
|
|
62
|
+
var header = panel.querySelector(':scope > .tool-pane-header, :scope > header, :scope > .file-viewer-header .file-viewer-tabbar, :scope > .terminal-header');
|
|
60
63
|
if (!header) return;
|
|
61
64
|
var button = panel.querySelector('.workbench-layout-toggle');
|
|
62
65
|
if (!button) {
|
|
63
|
-
var actions = header.querySelector('[class*="window-actions"], .terminal-header-actions, .shared-browser-window, :scope.mcp-skills-header > div') || header;
|
|
66
|
+
var actions = header.querySelector('[class*="window-actions"], .terminal-header-actions, .shared-browser-window, .file-viewer-window-actions, .workbench-pane-actions, :scope.mcp-skills-header > div') || header;
|
|
64
67
|
button = document.createElement('button'); button.type = 'button'; button.className = 'workbench-layout-toggle';
|
|
65
68
|
button.addEventListener('click', function () {
|
|
69
|
+
if (activeSavedView()) { setDocked(name, dockedOwners().indexOf(name) < 0); return; }
|
|
66
70
|
var current = store.get('workbenchLayoutPreference') || {};
|
|
67
71
|
if (!current.ready || current.loading || current.saving) { ensureLayoutPreference(); return; }
|
|
68
72
|
var owner = store.get('myUserId'), project = store.get('currentSlug'), revision = rightWorkbenchRevisionFor(name);
|
|
@@ -83,6 +87,7 @@ function renderLayoutControls() {
|
|
|
83
87
|
});
|
|
84
88
|
});
|
|
85
89
|
var anchor = actions === header ? header.querySelector('[data-knowledge-wide]') : actions.firstElementChild;
|
|
90
|
+
if (panel.dataset.fileInstance) { var pin = actions.querySelector('[data-action="pin"]'); anchor = pin && pin.nextSibling; }
|
|
86
91
|
actions.insertBefore(button, anchor || null);
|
|
87
92
|
}
|
|
88
93
|
if (button.dataset.mode !== (docked ? 'docked' : 'overlay')) {
|
|
@@ -92,20 +97,26 @@ function renderLayoutControls() {
|
|
|
92
97
|
button.setAttribute('aria-label', docked ? 'Float workbench over sessions' : 'Dock workbench beside sessions');
|
|
93
98
|
button.title = state.error || button.getAttribute('aria-label');
|
|
94
99
|
button.setAttribute('aria-pressed', docked ? 'true' : 'false');
|
|
95
|
-
|
|
100
|
+
// View edits are queued by the workspace controller. Disabling this button
|
|
101
|
+
// for an unrelated in-flight save can swallow a click between down and up.
|
|
102
|
+
button.disabled = activeSavedView() ? false : !!state.loading || !!state.saving;
|
|
96
103
|
button.setAttribute('aria-busy', state.loading || state.saving ? 'true' : 'false');
|
|
97
104
|
});
|
|
98
105
|
}
|
|
99
106
|
|
|
100
|
-
export function registerRightWorkbench(name, close) {
|
|
107
|
+
export function registerRightWorkbench(name, close, panelId) {
|
|
108
|
+
if (panelId) panelIds[name] = panelId;
|
|
101
109
|
if (!name || typeof close !== "function") return;
|
|
102
110
|
closers[name] = close;
|
|
103
111
|
}
|
|
104
112
|
|
|
113
|
+
export function unregisterRightWorkbench(name) {
|
|
114
|
+
delete closers[name];
|
|
115
|
+
if (name.indexOf('file-') === 0 && name !== 'file-viewer') delete panelIds[name];
|
|
116
|
+
}
|
|
105
117
|
export function claimRightWorkbench(name) {
|
|
106
118
|
if (typeof document !== 'undefined') ensureLayoutPreference();
|
|
107
|
-
var owner = store.get('rightWorkbenchOwner'), docked = dockedOwners().slice()
|
|
108
|
-
if (!owner && !docked.length && preference.ready && preference.mode === 'docked') docked.push(name);
|
|
119
|
+
var owner = store.get('rightWorkbenchOwner'), docked = dockedOwners().slice();
|
|
109
120
|
var retained = docked.indexOf(name) !== -1 && owner && docked.indexOf(owner) === -1 ? owner : name;
|
|
110
121
|
var revision = (store.get('rightWorkbenchRevision') || 0) + 1, revisions = Object.assign({}, store.get('rightWorkbenchPanelRevisions') || {});
|
|
111
122
|
Object.keys(revisions).forEach(function (key) { if (key !== retained && docked.indexOf(key) === -1) delete revisions[key]; });
|
|
@@ -133,10 +144,18 @@ export function resetRightWorkbench() {
|
|
|
133
144
|
}
|
|
134
145
|
store.subscribe(function (state, previous) {
|
|
135
146
|
if (state.currentSlug !== previous.currentSlug || state.myUserId !== previous.myUserId) { resetRightWorkbench(); return; }
|
|
136
|
-
|
|
137
|
-
if (!before.ready && preference.ready && preference.mode === 'docked' && !dockedOwners().length && state.rightWorkbenchOwner) {
|
|
138
|
-
setDocked(state.rightWorkbenchOwner, true); return;
|
|
139
|
-
}
|
|
140
|
-
if (state.workbenchLayoutPreference !== previous.workbenchLayoutPreference || state.rightWorkbenchOwner !== previous.rightWorkbenchOwner ||
|
|
147
|
+
if (state.projectSplitViewPending !== previous.projectSplitViewPending || state.workbenchLayoutPreference !== previous.workbenchLayoutPreference || state.rightWorkbenchOwner !== previous.rightWorkbenchOwner ||
|
|
141
148
|
state.rightWorkbenchDockedOwners !== previous.rightWorkbenchDockedOwners) renderLayoutControls();
|
|
142
149
|
});
|
|
150
|
+
|
|
151
|
+
export function restoreWorkbenchDock(name) { setDocked(name, true); }
|
|
152
|
+
|
|
153
|
+
// Document-level shortcuts belong to the focused pane, then the foreground overlay.
|
|
154
|
+
export function handlesWorkbenchKey(name, event) {
|
|
155
|
+
if (event.defaultPrevented) return false;
|
|
156
|
+
var target = event.target, focused = Object.keys(panelIds).find(function (owner) {
|
|
157
|
+
var panel = document.getElementById(panelIds[owner]);
|
|
158
|
+
return panel && target && panel.contains(target);
|
|
159
|
+
});
|
|
160
|
+
return (focused || store.get('rightWorkbenchOwner')) === name;
|
|
161
|
+
}
|
|
@@ -65,18 +65,19 @@ function ensurePanel() {
|
|
|
65
65
|
panel.innerHTML =
|
|
66
66
|
'<header class="scheduled-tasks-topbar">' +
|
|
67
67
|
'<span class="scheduled-tasks-title">' + iconHtml("calendar-clock") + 'Scheduled</span>' +
|
|
68
|
-
'<div class="scheduled-tasks-create-group">' +
|
|
69
|
-
'<button class="scheduled-tasks-new" type="button" data-action="new">' + iconHtml("plus") + 'New</button>' +
|
|
70
|
-
'<button id="scheduled-tasks-create-menu-trigger" class="scheduled-tasks-new-menu" type="button" data-action="new-menu" aria-label="More task creation options" aria-haspopup="menu" aria-expanded="false" aria-controls="scheduled-tasks-create-menu">' + iconHtml("chevron-down") + '</button>' +
|
|
71
|
-
'<div id="scheduled-tasks-create-menu" class="scheduled-tasks-create-menu hidden" role="menu" aria-labelledby="scheduled-tasks-create-menu-trigger"><button type="button" role="menuitem" data-action="manual">Manual creation</button></div>' +
|
|
72
|
-
'</div>' +
|
|
73
68
|
'<div class="scheduled-tasks-window-actions">' +
|
|
74
69
|
'<button class="scheduled-tasks-icon-btn" type="button" data-action="wide" title="Widen panel" aria-label="Widen Scheduled panel" aria-pressed="false">' + iconHtml("chevrons-left-right") + '</button>' +
|
|
75
70
|
'<button class="scheduled-tasks-icon-btn" type="button" data-action="fullscreen" title="Toggle fullscreen" aria-label="Toggle Scheduled fullscreen" aria-pressed="false">' + iconHtml("maximize-2") + '</button>' +
|
|
76
71
|
'<button class="scheduled-tasks-icon-btn" type="button" data-action="close" title="Close" aria-label="Close Scheduled">' + iconHtml("x") + '</button>' +
|
|
77
72
|
'</div>' +
|
|
78
73
|
'</header>' +
|
|
79
|
-
'<div class="scheduled-
|
|
74
|
+
'<div class="scheduled-tasks-toolbar" role="toolbar" aria-label="Scheduled task actions">' +
|
|
75
|
+
'<div class="scheduled-tasks-create-group">' +
|
|
76
|
+
'<button class="scheduled-tasks-new" type="button" data-action="new">' + iconHtml("plus") + 'New</button>' +
|
|
77
|
+
'<button id="scheduled-tasks-create-menu-trigger" class="scheduled-tasks-new-menu" type="button" data-action="new-menu" aria-label="More task creation options" aria-haspopup="menu" aria-expanded="false" aria-controls="scheduled-tasks-create-menu">' + iconHtml("chevron-down") + '</button>' +
|
|
78
|
+
'<div id="scheduled-tasks-create-menu" class="scheduled-tasks-create-menu hidden" role="menu" aria-labelledby="scheduled-tasks-create-menu-trigger"><button type="button" role="menuitem" data-action="manual">Manual creation</button></div>' +
|
|
79
|
+
'</div>' +
|
|
80
|
+
'<div class="scheduled-task-default-ai"><span>New interviews use your shared Default AI.</span><button type="button" data-action="default-ai">Change default</button></div></div>' +
|
|
80
81
|
'<div class="scheduled-task-draft-slot"></div>' +
|
|
81
82
|
'<div class="scheduled-tasks-list" role="list"></div>';
|
|
82
83
|
host.appendChild(panel);
|