clay-server 5.3.0-beta.5 → 5.3.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/clay-product-control-tools.js +2 -2
- package/lib/clay-product-control.js +1 -1
- package/lib/clay-screen-controls.js +1 -0
- package/lib/clay-split-view-awareness.js +5 -2
- package/lib/clay-split-view-controls.js +14 -7
- package/lib/clay-split-view-input.js +5 -4
- package/lib/clay-split-view-render-proof.js +14 -4
- package/lib/clay-view-workspace-input.js +71 -0
- package/lib/project-admin-overview.js +22 -11
- package/lib/project-background-tasks.js +76 -0
- package/lib/project-connection.js +1 -1
- package/lib/project-file-history.js +11 -9
- package/lib/project-file-watch.js +26 -7
- package/lib/project-filesystem.js +5 -4
- package/lib/project-sessions.js +12 -5
- package/lib/project-shared-browser.js +7 -3
- package/lib/project-split-view-document.js +16 -2
- package/lib/project-split-views.js +27 -297
- package/lib/project-user-message.js +4 -3
- package/lib/project-vendor-login.js +2 -2
- package/lib/project-view-input.js +11 -0
- package/lib/project-view-pair.js +26 -0
- package/lib/project-view-store.js +348 -0
- package/lib/project-view-terminal-binding.js +16 -0
- package/lib/project-view-workspace.js +126 -0
- package/lib/project-worker-conversation.js +5 -0
- package/lib/project.js +1 -0
- package/lib/public/app.js +9 -0
- package/lib/public/css/contextual-clay.css +2 -1
- package/lib/public/css/issues.css +3 -0
- package/lib/public/css/mate-conversation.css +1 -1
- package/lib/public/css/pane.css +5 -1
- package/lib/public/css/pending-message-queue.css +24 -19
- package/lib/public/css/project-admin-overview.css +42 -13
- package/lib/public/css/right-workbench.css +0 -1
- package/lib/public/css/scheduled-tasks.css +7 -0
- package/lib/public/css/shared-browser.css +31 -8
- package/lib/public/css/split-view-layout.css +129 -10
- package/lib/public/css/tool-pane.css +124 -0
- package/lib/public/index.html +6 -6
- package/lib/public/modules/agent-message-bubbles.js +96 -0
- package/lib/public/modules/app-header.js +6 -0
- package/lib/public/modules/app-messages.js +26 -9
- package/lib/public/modules/background-task-previews.js +8 -0
- package/lib/public/modules/background-task-viewer.js +97 -0
- package/lib/public/modules/background-tasks-ui.js +34 -125
- package/lib/public/modules/chat-bubble-renderer.js +11 -5
- package/lib/public/modules/clay-file-links.js +2 -1
- package/lib/public/modules/clay-split-view-screen.js +6 -3
- package/lib/public/modules/clay-tool-pane-screen.js +35 -0
- package/lib/public/modules/contextual-clay-geometry.js +7 -3
- package/lib/public/modules/file-history-snapshot.js +12 -0
- package/lib/public/modules/file-pane-entrypoints.js +55 -0
- package/lib/public/modules/file-pane-history.js +78 -0
- package/lib/public/modules/file-pane-instances.js +140 -0
- package/lib/public/modules/file-pane-links.js +41 -0
- package/lib/public/modules/file-pane-render.js +48 -0
- package/lib/public/modules/file-pane-review.js +26 -0
- package/lib/public/modules/file-pane-toolbar.js +54 -0
- package/lib/public/modules/file-viewer-source.js +70 -0
- package/lib/public/modules/filebrowser.js +43 -82
- package/lib/public/modules/git-panel.js +5 -0
- package/lib/public/modules/issues.js +3 -4
- package/lib/public/modules/linear-panel.js +2 -2
- package/lib/public/modules/mate-knowledge-workbench.js +10 -6
- package/lib/public/modules/notifications.js +4 -1
- package/lib/public/modules/pane-activation.js +7 -3
- package/lib/public/modules/pane-binding.js +5 -1
- package/lib/public/modules/pane-browser-bridge.js +6 -2
- package/lib/public/modules/pane-browser-transport.js +110 -0
- package/lib/public/modules/pane-header-tooltips.js +32 -0
- package/lib/public/modules/pending-message-queue-drag.js +69 -0
- package/lib/public/modules/pending-message-queue.js +3 -33
- package/lib/public/modules/permission-control.js +6 -5
- package/lib/public/modules/plantuml-preview.js +12 -12
- package/lib/public/modules/project-admin-overview.js +125 -10
- package/lib/public/modules/project-grouping.js +9 -0
- package/lib/public/modules/right-workbench.js +34 -15
- package/lib/public/modules/scheduled-tasks.js +7 -6
- package/lib/public/modules/server-settings-controls.js +33 -4
- package/lib/public/modules/session-folder-toolbar.js +1 -1
- package/lib/public/modules/session-tool-item.js +86 -0
- package/lib/public/modules/settings-select.js +2 -1
- package/lib/public/modules/shared-browser-tabs.js +37 -21
- package/lib/public/modules/shared-browser.js +69 -9
- package/lib/public/modules/sidebar-projects.js +1 -10
- package/lib/public/modules/split-pane-renderer.js +1 -0
- package/lib/public/modules/split-session-empty-pane.js +47 -0
- package/lib/public/modules/split-view-create-menu.js +74 -65
- package/lib/public/modules/split-view-drag-preview.js +90 -0
- package/lib/public/modules/split-view-layout-client.js +1 -1
- package/lib/public/modules/split-view-layout-scope.js +4 -0
- package/lib/public/modules/split-view-picker-actions.js +71 -0
- package/lib/public/modules/split-view-picker-render.js +81 -0
- package/lib/public/modules/split-view-presentation-drag.js +36 -13
- package/lib/public/modules/split-view-presentation-drop.js +21 -18
- package/lib/public/modules/split-view-presentation-renderer.js +22 -17
- package/lib/public/modules/split-view-presentations.js +25 -33
- package/lib/public/modules/split-view-worker-activity.js +2 -2
- package/lib/public/modules/split-view-worker-transcript.js +70 -33
- package/lib/public/modules/split-view-workers.js +6 -4
- package/lib/public/modules/split-view.js +9 -2
- package/lib/public/modules/sticky-notes-browser.js +2 -2
- package/lib/public/modules/terminal-viewport.js +14 -0
- package/lib/public/modules/terminal.js +49 -23
- package/lib/public/modules/theme-palette.js +177 -0
- package/lib/public/modules/theme.js +2 -180
- package/lib/public/modules/tool-pane-chrome.js +88 -0
- package/lib/public/modules/tool-pane-drag.js +134 -0
- package/lib/public/modules/tool-pane-layout.js +95 -0
- package/lib/public/modules/tool-pane-legacy.js +60 -0
- package/lib/public/modules/tool-pane-tree.js +51 -0
- package/lib/public/modules/tools.js +5 -89
- package/lib/public/modules/tooltip.js +32 -6
- package/lib/public/modules/view-legacy-coordinates.js +16 -0
- package/lib/public/modules/view-mutations.js +24 -0
- package/lib/public/modules/view-pair-presentation.js +74 -0
- package/lib/public/modules/view-pair-validation.js +12 -0
- package/lib/public/modules/view-presentation-state.js +37 -0
- package/lib/public/modules/view-workspace-controller.js +210 -0
- package/lib/public/modules/view-workspace-state.js +103 -0
- package/lib/public/modules/view-workspace-validation.js +47 -0
- package/lib/public/modules/wireframe-workbench.js +13 -2
- package/lib/public/modules/workbench-layout-preference.js +12 -5
- package/lib/public/modules/workbench-panels.js +21 -0
- package/lib/public/modules/worker-message-previews.js +39 -0
- package/lib/public/pane-assets/app-5W6KJXK5.js +1 -0
- package/lib/public/pane-assets/chunk-2IJUTAB6.js +2 -0
- package/lib/public/pane-assets/chunk-6QFLYOVY.js +1 -0
- package/lib/public/pane-assets/chunk-7OVNC5HT.js +1 -0
- package/lib/public/pane-assets/chunk-BQN7B62H.js +3 -0
- package/lib/public/pane-assets/chunk-EMLN2Y7L.js +1 -0
- package/lib/public/pane-assets/chunk-ENPAHXLB.js +1 -0
- package/lib/public/pane-assets/chunk-EPBGGZRT.js +1 -0
- package/lib/public/pane-assets/chunk-EYTE56IA.js +1 -0
- package/lib/public/pane-assets/chunk-FQTNR4ZS.js +1 -0
- package/lib/public/pane-assets/chunk-JIV7O5GN.js +1 -0
- package/lib/public/pane-assets/chunk-L6LSA5WI.js +1 -0
- package/lib/public/pane-assets/chunk-M57UIGZV.js +1 -0
- package/lib/public/pane-assets/chunk-MGTFTEJP.js +1 -0
- package/lib/public/pane-assets/chunk-MMG6MYCN.js +1 -0
- package/lib/public/pane-assets/chunk-MZLMEXMW.js +1 -0
- package/lib/public/pane-assets/chunk-OG3E5SVA.js +166 -0
- package/lib/public/pane-assets/chunk-R7QVVFUG.js +1 -0
- package/lib/public/pane-assets/chunk-RGAFZ3T2.js +1 -0
- package/lib/public/pane-assets/chunk-RNDRURMF.js +1 -0
- package/lib/public/pane-assets/chunk-SJOT74VW.js +2 -0
- package/lib/public/pane-assets/chunk-SJTGHIN5.js +1 -0
- package/lib/public/pane-assets/chunk-SLMAFKI2.js +1 -0
- package/lib/public/pane-assets/chunk-U3I5KY3B.js +1 -0
- package/lib/public/pane-assets/chunk-UPXU2IPC.js +1 -0
- package/lib/public/pane-assets/chunk-VEBYD7TL.js +1 -0
- package/lib/public/pane-assets/chunk-VZ2OKDAO.js +1 -0
- package/lib/public/pane-assets/chunk-XBHYNXXT.js +1 -0
- package/lib/public/pane-assets/manifest.json +438 -381
- package/lib/public/pane-assets/style-BSKKK54Q.css +1 -0
- package/lib/public/style.css +1 -0
- package/lib/sdk-message-processor.js +5 -2
- package/lib/server-storage.js +27 -3
- package/lib/server-workbench-layout-settings.js +3 -3
- package/lib/server.js +4 -2
- package/lib/sessions.js +1 -1
- package/lib/split-view-layout.js +11 -7
- package/lib/storage-usage.js +29 -7
- package/lib/terminal-manager.js +2 -2
- package/lib/users-workbench-layout-preferences.js +14 -4
- package/lib/workbench-window-layout.js +30 -0
- package/lib/worker-message-previews.js +50 -0
- package/lib/ws-schema.js +1 -1
- package/lib/yoke/adapters/claude-worker.js +8 -0
- package/lib/yoke/adapters/claude.js +17 -2
- package/lib/yoke/claude-task-reader.js +94 -0
- package/lib/yoke/interface.js +1 -0
- package/package.json +1 -1
- package/lib/public/pane-assets/app-6H5S2Y3Z.js +0 -1
- package/lib/public/pane-assets/chunk-3QV5SDC4.js +0 -1
- package/lib/public/pane-assets/chunk-4FM3U3DW.js +0 -1
- package/lib/public/pane-assets/chunk-7GZH3FZY.js +0 -1
- package/lib/public/pane-assets/chunk-AGJTF5WQ.js +0 -1
- package/lib/public/pane-assets/chunk-DGOFGEUK.js +0 -1
- package/lib/public/pane-assets/chunk-I57URYSL.js +0 -1
- package/lib/public/pane-assets/chunk-NPKRTUNG.js +0 -2
- package/lib/public/pane-assets/chunk-VGA2Y3D2.js +0 -2
- package/lib/public/pane-assets/chunk-WKO3T75E.js +0 -167
- package/lib/public/pane-assets/style-TSRDU54Z.css +0 -1
|
@@ -4,6 +4,23 @@ import { showToast } from './utils.js';
|
|
|
4
4
|
import { confirmSettingsAction, clearSettingsDraft, setSettingsDraft } from './server-settings-state.js';
|
|
5
5
|
import { enhanceSettingsSelects } from './settings-select.js';
|
|
6
6
|
|
|
7
|
+
var controlSettings = {
|
|
8
|
+
set_keep_awake: { config: 'keepAwake', field: 'value' },
|
|
9
|
+
set_image_retention: { config: 'imageRetentionDays', field: 'days' },
|
|
10
|
+
set_update_channel: { config: 'updateChannel', field: 'channel' }
|
|
11
|
+
};
|
|
12
|
+
function controlValue(input, type) {
|
|
13
|
+
if (type === 'set_image_retention') return Number(input.value);
|
|
14
|
+
if (type === 'set_update_channel') return input.checked ? 'beta' : 'stable';
|
|
15
|
+
return input.checked;
|
|
16
|
+
}
|
|
17
|
+
function savedControlValue(type) {
|
|
18
|
+
var value = (store.get('serverSettingsConfig') || {})[controlSettings[type].config];
|
|
19
|
+
if (type === 'set_image_retention') return value === undefined ? 7 : Number(value);
|
|
20
|
+
if (type === 'set_update_channel') return value === 'beta' ? 'beta' : 'stable';
|
|
21
|
+
return !!value;
|
|
22
|
+
}
|
|
23
|
+
|
|
7
24
|
function status(id, text) {
|
|
8
25
|
var el = document.getElementById(id);
|
|
9
26
|
if (el) { el.textContent = text; el.classList.remove('hidden'); }
|
|
@@ -19,7 +36,7 @@ function sendSetting(type, values, buttonId, statusId) {
|
|
|
19
36
|
if (input) { input.disabled = true; input.dispatchEvent(new Event('settings-sync')); }
|
|
20
37
|
status(statusId, 'Saving…');
|
|
21
38
|
var pending = Object.assign({}, store.get('serverSettingsPending') || {});
|
|
22
|
-
pending[type] = { buttonId: buttonId, statusId: statusId };
|
|
39
|
+
pending[type] = { buttonId: buttonId, statusId: statusId, submitted: controlSettings[type] ? values[controlSettings[type].field] : null };
|
|
23
40
|
store.set({ serverSettingsPending: pending });
|
|
24
41
|
ws.send(JSON.stringify(Object.assign({ type: type }, values)));
|
|
25
42
|
setTimeout(function () {
|
|
@@ -51,14 +68,25 @@ export function handleServerSettingResult(msg) {
|
|
|
51
68
|
var input = request.buttonId === 'settings-pin-save-btn' ? document.getElementById('settings-pin-input') : request.buttonId.endsWith('-save') ? document.getElementById(request.buttonId.slice(0, -5)) : null;
|
|
52
69
|
if (input) { input.disabled = false; input.dispatchEvent(new Event('settings-sync')); }
|
|
53
70
|
status(request.statusId, msg.ok ? 'Saved. ' + (msg.timing || '') : (msg.error || 'Could not save. Retry your change.'));
|
|
54
|
-
if (msg.ok)
|
|
71
|
+
if (msg.ok) {
|
|
72
|
+
var setting = controlSettings[type];
|
|
73
|
+
if (setting) {
|
|
74
|
+
var config = Object.assign({}, store.get('serverSettingsConfig') || {});
|
|
75
|
+
config[setting.config] = request.submitted;
|
|
76
|
+
store.set({ serverSettingsConfig: config });
|
|
77
|
+
var dirty = input && controlValue(input, type) !== savedControlValue(type);
|
|
78
|
+
setSettingsDraft(type, dirty);
|
|
79
|
+
if (button) button.hidden = !dirty;
|
|
80
|
+
if (dirty) status(request.statusId, 'Submitted value saved. Newer edits remain unsaved.');
|
|
81
|
+
} else clearSettingsDraft(type);
|
|
82
|
+
}
|
|
55
83
|
}
|
|
56
84
|
|
|
57
85
|
function finishSetting(msg, id, field) {
|
|
58
86
|
handleServerSettingResult(msg);
|
|
59
87
|
var el = document.getElementById(id);
|
|
60
88
|
var key = 'set_keep_awake';
|
|
61
|
-
if (
|
|
89
|
+
if ((store.get('serverSettingsDrafts') || {})[key]) return;
|
|
62
90
|
if (el && msg.ok !== false && msg[field] !== undefined) el.checked = !!msg[field];
|
|
63
91
|
}
|
|
64
92
|
|
|
@@ -108,7 +136,8 @@ export function initServerControls() {
|
|
|
108
136
|
var save = document.createElement('button'); save.type = 'button'; save.className = 'settings-btn-sm'; save.textContent = 'Save changes';
|
|
109
137
|
save.id = entry[0] + '-save'; save.hidden = true; receipt.insertAdjacentElement('beforebegin', save);
|
|
110
138
|
input.onchange = function () {
|
|
111
|
-
|
|
139
|
+
var dirty = controlValue(input, entry[1]) !== savedControlValue(entry[1]);
|
|
140
|
+
setSettingsDraft(entry[1], dirty); save.hidden = !dirty; receipt.textContent = dirty ? entry[3] : 'No unsaved changes';
|
|
112
141
|
};
|
|
113
142
|
save.onclick = function () {
|
|
114
143
|
var values = {}; values[entry[2]] = entry[2] === 'days' ? Number(input.value) : entry[2] === 'channel' ? (input.checked ? 'beta' : 'stable') : input.checked;
|
|
@@ -165,7 +165,7 @@ export function renderFolderToolbar(surface, displayCounts) {
|
|
|
165
165
|
view.setAttribute("aria-haspopup", "true");
|
|
166
166
|
var viewState = store.get('sessionFolderViewMenu');
|
|
167
167
|
view.setAttribute("aria-expanded", String(!!(viewState && viewState.surface === surface)));
|
|
168
|
-
view.innerHTML = iconHtml("sliders-horizontal") + "<span>View</span>";
|
|
168
|
+
view.innerHTML = iconHtml("sliders-horizontal") + "<span>View options</span>";
|
|
169
169
|
view.addEventListener("click", function () { toggleViewMenu(surface); });
|
|
170
170
|
|
|
171
171
|
var magnifier = document.createElement("button");
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { clayToolGroupSummary } from './clay-tool-presentation.js';
|
|
2
|
+
import { iconHtml } from './icons.js';
|
|
3
|
+
|
|
4
|
+
// Shared stateless activity chrome for live sessions and read-only transcripts.
|
|
5
|
+
export function createSessionToolItem(id, name) {
|
|
6
|
+
var el = document.createElement("div");
|
|
7
|
+
el.className = "tool-item";
|
|
8
|
+
if (id != null) el.dataset.toolId = id;
|
|
9
|
+
el.innerHTML =
|
|
10
|
+
'<div class="tool-header">' +
|
|
11
|
+
'<span class="tool-chevron">' + iconHtml("chevron-right") + '</span>' +
|
|
12
|
+
'<span class="tool-bullet"></span>' +
|
|
13
|
+
'<span class="tool-name"></span>' +
|
|
14
|
+
'<span class="tool-desc"></span>' +
|
|
15
|
+
'<span class="tool-status-icon">' + iconHtml("loader", "icon-spin") + '</span>' +
|
|
16
|
+
'</div>' +
|
|
17
|
+
'<div class="tool-subtitle">' +
|
|
18
|
+
'<span class="tool-connector">└</span>' +
|
|
19
|
+
'<span class="tool-subtitle-text">Running...</span>' +
|
|
20
|
+
'</div>';
|
|
21
|
+
|
|
22
|
+
el.querySelector(".tool-name").textContent = name;
|
|
23
|
+
|
|
24
|
+
return el;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function createSessionToolGroup(avatarUrl) {
|
|
28
|
+
var group = document.createElement('div'); group.className = 'tool-group';
|
|
29
|
+
var parent = group;
|
|
30
|
+
if (avatarUrl) {
|
|
31
|
+
var image = document.createElement('img'); image.className = 'dm-bubble-avatar dm-bubble-avatar-mate'; image.src = avatarUrl; image.alt = ''; group.appendChild(image);
|
|
32
|
+
parent = document.createElement('div'); parent.className = 'dm-bubble-content'; group.appendChild(parent);
|
|
33
|
+
}
|
|
34
|
+
parent.innerHTML = '<div class="tool-group-header" style="display:none"><span class="tool-group-chevron">' + iconHtml('chevron-right') + '</span><span class="tool-group-bullet"></span><span class="tool-group-label">Running...</span><span class="tool-group-status-icon">' + iconHtml('loader', 'icon-spin') + '</span></div><div class="tool-group-items"></div>';
|
|
35
|
+
return group;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function sessionToolGroupSummary(group, tools) {
|
|
39
|
+
var claySummary = clayToolGroupSummary(group, tools);
|
|
40
|
+
if (claySummary) return claySummary;
|
|
41
|
+
var names = group.toolNames;
|
|
42
|
+
var count = names.length;
|
|
43
|
+
var allDone = group.doneCount >= count;
|
|
44
|
+
|
|
45
|
+
// Count by tool name
|
|
46
|
+
var counts = {};
|
|
47
|
+
for (var i = 0; i < names.length; i++) {
|
|
48
|
+
counts[names[i]] = (counts[names[i]] || 0) + 1;
|
|
49
|
+
}
|
|
50
|
+
var uniqueNames = Object.keys(counts);
|
|
51
|
+
|
|
52
|
+
if (uniqueNames.length === 1) {
|
|
53
|
+
var name = uniqueNames[0];
|
|
54
|
+
var n = counts[name];
|
|
55
|
+
if (allDone) {
|
|
56
|
+
switch (name) {
|
|
57
|
+
case "Read": return "Read " + n + " file" + (n > 1 ? "s" : "");
|
|
58
|
+
case "Edit": return "Edited " + n + " file" + (n > 1 ? "s" : "");
|
|
59
|
+
case "Write": return "Wrote " + n + " file" + (n > 1 ? "s" : "");
|
|
60
|
+
case "Bash": return "Ran " + n + " command" + (n > 1 ? "s" : "");
|
|
61
|
+
case "Grep": return "Searched " + n + " pattern" + (n > 1 ? "s" : "");
|
|
62
|
+
case "Glob": return "Found " + n + " pattern" + (n > 1 ? "s" : "");
|
|
63
|
+
case "Task": return "Ran " + n + " task" + (n > 1 ? "s" : "");
|
|
64
|
+
case "WebSearch": return "Searched " + n + " quer" + (n > 1 ? "ies" : "y");
|
|
65
|
+
case "WebFetch": return "Fetched " + n + " URL" + (n > 1 ? "s" : "");
|
|
66
|
+
default: return "Ran " + n + " tool" + (n > 1 ? "s" : "");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
switch (name) {
|
|
70
|
+
case "Read": return "Reading " + n + " file" + (n > 1 ? "s" : "") + "...";
|
|
71
|
+
case "Edit": return "Editing " + n + " file" + (n > 1 ? "s" : "") + "...";
|
|
72
|
+
case "Write": return "Writing " + n + " file" + (n > 1 ? "s" : "") + "...";
|
|
73
|
+
case "Bash": return "Running " + n + " command" + (n > 1 ? "s" : "") + "...";
|
|
74
|
+
case "Grep": return "Searching " + n + " pattern" + (n > 1 ? "s" : "") + "...";
|
|
75
|
+
case "Glob": return "Finding " + n + " pattern" + (n > 1 ? "s" : "") + "...";
|
|
76
|
+
case "Task": return "Running " + n + " task" + (n > 1 ? "s" : "") + "...";
|
|
77
|
+
case "WebSearch": return "Searching " + n + " quer" + (n > 1 ? "ies" : "y") + "...";
|
|
78
|
+
case "WebFetch": return "Fetching " + n + " URL" + (n > 1 ? "s" : "") + "...";
|
|
79
|
+
default: return "Running " + n + " tool" + (n > 1 ? "s" : "") + "...";
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Mixed tools
|
|
84
|
+
if (allDone) return "Ran " + count + " tools";
|
|
85
|
+
return "Running " + count + " tools...";
|
|
86
|
+
}
|
|
@@ -55,8 +55,9 @@ export function enhanceSettingsSelects(root) {
|
|
|
55
55
|
item.hidden = option.hidden;
|
|
56
56
|
item.onclick = function (event) {
|
|
57
57
|
event.preventDefault();
|
|
58
|
+
var changed = select.value !== option.value;
|
|
58
59
|
select.value = option.value;
|
|
59
|
-
select.dispatchEvent(new Event('change', { bubbles: true }));
|
|
60
|
+
if (changed) select.dispatchEvent(new Event('change', { bubbles: true }));
|
|
60
61
|
sync();
|
|
61
62
|
close(true);
|
|
62
63
|
};
|
|
@@ -7,37 +7,53 @@ export function renderBrowserTabs(el, select, send) {
|
|
|
7
7
|
var selected = store.get('sharedBrowser');
|
|
8
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')]);
|
|
9
9
|
if (bar.dataset.key === key) return;
|
|
10
|
-
bar.
|
|
10
|
+
var focused = bar.contains(document.activeElement) ? document.activeElement : null;
|
|
11
|
+
var focusedId = focused && focused.dataset.browserId, focusedAction = focused && focused.dataset.tabAction;
|
|
12
|
+
var previousSelection = bar.dataset.selected;
|
|
13
|
+
var previousStrip = bar.querySelector('.shared-browser-tab-list'), scrollLeft = previousStrip ? previousStrip.scrollLeft : 0;
|
|
14
|
+
bar.dataset.key = key; bar.dataset.selected = selected && selected.id || '';
|
|
11
15
|
bar.replaceChildren();
|
|
16
|
+
var strip = document.createElement('div'); strip.className = 'shared-browser-tab-list'; strip.setAttribute('role', 'presentation');
|
|
17
|
+
bar.appendChild(strip);
|
|
12
18
|
tabs.forEach(function (tab, index) {
|
|
13
|
-
var item = document.createElement('div'); item.className = 'shared-browser-tab';
|
|
19
|
+
var item = document.createElement('div'); item.className = 'shared-browser-tab'; item.setAttribute('role', 'presentation');
|
|
20
|
+
var active = !!selected && selected.id === tab.id;
|
|
14
21
|
var button = document.createElement('button'); button.type = 'button'; button.setAttribute('role', 'tab');
|
|
15
|
-
button.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
var
|
|
20
|
-
|
|
22
|
+
button.dataset.browserId = tab.id; button.dataset.tabAction = 'select';
|
|
23
|
+
button.setAttribute('aria-selected', String(active)); button.tabIndex = active ? 0 : -1;
|
|
24
|
+
var title = 'New tab';
|
|
25
|
+
try { if (tab.url) title = new URL(tab.url).hostname || 'New tab'; } catch (e) {}
|
|
26
|
+
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;
|
|
28
|
+
var label = document.createElement('span'); label.className = 'shared-browser-tab-label'; label.textContent = title;
|
|
29
|
+
button.innerHTML = iconHtml(tab.personalCookiesAvailable && tab.incognito ? 'glasses' : 'globe');
|
|
30
|
+
button.appendChild(label); button.setAttribute('aria-label', description); button.setAttribute('data-tip', description);
|
|
31
|
+
item.dataset.phase = tab.phase; item.dataset.control = tab.control;
|
|
21
32
|
button.onclick = function () { select(tab.id); };
|
|
22
33
|
button.onkeydown = function (event) {
|
|
23
34
|
var next = event.key === 'ArrowRight' ? (index + 1) % tabs.length : event.key === 'ArrowLeft' ? (index + tabs.length - 1) % tabs.length : event.key === 'Home' ? 0 : event.key === 'End' ? tabs.length - 1 : -1;
|
|
24
35
|
if (next < 0) return;
|
|
25
36
|
event.preventDefault(); select(tabs[next].id); bar.querySelectorAll('[role="tab"]')[next].focus();
|
|
26
37
|
};
|
|
27
|
-
var close = document.createElement('button'); close.type = 'button'; close.
|
|
38
|
+
var close = document.createElement('button'); close.type = 'button'; close.className = 'shared-browser-tab-close'; close.innerHTML = iconHtml('x');
|
|
39
|
+
close.dataset.browserId = tab.id; close.dataset.tabAction = 'close';
|
|
40
|
+
close.setAttribute('aria-label', 'Close browser tab ' + (index + 1)); close.setAttribute('data-tip', 'Close tab'); close.disabled = !store.get('connected');
|
|
28
41
|
close.onclick = function () { send('close', { browserId: tab.id }); };
|
|
29
|
-
item.append(button, close);
|
|
42
|
+
item.append(button, close); strip.appendChild(item);
|
|
30
43
|
});
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
var incognito = document.createElement('button'); incognito.type = 'button';
|
|
37
|
-
incognito.innerHTML = iconHtml('glasses');
|
|
38
|
-
incognito.setAttribute('aria-label', 'New incognito tab'); incognito.title = 'New incognito tab · Site data is not saved';
|
|
39
|
-
incognito.disabled = !store.get('connected');
|
|
40
|
-
incognito.onclick = function () { send('open', { newTab: true, incognito: true }); };
|
|
41
|
-
bar.appendChild(incognito); refreshIcons();
|
|
44
|
+
function addButton(icon, label, description, fields, action) {
|
|
45
|
+
var button = document.createElement('button'); button.type = 'button'; button.className = 'shared-browser-new-tab';
|
|
46
|
+
button.innerHTML = iconHtml(icon); button.dataset.tabAction = action;
|
|
47
|
+
button.setAttribute('aria-label', label); button.setAttribute('data-tip', description); button.disabled = !store.get('connected');
|
|
48
|
+
button.onclick = function () { send('open', fields); }; bar.appendChild(button);
|
|
42
49
|
}
|
|
50
|
+
addButton('plus', 'New browser tab', selected && selected.personalCookiesAvailable ? 'New tab · Personal browser' : 'New incognito tab', { newTab: true }, 'new');
|
|
51
|
+
if (selected && selected.personalCookiesAvailable) addButton('glasses', 'New incognito tab', 'New incognito tab · Site data is not saved', { newTab: true, incognito: true }, 'incognito');
|
|
52
|
+
refreshIcons();
|
|
53
|
+
strip.scrollLeft = scrollLeft;
|
|
54
|
+
var buttons = Array.from(bar.querySelectorAll('button'));
|
|
55
|
+
var replacement = focused && buttons.find(function (button) { return button.dataset.browserId === focusedId && button.dataset.tabAction === focusedAction; });
|
|
56
|
+
if (focused) (replacement || bar.querySelector('[aria-selected="true"]') || buttons[0]).focus({ preventScroll: true });
|
|
57
|
+
var current = bar.querySelector('[aria-selected="true"]');
|
|
58
|
+
if (current && previousSelection !== bar.dataset.selected) current.scrollIntoView({ block: 'nearest', inline: 'nearest' });
|
|
43
59
|
}
|
|
@@ -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 () {
|
|
@@ -178,7 +182,28 @@ function selectBrowserTab(id) {
|
|
|
178
182
|
setBrowserTab(browser);
|
|
179
183
|
send('view', { visible: desktop() && !document.hidden }); render();
|
|
180
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
|
+
}
|
|
181
205
|
export function openSharedBrowserAt(value) {
|
|
206
|
+
ensureBrowserPaneSource();
|
|
182
207
|
var url = browserLinkTarget('browser:' + value);
|
|
183
208
|
if (!url) throw new Error('Enter a valid HTTP or HTTPS browser address.');
|
|
184
209
|
if (!desktop()) throw new Error('Clay Browser is available on desktop screens.');
|
|
@@ -186,19 +211,22 @@ export function openSharedBrowserAt(value) {
|
|
|
186
211
|
if (!store.get('connected')) throw new Error('Reconnect before opening this browser link.');
|
|
187
212
|
ensurePanel(); closeBrowserSettings(false);
|
|
188
213
|
claimRightWorkbench('browser'); store.set({ sharedBrowserOpen: true });
|
|
189
|
-
ui({ error: '' });
|
|
214
|
+
ui({ error: '' }); sendOrQueueBrowserOpen({ url: url }); render();
|
|
190
215
|
}
|
|
191
216
|
export function openSharedBrowser() {
|
|
217
|
+
ensureBrowserPaneSource();
|
|
192
218
|
if (!eligible() || !store.get('connected')) return;
|
|
193
219
|
ensurePanel();
|
|
194
220
|
claimRightWorkbench('browser');
|
|
195
221
|
store.set({ sharedBrowserOpen: true });
|
|
196
222
|
var browser = store.get('sharedBrowser');
|
|
197
|
-
if (!browser)
|
|
223
|
+
if (!browser) sendOrQueueBrowserOpen();
|
|
198
224
|
else send('view', { visible: true });
|
|
199
225
|
render();
|
|
200
226
|
}
|
|
201
227
|
export function hideSharedBrowser() {
|
|
228
|
+
setBrowserExpanded(false); recordToolExpansion('browser', false);
|
|
229
|
+
if (!store.get('savedWorkspaceApplying')) store.set({ pendingBrowserOpen: null });
|
|
202
230
|
closeViewportMenu();
|
|
203
231
|
closeBrowserSettings();
|
|
204
232
|
clearBrowserInput();
|
|
@@ -207,9 +235,11 @@ export function hideSharedBrowser() {
|
|
|
207
235
|
releaseRightWorkbench('browser');
|
|
208
236
|
render();
|
|
209
237
|
}
|
|
210
|
-
export function handleSharedBrowserMessage(msg) {
|
|
238
|
+
export function handleSharedBrowserMessage(msg, fromPane) {
|
|
211
239
|
if (!msg.type || msg.type.indexOf('shared_browser_') !== 0) return false;
|
|
212
|
-
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;
|
|
213
243
|
if (handleBrowserSettingsMessage(msg)) return true;
|
|
214
244
|
if (msg.type === 'shared_browser_state') {
|
|
215
245
|
var old = store.get('sharedBrowser');
|
|
@@ -218,6 +248,7 @@ export function handleSharedBrowserMessage(msg) {
|
|
|
218
248
|
var selected = !msg.focus && old && tabs.find(function (tab) { return tab.id === old.id; });
|
|
219
249
|
selected = selected || msg.browser || tabs[0] || null;
|
|
220
250
|
setBrowserTab(selected);
|
|
251
|
+
store.set({ savedViewBrowserListReady: true });
|
|
221
252
|
if (msg.present && eligible()) {
|
|
222
253
|
ensurePanel(); claimRightWorkbench('browser'); store.set({ sharedBrowserOpen: true });
|
|
223
254
|
}
|
|
@@ -228,23 +259,35 @@ export function handleSharedBrowserMessage(msg) {
|
|
|
228
259
|
var current = store.get('sharedBrowser');
|
|
229
260
|
if (!desktop() || !store.get('sharedBrowserOpen') || !current || current.id !== msg.browserId || (store.get('sharedBrowserUi') || {}).decoding) return true;
|
|
230
261
|
var sessionId = store.get('activeSessionId');
|
|
262
|
+
var paneStamp = browserPaneStamp();
|
|
231
263
|
ui({ decoding: true });
|
|
232
264
|
var image = new Image();
|
|
233
265
|
image.onload = function () {
|
|
234
266
|
var latest = store.get('sharedBrowser');
|
|
235
|
-
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()) {
|
|
236
268
|
var canvas = panel().querySelector('canvas'); canvas.width = image.naturalWidth; canvas.height = image.naturalHeight;
|
|
237
269
|
canvas.getContext('2d').drawImage(image, 0, 0);
|
|
238
270
|
ui({ decoding: false, hasFrame: true }); render();
|
|
239
271
|
registerStudioPaint(msg, canvas, null, canvas);
|
|
240
272
|
}
|
|
241
273
|
};
|
|
242
|
-
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 }); };
|
|
243
275
|
image.src = 'data:image/jpeg;base64,' + msg.data;
|
|
244
276
|
}
|
|
245
277
|
return true;
|
|
246
278
|
}
|
|
247
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);
|
|
248
291
|
initBrowserLinks();
|
|
249
292
|
var button = document.getElementById('shared-browser-btn');
|
|
250
293
|
if (button) button.onclick = openSharedBrowser;
|
|
@@ -261,14 +304,31 @@ export function initSharedBrowser() {
|
|
|
261
304
|
registerRightWorkbench('browser', hideSharedBrowser);
|
|
262
305
|
store.subscribe(function (state, previous) {
|
|
263
306
|
if (state.currentSlug !== previous.currentSlug || state.activeSessionId !== previous.activeSessionId || state.myUserId !== previous.myUserId) {
|
|
307
|
+
if (browserPaneSessionId() !== null) { render(); return; }
|
|
264
308
|
clearBrowserInput(); releaseRightWorkbench('browser');
|
|
265
|
-
store.set({ sharedBrowser: null, sharedBrowsers: [], sharedBrowserOpen: false, sharedBrowserUi: {}, sharedBrowserSettings: {} });
|
|
309
|
+
store.set({ sharedBrowser: null, sharedBrowsers: [], savedViewBrowserListReady: false, sharedBrowserOpen: false, sharedBrowserUi: {}, sharedBrowserSettings: {} });
|
|
266
310
|
if (state.connected) send('state_request');
|
|
267
311
|
render();
|
|
268
312
|
} else if (state.connected !== previous.connected) {
|
|
313
|
+
store.set({ savedViewBrowserListReady: false });
|
|
269
314
|
if (state.connected) send('state_request');
|
|
270
315
|
render();
|
|
271
316
|
} else if (state.permissions !== previous.permissions || state.dmMode !== previous.dmMode) {
|
|
272
317
|
if (!eligible()) hideSharedBrowser(); renderEntry();
|
|
273
318
|
}
|
|
274
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(); }
|
|
@@ -15,7 +15,7 @@ import { openAddProjectModal } from './project-start-dialog.js';
|
|
|
15
15
|
import { showHomeHub } from './app-home-hub.js';
|
|
16
16
|
import { getPendingTuiAttention } from './app-notifications.js';
|
|
17
17
|
import { isExternalWorktree, externalWorktreeTooltip, appendExternalWorktreeBadge } from './worktree-location.js';
|
|
18
|
-
import { groupProjects } from './project-grouping.js';
|
|
18
|
+
import { groupProjects, getProjectAbbrev } from './project-grouping.js';
|
|
19
19
|
import { showProjectAccessPopover } from './project-access.js';
|
|
20
20
|
import { renderProjectMates, mateForWorkspace } from './project-mate-navigation.js';
|
|
21
21
|
import { openProjectCreateChoice } from './project-mate-creation-workspace.js';
|
|
@@ -337,15 +337,6 @@ export function initSidebarProjects() {
|
|
|
337
337
|
export function getCachedProjectList() { return cachedProjectList; }
|
|
338
338
|
export function getCachedCurrentSlug() { return cachedCurrentSlug; }
|
|
339
339
|
|
|
340
|
-
function getProjectAbbrev(name) {
|
|
341
|
-
if (!name) return "?";
|
|
342
|
-
var words = name.replace(/[^a-zA-Z0-9\s]/g, "").trim().split(/\s+/);
|
|
343
|
-
if (words.length >= 2) {
|
|
344
|
-
return (words[0][0] + words[1][0]).toUpperCase();
|
|
345
|
-
}
|
|
346
|
-
return name.substring(0, 2).toUpperCase();
|
|
347
|
-
}
|
|
348
|
-
|
|
349
340
|
export { getProjectAbbrev };
|
|
350
341
|
|
|
351
342
|
// --- Project context menu ---
|
|
@@ -296,6 +296,7 @@ export function createSplitPane(pane, closePane) {
|
|
|
296
296
|
if (!vendorIcon.hidden) vendorIcon.src = VENDOR_AVATARS[vendor]; vendorIcon.alt = ""; vendorIcon.title = VENDOR_NAMES[vendor] || vendor; header.appendChild(vendorIcon);
|
|
297
297
|
var title = document.createElement("span"); title.className = "split-pane-title"; title.textContent = pane.title; title.title = "Rename session";
|
|
298
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);
|
|
299
300
|
var frame = document.createElement("iframe"); frame.className = "split-pane-frame"; frame.dataset.projectSlug = pane.slug;
|
|
300
301
|
frame.dataset.sessionId = String(pane.sessionId); frame.src = paneUrl(pane); frame.title = pane.title;
|
|
301
302
|
frame.dataset.sessionOriginId = session && session.sessionOriginId || '';
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { currentLayoutScope } from './split-view-layout-scope.js';
|
|
3
|
+
|
|
4
|
+
export function clearEmptySplit(host) {
|
|
5
|
+
var pane = host && host.querySelector(':scope > .split-session-empty-pane');
|
|
6
|
+
var menu = document.getElementById('split-view-create-menu');
|
|
7
|
+
if (pane && menu && pane.contains(menu)) {
|
|
8
|
+
menu.hidden = true;
|
|
9
|
+
if (menu.contains(document.activeElement)) document.activeElement.blur();
|
|
10
|
+
if (pane.contains(menu)) document.body.appendChild(menu);
|
|
11
|
+
}
|
|
12
|
+
if (pane) pane.remove();
|
|
13
|
+
var divider = host && host.querySelector(':scope > .split-session-reserved-divider');
|
|
14
|
+
if (divider) divider.remove();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function reserveEmptySplit(host, geometry, inset) {
|
|
18
|
+
var state = store.get('splitSessionPicker');
|
|
19
|
+
var receipt = state && state.receipt;
|
|
20
|
+
var target = receipt && geometry.panes[receipt.origin];
|
|
21
|
+
if (!state || !state.side || !target || !currentLayoutScope(receipt.scope)) { clearEmptySplit(host); return false; }
|
|
22
|
+
var side = state.side, vertical = side === 'top' || side === 'bottom', before = side === 'left' || side === 'top';
|
|
23
|
+
var size = vertical ? 'height' : 'width', axis = vertical ? 'y' : 'x';
|
|
24
|
+
var half = Math.max(0, (target[size] - 7) / 2);
|
|
25
|
+
var original = Object.assign({}, target), empty = Object.assign({}, target), gap = Object.assign({}, target);
|
|
26
|
+
original[size] = half; empty[size] = half; gap[size] = 7;
|
|
27
|
+
gap[axis] += half;
|
|
28
|
+
if (before) original[axis] += half + 7; else empty[axis] += half + 7;
|
|
29
|
+
geometry.panes[receipt.origin] = original;
|
|
30
|
+
var pane = host.querySelector(':scope > .split-session-empty-pane');
|
|
31
|
+
if (!pane) {
|
|
32
|
+
pane = document.createElement('section'); pane.className = 'split-session-empty-pane';
|
|
33
|
+
pane.innerHTML = '<div class="split-session-empty-body"></div>';
|
|
34
|
+
host.appendChild(pane);
|
|
35
|
+
}
|
|
36
|
+
pane.dataset.side = side; pane.setAttribute('aria-label', 'Empty ' + side + ' split');
|
|
37
|
+
var menu = document.getElementById('split-view-create-menu');
|
|
38
|
+
var body = pane.querySelector('.split-session-empty-body');
|
|
39
|
+
if (menu && menu.parentNode !== body) body.appendChild(menu);
|
|
40
|
+
var divider = host.querySelector(':scope > .split-session-reserved-divider');
|
|
41
|
+
if (!divider) { divider = document.createElement('div'); divider.className = 'split-session-reserved-divider'; divider.setAttribute('aria-hidden', 'true'); host.appendChild(divider); }
|
|
42
|
+
[[pane, empty], [divider, gap]].forEach(function (entry) {
|
|
43
|
+
entry[0].style.left = inset.left + entry[1].x + 'px'; entry[0].style.top = inset.top + entry[1].y + 'px';
|
|
44
|
+
entry[0].style.width = entry[1].width + 'px'; entry[0].style.height = entry[1].height + 'px';
|
|
45
|
+
});
|
|
46
|
+
return true;
|
|
47
|
+
}
|