clay-server 5.5.0-beta.1 → 5.5.0-beta.3
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-input.js +2 -2
- package/lib/clay-product-control-tools.js +1 -1
- package/lib/clay-product-control.js +4 -3
- package/lib/daemon-project-access.js +10 -2
- package/lib/daemon-projects.js +16 -4
- package/lib/daemon-worktree-controls.js +171 -0
- package/lib/daemon.js +14 -63
- package/lib/personal-provider-install.js +85 -0
- package/lib/personal-provider-settings.js +94 -0
- package/lib/project-logs-service.js +2 -2
- package/lib/project-pair-task-control.js +1 -1
- package/lib/project-pending-message-queue.js +6 -2
- package/lib/project-product-control.js +1 -0
- package/lib/project-session-actions.js +46 -3
- package/lib/project-session-pair.js +2 -0
- package/lib/project-sessions.js +11 -3
- package/lib/project-user-message.js +5 -1
- package/lib/project-vendor-login.js +95 -32
- package/lib/project-worker-auth.js +71 -0
- package/lib/project-worker-conversation.js +11 -2
- package/lib/project.js +9 -0
- package/lib/public/app.js +22 -0
- package/lib/public/css/icon-strip.css +5 -0
- package/lib/public/css/overlays.css +18 -0
- package/lib/public/css/settings-shell.css +14 -0
- package/lib/public/css/settings-ui.css +6 -0
- package/lib/public/css/split-view-layout.css +16 -11
- package/lib/public/modules/agent-message-bubbles.js +33 -4
- package/lib/public/modules/app-messages.js +7 -1
- package/lib/public/modules/app-projects.js +3 -4
- package/lib/public/modules/clay-control-copy.js +1 -0
- package/lib/public/modules/clay-tool-presentation.js +1 -1
- package/lib/public/modules/generation-stop.js +4 -4
- package/lib/public/modules/pane-activation.js +2 -2
- package/lib/public/modules/pane-bridge.js +4 -2
- package/lib/public/modules/personal-provider-context.js +11 -0
- package/lib/public/modules/project-settings.js +1 -1
- package/lib/public/modules/settings-section-nav.js +58 -0
- package/lib/public/modules/settings-shell-registry.js +4 -2
- package/lib/public/modules/settings-shell.js +17 -6
- package/lib/public/modules/sidebar-projects.js +23 -9
- package/lib/public/modules/sidebar-sessions.js +3 -0
- package/lib/public/modules/split-pane-force-stop.js +47 -0
- package/lib/public/modules/split-pane-header-state.js +11 -1
- package/lib/public/modules/split-pane-renderer.js +2 -2
- package/lib/public/modules/split-view-arrange-menu.js +38 -0
- package/lib/public/modules/split-view-create-menu.js +9 -5
- package/lib/public/modules/split-view-member-actions.js +42 -0
- package/lib/public/modules/split-view-picker-actions.js +2 -2
- package/lib/public/modules/split-view-presentation-renderer.js +10 -35
- package/lib/public/modules/split-view-presentations.js +9 -8
- package/lib/public/modules/split-view-sidebar-menu.js +2 -1
- package/lib/public/modules/split-view-workers.js +32 -17
- package/lib/public/modules/stt-settings.js +21 -4
- package/lib/public/modules/user-settings-layout.js +11 -8
- package/lib/public/modules/user-settings-provider-install.js +42 -0
- package/lib/public/modules/user-settings-providers.js +102 -0
- package/lib/public/modules/user-settings-voice.js +50 -41
- package/lib/public/modules/vendor-login-browser.js +1 -1
- package/lib/public/modules/vendor-login.js +72 -12
- package/lib/public/modules/view-workspace-state.js +1 -1
- package/lib/public/modules/worker-activity-indicator.js +49 -0
- package/lib/public/modules/worker-message-previews.js +47 -11
- package/lib/public/modules/worker-stop-controls.js +36 -0
- package/lib/public/modules/worktree-controls.js +86 -0
- package/lib/public/pane-assets/{app-4IZ6F6ND.js → app-ANJS2LSK.js} +1 -1
- package/lib/public/pane-assets/{chunk-L3FCTD7U.js → chunk-32YIYF7R.js} +1 -1
- package/lib/public/pane-assets/{chunk-RRYWV5JT.js → chunk-3EJ5IAHZ.js} +1 -1
- package/lib/public/pane-assets/{chunk-3E6LDNXF.js → chunk-3TIVBAKM.js} +1 -1
- package/lib/public/pane-assets/{chunk-I6X5J2V5.js → chunk-4QFNLHW5.js} +1 -1
- package/lib/public/pane-assets/{chunk-OB3KKICE.js → chunk-5XMZMIFG.js} +1 -1
- package/lib/public/pane-assets/chunk-6S2C7Q2N.js +1 -0
- package/lib/public/pane-assets/chunk-7B2GJI57.js +1 -0
- package/lib/public/pane-assets/{chunk-A7OB7HQS.js → chunk-BOFNCPTN.js} +1 -1
- package/lib/public/pane-assets/{chunk-ELL4UBGY.js → chunk-CXWAJEN5.js} +1 -1
- package/lib/public/pane-assets/{chunk-SEELA2D2.js → chunk-EWTXBZJO.js} +1 -1
- package/lib/public/pane-assets/{chunk-ADGKPVFD.js → chunk-G35ZOJGP.js} +1 -1
- package/lib/public/pane-assets/chunk-HYT24UBT.js +1 -0
- package/lib/public/pane-assets/chunk-ISQEPZMY.js +1 -0
- package/lib/public/pane-assets/{chunk-6ZF2FJT3.js → chunk-JM46WGDH.js} +1 -1
- package/lib/public/pane-assets/chunk-LBJG4MK2.js +174 -0
- package/lib/public/pane-assets/{chunk-YXJ7ISEK.js → chunk-P3AK4ORJ.js} +1 -1
- package/lib/public/pane-assets/chunk-QKPGE7DN.js +1 -0
- package/lib/public/pane-assets/chunk-TS5JUEVH.js +1 -0
- package/lib/public/pane-assets/{chunk-YWHBNEB2.js → chunk-UGH5MCNK.js} +1 -1
- package/lib/public/pane-assets/{chunk-YFUORWA6.js → chunk-V5DLPSLT.js} +1 -1
- package/lib/public/pane-assets/chunk-VFKDU64W.js +1 -0
- package/lib/public/pane-assets/{chunk-A4MLSYBJ.js → chunk-XSSTR6GC.js} +1 -1
- package/lib/public/pane-assets/{chunk-DJ5WXQKT.js → chunk-XVESKMU3.js} +1 -1
- package/lib/public/pane-assets/manifest.json +443 -433
- package/lib/public/pane-assets/style-VBVCWV44.css +1 -0
- package/lib/sdk-bridge.js +40 -6
- package/lib/sdk-force-stop.js +62 -0
- package/lib/sdk-message-processor.js +2 -1
- package/lib/server-worktree-controls.js +34 -0
- package/lib/server.js +11 -1
- package/lib/session-force-stop-support.js +15 -0
- package/lib/sessions.js +6 -1
- package/lib/terminal-manager.js +1 -1
- package/lib/terminal.js +13 -1
- package/lib/vendor-login-browser-flow.js +1 -1
- package/lib/worker-activity.js +26 -0
- package/lib/worker-message-previews.js +12 -7
- package/lib/worktree-branch.js +57 -0
- package/lib/worktree-identity.js +39 -0
- package/lib/worktree-preferences.js +25 -0
- package/lib/worktree-ref-transaction.js +36 -0
- package/lib/yoke/adapters/antigravity.js +3 -0
- package/lib/yoke/force-termination.js +26 -0
- package/lib/yoke/interface.js +14 -0
- package/lib/yoke/process-force-termination.js +24 -0
- package/lib/yoke/provider-install-recipes.js +20 -0
- package/lib/yoke/vendor-registry.js +20 -1
- package/package.json +1 -1
- package/lib/public/pane-assets/chunk-6KO36HMU.js +0 -1
- package/lib/public/pane-assets/chunk-FILRACQB.js +0 -168
- package/lib/public/pane-assets/chunk-H6MU72QE.js +0 -1
- package/lib/public/pane-assets/chunk-PJURCIJQ.js +0 -1
- package/lib/public/pane-assets/chunk-Q7RFSS5Q.js +0 -1
- package/lib/public/pane-assets/chunk-UTLQWAKG.js +0 -1
- package/lib/public/pane-assets/chunk-WE4NWXNK.js +0 -1
- package/lib/public/pane-assets/chunk-Z2MHS43B.js +0 -1
- package/lib/public/pane-assets/style-DZX6JAMF.css +0 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { activateSettingsSections, deactivateSettingsSections } from './settings-section-nav.js';
|
|
1
2
|
// Unified full-screen settings: one shell for Personal, Project and Server
|
|
2
3
|
// settings. Scope modules register a controller; the shell owns routing,
|
|
3
4
|
// access, project target binding, focus, close restoration and draft guards.
|
|
@@ -134,6 +135,7 @@ function applyRoute(route, focus) {
|
|
|
134
135
|
var resolved = resolveRoute(route, shell.access);
|
|
135
136
|
var ctrl = controllers[resolved.scope];
|
|
136
137
|
if (!ctrl) return;
|
|
138
|
+
deactivateSettingsSections();
|
|
137
139
|
if (shell.scope && shell.scope !== resolved.scope && controllers[shell.scope].leave) controllers[shell.scope].leave();
|
|
138
140
|
var entering = shell.scope !== resolved.scope;
|
|
139
141
|
shell.scope = resolved.scope;
|
|
@@ -152,7 +154,8 @@ function applyRoute(route, focus) {
|
|
|
152
154
|
markActivePage(page);
|
|
153
155
|
store.set({ settingsLastRoute: { scope: resolved.scope, page: page, slug: shell.slug } });
|
|
154
156
|
enhanceSettingsSelects(root); refreshIcons(root);
|
|
155
|
-
if (focus) focusHeading(ctrl);
|
|
157
|
+
if (focus && !resolved.anchor) focusHeading(ctrl);
|
|
158
|
+
activateSettingsSections(page, resolved.anchor);
|
|
156
159
|
}
|
|
157
160
|
|
|
158
161
|
function onScopeSelect(scope) {
|
|
@@ -160,12 +163,18 @@ function onScopeSelect(scope) {
|
|
|
160
163
|
guard('scope', function () { applyRoute({ scope: scope, page: shell.pages[scope], slug: scope === 'project' ? currentRoute().slug : null }, true); });
|
|
161
164
|
}
|
|
162
165
|
function onPageSelect(page) {
|
|
163
|
-
|
|
166
|
+
var route = resolveRoute({scope:shell.scope,page:page}, shell.access);
|
|
167
|
+
if (route.page === shell.pages[shell.scope]) {
|
|
168
|
+
if (!route.anchor) focusHeading(controller());
|
|
169
|
+
activateSettingsSections(route.page, route.anchor); return;
|
|
170
|
+
}
|
|
164
171
|
guard('page', function () {
|
|
165
172
|
var ctrl = controller();
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
173
|
+
deactivateSettingsSections();
|
|
174
|
+
shell.pages[shell.scope] = route.page; ctrl.activate(route.page, {access:shell.access,scope:shell.scope});
|
|
175
|
+
markActivePage(route.page); store.set({settingsLastRoute:{scope:shell.scope,page:route.page,slug:shell.slug}});
|
|
176
|
+
if (!route.anchor) focusHeading(ctrl);
|
|
177
|
+
activateSettingsSections(route.page, route.anchor);
|
|
169
178
|
}, shell.pages[shell.scope]);
|
|
170
179
|
}
|
|
171
180
|
// A project switch retargets only the settings view, never the workspace.
|
|
@@ -181,7 +190,8 @@ export function navigateSettings(route) {
|
|
|
181
190
|
var resolved = resolveRoute(route, shell.access);
|
|
182
191
|
if (resolved.scope === shell.scope && resolved.page === shell.pages[shell.scope] && (resolved.scope !== 'project' || !route.slug || route.slug === shell.slug)) {
|
|
183
192
|
setShellStatus(redirectNote(resolved), resolved.redirected ? 'error' : undefined);
|
|
184
|
-
focusHeading(controller());
|
|
193
|
+
if (!resolved.anchor) focusHeading(controller());
|
|
194
|
+
activateSettingsSections(resolved.page, resolved.anchor); return;
|
|
185
195
|
}
|
|
186
196
|
guard(resolved.scope === shell.scope ? (route.slug && route.slug !== shell.slug ? 'project' : 'page') : 'scope', function () { applyRoute(route, true); }, shell.pages[shell.scope]);
|
|
187
197
|
}
|
|
@@ -251,6 +261,7 @@ export function closeSettings(opts) {
|
|
|
251
261
|
function finish() {
|
|
252
262
|
shell.token += 1; binding.invalidate();
|
|
253
263
|
var ctrl = controller();
|
|
264
|
+
deactivateSettingsSections();
|
|
254
265
|
if (ctrl && ctrl.leave) ctrl.leave();
|
|
255
266
|
Object.keys(controllers).forEach(function (id) { if (controllers[id].close) controllers[id].close(); });
|
|
256
267
|
root.classList.add('hidden');
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { reviewWorktreeRemoval } from './worktree-controls.js';
|
|
1
2
|
// sidebar-projects.js - Project icon strip, context menus, emoji picker, drag-and-drop, worktree modal
|
|
2
3
|
// Extracted from sidebar.js (PR-36)
|
|
3
4
|
|
|
@@ -433,8 +434,9 @@ export function showProjectCtxMenu(anchorEl, slug, name, icon, position, options
|
|
|
433
434
|
}
|
|
434
435
|
|
|
435
436
|
var isWorktree = slug.indexOf("--") !== -1;
|
|
436
|
-
var
|
|
437
|
-
|
|
437
|
+
var ownsWorktree = !isWorktree || !store.get('isMultiUserMode') || !!(targetProject && targetProject.projectOwnerId === store.get('myUserId'));
|
|
438
|
+
if (!ownsWorktree) iconItem.disabled = true;
|
|
439
|
+
var canDeleteTarget = ownsWorktree && (!store.get('permissions') || store.get('permissions').deleteProject !== false);
|
|
438
440
|
if (!isWorktree || canDeleteTarget) {
|
|
439
441
|
var sep2 = document.createElement("div");
|
|
440
442
|
sep2.className = "project-ctx-separator";
|
|
@@ -465,15 +467,26 @@ export function showProjectCtxMenu(anchorEl, slug, name, icon, position, options
|
|
|
465
467
|
var deleteItem = document.createElement("button");
|
|
466
468
|
deleteItem.className = "project-ctx-item project-ctx-delete";
|
|
467
469
|
deleteItem.setAttribute("role", "menuitem");
|
|
468
|
-
deleteItem.innerHTML = iconHtml("trash-2") + " <span>" + (isWorktree ? "
|
|
470
|
+
deleteItem.innerHTML = iconHtml("trash-2") + " <span>" + (isWorktree ? "Exclude worktree (keep files)" : "Remove Project") + "</span>";
|
|
469
471
|
deleteItem.addEventListener("click", function (e) {
|
|
470
472
|
e.stopPropagation();
|
|
471
473
|
closeProjectCtxMenu();
|
|
472
|
-
if (
|
|
474
|
+
if (isWorktree) reviewWorktreeRemoval(slug, "exclude", anchorEl);
|
|
475
|
+
else if (getWs() && store.get('connected')) {
|
|
473
476
|
getWs().send(JSON.stringify({ type: "remove_project_check", slug: slug, name: name }));
|
|
474
477
|
}
|
|
475
478
|
});
|
|
476
479
|
menu.appendChild(deleteItem);
|
|
480
|
+
if (isWorktree) {
|
|
481
|
+
var diskItem = document.createElement("button");
|
|
482
|
+
diskItem.className = "project-ctx-item project-ctx-delete";
|
|
483
|
+
diskItem.setAttribute("role", "menuitem");
|
|
484
|
+
diskItem.innerHTML = iconHtml("trash-2") + " <span>Delete worktree…</span>";
|
|
485
|
+
diskItem.addEventListener("click", function (event) {
|
|
486
|
+
event.stopPropagation(); closeProjectCtxMenu(); reviewWorktreeRemoval(slug, "delete", anchorEl);
|
|
487
|
+
});
|
|
488
|
+
menu.appendChild(diskItem);
|
|
489
|
+
}
|
|
477
490
|
}
|
|
478
491
|
|
|
479
492
|
document.body.appendChild(menu);
|
|
@@ -1103,7 +1116,7 @@ export function renderIconStrip(projects, currentSlug) {
|
|
|
1103
1116
|
} else {
|
|
1104
1117
|
var abbrev = document.createElement("span");
|
|
1105
1118
|
abbrev.className = "wt-branch-abbrev";
|
|
1106
|
-
abbrev.
|
|
1119
|
+
abbrev.innerHTML = iconHtml("git-branch");
|
|
1107
1120
|
wtEl.appendChild(abbrev);
|
|
1108
1121
|
}
|
|
1109
1122
|
|
|
@@ -1124,13 +1137,13 @@ export function renderIconStrip(projects, currentSlug) {
|
|
|
1124
1137
|
});
|
|
1125
1138
|
})(wt.slug);
|
|
1126
1139
|
|
|
1127
|
-
(function (slug, name, elem
|
|
1140
|
+
(function (slug, name, elem) {
|
|
1128
1141
|
elem.addEventListener("contextmenu", function (e) {
|
|
1129
1142
|
e.preventDefault();
|
|
1130
1143
|
e.stopPropagation();
|
|
1131
|
-
showProjectCtxMenu(elem, slug, name, null, null, { shareTargetSlug: true
|
|
1144
|
+
showProjectCtxMenu(elem, slug, name, null, null, { shareTargetSlug: true });
|
|
1132
1145
|
});
|
|
1133
|
-
})(wt.slug, wt.name, wtEl
|
|
1146
|
+
})(wt.slug, wt.name, wtEl);
|
|
1134
1147
|
|
|
1135
1148
|
if (!isWtActive && (wt.pendingPermissions > 0 || getPendingTuiAttention(wt.slug) > 0)) {
|
|
1136
1149
|
wtEl.classList.add("has-pending-perm");
|
|
@@ -1250,7 +1263,8 @@ function createMobileProjectItem(p, currentSlug, isWorktree) {
|
|
|
1250
1263
|
abbrev.textContent = p.icon;
|
|
1251
1264
|
parseEmojis(abbrev);
|
|
1252
1265
|
} else {
|
|
1253
|
-
abbrev.
|
|
1266
|
+
if (p.isWorktree) abbrev.innerHTML = iconHtml("git-branch");
|
|
1267
|
+
else abbrev.textContent = getProjectAbbrev(p.name);
|
|
1254
1268
|
}
|
|
1255
1269
|
el.appendChild(abbrev);
|
|
1256
1270
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { appendRemoveFromView } from './split-view-member-actions.js';
|
|
1
2
|
import { appendGithubWork } from './session-github.js';
|
|
2
3
|
// sidebar-sessions.js - Session list, search, presence, countdown, CLI picker
|
|
3
4
|
// Extracted from sidebar.js (PR-35)
|
|
@@ -290,6 +291,8 @@ function showSessionCtxMenu(anchorBtn, sessionId, title, cliSid, sessionData, po
|
|
|
290
291
|
menu.appendChild(renameItem);
|
|
291
292
|
|
|
292
293
|
|
|
294
|
+
appendRemoveFromView(menu, sessionData, currentAnchor);
|
|
295
|
+
|
|
293
296
|
// Session visibility toggle (only the session owner can change)
|
|
294
297
|
if (!isMateWorkspace(store.snap()) && store.get('isMultiUserMode') && sessionData && !sessionData.isMate && sessionData.ownerId && sessionData.ownerId === store.get('myUserId')) {
|
|
295
298
|
var currentVis = (sessionData && sessionData.sessionVisibility) || "private";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { iconHtml } from './icons.js';
|
|
2
|
+
import { livePaneBinding, samePaneBinding } from './pane-binding.js';
|
|
3
|
+
import { syncPaneHeaderState } from './split-pane-header-state.js';
|
|
4
|
+
import { showConfirm, hideConfirm } from './app-misc.js';
|
|
5
|
+
|
|
6
|
+
export function createPaneForceStop(paneEl, frame) {
|
|
7
|
+
var button = document.createElement('button');
|
|
8
|
+
button.type = 'button'; button.className = 'split-pane-force-stop';
|
|
9
|
+
button.setAttribute('aria-label', 'Force stop session'); button.title = 'Force stop session and all its Workers';
|
|
10
|
+
button.innerHTML = iconHtml('power');
|
|
11
|
+
button.addEventListener('click', function (event) {
|
|
12
|
+
event.stopPropagation();
|
|
13
|
+
if (!syncPaneHeaderState(paneEl) || button.disabled || !frame.contentWindow || !frame.clayPane || !livePaneBinding(frame)) return;
|
|
14
|
+
var binding = frame.clayPane.identity().binding, sourceDocument = frame.contentDocument, settled = false;
|
|
15
|
+
var title = paneEl.querySelector('.split-pane-title');
|
|
16
|
+
var text = 'Force stop all work in ' + (title && title.textContent || 'this session') + ' and all of its Workers? Queued messages will be paused. The pane and conversation history will stay open. Commands or external requests already sent may still be running.';
|
|
17
|
+
function current() {
|
|
18
|
+
return paneEl.isConnected && !paneEl.hidden && !paneEl.inert && frame.contentDocument === sourceDocument &&
|
|
19
|
+
syncPaneHeaderState(paneEl) && !button.disabled && livePaneBinding(frame, { paneBinding: binding }) && samePaneBinding(binding, frame.clayPane.identity().binding);
|
|
20
|
+
}
|
|
21
|
+
var modal = document.getElementById('confirm-modal'), panel = modal && modal.querySelector('.confirm-dialog');
|
|
22
|
+
var cancel = document.getElementById('confirm-cancel'), ok = document.getElementById('confirm-ok');
|
|
23
|
+
function cleanup() {
|
|
24
|
+
settled = true;
|
|
25
|
+
window.removeEventListener('keydown', onKey, true);
|
|
26
|
+
if (panel) { panel.removeAttribute('role'); panel.removeAttribute('aria-modal'); panel.removeAttribute('aria-labelledby'); }
|
|
27
|
+
if (current()) button.focus();
|
|
28
|
+
}
|
|
29
|
+
function onKey(event) {
|
|
30
|
+
if (event.key === 'Escape') { event.preventDefault(); event.stopImmediatePropagation(); hideConfirm(); return; }
|
|
31
|
+
if (event.key !== 'Tab') return;
|
|
32
|
+
event.preventDefault(); event.stopImmediatePropagation();
|
|
33
|
+
var controls = [cancel, ok].filter(Boolean), index = controls.indexOf(document.activeElement);
|
|
34
|
+
controls[(index + (event.shiftKey ? controls.length - 1 : 1)) % controls.length].focus();
|
|
35
|
+
}
|
|
36
|
+
showConfirm(text, function () {
|
|
37
|
+
if (settled) return;
|
|
38
|
+
var valid = current(); cleanup();
|
|
39
|
+
if (!valid) return;
|
|
40
|
+
frame.contentWindow.postMessage({ type: 'clay-pane-force-stop', paneBinding: binding }, window.location.origin);
|
|
41
|
+
}, 'Force stop all work', true, cleanup);
|
|
42
|
+
if (panel) { panel.setAttribute('role', 'dialog'); panel.setAttribute('aria-modal', 'true'); panel.setAttribute('aria-labelledby', 'confirm-text'); }
|
|
43
|
+
window.addEventListener('keydown', onKey, true);
|
|
44
|
+
if (cancel) cancel.focus();
|
|
45
|
+
});
|
|
46
|
+
return button;
|
|
47
|
+
}
|
|
@@ -20,7 +20,7 @@ export function syncPaneHeaderState(paneEl) {
|
|
|
20
20
|
header.setAttribute('aria-busy', ready ? 'false' : 'true');
|
|
21
21
|
header.dataset.paneLoading = ready ? 'false' : 'true';
|
|
22
22
|
header.inert = !ready;
|
|
23
|
-
var actions = header.querySelectorAll('.split-pane-info, .split-pane-search, .split-pane-context, .split-pane-sidebar, .split-view-arrange-trigger');
|
|
23
|
+
var actions = header.querySelectorAll('.split-pane-info, .split-pane-force-stop, .split-pane-search, .split-pane-context, .split-pane-sidebar, .split-view-arrange-trigger');
|
|
24
24
|
for (var i = 0; i < actions.length; i++) {
|
|
25
25
|
var action = actions[i];
|
|
26
26
|
if (!ready) {
|
|
@@ -31,6 +31,16 @@ export function syncPaneHeaderState(paneEl) {
|
|
|
31
31
|
delete action.dataset.panePriorDisabled;
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
+
var force = header.querySelector('.split-pane-force-stop');
|
|
35
|
+
if (force) {
|
|
36
|
+
var session = getCachedSessions().find(function (item) { return item.id === Number(paneEl.dataset.sessionId); });
|
|
37
|
+
var support = session && session.forceTermination;
|
|
38
|
+
var reason = support && support.reason || 'Force termination is not yet supported for this vendor';
|
|
39
|
+
force.disabled = !ready || !support || support.supported !== true;
|
|
40
|
+
force.title = support && support.supported === true ? 'Force stop session and all its Workers' : reason;
|
|
41
|
+
force.setAttribute('aria-label', 'Force stop session' + (support && support.supported === true ? '' : ': ' + reason));
|
|
42
|
+
force.setAttribute('aria-disabled', force.disabled ? 'true' : 'false');
|
|
43
|
+
}
|
|
34
44
|
var close = paneEl.querySelector('.split-pane-close');
|
|
35
45
|
if (close) {
|
|
36
46
|
var host = paneEl.parentNode;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createPaneForceStop } from './split-pane-force-stop.js';
|
|
1
2
|
import { renderPaneRuntime } from './split-pane-runtime.js';
|
|
2
3
|
import { store } from './store.js';
|
|
3
4
|
import { getWs } from './ws-ref.js';
|
|
@@ -201,7 +202,6 @@ function createPaneSearchPopover(paneEl, frame, searchButton) {
|
|
|
201
202
|
throw error;
|
|
202
203
|
}
|
|
203
204
|
}
|
|
204
|
-
|
|
205
205
|
export function handlePaneSearchMessage(event, host) {
|
|
206
206
|
var frames = host ? host.querySelectorAll(".split-pane-frame") : [];
|
|
207
207
|
for (var i = 0; i < frames.length; i++) {
|
|
@@ -287,7 +287,6 @@ function isConfiguredWorker(groups, sessionId) {
|
|
|
287
287
|
export function updatePaneFullAccessButton(button, session) {
|
|
288
288
|
if (button) renderPermissionControl(button, permissionState(session));
|
|
289
289
|
}
|
|
290
|
-
|
|
291
290
|
export function createSplitPane(pane, closePane) {
|
|
292
291
|
var paneEl = document.createElement("section"); paneEl.className = "split-pane";
|
|
293
292
|
var header = document.createElement("header"); header.className = "split-pane-header"; header.title = pane.title;
|
|
@@ -345,6 +344,7 @@ export function createSplitPane(pane, closePane) {
|
|
|
345
344
|
}); header.appendChild(sidebarButton);
|
|
346
345
|
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");
|
|
347
346
|
header.insertBefore(search, close);
|
|
347
|
+
header.insertBefore(createPaneForceStop(paneEl, frame), close);
|
|
348
348
|
function refreshReadyHeader() {
|
|
349
349
|
if (paneEl.classList.contains('split-pane-cached')) return;
|
|
350
350
|
var id = Number(frame.dataset.requestedSessionId || frame.dataset.sessionId), current = sessionById(id);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { iconHtml } from './icons.js';
|
|
2
|
+
import { appendLayoutOption } from './split-view-layout-icons.js';
|
|
3
|
+
import { SPLIT_SIDES } from './split-view-picker-render.js';
|
|
4
|
+
|
|
5
|
+
export function ensureArrangeMenu(closePresentationArrange) {
|
|
6
|
+
var menu = document.getElementById("split-view-arrange-menu");
|
|
7
|
+
if (menu) return menu;
|
|
8
|
+
menu = document.createElement("div"); menu.id = "split-view-arrange-menu"; menu.className = "split-view-arrange";
|
|
9
|
+
menu.setAttribute("role", "dialog"); menu.setAttribute("aria-label", "Arrange Split view"); menu.hidden = true;
|
|
10
|
+
var header = document.createElement("div"); header.className = "split-view-arrange-header";
|
|
11
|
+
var title = document.createElement("strong"); title.textContent = "Arrange Split view"; header.appendChild(title);
|
|
12
|
+
var close = document.createElement("button"); close.type = "button"; close.setAttribute("aria-label", "Close arrangement menu"); close.innerHTML = iconHtml("x"); header.appendChild(close);
|
|
13
|
+
close.addEventListener("click", function () { closePresentationArrange(); }); menu.appendChild(header);
|
|
14
|
+
var presets = [["balanced", "Balanced"], ["columns", "Columns"], ["rows", "Rows"], ["grid", "Grid"], ["main-left", "Main left"], ["main-right", "Main right"]];
|
|
15
|
+
var presetGrid = document.createElement("div"); presetGrid.className = "split-view-arrange-grid";
|
|
16
|
+
presets.forEach(function (item) { var button = document.createElement("button"); button.type = "button"; button.dataset.preset = item[0]; appendLayoutOption(button, item[1], "preset", item[0]); presetGrid.appendChild(button); });
|
|
17
|
+
menu.appendChild(presetGrid);
|
|
18
|
+
var positions = [["left", "Left half"], ["right", "Right half"], ["top", "Top half"], ["bottom", "Bottom half"], ["top-left", "Top left"], ["top-right", "Top right"], ["bottom-left", "Bottom left"], ["bottom-right", "Bottom right"], ["left-third", "Left third"], ["right-third", "Right third"]];
|
|
19
|
+
var positionGrid = document.createElement("div"); positionGrid.className = "split-view-arrange-grid split-view-arrange-positions";
|
|
20
|
+
positions.forEach(function (item) { var button = document.createElement("button"); button.type = "button"; button.dataset.position = item[0]; appendLayoutOption(button, item[1], "position", item[0]); positionGrid.appendChild(button); });
|
|
21
|
+
menu.appendChild(positionGrid);
|
|
22
|
+
var additions = document.createElement('div'); additions.className = 'split-session-directions';
|
|
23
|
+
SPLIT_SIDES.forEach(function (side) {
|
|
24
|
+
var button = document.createElement('button'); button.type = 'button'; button.dataset.arrangeAddSide = side[0];
|
|
25
|
+
button.innerHTML = iconHtml(side[2]); var label = document.createElement('span'); label.textContent = 'Add ' + side[1] + ' Split'; button.appendChild(label); additions.appendChild(button);
|
|
26
|
+
});
|
|
27
|
+
menu.appendChild(additions);
|
|
28
|
+
var focusButton = document.createElement("button"); focusButton.type = "button"; focusButton.dataset.focusLayout = "true"; menu.appendChild(focusButton);
|
|
29
|
+
var membership = document.createElement("button"); membership.type = "button"; membership.dataset.arrangeMembership = "true";
|
|
30
|
+
membership.textContent = "Choose sessions"; menu.appendChild(membership);
|
|
31
|
+
var context = document.createElement("div"); context.className = "split-view-arrange-context";
|
|
32
|
+
var contextButton = document.createElement("button"); contextButton.type = "button"; contextButton.dataset.arrangeContext = "true";
|
|
33
|
+
contextButton.setAttribute("aria-expanded", "false"); contextButton.setAttribute("aria-controls", "split-view-arrange-context-list");
|
|
34
|
+
var contextTitle = document.createElement("span"); contextTitle.dataset.arrangeContextTitle = "true"; contextButton.appendChild(contextTitle); context.appendChild(contextButton);
|
|
35
|
+
var contextList = document.createElement("div"); contextList.id = "split-view-arrange-context-list"; contextList.hidden = true; contextList.setAttribute("role", "listbox");
|
|
36
|
+
context.appendChild(contextList); menu.insertBefore(context, presetGrid);
|
|
37
|
+
document.body.appendChild(menu); return menu;
|
|
38
|
+
}
|
|
@@ -29,8 +29,8 @@ function position() {
|
|
|
29
29
|
menu.style.left = Math.max(8, Math.min(rect.right - width, innerWidth - width - 8)) + 'px';
|
|
30
30
|
menu.style.top = Math.max(8, Math.min(rect.bottom + 5, innerHeight - menu.offsetHeight - 8)) + 'px';
|
|
31
31
|
}
|
|
32
|
-
function open(anchor) {
|
|
33
|
-
var receipt = pickerReceipt(anchor);
|
|
32
|
+
function open(anchor, side, origin) {
|
|
33
|
+
var receipt = pickerReceipt(anchor, origin);
|
|
34
34
|
if (!receipt || !pickerCurrent(receipt)) { showToast('Wait for this View to finish loading, then try again.', 'warn'); return; }
|
|
35
35
|
var menu = menuElement();
|
|
36
36
|
if (menu && !menu.hidden && menu.clayCreateAnchor === anchor) { close(true); return; }
|
|
@@ -41,8 +41,12 @@ function open(anchor) {
|
|
|
41
41
|
}
|
|
42
42
|
receipt.reservationId = 'split-picker-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2);
|
|
43
43
|
menu.clayCreateAnchor = anchor; anchor.setAttribute('aria-expanded', 'true');
|
|
44
|
-
store.set({ splitSessionPicker: { receipt: receipt, side: null, query: '', error: '', priorFocus: store.get('projectSplitViewFocusedOrigin') } });
|
|
45
|
-
|
|
44
|
+
store.set({ splitSessionPicker: { receipt: receipt, side: side || null, query: '', error: '', priorFocus: store.get('projectSplitViewFocusedOrigin') } });
|
|
45
|
+
if (side) store.set({ projectSplitViewFocusedOrigin: null });
|
|
46
|
+
renderPicker(menu); menu.hidden = false;
|
|
47
|
+
if (side) document.dispatchEvent(new CustomEvent('clay-split-picker-layout'));
|
|
48
|
+
position(); menu.querySelector(side ? 'input' : '[data-split-side]').focus();
|
|
49
|
+
if (side) retryOptions();
|
|
46
50
|
}
|
|
47
51
|
function onClick(event) {
|
|
48
52
|
var menu = menuElement(), state = store.get('splitSessionPicker'); if (!menu || menu.hidden || !state) return;
|
|
@@ -76,7 +80,7 @@ function onKey(event) {
|
|
|
76
80
|
items[next].focus();
|
|
77
81
|
}
|
|
78
82
|
export function initSplitViewCreateMenu() {
|
|
79
|
-
document.addEventListener('clay-split-view-open-create', function (event) { if (event.detail && event.detail.anchor) open(event.detail.anchor); });
|
|
83
|
+
document.addEventListener('clay-split-view-open-create', function (event) { if (event.detail && event.detail.anchor) open(event.detail.anchor, event.detail.side, event.detail.origin); });
|
|
80
84
|
document.addEventListener('click', onClick, true); document.addEventListener('keydown', onKey, true);
|
|
81
85
|
document.addEventListener('input', function (event) {
|
|
82
86
|
var state = store.get('splitSessionPicker'); if (!state || !event.target.matches('#split-view-create-menu input')) return;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { getWs } from './ws-ref.js';
|
|
3
|
+
import { mutation } from './view-mutations.js';
|
|
4
|
+
import { remove, validateTree } from './split-view-layout-client.js';
|
|
5
|
+
import { showToast } from './utils.js';
|
|
6
|
+
import { iconHtml } from './icons.js';
|
|
7
|
+
import { closeSessionContextMenu } from './session-context-menu.js';
|
|
8
|
+
|
|
9
|
+
export function removeViewSession(view, origin) {
|
|
10
|
+
var live = (store.get('projectSplitViews') || []).find(function (item) { return item.id === view.id && item.revision === view.revision; });
|
|
11
|
+
if (!live || !live.sessions.some(function (session) { return session.sessionOriginId === origin; })) return false;
|
|
12
|
+
var active = live.id === store.get('projectSplitViewActiveId');
|
|
13
|
+
if (active) {
|
|
14
|
+
try {
|
|
15
|
+
document.querySelectorAll('#split-host .split-pane-frame').forEach(function (frame) { if (frame.clayPane) frame.clayPane.checkDraft(); });
|
|
16
|
+
} catch (error) { showToast(error.message, 'warn'); return false; }
|
|
17
|
+
}
|
|
18
|
+
var origins = live.sessions.map(function (session) { return session.sessionOriginId; }).filter(function (item) { return item !== origin; });
|
|
19
|
+
var tree = remove(live.tree, origin);
|
|
20
|
+
if (!validateTree(tree, origins).ok) return false;
|
|
21
|
+
var nextActive = live.activeSessionOriginId === origin ? origins[0] || null : live.activeSessionOriginId;
|
|
22
|
+
return mutation('project_split_view_update', { id: live.id, sessionOrigins: origins, tree: tree,
|
|
23
|
+
layout: live.layout, activeSessionOriginId: nextActive, active: active }, active ? { closedOrigin: origin, activeOrigin: nextActive } : null);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function appendRemoveFromView(menu, session, currentAnchor) {
|
|
27
|
+
if (!session || session.sessionRole === 'worker') return;
|
|
28
|
+
var view = (store.get('projectSplitViews') || []).find(function (item) {
|
|
29
|
+
return item.sessions.length > 1 && item.sessions.some(function (ref) { return ref.sessionOriginId === session.sessionOriginId; });
|
|
30
|
+
});
|
|
31
|
+
if (!view) return;
|
|
32
|
+
var socket = getWs(), slug = store.get('currentSlug'), viewer = store.get('myUserId'), generation = store.get('projectSplitViewSocketGeneration');
|
|
33
|
+
var button = document.createElement('button'); button.className = 'session-ctx-item';
|
|
34
|
+
button.innerHTML = iconHtml('panel-left-close') + ' <span>Remove from View</span>';
|
|
35
|
+
button.disabled = !!store.get('projectSplitViewPending');
|
|
36
|
+
button.addEventListener('click', function (event) {
|
|
37
|
+
event.stopPropagation(); var anchor = currentAnchor(); closeSessionContextMenu(false);
|
|
38
|
+
if (!anchor || socket !== getWs() || slug !== store.get('currentSlug') || viewer !== store.get('myUserId') || generation !== store.get('projectSplitViewSocketGeneration')) return;
|
|
39
|
+
removeViewSession(view, session.sessionOriginId);
|
|
40
|
+
});
|
|
41
|
+
menu.appendChild(button);
|
|
42
|
+
}
|
|
@@ -11,9 +11,9 @@ import { checkComposerDeparture } from './session-rich-drafts.js';
|
|
|
11
11
|
import { createOptions } from './session-create.js';
|
|
12
12
|
import { showToast } from './utils.js';
|
|
13
13
|
|
|
14
|
-
export function pickerReceipt(anchor) {
|
|
14
|
+
export function pickerReceipt(anchor, targetOrigin) {
|
|
15
15
|
var view = activeSavedView(), pane = anchor && anchor.closest('.split-pane'), frame = pane && pane.querySelector('.split-pane-frame');
|
|
16
|
-
var origin = pane && pane.dataset.presentationOrigin || frame && frame.dataset.sessionOriginId || store.get('sessionOriginId');
|
|
16
|
+
var origin = targetOrigin || pane && pane.dataset.presentationOrigin || frame && frame.dataset.sessionOriginId || store.get('sessionOriginId');
|
|
17
17
|
return view && view.sessions.some(function (session) { return session.sessionOriginId === origin; }) ? { scope: layoutScope(view), origin: origin } : null;
|
|
18
18
|
}
|
|
19
19
|
export function pickerCurrent(receipt) {
|
|
@@ -16,7 +16,8 @@ import { createSplitPane, updateSplitPaneMetadata, updateSplitPaneCloseVisibilit
|
|
|
16
16
|
import { syncPaneHeaderState } from './split-pane-header-state.js';
|
|
17
17
|
import { rectangles, resize, validateTree, listIds } from './split-view-layout-client.js';
|
|
18
18
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
19
|
-
import {
|
|
19
|
+
import { ensureArrangeMenu as buildArrangeMenu } from './split-view-arrange-menu.js';
|
|
20
|
+
import { pickerCapacity } from './split-view-picker-actions.js';
|
|
20
21
|
import { reserveEmptySplit, clearEmptySplit } from './split-session-empty-pane.js';
|
|
21
22
|
|
|
22
23
|
var host = null;
|
|
@@ -51,7 +52,7 @@ function dispatch(name, detail) {
|
|
|
51
52
|
export function initPresentationRenderer(hostElement) {
|
|
52
53
|
host = hostElement;
|
|
53
54
|
app = document.getElementById("app");
|
|
54
|
-
|
|
55
|
+
buildArrangeMenu(closePresentationArrange);
|
|
55
56
|
document.addEventListener('clay-split-picker-layout', renderCurrentGeometry);
|
|
56
57
|
document.addEventListener("click", onClick, true);
|
|
57
58
|
document.addEventListener("keydown", onKeyDown, true);
|
|
@@ -60,7 +61,7 @@ export function initPresentationRenderer(hostElement) {
|
|
|
60
61
|
document.addEventListener("pointercancel", onPointerCancel, true);
|
|
61
62
|
document.addEventListener("lostpointercapture", onPointerCancel, true);
|
|
62
63
|
window.addEventListener("blur", onWindowBlur);
|
|
63
|
-
window.addEventListener("resize", function () { renderCurrentGeometry(); });
|
|
64
|
+
window.addEventListener("resize", function () { closePresentationArrange(false); renderCurrentGeometry(); });
|
|
64
65
|
window.addEventListener("scroll", closePresentationArrange, true);
|
|
65
66
|
if (typeof ResizeObserver === "function") {
|
|
66
67
|
resizeObserver = new ResizeObserver(function () { renderCurrentGeometry(); });
|
|
@@ -136,7 +137,7 @@ export function renderPresentationView(view, tree, sessions) {
|
|
|
136
137
|
if (app) app.classList.add("split-native-hidden");
|
|
137
138
|
reconcileSplitPanes(host, panes, function (pane) { return createSplitPane(pane, function (sessionId) { dispatch("clay-split-view-close-pane", { sessionId: sessionId }); }); }, updateSplitPaneMetadata, paneRetention(host));
|
|
138
139
|
Array.from(host.querySelectorAll(':scope > .split-pane .split-pane-header')).forEach(function (header) { ensureArrangeTrigger(header); });
|
|
139
|
-
|
|
140
|
+
buildArrangeMenu(closePresentationArrange);
|
|
140
141
|
Array.from(host.querySelectorAll(':scope > .split-pane')).forEach(function (node) {
|
|
141
142
|
var target = panes.find(function (pane) { return node.dataset.splitPaneKey === pane.slug + '#' + pane.sessionId; });
|
|
142
143
|
var session = target && sessions.find(function (item) { return item.id === target.sessionId; });
|
|
@@ -278,34 +279,6 @@ export function ensureArrangeTrigger(header) {
|
|
|
278
279
|
if (pane) syncPaneHeaderState(pane);
|
|
279
280
|
}
|
|
280
281
|
|
|
281
|
-
function ensureArrangeMenu() {
|
|
282
|
-
var menu = document.getElementById("split-view-arrange-menu");
|
|
283
|
-
if (menu) return menu;
|
|
284
|
-
menu = document.createElement("div"); menu.id = "split-view-arrange-menu"; menu.className = "split-view-arrange";
|
|
285
|
-
menu.setAttribute("role", "dialog"); menu.setAttribute("aria-label", "Arrange Split view"); menu.hidden = true;
|
|
286
|
-
var header = document.createElement("div"); header.className = "split-view-arrange-header";
|
|
287
|
-
var title = document.createElement("strong"); title.textContent = "Arrange Split view"; header.appendChild(title);
|
|
288
|
-
var close = document.createElement("button"); close.type = "button"; close.setAttribute("aria-label", "Close arrangement menu"); close.innerHTML = iconHtml("x"); header.appendChild(close);
|
|
289
|
-
close.addEventListener("click", function () { closePresentationArrange(); }); menu.appendChild(header);
|
|
290
|
-
var presets = [["balanced", "Balanced"], ["columns", "Columns"], ["rows", "Rows"], ["grid", "Grid"], ["main-left", "Main left"], ["main-right", "Main right"]];
|
|
291
|
-
var presetGrid = document.createElement("div"); presetGrid.className = "split-view-arrange-grid";
|
|
292
|
-
presets.forEach(function (item) { var button = document.createElement("button"); button.type = "button"; button.dataset.preset = item[0]; appendLayoutOption(button, item[1], "preset", item[0]); presetGrid.appendChild(button); });
|
|
293
|
-
menu.appendChild(presetGrid);
|
|
294
|
-
var positions = [["left", "Left half"], ["right", "Right half"], ["top", "Top half"], ["bottom", "Bottom half"], ["top-left", "Top left"], ["top-right", "Top right"], ["bottom-left", "Bottom left"], ["bottom-right", "Bottom right"], ["left-third", "Left third"], ["right-third", "Right third"]];
|
|
295
|
-
var positionGrid = document.createElement("div"); positionGrid.className = "split-view-arrange-grid split-view-arrange-positions";
|
|
296
|
-
positions.forEach(function (item) { var button = document.createElement("button"); button.type = "button"; button.dataset.position = item[0]; appendLayoutOption(button, item[1], "position", item[0]); positionGrid.appendChild(button); });
|
|
297
|
-
menu.appendChild(positionGrid);
|
|
298
|
-
var focusButton = document.createElement("button"); focusButton.type = "button"; focusButton.dataset.focusLayout = "true"; menu.appendChild(focusButton);
|
|
299
|
-
var membership = document.createElement("button"); membership.type = "button"; membership.dataset.arrangeMembership = "true";
|
|
300
|
-
membership.textContent = "Choose sessions"; menu.appendChild(membership);
|
|
301
|
-
var context = document.createElement("div"); context.className = "split-view-arrange-context";
|
|
302
|
-
var contextButton = document.createElement("button"); contextButton.type = "button"; contextButton.dataset.arrangeContext = "true";
|
|
303
|
-
contextButton.setAttribute("aria-expanded", "false"); contextButton.setAttribute("aria-controls", "split-view-arrange-context-list");
|
|
304
|
-
var contextTitle = document.createElement("span"); contextTitle.dataset.arrangeContextTitle = "true"; contextButton.appendChild(contextTitle); context.appendChild(contextButton);
|
|
305
|
-
var contextList = document.createElement("div"); contextList.id = "split-view-arrange-context-list"; contextList.hidden = true; contextList.setAttribute("role", "listbox");
|
|
306
|
-
context.appendChild(contextList); menu.insertBefore(context, presetGrid);
|
|
307
|
-
document.body.appendChild(menu); return menu;
|
|
308
|
-
}
|
|
309
282
|
|
|
310
283
|
function openArrangeMenu(trigger) {
|
|
311
284
|
if (!syncPaneHeaderState(trigger.closest('.split-pane'))) return;
|
|
@@ -313,7 +286,7 @@ function openArrangeMenu(trigger) {
|
|
|
313
286
|
dispatch("clay-split-view-open-create", { anchor: trigger });
|
|
314
287
|
return;
|
|
315
288
|
}
|
|
316
|
-
var menu =
|
|
289
|
+
var menu = buildArrangeMenu(closePresentationArrange);
|
|
317
290
|
if (!menu.hidden && menuTrigger === trigger) { closePresentationArrange(); return; }
|
|
318
291
|
closePresentationArrange(false); menuTrigger = trigger; menu.hidden = false;
|
|
319
292
|
trigger.setAttribute("aria-expanded", "true");
|
|
@@ -340,6 +313,7 @@ function openArrangeMenu(trigger) {
|
|
|
340
313
|
Array.from(menu.querySelectorAll('[data-position*="-"]:not([data-position$="-third"])')).forEach(function (button) {
|
|
341
314
|
button.disabled = count < 3; button.title = count < 3 ? "Quarter layouts require at least three sessions" : "";
|
|
342
315
|
});
|
|
316
|
+
menu.querySelectorAll('[data-arrange-add-side]').forEach(function (button) { button.disabled = !pickerCapacity() || !!store.get('projectSplitViewPending'); });
|
|
343
317
|
var rect = trigger.getBoundingClientRect(), width = Math.min(280, window.innerWidth - 16);
|
|
344
318
|
menu.style.left = Math.max(8, Math.min(rect.left, window.innerWidth - width - 8)) + "px";
|
|
345
319
|
menu.style.top = Math.max(8, Math.min(rect.bottom + 4, window.innerHeight - menu.offsetHeight - 8)) + "px";
|
|
@@ -362,10 +336,11 @@ function onClick(event) {
|
|
|
362
336
|
if (trigger) { event.preventDefault(); event.stopImmediatePropagation(); openArrangeMenu(trigger); return; }
|
|
363
337
|
var menu = event.target.closest('#split-view-arrange-menu');
|
|
364
338
|
if (menu) {
|
|
365
|
-
|
|
339
|
+
var addSide = event.target.closest('[data-arrange-add-side]');
|
|
340
|
+
if (addSide || event.target.closest('[data-arrange-membership]')) {
|
|
366
341
|
event.preventDefault(); event.stopImmediatePropagation();
|
|
367
342
|
var anchor = menuTrigger; closePresentationArrange(false);
|
|
368
|
-
if (anchor && anchor.isConnected) dispatch("clay-split-view-open-create", { anchor: anchor });
|
|
343
|
+
if (anchor && anchor.isConnected) dispatch("clay-split-view-open-create", { anchor: anchor, side: addSide && addSide.dataset.arrangeAddSide, origin: menu.dataset.sourceOrigin });
|
|
369
344
|
return;
|
|
370
345
|
}
|
|
371
346
|
var contextButton = event.target.closest('[data-arrange-context]');
|
|
@@ -19,10 +19,11 @@ import { initPresentationDrag, restorePresentationDrag } from './split-view-pres
|
|
|
19
19
|
import { detachTuiView } from './session-tui-view.js';
|
|
20
20
|
import { checkComposerDeparture } from './session-rich-drafts.js';
|
|
21
21
|
import { showToast } from './utils.js';
|
|
22
|
-
import { preset, listIds, validateTree,
|
|
22
|
+
import { preset, listIds, validateTree, arrangeRegion } from './split-view-layout-client.js';
|
|
23
23
|
import { viewIdForOrigin, updateActiveSession, exactDriverForOrigin, restorablePresentation } from './split-view-presentation-lookup.js';
|
|
24
24
|
import { groupedSessionIds } from './split-group-helpers.js';
|
|
25
25
|
import { handleSplitViewCreateEvent } from './split-view-create-action.js';
|
|
26
|
+
import { removeViewSession } from './split-view-member-actions.js';
|
|
26
27
|
import { initSplitViewCreateMenu } from './split-view-create-menu.js';
|
|
27
28
|
import { currentSingletonPresentation } from './split-view-singleton.js';
|
|
28
29
|
var host = null;
|
|
@@ -333,6 +334,12 @@ function toggleGroupMenu(group, anchor, point) {
|
|
|
333
334
|
}
|
|
334
335
|
function runGroupAction(view, action, root) {
|
|
335
336
|
closeGroupMenus(false);
|
|
337
|
+
if (action === 'add-pane') {
|
|
338
|
+
if (!setActiveView(view.id)) return;
|
|
339
|
+
var anchor = host && host.querySelector('.split-pane:not(.split-pane-cached) [data-action="arrange"]');
|
|
340
|
+
if (anchor) document.dispatchEvent(new CustomEvent('clay-split-view-open-create', { detail: { anchor: anchor, side: 'right', origin: view.activeSessionOriginId || view.sessions[0].sessionOriginId } }));
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
336
343
|
if (action === 'favorite' || action === 'folder') { organizeViewAction(view, action); return; }
|
|
337
344
|
if (action === "rename") { startRename(root, view); return; }
|
|
338
345
|
if (action === "separate") {
|
|
@@ -418,13 +425,7 @@ function onClosePane(event) {
|
|
|
418
425
|
if (!view || !event.detail || !validPresentationClose(event.detail)) return;
|
|
419
426
|
var session = (getCachedSessions() || []).find(function (item) { return item.id === Number(event.detail.sessionId); });
|
|
420
427
|
if (!session || !view.sessions.some(function (item) { return item.sessionOriginId === session.sessionOriginId; })) return;
|
|
421
|
-
|
|
422
|
-
var origins = view.sessions.map(function (item) { return item.sessionOriginId; }).filter(function (origin) { return origin !== session.sessionOriginId; });
|
|
423
|
-
var nextTree = remove(view.tree, session.sessionOriginId);
|
|
424
|
-
if (!validateTree(nextTree, origins).ok) return;
|
|
425
|
-
var nextActive = view.activeSessionOriginId === session.sessionOriginId ? origins[0] || null : view.activeSessionOriginId;
|
|
426
|
-
mutation("project_split_view_update", { id: view.id, sessionOrigins: origins, tree: nextTree,
|
|
427
|
-
layout: view.layout, activeSessionOriginId: nextActive }, { closedOrigin: session.sessionOriginId, activeOrigin: nextActive });
|
|
428
|
+
removeViewSession(view, session.sessionOriginId);
|
|
428
429
|
}
|
|
429
430
|
function onFocus(event) {
|
|
430
431
|
var view = viewExists(store.get('projectSplitViews'), store.get('projectSplitViewActiveId'));
|
|
@@ -25,8 +25,9 @@ export function openSplitViewMenu(group, anchor, point, onAction) {
|
|
|
25
25
|
}
|
|
26
26
|
closeFolderMenu(false); closeSessionContextMenu(false);
|
|
27
27
|
var menu = document.createElement('div'); menu.className = 'session-ctx-menu split-view-menu';
|
|
28
|
-
[['favorite', view.favorite ? 'Remove from Favorites' : 'Add to Favorites', 'star'], ['folder', 'Move to folder', 'folder-input'], ['rename', 'Rename', 'pencil'], ['separate', 'Separate', 'columns-2']].forEach(function (entry) {
|
|
28
|
+
[['add-pane', 'Add pane', 'panel-right-open'], ['favorite', view.favorite ? 'Remove from Favorites' : 'Add to Favorites', 'star'], ['folder', 'Move to folder', 'folder-input'], ['rename', 'Rename', 'pencil'], ['separate', 'Separate', 'columns-2']].forEach(function (entry) {
|
|
29
29
|
var button = document.createElement('button'); button.className = 'session-ctx-item'; button.dataset.splitViewAction = entry[0];
|
|
30
|
+
if (entry[0] === 'add-pane') button.disabled = !view.sessions.length || view.sessions.length + (view.workspace && view.workspace.linkedPair ? view.workspace.linkedPair.workerOrigins.length : 0) >= 8 || !!store.get('projectSplitViewPending');
|
|
30
31
|
button.innerHTML = iconHtml(entry[2]) + ' <span>' + entry[1] + '</span>';
|
|
31
32
|
button.addEventListener('click', function (event) {
|
|
32
33
|
event.stopPropagation(); var root = currentRoot(); closeSessionContextMenu(false);
|