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
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
|
|
3
|
+
// Read-only presentation metadata from this pane's committed authenticated view.
|
|
4
|
+
export function paneHeaderRuntime() {
|
|
5
|
+
var state = store.snap(), binding = state.paneCommittedBinding;
|
|
6
|
+
if (!binding || !state.connected || !state.paneReady || state.paneSuspended || state.panePendingBinding ||
|
|
7
|
+
state.activeSessionId !== binding.sessionId || state.sessionOriginId !== binding.sessionOriginId ||
|
|
8
|
+
state.currentSlug !== binding.projectSlug || (state.myUserId || null) !== (binding.viewerId || null)) return null;
|
|
9
|
+
return { binding: binding, vendor: state.currentVendor, model: state.currentModel || '' };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function initPaneHeaderRuntime(frame) {
|
|
13
|
+
store.subscribe(function (state, previous) {
|
|
14
|
+
if (state.currentModel === previous.currentModel && state.currentVendor === previous.currentVendor) return;
|
|
15
|
+
if (!paneHeaderRuntime() || frame.contentWindow !== window || frame.contentDocument !== document) return;
|
|
16
|
+
frame.dispatchEvent(new window.parent.CustomEvent('clay-pane-config'));
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -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,14 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
import { WORKBENCH_PANELS } from './workbench-panels.js';
|
|
2
|
+
import { activeSavedView, workspaceChanged } from './view-workspace-state.js';
|
|
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() {
|
|
@@ -25,88 +26,136 @@ function ensureLayoutPreference() {
|
|
|
25
26
|
});
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
function dockedOwners() { return store.get('rightWorkbenchDockedOwners') || []; }
|
|
30
|
+
function openOwner(name) { return store.get('rightWorkbenchOwner') === name || dockedOwners().indexOf(name) !== -1; }
|
|
31
|
+
export function rightWorkbenchRevisionFor(name) {
|
|
32
|
+
var revisions = store.get('rightWorkbenchPanelRevisions') || {};
|
|
33
|
+
return revisions[name] == null ? store.get('rightWorkbenchRevision') || 0 : revisions[name];
|
|
34
|
+
}
|
|
35
|
+
function publish(owner, docked, revisions) {
|
|
36
|
+
store.set({ rightWorkbenchOwner: owner, rightWorkbenchDockedOwners: docked,
|
|
37
|
+
rightWorkbenchPanelRevisions: revisions, rightWorkbenchRevision: (store.get('rightWorkbenchRevision') || 0) + 1 });
|
|
38
|
+
workspaceChanged();
|
|
39
|
+
}
|
|
40
|
+
function setDocked(name, docked) {
|
|
41
|
+
if (!openOwner(name)) return;
|
|
42
|
+
var owners = dockedOwners().filter(function (item) { return item !== name; }), previous = store.get('rightWorkbenchOwner');
|
|
43
|
+
if (docked) owners.push(name);
|
|
44
|
+
publish(docked ? previous : name, owners, store.get('rightWorkbenchPanelRevisions') || {});
|
|
45
|
+
if (!docked && previous && previous !== name && owners.indexOf(previous) === -1 && closers[previous]) closers[previous]();
|
|
46
|
+
}
|
|
28
47
|
function renderLayoutControls() {
|
|
29
48
|
if (typeof document === 'undefined') return;
|
|
30
49
|
var main = document.getElementById('main-panels');
|
|
31
50
|
if (!main) return;
|
|
32
|
-
var state = store.get('workbenchLayoutPreference') || {}
|
|
33
|
-
main.dataset.workbenchMode =
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
var owners = dockedOwners(), state = store.get('workbenchLayoutPreference') || {};
|
|
52
|
+
main.dataset.workbenchMode = owners.length ? 'docked' : 'overlay';
|
|
53
|
+
main.dataset.workbenchDocks = String(owners.length);
|
|
54
|
+
Object.keys(panelIds).forEach(function (name) {
|
|
55
|
+
var panel = document.getElementById(panelIds[name]);
|
|
56
|
+
if (!panel) return;
|
|
57
|
+
var docked = owners.indexOf(name) !== -1;
|
|
58
|
+
renderToolPaneChrome(panel, name);
|
|
59
|
+
panel.dataset.workbenchPlacement = docked ? 'docked' : 'overlay';
|
|
60
|
+
panel.dataset.workbenchActive = store.get('rightWorkbenchOwner') === name ? 'true' : 'false';
|
|
61
|
+
if (!openOwner(name)) return;
|
|
62
|
+
var header = panel.querySelector(':scope > .tool-pane-header, :scope > header, :scope > .file-viewer-header .file-viewer-tabbar, :scope > .terminal-header');
|
|
63
|
+
if (!header) return;
|
|
64
|
+
var button = panel.querySelector('.workbench-layout-toggle');
|
|
65
|
+
if (!button) {
|
|
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;
|
|
67
|
+
button = document.createElement('button'); button.type = 'button'; button.className = 'workbench-layout-toggle';
|
|
68
|
+
button.addEventListener('click', function () {
|
|
69
|
+
if (activeSavedView()) { setDocked(name, dockedOwners().indexOf(name) < 0); return; }
|
|
70
|
+
var current = store.get('workbenchLayoutPreference') || {};
|
|
71
|
+
if (!current.ready || current.loading || current.saving) { ensureLayoutPreference(); return; }
|
|
72
|
+
var owner = store.get('myUserId'), project = store.get('currentSlug'), revision = rightWorkbenchRevisionFor(name);
|
|
73
|
+
var wasDocked = dockedOwners().indexOf(name) !== -1;
|
|
74
|
+
function currentTarget() { return button.isConnected && openOwner(name) && owner === store.get('myUserId') && project === store.get('currentSlug') &&
|
|
75
|
+
revision === rightWorkbenchRevisionFor(name) && wasDocked === (dockedOwners().indexOf(name) !== -1); }
|
|
76
|
+
import('./workbench-layout-preference.js').then(function (api) {
|
|
77
|
+
var fresh = store.get('workbenchLayoutPreference') || {};
|
|
78
|
+
if (!currentTarget() || fresh.mode !== current.mode || fresh.revision !== current.revision || fresh.loading || fresh.saving || !fresh.ready) return false;
|
|
79
|
+
return api.saveWorkbenchLayoutMode(wasDocked ? 'overlay' : 'docked');
|
|
80
|
+
}).then(function (saved) {
|
|
81
|
+
if (!currentTarget()) return;
|
|
82
|
+
var fresh = store.get('workbenchLayoutPreference') || {};
|
|
83
|
+
if (saved === true && fresh.ready && fresh.mode === (wasDocked ? 'overlay' : 'docked')) setDocked(name, !wasDocked);
|
|
84
|
+
else if (saved === false) showToast(fresh.error || 'Workbench layout could not be saved. Try again.', 'error');
|
|
85
|
+
}).catch(function (error) {
|
|
86
|
+
if (currentTarget()) showToast(error.message || 'Workbench layout could not be saved. Try again.', 'error');
|
|
87
|
+
});
|
|
55
88
|
});
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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; }
|
|
91
|
+
actions.insertBefore(button, anchor || null);
|
|
92
|
+
}
|
|
93
|
+
if (button.dataset.mode !== (docked ? 'docked' : 'overlay')) {
|
|
94
|
+
button.dataset.mode = docked ? 'docked' : 'overlay';
|
|
95
|
+
button.innerHTML = iconHtml(docked ? 'layers-2' : 'panel-right'); refreshIcons();
|
|
96
|
+
}
|
|
97
|
+
button.setAttribute('aria-label', docked ? 'Float workbench over sessions' : 'Dock workbench beside sessions');
|
|
98
|
+
button.title = state.error || button.getAttribute('aria-label');
|
|
99
|
+
button.setAttribute('aria-pressed', docked ? 'true' : 'false');
|
|
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;
|
|
103
|
+
button.setAttribute('aria-busy', state.loading || state.saving ? 'true' : 'false');
|
|
104
|
+
});
|
|
70
105
|
}
|
|
71
106
|
|
|
72
|
-
export function registerRightWorkbench(name, close) {
|
|
107
|
+
export function registerRightWorkbench(name, close, panelId) {
|
|
108
|
+
if (panelId) panelIds[name] = panelId;
|
|
73
109
|
if (!name || typeof close !== "function") return;
|
|
74
110
|
closers[name] = close;
|
|
75
111
|
}
|
|
76
112
|
|
|
113
|
+
export function unregisterRightWorkbench(name) {
|
|
114
|
+
delete closers[name];
|
|
115
|
+
if (name.indexOf('file-') === 0 && name !== 'file-viewer') delete panelIds[name];
|
|
116
|
+
}
|
|
77
117
|
export function claimRightWorkbench(name) {
|
|
78
118
|
if (typeof document !== 'undefined') ensureLayoutPreference();
|
|
79
|
-
var
|
|
80
|
-
var
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
119
|
+
var owner = store.get('rightWorkbenchOwner'), docked = dockedOwners().slice();
|
|
120
|
+
var retained = docked.indexOf(name) !== -1 && owner && docked.indexOf(owner) === -1 ? owner : name;
|
|
121
|
+
var revision = (store.get('rightWorkbenchRevision') || 0) + 1, revisions = Object.assign({}, store.get('rightWorkbenchPanelRevisions') || {});
|
|
122
|
+
Object.keys(revisions).forEach(function (key) { if (key !== retained && docked.indexOf(key) === -1) delete revisions[key]; });
|
|
123
|
+
revisions[name] = revision;
|
|
124
|
+
publish(retained, docked, revisions);
|
|
125
|
+
Object.keys(closers).forEach(function (key) {
|
|
126
|
+
if (key === name || key === retained || docked.indexOf(key) !== -1) return;
|
|
127
|
+
try { closers[key](); } catch (error) { /* one panel must not block another */ }
|
|
128
|
+
});
|
|
87
129
|
if (typeof requestAnimationFrame === 'function') requestAnimationFrame(renderLayoutControls);
|
|
88
|
-
return
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function storeSet(name, value) {
|
|
92
|
-
store.set({ rightWorkbenchOwner: name, rightWorkbenchRevision: value });
|
|
130
|
+
return revision;
|
|
93
131
|
}
|
|
94
|
-
|
|
95
132
|
export function releaseRightWorkbench(name) {
|
|
96
|
-
if (
|
|
97
|
-
|
|
133
|
+
if (!openOwner(name)) return;
|
|
134
|
+
var docked = dockedOwners().filter(function (item) { return item !== name; }), revisions = Object.assign({}, store.get('rightWorkbenchPanelRevisions') || {});
|
|
135
|
+
delete revisions[name];
|
|
136
|
+
publish(store.get('rightWorkbenchOwner') === name ? docked[docked.length - 1] || null : store.get('rightWorkbenchOwner'), docked, revisions);
|
|
98
137
|
}
|
|
99
|
-
|
|
100
138
|
export function isRightWorkbenchCurrent(name, expectedRevision) {
|
|
101
|
-
return
|
|
139
|
+
return openOwner(name) && rightWorkbenchRevisionFor(name) === expectedRevision;
|
|
102
140
|
}
|
|
103
|
-
|
|
104
141
|
export function resetRightWorkbench() {
|
|
105
|
-
|
|
142
|
+
publish(null, [], {});
|
|
143
|
+
Object.keys(closers).forEach(function (name) { try { closers[name](); } catch (error) { /* Clear other panes after a context change. */ } });
|
|
106
144
|
}
|
|
107
|
-
|
|
108
145
|
store.subscribe(function (state, previous) {
|
|
109
|
-
if (state.
|
|
110
|
-
if (state.
|
|
111
|
-
|
|
146
|
+
if (state.currentSlug !== previous.currentSlug || state.myUserId !== previous.myUserId) { resetRightWorkbench(); return; }
|
|
147
|
+
if (state.projectSplitViewPending !== previous.projectSplitViewPending || state.workbenchLayoutPreference !== previous.workbenchLayoutPreference || state.rightWorkbenchOwner !== previous.rightWorkbenchOwner ||
|
|
148
|
+
state.rightWorkbenchDockedOwners !== previous.rightWorkbenchDockedOwners) renderLayoutControls();
|
|
112
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);
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { recordToolExpansion } from './view-presentation-state.js';
|
|
1
2
|
import { registerStudioPaint } from './clay-studio-render.js';
|
|
3
|
+
import { hasBrowserPaneHost, initBrowserPaneTransport, forwardBrowserPaneMessage, sendBrowserPaneCommand, ensureBrowserPaneSource, browserPaneSessionId, browserPaneStamp, browserPaneBound } from './pane-browser-transport.js';
|
|
2
4
|
import { browserLinkTarget, initBrowserLinks } from './clay-browser-links.js';
|
|
3
5
|
import { browserSettingsHtml, bindBrowserSettings, renderBrowserSettings, closeBrowserSettings, handleBrowserSettingsMessage } from './shared-browser-settings.js';
|
|
4
6
|
import { renderBrowserTabs } from './shared-browser-tabs.js';
|
|
@@ -19,9 +21,11 @@ function eligible() { return visibleEntry() && !!store.get('activeSessionId'); }
|
|
|
19
21
|
function panel() { return document.getElementById('shared-browser-panel'); }
|
|
20
22
|
function ui(patch) { store.set({ sharedBrowserUi: Object.assign({}, store.get('sharedBrowserUi'), patch) }); }
|
|
21
23
|
function send(type, fields) {
|
|
24
|
+
var browser = store.get('sharedBrowser');
|
|
25
|
+
var paneSent = sendBrowserPaneCommand(type, Object.assign({ browserId: browser && browser.id, epoch: browser && browser.epoch }, fields || {}));
|
|
26
|
+
if (paneSent !== null) return paneSent;
|
|
22
27
|
var ws = getWs();
|
|
23
28
|
if (!ws || ws.readyState !== 1 || !store.get('activeSessionId')) return false;
|
|
24
|
-
var browser = store.get('sharedBrowser');
|
|
25
29
|
ws.send(JSON.stringify(Object.assign({ type: 'shared_browser_' + type, sessionId: store.get('activeSessionId'),
|
|
26
30
|
browserId: browser && browser.id, epoch: browser && browser.epoch }, fields || {})));
|
|
27
31
|
return true;
|
|
@@ -65,7 +69,7 @@ function ensurePanel() {
|
|
|
65
69
|
root.appendChild(el);
|
|
66
70
|
el.querySelector('[data-action="hide"]').onclick = hideSharedBrowser;
|
|
67
71
|
el.querySelector('[data-action="wide"]').onclick = function () { var wide = !(store.get('sharedBrowserUi') || {}).wide; ui({ wide: wide }); render(); };
|
|
68
|
-
el.querySelector('[data-action="fullscreen"]').onclick = function () { var full = !(store.get('sharedBrowserUi') || {}).fullscreen;
|
|
72
|
+
el.querySelector('[data-action="fullscreen"]').onclick = function () { var full = !(store.get('sharedBrowserUi') || {}).fullscreen; setBrowserExpanded(full); recordToolExpansion('browser', full); };
|
|
69
73
|
['back', 'forward', 'reload'].forEach(function (action) { el.querySelector('[data-action="' + action + '"]').onclick = function () { sendBrowserInput({ kind: action }); }; });
|
|
70
74
|
el.querySelector('form').onsubmit = function (event) { event.preventDefault(); sendBrowserInput({ kind: 'navigate', url: el.querySelector('form input').value }); };
|
|
71
75
|
el.querySelector('[data-action="control"]').onclick = function () {
|
|
@@ -82,12 +86,18 @@ function ensurePanel() {
|
|
|
82
86
|
}
|
|
83
87
|
function renderEntry() {
|
|
84
88
|
var browser = store.get('sharedBrowser');
|
|
89
|
+
var count = (store.get('sharedBrowsers') || []).length;
|
|
85
90
|
var ready = !!store.get('connected') && !!store.get('activeSessionId');
|
|
86
91
|
var label = !store.get('connected') ? 'Browser · Connecting…' : !store.get('activeSessionId') ? 'Browser · Loading session…' : browser ? 'View browser' : 'Open browser';
|
|
87
92
|
['shared-browser-btn', 'shared-browser-toggle-btn'].forEach(function (id) {
|
|
88
93
|
var button = document.getElementById(id);
|
|
89
94
|
if (!button) return;
|
|
90
95
|
var title = id === 'shared-browser-toggle-btn' && ready && store.get('sharedBrowserOpen') ? 'Hide browser' : label;
|
|
96
|
+
if (id === 'shared-browser-toggle-btn') {
|
|
97
|
+
var badge = button.querySelector('.shared-browser-count');
|
|
98
|
+
if (badge) { badge.textContent = String(count); badge.classList.toggle('hidden', count === 0); }
|
|
99
|
+
if (count) title += ' · ' + count + (count === 1 ? ' tab' : ' tabs');
|
|
100
|
+
}
|
|
91
101
|
button.classList.toggle('hidden', !visibleEntry());
|
|
92
102
|
button.title = ready && browser ? title + ' · ' + browser.phase : title;
|
|
93
103
|
button.setAttribute('aria-label', title);
|
|
@@ -172,7 +182,28 @@ function selectBrowserTab(id) {
|
|
|
172
182
|
setBrowserTab(browser);
|
|
173
183
|
send('view', { visible: desktop() && !document.hidden }); render();
|
|
174
184
|
}
|
|
185
|
+
function browserOpenContext() {
|
|
186
|
+
return JSON.stringify([store.get('myUserId'), store.get('currentSlug'), store.get('projectSplitViewActiveId'), store.get('sessionOriginId'), store.get('projectSplitViewSocketGeneration')]);
|
|
187
|
+
}
|
|
188
|
+
function sendOrQueueBrowserOpen(fields) {
|
|
189
|
+
store.set({ pendingBrowserOpen: null });
|
|
190
|
+
if (send('open', fields)) return;
|
|
191
|
+
// A false transport result means nothing was sent. Only this fresh human
|
|
192
|
+
// opening can wait for pane readiness; restoration never enters this path.
|
|
193
|
+
store.set({ pendingBrowserOpen: { context: browserOpenContext(), socket: getWs(), fields: fields || {}, expires: Date.now() + 15000 } });
|
|
194
|
+
}
|
|
195
|
+
function resumePendingBrowserOpen() {
|
|
196
|
+
var pending = store.get('pendingBrowserOpen');
|
|
197
|
+
if (!pending) return;
|
|
198
|
+
if (pending.context !== browserOpenContext() || pending.socket !== getWs() || pending.expires < Date.now() || !store.get('connected') || !store.get('sharedBrowserOpen')) {
|
|
199
|
+
store.set({ pendingBrowserOpen: null }); return;
|
|
200
|
+
}
|
|
201
|
+
if (!ensureBrowserPaneSource()) return;
|
|
202
|
+
store.set({ pendingBrowserOpen: null });
|
|
203
|
+
if (!send('open', pending.fields)) store.set({ pendingBrowserOpen: pending });
|
|
204
|
+
}
|
|
175
205
|
export function openSharedBrowserAt(value) {
|
|
206
|
+
ensureBrowserPaneSource();
|
|
176
207
|
var url = browserLinkTarget('browser:' + value);
|
|
177
208
|
if (!url) throw new Error('Enter a valid HTTP or HTTPS browser address.');
|
|
178
209
|
if (!desktop()) throw new Error('Clay Browser is available on desktop screens.');
|
|
@@ -180,19 +211,22 @@ export function openSharedBrowserAt(value) {
|
|
|
180
211
|
if (!store.get('connected')) throw new Error('Reconnect before opening this browser link.');
|
|
181
212
|
ensurePanel(); closeBrowserSettings(false);
|
|
182
213
|
claimRightWorkbench('browser'); store.set({ sharedBrowserOpen: true });
|
|
183
|
-
ui({ error: '' });
|
|
214
|
+
ui({ error: '' }); sendOrQueueBrowserOpen({ url: url }); render();
|
|
184
215
|
}
|
|
185
216
|
export function openSharedBrowser() {
|
|
217
|
+
ensureBrowserPaneSource();
|
|
186
218
|
if (!eligible() || !store.get('connected')) return;
|
|
187
219
|
ensurePanel();
|
|
188
220
|
claimRightWorkbench('browser');
|
|
189
221
|
store.set({ sharedBrowserOpen: true });
|
|
190
222
|
var browser = store.get('sharedBrowser');
|
|
191
|
-
if (!browser)
|
|
223
|
+
if (!browser) sendOrQueueBrowserOpen();
|
|
192
224
|
else send('view', { visible: true });
|
|
193
225
|
render();
|
|
194
226
|
}
|
|
195
227
|
export function hideSharedBrowser() {
|
|
228
|
+
setBrowserExpanded(false); recordToolExpansion('browser', false);
|
|
229
|
+
if (!store.get('savedWorkspaceApplying')) store.set({ pendingBrowserOpen: null });
|
|
196
230
|
closeViewportMenu();
|
|
197
231
|
closeBrowserSettings();
|
|
198
232
|
clearBrowserInput();
|
|
@@ -201,9 +235,11 @@ export function hideSharedBrowser() {
|
|
|
201
235
|
releaseRightWorkbench('browser');
|
|
202
236
|
render();
|
|
203
237
|
}
|
|
204
|
-
export function handleSharedBrowserMessage(msg) {
|
|
238
|
+
export function handleSharedBrowserMessage(msg, fromPane) {
|
|
205
239
|
if (!msg.type || msg.type.indexOf('shared_browser_') !== 0) return false;
|
|
206
|
-
if (
|
|
240
|
+
if (forwardBrowserPaneMessage(msg)) return true;
|
|
241
|
+
if (browserPaneBound() && !fromPane) return true;
|
|
242
|
+
if (String(msg.sessionId) !== String(browserPaneSessionId() || store.get('activeSessionId'))) return true;
|
|
207
243
|
if (handleBrowserSettingsMessage(msg)) return true;
|
|
208
244
|
if (msg.type === 'shared_browser_state') {
|
|
209
245
|
var old = store.get('sharedBrowser');
|
|
@@ -212,6 +248,7 @@ export function handleSharedBrowserMessage(msg) {
|
|
|
212
248
|
var selected = !msg.focus && old && tabs.find(function (tab) { return tab.id === old.id; });
|
|
213
249
|
selected = selected || msg.browser || tabs[0] || null;
|
|
214
250
|
setBrowserTab(selected);
|
|
251
|
+
store.set({ savedViewBrowserListReady: true });
|
|
215
252
|
if (msg.present && eligible()) {
|
|
216
253
|
ensurePanel(); claimRightWorkbench('browser'); store.set({ sharedBrowserOpen: true });
|
|
217
254
|
}
|
|
@@ -222,23 +259,35 @@ export function handleSharedBrowserMessage(msg) {
|
|
|
222
259
|
var current = store.get('sharedBrowser');
|
|
223
260
|
if (!desktop() || !store.get('sharedBrowserOpen') || !current || current.id !== msg.browserId || (store.get('sharedBrowserUi') || {}).decoding) return true;
|
|
224
261
|
var sessionId = store.get('activeSessionId');
|
|
262
|
+
var paneStamp = browserPaneStamp();
|
|
225
263
|
ui({ decoding: true });
|
|
226
264
|
var image = new Image();
|
|
227
265
|
image.onload = function () {
|
|
228
266
|
var latest = store.get('sharedBrowser');
|
|
229
|
-
if (latest && latest.id === msg.browserId && store.get('activeSessionId') === sessionId && panel()) {
|
|
267
|
+
if (latest && latest.id === msg.browserId && browserPaneStamp() === paneStamp && store.get('activeSessionId') === sessionId && panel()) {
|
|
230
268
|
var canvas = panel().querySelector('canvas'); canvas.width = image.naturalWidth; canvas.height = image.naturalHeight;
|
|
231
269
|
canvas.getContext('2d').drawImage(image, 0, 0);
|
|
232
270
|
ui({ decoding: false, hasFrame: true }); render();
|
|
233
271
|
registerStudioPaint(msg, canvas, null, canvas);
|
|
234
272
|
}
|
|
235
273
|
};
|
|
236
|
-
image.onerror = function () { if (store.get('activeSessionId') === sessionId) ui({ decoding: false }); };
|
|
274
|
+
image.onerror = function () { if (browserPaneStamp() === paneStamp && store.get('activeSessionId') === sessionId) ui({ decoding: false }); };
|
|
237
275
|
image.src = 'data:image/jpeg;base64,' + msg.data;
|
|
238
276
|
}
|
|
239
277
|
return true;
|
|
240
278
|
}
|
|
241
279
|
export function initSharedBrowser() {
|
|
280
|
+
initBrowserPaneTransport(handleSharedBrowserMessage, function (available) {
|
|
281
|
+
var applying = store.get('savedWorkspaceApplying');
|
|
282
|
+
store.set({ savedWorkspaceApplying: true, savedViewBrowserListReady: false });
|
|
283
|
+
try {
|
|
284
|
+
clearBrowserInput(); setBrowserTab(null);
|
|
285
|
+
store.set({ sharedBrowsers: [], sharedBrowserUi: {}, sharedBrowserSettings: {} });
|
|
286
|
+
if (!available) hideSharedBrowser();
|
|
287
|
+
render();
|
|
288
|
+
} finally { store.set({ savedWorkspaceApplying: applying }); }
|
|
289
|
+
});
|
|
290
|
+
document.addEventListener('clay-pane-ready', function () { queueMicrotask(resumePendingBrowserOpen); }, true);
|
|
242
291
|
initBrowserLinks();
|
|
243
292
|
var button = document.getElementById('shared-browser-btn');
|
|
244
293
|
if (button) button.onclick = openSharedBrowser;
|
|
@@ -255,14 +304,31 @@ export function initSharedBrowser() {
|
|
|
255
304
|
registerRightWorkbench('browser', hideSharedBrowser);
|
|
256
305
|
store.subscribe(function (state, previous) {
|
|
257
306
|
if (state.currentSlug !== previous.currentSlug || state.activeSessionId !== previous.activeSessionId || state.myUserId !== previous.myUserId) {
|
|
307
|
+
if (browserPaneSessionId() !== null) { render(); return; }
|
|
258
308
|
clearBrowserInput(); releaseRightWorkbench('browser');
|
|
259
|
-
store.set({ sharedBrowser: null, sharedBrowsers: [], sharedBrowserOpen: false, sharedBrowserUi: {}, sharedBrowserSettings: {} });
|
|
309
|
+
store.set({ sharedBrowser: null, sharedBrowsers: [], savedViewBrowserListReady: false, sharedBrowserOpen: false, sharedBrowserUi: {}, sharedBrowserSettings: {} });
|
|
260
310
|
if (state.connected) send('state_request');
|
|
261
311
|
render();
|
|
262
312
|
} else if (state.connected !== previous.connected) {
|
|
313
|
+
store.set({ savedViewBrowserListReady: false });
|
|
263
314
|
if (state.connected) send('state_request');
|
|
264
315
|
render();
|
|
265
316
|
} else if (state.permissions !== previous.permissions || state.dmMode !== previous.dmMode) {
|
|
266
317
|
if (!eligible()) hideSharedBrowser(); renderEntry();
|
|
267
318
|
}
|
|
268
319
|
});
|
|
320
|
+
|
|
321
|
+
export function restoreExistingBrowser(resource) {
|
|
322
|
+
if (hasBrowserPaneHost() && !ensureBrowserPaneSource()) return 'pending';
|
|
323
|
+
if (!store.get('savedViewBrowserListReady')) { send('state_request'); return 'pending'; }
|
|
324
|
+
if (!store.get('connected') || !store.get('controlRuntimeId')) return 'pending';
|
|
325
|
+
ensurePanel();
|
|
326
|
+
var browser = (store.get('sharedBrowsers') || []).find(function (item) { return item.id === resource.id && item.phase !== 'ended'; });
|
|
327
|
+
var exact = browser && resource.runtimeId === store.get('controlRuntimeId') && resource.sessionOriginId === store.get('sessionOriginId');
|
|
328
|
+
claimRightWorkbench('browser'); store.set({ sharedBrowserOpen: true });
|
|
329
|
+
if (exact) { setBrowserTab(browser); send('view', { visible: true }); ui({ error: '' }); render(); return 'restored'; }
|
|
330
|
+
ui({ error: 'This saved browser is unavailable in the current conversation. No replacement browser was opened.' }); render();
|
|
331
|
+
return 'unavailable';
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export function setBrowserExpanded(enabled) { ui({ fullscreen: !!enabled }); if (document.getElementById('shared-browser-panel')) render(); }
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { renderPaneRuntime } from './split-pane-runtime.js';
|
|
1
2
|
import { store } from './store.js';
|
|
2
3
|
import { getWs } from './ws-ref.js';
|
|
3
4
|
import { copyToClipboard } from './utils.js';
|
|
@@ -295,6 +296,7 @@ export function createSplitPane(pane, closePane) {
|
|
|
295
296
|
if (!vendorIcon.hidden) vendorIcon.src = VENDOR_AVATARS[vendor]; vendorIcon.alt = ""; vendorIcon.title = VENDOR_NAMES[vendor] || vendor; header.appendChild(vendorIcon);
|
|
296
297
|
var title = document.createElement("span"); title.className = "split-pane-title"; title.textContent = pane.title; title.title = "Rename session";
|
|
297
298
|
title.addEventListener("click", function () { if (title.getAttribute('aria-disabled') === 'true' || !syncPaneHeaderState(paneEl)) return; var current = sessionById(Number(paneEl.dataset.sessionId)); if (current) startPaneRename(header, title, { sessionId: current.id, title: current.title }); }); header.appendChild(title);
|
|
299
|
+
var dragSpace = document.createElement("span"); dragSpace.className = "split-pane-drag-space"; dragSpace.title = "Drag to rearrange panes"; dragSpace.setAttribute("aria-hidden", "true"); header.appendChild(dragSpace);
|
|
298
300
|
var frame = document.createElement("iframe"); frame.className = "split-pane-frame"; frame.dataset.projectSlug = pane.slug;
|
|
299
301
|
frame.dataset.sessionId = String(pane.sessionId); frame.src = paneUrl(pane); frame.title = pane.title;
|
|
300
302
|
frame.dataset.sessionOriginId = session && session.sessionOriginId || '';
|
|
@@ -343,7 +345,7 @@ export function createSplitPane(pane, closePane) {
|
|
|
343
345
|
}); header.appendChild(sidebarButton);
|
|
344
346
|
var search = document.createElement("button"); search.type = "button"; search.className = "split-pane-search"; search.setAttribute("aria-label", "Search this session"); search.title = "Search this session"; search.innerHTML = iconHtml("search");
|
|
345
347
|
header.insertBefore(search, close);
|
|
346
|
-
|
|
348
|
+
function refreshReadyHeader() {
|
|
347
349
|
if (paneEl.classList.contains('split-pane-cached')) return;
|
|
348
350
|
var id = Number(frame.dataset.requestedSessionId || frame.dataset.sessionId), current = sessionById(id);
|
|
349
351
|
var live = frame.clayPane && frame.clayPane.identity(), target = paneTarget(store.snap(), getCachedSessions(), id);
|
|
@@ -352,7 +354,9 @@ export function createSplitPane(pane, closePane) {
|
|
|
352
354
|
frame.dataset.sessionOriginId = live.binding.sessionOriginId;
|
|
353
355
|
updateSplitPaneMetadata(paneEl, { slug: frame.dataset.projectSlug, sessionId: id, title: current.title });
|
|
354
356
|
syncPairChrome(paneEl.parentNode, store.get('splitPanes'));
|
|
355
|
-
}
|
|
357
|
+
}
|
|
358
|
+
frame.addEventListener('clay-pane-ready', refreshReadyHeader);
|
|
359
|
+
frame.addEventListener('clay-pane-config', refreshReadyHeader);
|
|
356
360
|
frame.addEventListener('clay-pane-activation-failed', function () {
|
|
357
361
|
if (paneEl.classList.contains('split-pane-cached')) return;
|
|
358
362
|
syncPaneHeaderState(paneEl);
|
|
@@ -453,12 +457,7 @@ export function updateSplitPaneMetadata(paneEl, pane) {
|
|
|
453
457
|
var sidebarButton = paneEl.querySelector('.split-pane-sidebar');
|
|
454
458
|
if (sidebarButton) sidebarButton.hidden = store.get('projectSplitViewSingletonOrigin') !== (session && session.sessionOriginId) && paneEl.dataset.active !== 'true';
|
|
455
459
|
if (vendor) { var vendorName = session && session.vendor || "unknown"; vendor.hidden = !VENDOR_AVATARS[vendorName]; if (!vendor.hidden) vendor.src = VENDOR_AVATARS[vendorName]; vendor.title = VENDOR_NAMES[vendorName] || vendorName; }
|
|
456
|
-
|
|
457
|
-
var runtimeName = session && session.vendor || "unknown", runtimeModelName = session && typeof session.model === "string" && session.model.trim() || "Model unavailable";
|
|
458
|
-
var runtimeIcon = runtime.querySelector('.split-view-runtime-icon'), runtimeModel = runtime.querySelector('.split-view-model');
|
|
459
|
-
if (runtimeIcon) { runtimeIcon.hidden = !VENDOR_AVATARS[runtimeName]; if (!runtimeIcon.hidden) runtimeIcon.src = VENDOR_AVATARS[runtimeName]; runtimeIcon.title = VENDOR_NAMES[runtimeName] || runtimeName; runtimeIcon.alt = ""; }
|
|
460
|
-
if (runtimeModel) { runtimeModel.textContent = runtimeModelName; runtimeModel.title = (VENDOR_NAMES[runtimeName] || runtimeName) + " · " + runtimeModelName; }
|
|
461
|
-
runtime.setAttribute("aria-label", (VENDOR_NAMES[runtimeName] || runtimeName) + " · " + runtimeModelName);
|
|
460
|
+
renderPaneRuntime(paneEl, session);
|
|
462
461
|
renderPaneWorkers(paneEl, session);
|
|
463
462
|
updatePaneFullAccessButton(paneEl.querySelector('.split-pane-full-access'), session);
|
|
464
463
|
syncPaneHeaderState(paneEl);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { getCachedSessions } from './sidebar-sessions.js';
|
|
3
|
+
import { livePaneBinding, samePaneBinding, samePaneTarget, paneTarget } from './pane-binding.js';
|
|
4
|
+
import { VENDOR_AVATARS, VENDOR_NAMES } from './app-rendering.js';
|
|
5
|
+
import { modelEntryValue } from './model-picker.js';
|
|
6
|
+
|
|
7
|
+
export function renderPaneRuntime(paneEl, session) {
|
|
8
|
+
var runtime = paneEl.querySelector('.split-view-runtime');
|
|
9
|
+
if (!runtime) return;
|
|
10
|
+
var vendor = session && session.vendor || 'unknown', model = modelEntryValue(session && session.model);
|
|
11
|
+
var frame = paneEl.querySelector('.split-pane-frame'), controller = frame && frame.clayPane;
|
|
12
|
+
var live = controller && controller.identity(), target = session && paneTarget(store.snap(), getCachedSessions(), session.id);
|
|
13
|
+
if (session && live && live.ready && live.sessionId === session.id && !frame.dataset.requestedSessionId &&
|
|
14
|
+
samePaneTarget(target, live.binding) && livePaneBinding(frame) && typeof controller.headerRuntime === 'function') {
|
|
15
|
+
var observed = controller.headerRuntime();
|
|
16
|
+
if (observed && observed.vendor === vendor && samePaneBinding(live.binding, observed.binding)) model = modelEntryValue(observed.model) || model;
|
|
17
|
+
}
|
|
18
|
+
var label = model || (vendor === 'unknown' ? 'Model unavailable' : 'Provider default');
|
|
19
|
+
var icon = runtime.querySelector('.split-view-runtime-icon'), text = runtime.querySelector('.split-view-model');
|
|
20
|
+
if (icon) { icon.hidden = !VENDOR_AVATARS[vendor]; if (!icon.hidden) icon.src = VENDOR_AVATARS[vendor]; icon.title = VENDOR_NAMES[vendor] || vendor; icon.alt = ''; }
|
|
21
|
+
if (text) { text.textContent = label; text.title = (VENDOR_NAMES[vendor] || vendor) + ' · ' + label; }
|
|
22
|
+
runtime.setAttribute('aria-label', (VENDOR_NAMES[vendor] || vendor) + ' · ' + label);
|
|
23
|
+
}
|