clay-server 5.3.0-beta.4 → 5.3.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/ask-user-request-identity.js +18 -1
- package/lib/clay-product-control-tools.js +2 -2
- package/lib/clay-product-control.js +1 -1
- package/lib/clay-screen-controls.js +1 -0
- package/lib/clay-split-view-awareness.js +5 -2
- package/lib/clay-split-view-controls.js +14 -7
- package/lib/clay-split-view-input.js +5 -4
- package/lib/clay-split-view-render-proof.js +14 -4
- package/lib/clay-view-workspace-input.js +71 -0
- package/lib/project-delegated-follow-up.js +18 -9
- package/lib/project-file-history.js +11 -9
- package/lib/project-file-watch.js +26 -7
- package/lib/project-filesystem.js +5 -4
- package/lib/project-shared-browser.js +7 -3
- package/lib/project-split-view-document.js +16 -2
- package/lib/project-split-views.js +15 -294
- package/lib/project-user-message.js +4 -3
- package/lib/project-vendor-login.js +2 -2
- package/lib/project-view-input.js +11 -0
- package/lib/project-view-pair.js +26 -0
- package/lib/project-view-store.js +346 -0
- package/lib/project-view-terminal-binding.js +16 -0
- package/lib/project-view-workspace.js +126 -0
- package/lib/project.js +1 -0
- package/lib/public/app.js +2 -0
- package/lib/public/css/issues.css +3 -0
- package/lib/public/css/pane.css +5 -1
- package/lib/public/css/right-workbench.css +25 -3
- package/lib/public/css/scheduled-tasks.css +7 -0
- package/lib/public/css/shared-browser.css +21 -0
- package/lib/public/css/split-view-layout.css +37 -3
- package/lib/public/css/sticky-notes.css +3 -2
- package/lib/public/css/tool-pane.css +127 -0
- package/lib/public/index.html +3 -3
- package/lib/public/modules/app-header.js +24 -2
- package/lib/public/modules/app-messages.js +1 -1
- package/lib/public/modules/clay-file-links.js +2 -1
- package/lib/public/modules/clay-session-links.js +1 -1
- package/lib/public/modules/clay-split-view-screen.js +6 -3
- package/lib/public/modules/clay-tool-pane-screen.js +35 -0
- package/lib/public/modules/file-history-snapshot.js +12 -0
- package/lib/public/modules/file-pane-entrypoints.js +55 -0
- package/lib/public/modules/file-pane-history.js +78 -0
- package/lib/public/modules/file-pane-instances.js +140 -0
- package/lib/public/modules/file-pane-links.js +41 -0
- package/lib/public/modules/file-pane-render.js +48 -0
- package/lib/public/modules/file-pane-review.js +26 -0
- package/lib/public/modules/file-pane-toolbar.js +54 -0
- package/lib/public/modules/file-viewer-source.js +70 -0
- package/lib/public/modules/filebrowser.js +44 -83
- package/lib/public/modules/git-panel.js +5 -0
- package/lib/public/modules/issues.js +3 -4
- package/lib/public/modules/linear-panel.js +2 -2
- package/lib/public/modules/mate-knowledge-workbench.js +10 -6
- package/lib/public/modules/notifications.js +4 -1
- package/lib/public/modules/pane-activation.js +5 -0
- package/lib/public/modules/pane-binding.js +5 -1
- package/lib/public/modules/pane-browser-bridge.js +20 -6
- package/lib/public/modules/pane-browser-transport.js +110 -0
- package/lib/public/modules/pane-header-runtime.js +18 -0
- package/lib/public/modules/plantuml-preview.js +12 -12
- package/lib/public/modules/right-workbench.js +116 -67
- package/lib/public/modules/scheduled-tasks.js +7 -6
- package/lib/public/modules/shared-browser.js +75 -9
- package/lib/public/modules/split-pane-renderer.js +7 -8
- package/lib/public/modules/split-pane-runtime.js +23 -0
- package/lib/public/modules/split-view-drag-preview.js +90 -0
- package/lib/public/modules/split-view-layout-client.js +1 -1
- package/lib/public/modules/split-view-presentation-drag.js +36 -13
- package/lib/public/modules/split-view-presentation-drop.js +21 -18
- package/lib/public/modules/split-view-presentation-renderer.js +15 -10
- package/lib/public/modules/split-view-presentations.js +25 -33
- package/lib/public/modules/split-view.js +17 -16
- package/lib/public/modules/sticky-notes-browser.js +2 -2
- package/lib/public/modules/sticky-notes.js +8 -10
- package/lib/public/modules/terminal.js +45 -13
- package/lib/public/modules/theme.js +1 -1
- package/lib/public/modules/tool-pane-chrome.js +82 -0
- package/lib/public/modules/tool-pane-drag.js +134 -0
- package/lib/public/modules/tool-pane-layout.js +95 -0
- package/lib/public/modules/tool-pane-legacy.js +60 -0
- package/lib/public/modules/tool-pane-tree.js +51 -0
- package/lib/public/modules/view-legacy-coordinates.js +16 -0
- package/lib/public/modules/view-mutations.js +24 -0
- package/lib/public/modules/view-pair-presentation.js +74 -0
- package/lib/public/modules/view-pair-validation.js +12 -0
- package/lib/public/modules/view-presentation-state.js +37 -0
- package/lib/public/modules/view-workspace-controller.js +210 -0
- package/lib/public/modules/view-workspace-state.js +103 -0
- package/lib/public/modules/view-workspace-validation.js +47 -0
- package/lib/public/modules/wireframe-workbench.js +11 -0
- package/lib/public/modules/workbench-layout-preference.js +12 -5
- package/lib/public/modules/workbench-panels.js +21 -0
- package/lib/public/modules/workspace-assignment-card.js +9 -6
- package/lib/public/pane-assets/{app-ED2VCGNQ.js → app-7UZ727NA.js} +1 -1
- package/lib/public/pane-assets/chunk-2AIWCI6Y.js +1 -0
- package/lib/public/pane-assets/chunk-6MIHIQ2W.js +1 -0
- package/lib/public/pane-assets/chunk-6NYCNWVF.js +1 -0
- package/lib/public/pane-assets/chunk-6QFLYOVY.js +1 -0
- package/lib/public/pane-assets/chunk-6UJ2ALOB.js +1 -0
- package/lib/public/pane-assets/chunk-6XK2OATZ.js +1 -0
- package/lib/public/pane-assets/chunk-BKIDR2ME.js +1 -0
- package/lib/public/pane-assets/chunk-BMHTBRMO.js +1 -0
- package/lib/public/pane-assets/chunk-GEFCFZEN.js +1 -0
- package/lib/public/pane-assets/chunk-GN2NQEVC.js +2 -0
- package/lib/public/pane-assets/chunk-IMLMBPT4.js +166 -0
- package/lib/public/pane-assets/chunk-JIV7O5GN.js +1 -0
- package/lib/public/pane-assets/chunk-K5S6AILM.js +1 -0
- package/lib/public/pane-assets/chunk-LDIZLKB2.js +1 -0
- package/lib/public/pane-assets/chunk-OWNNLZ36.js +1 -0
- package/lib/public/pane-assets/chunk-P2C3LOWL.js +3 -0
- package/lib/public/pane-assets/chunk-PV4PCH2V.js +2 -0
- package/lib/public/pane-assets/chunk-R4LMPTKU.js +1 -0
- package/lib/public/pane-assets/chunk-R7QVVFUG.js +1 -0
- package/lib/public/pane-assets/chunk-TMGSDFJI.js +1 -0
- package/lib/public/pane-assets/chunk-VKYBAVWT.js +1 -0
- package/lib/public/pane-assets/chunk-VNAV23CM.js +1 -0
- package/lib/public/pane-assets/chunk-VWTHIQCE.js +1 -0
- package/lib/public/pane-assets/chunk-VZ2OKDAO.js +1 -0
- package/lib/public/pane-assets/chunk-WM3K5AFH.js +1 -0
- package/lib/public/pane-assets/chunk-XOB3O327.js +1 -0
- package/lib/public/pane-assets/chunk-XOOUCFYT.js +1 -0
- package/lib/public/pane-assets/manifest.json +383 -336
- package/lib/public/pane-assets/style-ZM7OOU3M.css +1 -0
- package/lib/public/style.css +1 -0
- package/lib/server-workbench-layout-settings.js +3 -3
- package/lib/server.js +1 -0
- package/lib/split-view-layout.js +11 -7
- package/lib/users-workbench-layout-preferences.js +14 -4
- package/lib/workbench-window-layout.js +30 -0
- package/lib/workspace-assignment-service.js +56 -11
- package/lib/workspace-follow-up-queue.js +92 -0
- package/lib/workspace-query-mcp-server.js +1 -1
- package/package.json +1 -1
- package/lib/public/pane-assets/chunk-6D4DPE5X.js +0 -1
- package/lib/public/pane-assets/chunk-6SM2WQJX.js +0 -1
- package/lib/public/pane-assets/chunk-7H36SUZA.js +0 -2
- package/lib/public/pane-assets/chunk-DGOFGEUK.js +0 -1
- package/lib/public/pane-assets/chunk-I57URYSL.js +0 -1
- package/lib/public/pane-assets/chunk-IYFILQHZ.js +0 -2
- package/lib/public/pane-assets/chunk-IZ2ZANEY.js +0 -167
- package/lib/public/pane-assets/chunk-JAEQCN5S.js +0 -1
- package/lib/public/pane-assets/chunk-ST2Q62ET.js +0 -1
- package/lib/public/pane-assets/style-JVCOCG3W.css +0 -1
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { syncPairView, pairPresentationPanes, pairViewCapacity } from './view-pair-presentation.js';
|
|
2
|
+
import { paintLegacyToolPanes } from './tool-pane-legacy.js';
|
|
1
3
|
import { adoptDocumentSource } from './clay-studio-document.js';
|
|
2
4
|
import { trustedStudioFrame } from './clay-studio-render.js';
|
|
3
5
|
import { handlePaneBrowserMessage } from './pane-browser-bridge.js';
|
|
@@ -131,21 +133,15 @@ function closePane(sessionId) {
|
|
|
131
133
|
// Called on session_list so pane headers follow renames made elsewhere
|
|
132
134
|
// (sidebar, in-pane app). Panes being renamed inline are left alone.
|
|
133
135
|
export function syncPaneTitles() {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
var
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
var session = sessionById(
|
|
140
|
-
if (
|
|
141
|
-
if (titleEls[i].parentNode) titleEls[i].parentNode.title = session.title;
|
|
142
|
-
if (titleEls[i].style.display !== "none") titleEls[i].textContent = session.title;
|
|
143
|
-
}
|
|
144
|
-
var accessBtns = host.querySelectorAll(".split-pane-full-access");
|
|
145
|
-
for (var ai = 0; ai < split.panes.length && ai < accessBtns.length; ai++) {
|
|
146
|
-
updatePaneFullAccessButton(accessBtns[ai], sessionById(paneEls[ai] && Number(paneEls[ai].dataset.sessionId) || split.panes[ai].sessionId));
|
|
136
|
+
if (!host) return;
|
|
137
|
+
var panes = host.querySelectorAll('.split-pane:not(.split-pane-cached)');
|
|
138
|
+
for (var i = 0; i < panes.length; i++) {
|
|
139
|
+
var frame = panes[i].querySelector('.split-pane-frame');
|
|
140
|
+
if (frame && frame.dataset.requestedSessionId) continue;
|
|
141
|
+
var session = sessionById(Number(panes[i].dataset.sessionId));
|
|
142
|
+
if (session) updateSplitPaneMetadata(panes[i], { sessionId: session.id, slug: panes[i].dataset.projectSlug, title: session.title });
|
|
147
143
|
}
|
|
148
|
-
syncPairChrome(host,
|
|
144
|
+
syncPairChrome(host, store.get('splitPanes'));
|
|
149
145
|
}
|
|
150
146
|
|
|
151
147
|
function handlePaneMessage(event) {
|
|
@@ -191,7 +187,8 @@ function renderSplit(split) {
|
|
|
191
187
|
singleton || split && split.panes && split.panes.length >= 2 || !!state.projectSplitViewActiveId);
|
|
192
188
|
// The exact singleton presentation is verified before the redundant cache catches up.
|
|
193
189
|
if (singleton) { renderPresentationSplitView(); return; }
|
|
194
|
-
|
|
190
|
+
var savedPair = split && split.panes && syncPairView();
|
|
191
|
+
if (split && split.panes && !savedPair && store.get('projectSplitViewActiveId')) {
|
|
195
192
|
store.set({ projectSplitViewActiveId: null, projectSplitViewFocusedOrigin: null, projectSplitViewPreviewTree: null });
|
|
196
193
|
}
|
|
197
194
|
var expectedPresentationContext = [store.get('currentSlug') || "", store.get('currentProjectOwnerId') || "", store.get('myUserId') || "", store.get('projectSplitViewSocketGeneration') || 0].join("|");
|
|
@@ -219,8 +216,10 @@ function renderSplit(split) {
|
|
|
219
216
|
placeStickyNotesOverlay(true);
|
|
220
217
|
nativeApp.classList.add("split-native-hidden");
|
|
221
218
|
host.classList.add("visible");
|
|
219
|
+
if (savedPair) panes = pairPresentationPanes(panes);
|
|
222
220
|
reconcileSplitPanes(host, panes, function (pane) { return createSplitPane(pane, closePane); }, updateSplitPaneMetadata);
|
|
223
221
|
syncPairChrome(host, split);
|
|
222
|
+
paintLegacyToolPanes(host);
|
|
224
223
|
refreshIcons();
|
|
225
224
|
}
|
|
226
225
|
|
|
@@ -252,7 +251,7 @@ function openSplit(side, draggedId) {
|
|
|
252
251
|
|
|
253
252
|
export function openGroup(group) {
|
|
254
253
|
if (!canLeavePanes()) return false;
|
|
255
|
-
if (!group || !Array.isArray(group.members) || group.members.length < 2) return false;
|
|
254
|
+
if (!group || !Array.isArray(group.members) || group.members.length < 2 || !pairViewCapacity(group)) return false;
|
|
256
255
|
var memberIds = splitGroupMemberIds(group);
|
|
257
256
|
if (memberIds.length !== group.members.length) return false;
|
|
258
257
|
for (var mi = 0; mi < memberIds.length; mi++) if (!sessionById(memberIds[mi])) return false;
|
|
@@ -497,3 +496,5 @@ export function initSplitView() {
|
|
|
497
496
|
initSplitViewPresentations(host);
|
|
498
497
|
renderSplit(store.get('splitPanes'));
|
|
499
498
|
}
|
|
499
|
+
|
|
500
|
+
export function repaintLegacySplit() { if (store.get('splitPanes')) renderSplit(store.get('splitPanes')); }
|
|
@@ -14,7 +14,7 @@ import { enhanceClayLogLinks } from './clay-log-links.js';
|
|
|
14
14
|
import { closeHumanNote } from './sticky-notes-actions.js';
|
|
15
15
|
import { send, getNoteUiState } from './sticky-notes-shared.js';
|
|
16
16
|
import { listNoteData, focusNoteOnCanvas, hideNotes, showNotes } from './sticky-notes.js';
|
|
17
|
-
import { claimRightWorkbench, registerRightWorkbench, releaseRightWorkbench } from './right-workbench.js';
|
|
17
|
+
import { handlesWorkbenchKey, claimRightWorkbench, registerRightWorkbench, releaseRightWorkbench } from './right-workbench.js';
|
|
18
18
|
|
|
19
19
|
var panel = null;
|
|
20
20
|
var gridEl = null;
|
|
@@ -427,7 +427,7 @@ export function isNotesBrowserOpen() {
|
|
|
427
427
|
export function initNotesBrowser() {
|
|
428
428
|
registerRightWorkbench("notes-browser", closeNotesBrowser);
|
|
429
429
|
document.addEventListener("keydown", function (e) {
|
|
430
|
-
if (e.key !== "Escape") return;
|
|
430
|
+
if (e.key !== "Escape" || !handlesWorkbenchKey("notes-browser", e)) return;
|
|
431
431
|
if (deleteDialog && !deleteDialog.classList.contains("hidden")) return;
|
|
432
432
|
if (!store.get('notesBrowserOpen')) return;
|
|
433
433
|
closeNotesBrowser();
|
|
@@ -104,11 +104,17 @@ function recomputeFlightGeometry() {
|
|
|
104
104
|
function syncFlightControls() {
|
|
105
105
|
var buttons = document.querySelectorAll("#sticky-notes-toggle-btn");
|
|
106
106
|
var hidden = !!store.get("notesTemporarilyHidden");
|
|
107
|
-
var
|
|
107
|
+
var count = openCount();
|
|
108
|
+
var label = hidden ? "Show sticky notes (" + count + ")" : "Hide sticky notes";
|
|
108
109
|
for (var i = 0; i < buttons.length; i++) {
|
|
109
110
|
buttons[i].setAttribute("aria-label", label);
|
|
110
111
|
buttons[i].setAttribute("aria-pressed", hidden ? "true" : "false");
|
|
111
112
|
buttons[i].title = label;
|
|
113
|
+
var badge = buttons[i].querySelector('.sticky-notes-count');
|
|
114
|
+
if (badge) {
|
|
115
|
+
badge.textContent = String(count);
|
|
116
|
+
badge.classList.toggle('hidden', !hidden || count === 0);
|
|
117
|
+
}
|
|
112
118
|
}
|
|
113
119
|
refreshIcons();
|
|
114
120
|
}
|
|
@@ -246,15 +252,7 @@ export function createNote() {
|
|
|
246
252
|
|
|
247
253
|
function updateBadge() {
|
|
248
254
|
syncToggleAvailability();
|
|
249
|
-
|
|
250
|
-
if (!badge) return;
|
|
251
|
-
var count = openCount();
|
|
252
|
-
if (count > 0) {
|
|
253
|
-
badge.textContent = count;
|
|
254
|
-
badge.classList.remove("hidden");
|
|
255
|
-
} else {
|
|
256
|
-
badge.classList.add("hidden");
|
|
257
|
-
}
|
|
255
|
+
syncFlightControls();
|
|
258
256
|
}
|
|
259
257
|
|
|
260
258
|
// --- WS message handlers ---
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { recordToolExpansion } from './view-presentation-state.js';
|
|
2
|
+
import { workspaceChanged } from './view-workspace-state.js';
|
|
1
3
|
import { store } from './store.js';
|
|
2
4
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
3
5
|
import { closeSidebar } from './sidebar.js';
|
|
@@ -145,13 +147,8 @@ export function initTerminal(_ctx) {
|
|
|
145
147
|
|
|
146
148
|
// Fullscreen toggle
|
|
147
149
|
document.getElementById("terminal-fullscreen").addEventListener("click", function () {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (icon) {
|
|
151
|
-
icon.setAttribute("data-lucide", isFs ? "minimize-2" : "maximize-2");
|
|
152
|
-
refreshIcons();
|
|
153
|
-
}
|
|
154
|
-
fitTerminal();
|
|
150
|
+
setTerminalExpanded(!ctx.terminalContainerEl.classList.contains('panel-fullscreen'));
|
|
151
|
+
recordToolExpansion('terminal', ctx.terminalContainerEl.classList.contains('panel-fullscreen'));
|
|
155
152
|
});
|
|
156
153
|
|
|
157
154
|
// Header toggle button
|
|
@@ -224,6 +221,7 @@ export function openTerminal() {
|
|
|
224
221
|
|
|
225
222
|
// --- Close terminal panel (hide, detach, but keep PTYs alive) ---
|
|
226
223
|
export function closeTerminal() {
|
|
224
|
+
recordToolExpansion('terminal', false);
|
|
227
225
|
releaseRightWorkbench("terminal");
|
|
228
226
|
var container = ctx.terminalContainerEl;
|
|
229
227
|
container.classList.remove("panel-fullscreen");
|
|
@@ -279,7 +277,7 @@ function closeTab(termId) {
|
|
|
279
277
|
}
|
|
280
278
|
|
|
281
279
|
// --- Activate a tab (show xterm, attach) ---
|
|
282
|
-
function activateTab(termId) {
|
|
280
|
+
function activateTab(termId, savedBinding) {
|
|
283
281
|
var tab = tabs.get(termId);
|
|
284
282
|
if (!tab) return;
|
|
285
283
|
|
|
@@ -294,6 +292,10 @@ function activateTab(termId) {
|
|
|
294
292
|
}
|
|
295
293
|
|
|
296
294
|
activeTabId = termId;
|
|
295
|
+
var binding = (store.get("clayControlTerminalViews") || []).find(function (item) { return item.id === termId; });
|
|
296
|
+
if (binding) store.set({ savedViewTerminalResource: { id: binding.id, identity: binding.identity, runtimeId: binding.runtimeId } });
|
|
297
|
+
var unavailable = document.getElementById("terminal-saved-unavailable"); if (unavailable) unavailable.remove();
|
|
298
|
+
workspaceChanged();
|
|
297
299
|
|
|
298
300
|
// Lazy-create xterm instance
|
|
299
301
|
if (!tab.xterm) {
|
|
@@ -305,7 +307,7 @@ function activateTab(termId) {
|
|
|
305
307
|
|
|
306
308
|
// Attach to server
|
|
307
309
|
if (ctx.ws && ctx.connected) {
|
|
308
|
-
ctx.ws.send(JSON.stringify({ type: "term_attach", id: termId }));
|
|
310
|
+
ctx.ws.send(JSON.stringify({ type: "term_attach", id: termId, savedBinding: savedBinding }));
|
|
309
311
|
}
|
|
310
312
|
|
|
311
313
|
// Fit and focus
|
|
@@ -557,11 +559,13 @@ function renderTabBar() {
|
|
|
557
559
|
|
|
558
560
|
var moreBtn = document.createElement("button");
|
|
559
561
|
moreBtn.className = "terminal-tab-more";
|
|
560
|
-
moreBtn.innerHTML = '
|
|
562
|
+
moreBtn.innerHTML = iconHtml('x');
|
|
563
|
+
moreBtn.setAttribute('aria-label', 'Close terminal tab ' + t.title);
|
|
561
564
|
moreBtn.addEventListener("click", function (e) {
|
|
562
565
|
e.stopPropagation();
|
|
563
|
-
|
|
566
|
+
closeTab(t.id);
|
|
564
567
|
});
|
|
568
|
+
el.addEventListener('contextmenu', function (event) { event.preventDefault(); showTabContextMenu(event, t, label); });
|
|
565
569
|
el.appendChild(moreBtn);
|
|
566
570
|
|
|
567
571
|
el.addEventListener("click", function () {
|
|
@@ -699,7 +703,7 @@ function updateTerminalBadge() {
|
|
|
699
703
|
// --- Handle server messages ---
|
|
700
704
|
|
|
701
705
|
export function handleTermList(msg) {
|
|
702
|
-
store.set({ clayControlTerminalViews: (msg.terminals || []).map(function (item) { return { id: item.id, identity: item.controlIdentity, revision: item.controlRevision, runtimeId: store.get('controlRuntimeId') }; }) });
|
|
706
|
+
store.set({ clayControlTerminalViews: (msg.terminals || []).map(function (item) { return { id: item.id, identity: item.controlIdentity, revision: item.controlRevision, exited: item.exited, runtimeId: store.get('controlRuntimeId') }; }) });
|
|
703
707
|
// TUI session terminals are owned by session-tui-view.js and must not
|
|
704
708
|
// appear in the bottom terminal panel. Filter them out before any tab
|
|
705
709
|
// bookkeeping so the panel never tries to render or auto-attach them.
|
|
@@ -744,6 +748,7 @@ export function handleTermList(msg) {
|
|
|
744
748
|
}
|
|
745
749
|
|
|
746
750
|
renderTabBar();
|
|
751
|
+
store.set({ savedViewTerminalListReady: true });
|
|
747
752
|
|
|
748
753
|
// If panel is open and we have tabs, re-attach
|
|
749
754
|
if (isOpen && tabs.size > 0) {
|
|
@@ -825,6 +830,7 @@ export function handleTermClosed(msg) {
|
|
|
825
830
|
|
|
826
831
|
// --- Reset on reconnect ---
|
|
827
832
|
export function resetTerminals() {
|
|
833
|
+
store.set({ savedViewTerminalListReady: false, savedViewTerminalResource: null });
|
|
828
834
|
// Dispose all xterm instances (server state survives, client re-syncs via term_list)
|
|
829
835
|
for (var tab of tabs.values()) {
|
|
830
836
|
disposeTab(tab);
|
|
@@ -842,7 +848,8 @@ export function sendTerminalCommand(command) {
|
|
|
842
848
|
}
|
|
843
849
|
}
|
|
844
850
|
|
|
845
|
-
export function setTerminalTheme(
|
|
851
|
+
export function setTerminalTheme() {
|
|
852
|
+
var xtermTheme = getTerminalTheme();
|
|
846
853
|
for (var tab of tabs.values()) {
|
|
847
854
|
if (tab.xterm) {
|
|
848
855
|
tab.xterm.options.theme = xtermTheme;
|
|
@@ -850,6 +857,7 @@ export function setTerminalTheme(xtermTheme) {
|
|
|
850
857
|
}
|
|
851
858
|
}
|
|
852
859
|
|
|
860
|
+
|
|
853
861
|
// Live font update: applies family/size to every open shell terminal
|
|
854
862
|
// and refits each (cell dimensions change with font size, so cols/rows
|
|
855
863
|
// shift too). Hooked via onTerminalFontChange below.
|
|
@@ -978,3 +986,27 @@ function showTermCtxMenu(e, tab) {
|
|
|
978
986
|
document.addEventListener("click", closeTermCtxMenu, { once: true });
|
|
979
987
|
}, 0);
|
|
980
988
|
}
|
|
989
|
+
|
|
990
|
+
// Restoration must never enter openTerminal's create-new branch.
|
|
991
|
+
export function restoreExistingTerminal(resource) {
|
|
992
|
+
if (!store.get('connected') || !store.get('controlRuntimeId')) return 'pending';
|
|
993
|
+
if (!store.get('savedViewTerminalListReady')) return 'pending';
|
|
994
|
+
var live = (store.get('clayControlTerminalViews') || []).find(function (item) { return item.id === resource.id && item.identity === resource.identity && item.runtimeId === resource.runtimeId && !item.exited; });
|
|
995
|
+
claimRightWorkbench('terminal');
|
|
996
|
+
var container = document.getElementById('terminal-container'); container.classList.remove('hidden');
|
|
997
|
+
if (live && tabs.has(resource.id)) { isOpen = true; activateTab(resource.id, resource); return 'restored'; }
|
|
998
|
+
isOpen = false;
|
|
999
|
+
tabs.forEach(function (tab) { if (tab.bodyEl) tab.bodyEl.style.display = 'none'; });
|
|
1000
|
+
var notice = document.getElementById('terminal-saved-unavailable');
|
|
1001
|
+
if (!notice) { notice = document.createElement('p'); notice.id = 'terminal-saved-unavailable'; notice.className = 'saved-view-unavailable'; notice.setAttribute('role', 'status'); document.getElementById('terminal-body').appendChild(notice); }
|
|
1002
|
+
notice.textContent = 'This saved terminal is unavailable. Close this pane and open Terminal to choose an available shell.';
|
|
1003
|
+
return 'unavailable';
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
export function setTerminalExpanded(enabled) {
|
|
1007
|
+
var panel = document.getElementById('terminal-container'); if (!panel) return;
|
|
1008
|
+
panel.classList.toggle('panel-fullscreen', !!enabled);
|
|
1009
|
+
var icon = panel.querySelector('#terminal-fullscreen [data-lucide]');
|
|
1010
|
+
if (icon) { icon.setAttribute('data-lucide', enabled ? 'minimize-2' : 'maximize-2'); refreshIcons(); }
|
|
1011
|
+
if (!panel.classList.contains('hidden')) fitTerminal();
|
|
1012
|
+
}
|
|
@@ -327,7 +327,7 @@ export function applyTheme(themeId, fromPicker) {
|
|
|
327
327
|
// Terminals deliberately keep Clay Studio Dark for predictable ANSI
|
|
328
328
|
// contrast, even while the surrounding workspace uses a light theme.
|
|
329
329
|
var termTheme = getTerminalTheme();
|
|
330
|
-
try { setTerminalTheme(
|
|
330
|
+
try { setTerminalTheme(); } catch (e) {}
|
|
331
331
|
try { setTuiSessionTheme(termTheme); } catch (e) {}
|
|
332
332
|
try { setTuiAttentionTheme(termTheme); } catch (e) {}
|
|
333
333
|
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { WORKBENCH_PANELS } from './workbench-panels.js';
|
|
2
|
+
import { recordToolExpansion } from './view-presentation-state.js';
|
|
3
|
+
import { store } from './store.js';
|
|
4
|
+
import { iconHtml, refreshIcons } from './icons.js';
|
|
5
|
+
import { TOOL_PANES } from './tool-pane-tree.js';
|
|
6
|
+
|
|
7
|
+
function titleFor(panel, name) {
|
|
8
|
+
if (name === 'terminal') {
|
|
9
|
+
var state = store.snap(), project = (state.projectsHubList || []).find(function (item) { return item && item.slug === state.currentSlug; });
|
|
10
|
+
var projectName = project && (project.title || project.project || project.name) || state.projectName || state.currentSlug;
|
|
11
|
+
return projectName ? projectName + ' · Terminal' : 'Terminal';
|
|
12
|
+
}
|
|
13
|
+
if (name === 'file-viewer') { var tab = panel.querySelector('.file-viewer-tab.active .file-viewer-tab-label'); return tab && tab.textContent || 'Files'; }
|
|
14
|
+
var browser = store.get('sharedBrowser');
|
|
15
|
+
try { return browser && browser.url ? new URL(browser.url).hostname : 'Browser'; } catch (error) { return 'Browser'; }
|
|
16
|
+
}
|
|
17
|
+
export function renderToolPaneChrome(panel, name) {
|
|
18
|
+
if (panel.dataset.fileInstance) return;
|
|
19
|
+
if (WORKBENCH_PANELS[name]) { renderWorkbenchChrome(panel, name); return; }
|
|
20
|
+
var definition = TOOL_PANES[name];
|
|
21
|
+
if (!definition) return;
|
|
22
|
+
var header = panel.querySelector(':scope > .tool-pane-header');
|
|
23
|
+
if (!header) {
|
|
24
|
+
var original = panel.querySelector(name === 'terminal' ? '.terminal-header' : name === 'browser' ? '.shared-browser-header' : '.file-viewer-tabbar');
|
|
25
|
+
if (!original) return;
|
|
26
|
+
header = document.createElement('header'); header.className = 'tool-pane-header split-pane-header'; header.dataset.toolPane = name;
|
|
27
|
+
var kind = document.createElement('span'); kind.className = 'tool-pane-kind' + (name === 'terminal' ? '' : ' split-view-runtime');
|
|
28
|
+
kind.innerHTML = iconHtml(definition.icon) + (name === 'terminal' ? '' : '<span class="split-view-model">' + definition.label + '</span>');
|
|
29
|
+
var title = document.createElement('span'); title.className = 'tool-pane-title split-pane-title';
|
|
30
|
+
var space = document.createElement('span'); space.className = 'split-pane-drag-space'; space.title = 'Drag to rearrange panes'; space.setAttribute('aria-hidden', 'true');
|
|
31
|
+
var actions = original.querySelector('.terminal-header-actions, .shared-browser-window, .file-viewer-window-actions');
|
|
32
|
+
header.append(kind, title, space); if (actions) header.appendChild(actions);
|
|
33
|
+
panel.insertBefore(header, panel.firstChild);
|
|
34
|
+
if (name === 'browser') { original.hidden = true; var close = header.querySelector('[data-action="hide"]'); if (close) { close.title = 'Close browser pane'; close.setAttribute('aria-label', 'Close browser pane'); } }
|
|
35
|
+
else original.classList.add('tool-pane-tab-row');
|
|
36
|
+
var observer = new MutationObserver(function () { update(); });
|
|
37
|
+
observer.observe(panel, { childList: true, characterData: true, subtree: true, attributes: true, attributeFilter: ['aria-selected'] });
|
|
38
|
+
panel.addEventListener('clay-tool-pane-dispose', function () { observer.disconnect(); }, { once: true });
|
|
39
|
+
refreshIcons();
|
|
40
|
+
}
|
|
41
|
+
function update() {
|
|
42
|
+
var label = titleFor(panel, name), title = header.querySelector('.tool-pane-title');
|
|
43
|
+
if (title.textContent !== label) { title.textContent = label; title.title = label; }
|
|
44
|
+
}
|
|
45
|
+
update();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
store.subscribe(function (state, previous) {
|
|
49
|
+
if (state.projectName === previous.projectName && state.projectsHubList === previous.projectsHubList && state.currentSlug === previous.currentSlug) return;
|
|
50
|
+
var panel = document.getElementById('terminal-container');
|
|
51
|
+
if (panel && panel.querySelector(':scope > .tool-pane-header')) renderToolPaneChrome(panel, 'terminal');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
function renderWorkbenchChrome(panel, name) {
|
|
55
|
+
panel.classList.add('workbench-pane-shell');
|
|
56
|
+
var header = panel.querySelector(':scope > header');
|
|
57
|
+
if (!header || header.dataset.toolPane === name) return;
|
|
58
|
+
header.dataset.toolPane = name;
|
|
59
|
+
header.classList.add('tool-pane-header', 'split-pane-header');
|
|
60
|
+
var actions = header.querySelector('[class*="window-actions"], :scope > div:last-child');
|
|
61
|
+
if (!actions) {
|
|
62
|
+
actions = document.createElement('div');
|
|
63
|
+
Array.from(header.querySelectorAll(':scope > button')).forEach(function (button) {
|
|
64
|
+
var label = button.getAttribute('aria-label') || '';
|
|
65
|
+
if (/^(Widen|Close)|fullscreen/i.test(label)) actions.appendChild(button);
|
|
66
|
+
});
|
|
67
|
+
header.appendChild(actions);
|
|
68
|
+
}
|
|
69
|
+
actions.classList.add('workbench-pane-actions');
|
|
70
|
+
var title = header.querySelector('strong, [class$="-title"]');
|
|
71
|
+
if (title) title.classList.add('tool-pane-title');
|
|
72
|
+
var space = document.createElement('span'); space.className = 'split-pane-drag-space';
|
|
73
|
+
space.title = 'Drag to rearrange panes'; space.setAttribute('aria-hidden', 'true');
|
|
74
|
+
header.insertBefore(space, actions);
|
|
75
|
+
var expanded = panel.classList.contains('panel-fullscreen');
|
|
76
|
+
var observer = new MutationObserver(function () {
|
|
77
|
+
var next = panel.classList.contains('panel-fullscreen');
|
|
78
|
+
if (next !== expanded) { expanded = next; recordToolExpansion(name, next); }
|
|
79
|
+
});
|
|
80
|
+
observer.observe(panel, { attributes: true, attributeFilter: ['class'] });
|
|
81
|
+
panel.addEventListener('clay-tool-pane-dispose', function () { observer.disconnect(); }, { once: true });
|
|
82
|
+
}
|
|
@@ -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
|
+
});
|