clay-server 5.3.1-beta.1 → 5.4.0-beta.1
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/contextual-screen-snapshot.js +13 -4
- package/lib/project-shared-browser.js +69 -21
- package/lib/project.js +2 -0
- package/lib/public/css/issues.css +1 -1
- package/lib/public/css/linear.css +7 -24
- package/lib/public/css/menus.css +2 -2
- package/lib/public/css/notifications-center.css +2 -2
- package/lib/public/css/split-view-layout.css +0 -16
- package/lib/public/css/tool-pane.css +12 -0
- package/lib/public/index.html +1 -1
- package/lib/public/modules/contextual-clay-session.js +2 -2
- package/lib/public/modules/contextual-mate-context.js +7 -3
- package/lib/public/modules/contextual-view-metadata.js +23 -0
- package/lib/public/modules/linear-panel.js +5 -5
- package/lib/public/modules/pane-activation.js +5 -1
- package/lib/public/modules/shared-browser-tabs.js +4 -2
- package/lib/public/modules/split-view-presentations.js +3 -1
- package/lib/public/modules/split-view-worker-scroll.js +1 -1
- package/lib/public/modules/split-view-workers.js +20 -51
- package/lib/public/modules/tool-pane-chrome.js +3 -0
- package/lib/public/modules/tool-pane-drag.js +1 -1
- package/lib/public/modules/tool-pane-layout.js +4 -3
- package/lib/public/modules/workbench-window-controls.js +39 -0
- package/lib/public/pane-assets/{app-DR227IBJ.js → app-BDYH6Y7R.js} +1 -1
- package/lib/public/pane-assets/{chunk-UQPN3SQX.js → chunk-42C2CZEP.js} +1 -1
- package/lib/public/pane-assets/chunk-46OOGVHU.js +1 -0
- package/lib/public/pane-assets/chunk-4OJW4FF4.js +1 -0
- package/lib/public/pane-assets/chunk-E6NRPGVH.js +2 -0
- package/lib/public/pane-assets/chunk-F7VIE6FJ.js +1 -0
- package/lib/public/pane-assets/chunk-FCJYJLVZ.js +1 -0
- package/lib/public/pane-assets/chunk-FEJDEXOW.js +1 -0
- package/lib/public/pane-assets/chunk-FQQMXMH7.js +168 -0
- package/lib/public/pane-assets/chunk-HEMCIILG.js +1 -0
- package/lib/public/pane-assets/chunk-HWLBH6AL.js +1 -0
- package/lib/public/pane-assets/{chunk-KWUKYV6X.js → chunk-KB4T5B2R.js} +1 -1
- package/lib/public/pane-assets/chunk-KF3LZRJK.js +1 -0
- package/lib/public/pane-assets/{chunk-MLNNDUPE.js → chunk-N5SIEIOG.js} +1 -1
- package/lib/public/pane-assets/{chunk-YU4R2HSO.js → chunk-QRVKAVH7.js} +1 -1
- package/lib/public/pane-assets/chunk-RV5O66TO.js +1 -0
- package/lib/public/pane-assets/{chunk-4KFYASLC.js → chunk-TMUCEZ7C.js} +1 -1
- package/lib/public/pane-assets/{chunk-3CQA7ZCS.js → chunk-TT3NECKK.js} +1 -1
- package/lib/public/pane-assets/{chunk-FK43VMMW.js → chunk-U5GH2UJM.js} +1 -1
- package/lib/public/pane-assets/{chunk-5P3D2RTQ.js → chunk-UMIYQYGM.js} +1 -1
- package/lib/public/pane-assets/chunk-VCNLKOVT.js +1 -0
- package/lib/public/pane-assets/{chunk-3VNXCTTY.js → chunk-X452YCE5.js} +1 -1
- package/lib/public/pane-assets/{chunk-LLBLEJM4.js → chunk-XI2DJM4H.js} +1 -1
- package/lib/public/pane-assets/chunk-YE45RR7W.js +1 -0
- package/lib/public/pane-assets/manifest.json +431 -429
- package/lib/public/pane-assets/style-VDIGFFLR.css +1 -0
- package/lib/shared-browser-workspace.js +28 -0
- package/lib/workbench-window-layout.js +7 -5
- package/package.json +7 -1
- package/lib/public/pane-assets/chunk-3SZABBN7.js +0 -1
- package/lib/public/pane-assets/chunk-A3MDESCE.js +0 -1
- package/lib/public/pane-assets/chunk-G6ZL6VPR.js +0 -2
- package/lib/public/pane-assets/chunk-GMASVOWL.js +0 -1
- package/lib/public/pane-assets/chunk-HQUGZW4P.js +0 -1
- package/lib/public/pane-assets/chunk-LA7KKFI2.js +0 -1
- package/lib/public/pane-assets/chunk-MOSMN3CU.js +0 -1
- package/lib/public/pane-assets/chunk-MUAMABNY.js +0 -1
- package/lib/public/pane-assets/chunk-TABMZ63N.js +0 -1
- package/lib/public/pane-assets/chunk-TEHDJOM4.js +0 -1
- package/lib/public/pane-assets/chunk-UNTZB4WP.js +0 -166
- package/lib/public/pane-assets/chunk-WQE7BLFG.js +0 -1
- package/lib/public/pane-assets/chunk-XFUKDMBX.js +0 -1
- package/lib/public/pane-assets/style-BSKKK54Q.css +0 -1
|
@@ -5,7 +5,7 @@ export function renderBrowserTabs(el, select, send) {
|
|
|
5
5
|
var bar = el.querySelector('.shared-browser-tabs');
|
|
6
6
|
var tabs = store.get('sharedBrowsers') || [];
|
|
7
7
|
var selected = store.get('sharedBrowser');
|
|
8
|
-
var key = JSON.stringify([tabs.map(function (tab) { return [tab.id, tab.url, tab.control, tab.phase, tab.incognito, tab.personalCookiesAvailable]; }), selected && selected.id, store.get('connected')]);
|
|
8
|
+
var key = JSON.stringify([tabs.map(function (tab) { return [tab.id, tab.url, tab.control, tab.phase, tab.incognito, tab.personalCookiesAvailable, tab.sourceSessionId, tab.sourceRole, tab.sourceTitle, tab.sourceGeneration]; }), selected && selected.id, store.get('connected')]);
|
|
9
9
|
if (bar.dataset.key === key) return;
|
|
10
10
|
var focused = bar.contains(document.activeElement) ? document.activeElement : null;
|
|
11
11
|
var focusedId = focused && focused.dataset.browserId, focusedAction = focused && focused.dataset.tabAction;
|
|
@@ -23,8 +23,10 @@ export function renderBrowserTabs(el, select, send) {
|
|
|
23
23
|
button.setAttribute('aria-selected', String(active)); button.tabIndex = active ? 0 : -1;
|
|
24
24
|
var title = 'New tab';
|
|
25
25
|
try { if (tab.url) title = new URL(tab.url).hostname || 'New tab'; } catch (e) {}
|
|
26
|
+
var source = tab.sourceRole === 'worker' ? 'Worker #' + tab.sourceSessionId : '';
|
|
27
|
+
if (source) title = source + ' · ' + title;
|
|
26
28
|
var mode = tab.phase === 'ended' ? 'Ended' : tab.phase === 'starting' ? 'Starting' : tab.control === 'agent' ? 'You and Clay can control this tab' : 'Only you can control this tab. Clay is paused.';
|
|
27
|
-
var description = title + ' · ' + (tab.incognito === false ? 'Personal browser' : 'Incognito') + ' · ' + mode;
|
|
29
|
+
var description = title + (source ? ' · ' + tab.sourceTitle + ' · Gen ' + tab.sourceGeneration : '') + ' · ' + (tab.incognito === false ? 'Personal browser' : 'Incognito') + ' · ' + mode;
|
|
28
30
|
var label = document.createElement('span'); label.className = 'shared-browser-tab-label'; label.textContent = title;
|
|
29
31
|
button.innerHTML = iconHtml(tab.personalCookiesAvailable && tab.incognito ? 'glasses' : 'globe');
|
|
30
32
|
button.appendChild(label); button.setAttribute('aria-label', description); button.setAttribute('data-tip', description);
|
|
@@ -292,7 +292,9 @@ function onClick(event) {
|
|
|
292
292
|
if (store.get('projectSplitViewActiveId') && target.closest('.split-group-item[data-split-group-id]')) {
|
|
293
293
|
store.set({ projectSplitViewActiveId: null, projectSplitViewFocusedOrigin: null }); return;
|
|
294
294
|
}
|
|
295
|
-
var row = target.closest(".session-item[data-session-id], .session-loop-child[data-session-id]");
|
|
295
|
+
var row = target.closest(".session-item[data-session-id], .session-loop-child[data-session-id]");
|
|
296
|
+
if (row && target.closest("button, a, input, textarea, select, [contenteditable='true']")) return;
|
|
297
|
+
if (row && store.get('projectSplitViewActiveId')) {
|
|
296
298
|
var session = (getCachedSessions() || []).find(function (item) { return item.id === Number(row.dataset.sessionId); });
|
|
297
299
|
var membershipViewId = session && viewIdForOrigin(session.sessionOriginId); if (membershipViewId) {
|
|
298
300
|
event.preventDefault(); event.stopImmediatePropagation(); var membershipView = viewExists(store.get('projectSplitViews'), membershipViewId);
|
|
@@ -14,7 +14,7 @@ export function shouldLoadWorkerPage(state, body, intentional) {
|
|
|
14
14
|
if (!state || !body || state.inFlight || !state.hasMore || state.nextBefore == null || !workerCursorKey(state.nextBefore)) return false;
|
|
15
15
|
if (state.status === 'error' && !intentional) return false;
|
|
16
16
|
if (state.status !== 'ready' && state.status !== 'error') return false;
|
|
17
|
-
return intentional
|
|
17
|
+
return intentional === true && body.scrollTop <= 96;
|
|
18
18
|
}
|
|
19
19
|
export function captureWorkerAnchor(body) {
|
|
20
20
|
var top = body.getBoundingClientRect().top;
|
|
@@ -34,15 +34,6 @@ function workersFor(driver) {
|
|
|
34
34
|
(Number(b.workerGeneration) || 0) - (Number(a.workerGeneration) || 0); });
|
|
35
35
|
return { items: all, currentIds: currentIds };
|
|
36
36
|
}
|
|
37
|
-
function configuredGroup(driver) {
|
|
38
|
-
if (!driver) return null;
|
|
39
|
-
var groups = store.get('splitGroups') || [];
|
|
40
|
-
for (var i = 0; i < groups.length; i++) {
|
|
41
|
-
var roles = splitGroupRoles(groups[i]);
|
|
42
|
-
if (roles && roles.version === 2 && roles.driverId === driver.id && roles.workerIds.length) return groups[i];
|
|
43
|
-
}
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
37
|
function presentationSource(container, driver, connectionErrorBinding) {
|
|
47
38
|
var frame = container && container.querySelector('.split-pane-frame'), live = frame && frame.clayPane && frame.clayPane.identity();
|
|
48
39
|
// A read-only connection error may outlive the child's committed binding.
|
|
@@ -130,7 +121,6 @@ function close(returnFocus) {
|
|
|
130
121
|
requestId: state.subscriptionId, parentSessionOriginId: state.driverOriginId,
|
|
131
122
|
workerSessionOriginId: state.workerOriginId, generation: state.generation })); } catch (_error) {}
|
|
132
123
|
}
|
|
133
|
-
if (state.fillTimer) clearTimeout(state.fillTimer);
|
|
134
124
|
if (state.requestTimer) clearTimeout(state.requestTimer);
|
|
135
125
|
store.set({ splitViewWorkerConversation: null });
|
|
136
126
|
syncWorkerSelection();
|
|
@@ -152,7 +142,6 @@ function requestPage(state, before, append, refresh) {
|
|
|
152
142
|
if (!state || !current || state.requestId !== current.requestId || current.inFlight || workerCursorKey(before) === null || !ensurePopover()) return;
|
|
153
143
|
state = store.get('splitViewWorkerConversation');
|
|
154
144
|
if (append && workerCursorKey(before) !== workerCursorKey(state.nextBefore)) return;
|
|
155
|
-
if (state.fillTimer) clearTimeout(state.fillTimer);
|
|
156
145
|
var ws = getWs();
|
|
157
146
|
if (!store.get('connected') || !ws || ws.readyState !== 1) {
|
|
158
147
|
store.set({ splitViewWorkerConversation: Object.assign({}, state, { status: "error", error: "Worker conversation is unavailable while disconnected.", append: false }) });
|
|
@@ -161,7 +150,7 @@ function requestPage(state, before, append, refresh) {
|
|
|
161
150
|
var requestId = "worker-read-" + Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 9);
|
|
162
151
|
var subscribe = !state.subscriptionId || !state.subscriptionConfirmed;
|
|
163
152
|
var pending = Object.assign({}, state, { subscriptionId: subscribe ? requestId : state.subscriptionId, requestId: requestId, socket: ws, projectSlug: store.get('currentSlug'), ownerId: store.get('myUserId') || null,
|
|
164
|
-
socketGeneration: store.get('projectSplitViewSocketGeneration') || 0, status: "loading", error: null, inFlight: true,
|
|
153
|
+
socketGeneration: store.get('projectSplitViewSocketGeneration') || 0, status: "loading", error: null, inFlight: true, requestedBefore: before == null ? null : before, append: append === true, refreshing: refresh === true, refreshPending: false });
|
|
165
154
|
pending.requestTimer = setTimeout(function () {
|
|
166
155
|
var current = store.get('splitViewWorkerConversation');
|
|
167
156
|
if (!current || current.requestId !== requestId || !current.inFlight) return;
|
|
@@ -219,8 +208,23 @@ function ensurePopover() {
|
|
|
219
208
|
closeButton.addEventListener("click", function () { close(true); }); header.appendChild(closeButton); element.appendChild(header);
|
|
220
209
|
var source = document.createElement("div"); source.className = "split-view-worker-popover-source"; element.appendChild(source);
|
|
221
210
|
var body = document.createElement("div"); body.className = "split-view-worker-popover-body readonly-session-transcript"; body.tabIndex = 0; body.setAttribute("aria-label", "Read-only Worker transcript"); element.appendChild(body);
|
|
222
|
-
body.addEventListener('scroll', function () {
|
|
223
|
-
|
|
211
|
+
body.addEventListener('scroll', function () {
|
|
212
|
+
var state = store.get('splitViewWorkerConversation');
|
|
213
|
+
if (!state) return;
|
|
214
|
+
var upward = body.scrollTop < state.scrollTop;
|
|
215
|
+
store.set({ splitViewWorkerConversation: Object.assign({}, state, { scrollTop: body.scrollTop }) });
|
|
216
|
+
if (upward && state.scrollIntentUntil > Date.now()) loadEarlier(body, true);
|
|
217
|
+
}, { passive: true });
|
|
218
|
+
function scrollIntent() {
|
|
219
|
+
var state = store.get('splitViewWorkerConversation');
|
|
220
|
+
if (state) store.set({ splitViewWorkerConversation: Object.assign({}, state, { scrollIntentUntil: Date.now() + 1500 }) });
|
|
221
|
+
}
|
|
222
|
+
body.addEventListener('wheel', function (event) { if (event.deltaY < 0) { scrollIntent(); loadEarlier(body, true); } }, { passive: true });
|
|
223
|
+
body.addEventListener('keydown', function (event) {
|
|
224
|
+
if (['ArrowUp', 'PageUp', 'Home'].indexOf(event.key) >= 0 || event.key === ' ' && event.shiftKey) scrollIntent();
|
|
225
|
+
});
|
|
226
|
+
body.addEventListener('pointerdown', function (event) { if (event.target === body) scrollIntent(); }, { passive: true });
|
|
227
|
+
body.addEventListener('touchstart', scrollIntent, { passive: true });
|
|
224
228
|
var footer = document.createElement("footer"); footer.className = "split-view-worker-popover-footer"; element.appendChild(footer);
|
|
225
229
|
}
|
|
226
230
|
return element;
|
|
@@ -249,7 +253,7 @@ function renderContent() {
|
|
|
249
253
|
else body.scrollTop = oldTop;
|
|
250
254
|
if (wasFocused) body.focus({ preventScroll: true });
|
|
251
255
|
}
|
|
252
|
-
if (body) store.set({ splitViewWorkerConversation: Object.assign({}, state, { renderedMessages: state.messages, prepend: false, liveRefresh: false,
|
|
256
|
+
if (body) store.set({ splitViewWorkerConversation: Object.assign({}, state, { renderedMessages: state.messages, scrollTop: body.scrollTop, scrollIntentUntil: 0, prepend: false, liveRefresh: false,
|
|
253
257
|
initialPage: state.status === 'ready' ? false : state.initialPage }) });
|
|
254
258
|
if (footer) {
|
|
255
259
|
footer.textContent = state.status === 'error' ? 'Read-only conversation' : state.refreshing ? 'Updating live conversation…' : state.subscriptionConfirmed ? 'Read-only · Live updates' : 'Read-only conversation';
|
|
@@ -265,25 +269,13 @@ function renderContent() {
|
|
|
265
269
|
}
|
|
266
270
|
if (footerFocused) { var nextFocus = footer.querySelector('button') || body; if (nextFocus) nextFocus.focus({ preventScroll: true }); }
|
|
267
271
|
refreshIcons();
|
|
268
|
-
|
|
272
|
+
|
|
269
273
|
}
|
|
270
274
|
function loadEarlier(body, intentional) {
|
|
271
275
|
var state = store.get('splitViewWorkerConversation');
|
|
272
276
|
if (!shouldLoadWorkerPage(state, body, intentional)) return;
|
|
273
277
|
requestPage(state, state.nextBefore, true);
|
|
274
278
|
}
|
|
275
|
-
function scheduleWorkerFill(body) {
|
|
276
|
-
var state = store.get('splitViewWorkerConversation');
|
|
277
|
-
if (!shouldLoadWorkerPage(state, body, false) || state.fillTimer) return;
|
|
278
|
-
var requestId = state.requestId;
|
|
279
|
-
var timer = setTimeout(function () {
|
|
280
|
-
var current = store.get('splitViewWorkerConversation');
|
|
281
|
-
if (!current || current.requestId !== requestId || current.fillTimer !== timer || !body.isConnected) return;
|
|
282
|
-
store.set({ splitViewWorkerConversation: Object.assign({}, current, { fillTimer: null }) });
|
|
283
|
-
loadEarlier(body, false);
|
|
284
|
-
}, 120);
|
|
285
|
-
store.set({ splitViewWorkerConversation: Object.assign({}, state, { fillTimer: timer }) });
|
|
286
|
-
}
|
|
287
279
|
function anchorChip(state) {
|
|
288
280
|
if (!state) return null;
|
|
289
281
|
return document.querySelector('.split-pane[data-session-origin="' + CSS.escape(state.driverOriginId) + '"] [data-worker-origin="' + CSS.escape(state.workerOriginId) + '"]');
|
|
@@ -397,28 +389,6 @@ export function renderPaneWorkers(container, driver) {
|
|
|
397
389
|
button.addEventListener("click", function () { if (presentationSource(container, exactDriver(driver.sessionOriginId))) open(button, driver, worker); }); pill.appendChild(button); root.appendChild(pill);
|
|
398
390
|
appendWorkerRemoval(pill, button, container, driver, worker);
|
|
399
391
|
});
|
|
400
|
-
var activeView = (store.get('projectSplitViews') || []).find(function (view) { return view.id === store.get('projectSplitViewActiveId'); });
|
|
401
|
-
var group = configuredGroup(driver);
|
|
402
|
-
if ((activeView && activeView.sessions.some(function (session) { return session.sessionOriginId === driver.sessionOriginId; }) || source.singleton) && group) {
|
|
403
|
-
var controls = document.createElement('button'); controls.type = 'button'; controls.className = 'split-view-worker-controls';
|
|
404
|
-
controls.textContent = 'Controls'; controls.setAttribute('aria-label', 'Open Worker controls for ' + (driver.title || 'Driver session'));
|
|
405
|
-
controls.dataset.driverOrigin = driver.sessionOriginId; controls.dataset.groupId = String(group.id);
|
|
406
|
-
controls.addEventListener('click', function (event) {
|
|
407
|
-
event.preventDefault(); event.stopPropagation();
|
|
408
|
-
var frame = container.querySelector('.split-pane-frame'), live = frame && frame.clayPane && frame.clayPane.identity();
|
|
409
|
-
var currentSource = presentationSource(container, exactDriver(driver.sessionOriginId));
|
|
410
|
-
if (!frame || !live || !live.ready || !currentSource || !frame.isConnected || frame.dataset.sessionOriginId !== driver.sessionOriginId ||
|
|
411
|
-
Number(frame.dataset.sessionId) !== driver.id || live.sessionId !== driver.id || live.binding.sessionOriginId !== driver.sessionOriginId ||
|
|
412
|
-
frame.dataset.projectSlug !== store.get('currentSlug') || configuredGroup(exactDriver(driver.sessionOriginId)) !== group) return;
|
|
413
|
-
document.dispatchEvent(new CustomEvent('clay-split-view-open-worker-controls', { detail: {
|
|
414
|
-
driverOriginId: driver.sessionOriginId, driverId: driver.id, groupId: group.id, projectSlug: store.get('currentSlug'),
|
|
415
|
-
ownerId: driver.ownerId || null, workerOrigins: group.members.map(function (id) {
|
|
416
|
-
var member = sessions().find(function (session) { return session.id === id; }); return member && member.sessionRole === 'worker' ? member.sessionOriginId : null;
|
|
417
|
-
})
|
|
418
|
-
} }));
|
|
419
|
-
});
|
|
420
|
-
root.appendChild(controls);
|
|
421
|
-
}
|
|
422
392
|
introduceWorkerViewer(container, driver);
|
|
423
393
|
syncWorkerMessagePreviews();
|
|
424
394
|
var conversation = store.get('splitViewWorkerConversation');
|
|
@@ -468,7 +438,6 @@ store.subscribe(function (state, previous) {
|
|
|
468
438
|
var conversation = state.splitViewWorkerConversation;
|
|
469
439
|
if (conversation) {
|
|
470
440
|
if (conversation.requestTimer) clearTimeout(conversation.requestTimer);
|
|
471
|
-
if (conversation.fillTimer) clearTimeout(conversation.fillTimer);
|
|
472
441
|
store.set({ splitViewWorkerConversation: Object.assign({}, conversation, { status: "error", error: "Worker conversation connection was lost.", inFlight: false, append: false }) });
|
|
473
442
|
renderContent();
|
|
474
443
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ensureWorkbenchWindowControls, syncWorkbenchWindowControls } from './workbench-window-controls.js';
|
|
1
2
|
import { WORKBENCH_PANELS } from './workbench-panels.js';
|
|
2
3
|
import { recordToolExpansion } from './view-presentation-state.js';
|
|
3
4
|
import { store } from './store.js';
|
|
@@ -73,6 +74,7 @@ function renderWorkbenchChrome(panel, name) {
|
|
|
73
74
|
header.appendChild(actions);
|
|
74
75
|
}
|
|
75
76
|
actions.classList.add('workbench-pane-actions');
|
|
77
|
+
ensureWorkbenchWindowControls(panel, name, actions);
|
|
76
78
|
var title = header.querySelector('strong, [class$="-title"]');
|
|
77
79
|
if (title) title.classList.add('tool-pane-title');
|
|
78
80
|
var space = document.createElement('span'); space.className = 'split-pane-drag-space';
|
|
@@ -80,6 +82,7 @@ function renderWorkbenchChrome(panel, name) {
|
|
|
80
82
|
header.insertBefore(space, actions);
|
|
81
83
|
var expanded = panel.classList.contains('panel-fullscreen');
|
|
82
84
|
var observer = new MutationObserver(function () {
|
|
85
|
+
syncWorkbenchWindowControls(panel, actions);
|
|
83
86
|
var next = panel.classList.contains('panel-fullscreen');
|
|
84
87
|
if (next !== expanded) { expanded = next; recordToolExpansion(name, next); }
|
|
85
88
|
});
|
|
@@ -122,7 +122,7 @@ export function initToolPaneDrag() {
|
|
|
122
122
|
document.addEventListener('pointerdown', pointerDown, true);
|
|
123
123
|
document.addEventListener('pointermove', move, true); document.addEventListener('pointerup', up, true);
|
|
124
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);
|
|
125
|
+
document.addEventListener('keydown', function (event) { if (event.key === 'Escape' && store.get('toolPaneDrag')) { event.preventDefault(); event.stopImmediatePropagation(); finish(false); } }, true);
|
|
126
126
|
window.addEventListener('blur', function () { finish(false); });
|
|
127
127
|
}
|
|
128
128
|
|
|
@@ -6,7 +6,7 @@ import { store } from './store.js';
|
|
|
6
6
|
import { getWs } from './ws-ref.js';
|
|
7
7
|
import { TOOL_PANES, toolWindowId, workspaceWindowTree } from './tool-pane-tree.js';
|
|
8
8
|
import { renderPresentationSplitView } from './split-view-presentations.js';
|
|
9
|
-
import { ensureWorkbenchLayoutPreference } from './workbench-layout-preference.js';
|
|
9
|
+
import { ensureWorkbenchLayoutPreference, saveWorkbenchWindowLayout } from './workbench-layout-preference.js';
|
|
10
10
|
import { showToast } from './utils.js';
|
|
11
11
|
import { initToolPaneDrag, bindToolPaneDivider } from './tool-pane-drag.js';
|
|
12
12
|
|
|
@@ -79,8 +79,9 @@ export async function saveToolPaneTree(receipt, tree) {
|
|
|
79
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
80
|
var view = activeSavedView();
|
|
81
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
|
-
|
|
83
|
-
|
|
82
|
+
var saved = await saveWorkbenchWindowLayout(receipt.key, tree, receipt.base);
|
|
83
|
+
if (!saved) showToast((store.get('workbenchLayoutPreference') || {}).error || 'Pane positions could not be saved. Try again.', 'warn');
|
|
84
|
+
return saved;
|
|
84
85
|
}
|
|
85
86
|
store.subscribe(function (state, previous) {
|
|
86
87
|
if (state.paneMode) return;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { iconHtml } from './icons.js';
|
|
3
|
+
import { WORKBENCH_PANELS } from './workbench-panels.js';
|
|
4
|
+
|
|
5
|
+
// Every registered workbench gets the same window actions. Existing controllers
|
|
6
|
+
// retain their handlers; missing actions use the common shell and presentation.
|
|
7
|
+
export function ensureWorkbenchWindowControls(panel, name, actions) {
|
|
8
|
+
var definition = WORKBENCH_PANELS[name];
|
|
9
|
+
if (!definition || !actions) return;
|
|
10
|
+
function ensure(kind, selector, label, icon) {
|
|
11
|
+
var button = actions.querySelector(selector);
|
|
12
|
+
if (!button) {
|
|
13
|
+
button = document.createElement('button'); button.type = 'button';
|
|
14
|
+
button.setAttribute('aria-label', label); button.setAttribute('data-tip', label);
|
|
15
|
+
button.setAttribute('aria-pressed', String(kind === 'wide' && !!((store.get('workbenchWindowStates') || {})[name] || {}).wide)); button.innerHTML = iconHtml(icon);
|
|
16
|
+
var close = actions.querySelector('button[aria-label^="Close"]');
|
|
17
|
+
actions.insertBefore(button, close || null);
|
|
18
|
+
button.addEventListener('click', function () {
|
|
19
|
+
if (kind === 'fullscreen') panel.classList.toggle('panel-fullscreen');
|
|
20
|
+
else {
|
|
21
|
+
var values = store.get('workbenchWindowStates') || {}, previous = values[name] || {};
|
|
22
|
+
var wide = !previous.wide;
|
|
23
|
+
store.set({ workbenchWindowStates: Object.assign({}, values, { [name]: Object.assign({}, previous, { wide: wide }) }) });
|
|
24
|
+
button.setAttribute('aria-pressed', String(wide));
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
button.dataset.workbenchWindow = kind;
|
|
29
|
+
return button;
|
|
30
|
+
}
|
|
31
|
+
ensure('wide', 'button[aria-label^="Widen"]', 'Widen ' + definition.label, 'chevrons-left-right');
|
|
32
|
+
ensure('fullscreen', 'button[aria-label*="fullscreen"]', 'Toggle ' + definition.label + ' fullscreen', 'maximize-2');
|
|
33
|
+
syncWorkbenchWindowControls(panel, actions);
|
|
34
|
+
}
|
|
35
|
+
export function syncWorkbenchWindowControls(panel, actions) {
|
|
36
|
+
var full = panel.classList.contains('panel-fullscreen');
|
|
37
|
+
var button = actions.querySelector('[data-workbench-window="fullscreen"]');
|
|
38
|
+
if (button) button.setAttribute('aria-pressed', String(full));
|
|
39
|
+
}
|