clay-server 5.2.1-beta.2 → 5.3.0-beta.2
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-control-presentation-binding.js +5 -3
- package/lib/clay-product-control-tools.js +3 -3
- package/lib/clay-product-control.js +4 -2
- package/lib/clay-screen-controls.js +18 -3
- package/lib/clay-split-view-awareness.js +35 -0
- package/lib/clay-split-view-controls.js +122 -0
- package/lib/clay-split-view-input.js +20 -0
- package/lib/clay-split-view-render-proof.js +22 -0
- package/lib/pane-asset-manifest.js +63 -0
- package/lib/project-connection.js +19 -10
- package/lib/project-session-actions.js +1 -0
- package/lib/project-session-delete.js +2 -2
- package/lib/project-session-folders.js +1 -0
- package/lib/project-sessions.js +6 -1
- package/lib/project-split-view-document.js +33 -0
- package/lib/project-split-view-names.js +67 -0
- package/lib/project-split-view-organization.js +23 -0
- package/lib/project-split-views.js +479 -0
- package/lib/project-worker-conversation.js +222 -0
- package/lib/project-worker-creation-proposal.js +3 -4
- package/lib/project-worker-proposal.js +1 -1
- package/lib/project-worker-removal.js +101 -0
- package/lib/project.js +28 -1
- package/lib/public/app.js +32 -13
- package/lib/public/css/base.css +9 -0
- package/lib/public/css/filebrowser.css +0 -1
- package/lib/public/css/pane-header-controls.css +100 -0
- package/lib/public/css/pane.css +24 -41
- package/lib/public/css/popup-chat.css +123 -0
- package/lib/public/css/right-workbench.css +50 -18
- package/lib/public/css/session-folders.css +1 -1
- package/lib/public/css/split-view-layout.css +463 -0
- package/lib/public/css/split-view-worker-activity.css +97 -0
- package/lib/public/css/sticky-notes.css +13 -0
- package/lib/public/index.html +3 -3
- package/lib/public/modules/app-connection.js +4 -1
- package/lib/public/modules/app-favicon.js +1 -1
- package/lib/public/modules/app-messages.js +31 -9
- package/lib/public/modules/chat-render-runtime.js +54 -0
- package/lib/public/modules/clay-control-copy.js +3 -0
- package/lib/public/modules/clay-navigation-render.js +1 -1
- package/lib/public/modules/clay-owner-navigation.js +15 -5
- package/lib/public/modules/clay-split-view-screen.js +74 -0
- package/lib/public/modules/clay-tool-activity.js +5 -0
- package/lib/public/modules/command-palette.js +30 -28
- package/lib/public/modules/contextual-clay-session.js +26 -2
- package/lib/public/modules/contextual-clay.js +5 -2
- package/lib/public/modules/contextual-mate-sessions.js +2 -0
- package/lib/public/modules/filebrowser.js +6 -2
- package/lib/public/modules/generation-stop.js +7 -1
- package/lib/public/modules/knowledge-workbench-entry.js +43 -0
- package/lib/public/modules/markdown-view-mode.js +34 -0
- package/lib/public/modules/markdown.js +11 -0
- package/lib/public/modules/mate-knowledge-workbench.js +2 -7
- package/lib/public/modules/mate-sidebar.js +1 -1
- package/lib/public/modules/palette-clay-conversation.js +87 -0
- package/lib/public/modules/palette-render-helpers.js +25 -0
- package/lib/public/modules/pane-activation.js +46 -7
- package/lib/public/modules/pane-binding.js +12 -2
- package/lib/public/modules/pane-bridge.js +12 -3
- package/lib/public/modules/pane-deferred-bootstrap.js +32 -0
- package/lib/public/modules/pane-feature-assets.js +35 -0
- package/lib/public/modules/pane-file-bridge.js +8 -3
- package/lib/public/modules/pane-search-receipt.js +12 -0
- package/lib/public/modules/permission-control.js +6 -6
- package/lib/public/modules/popup-chat-rendering.js +244 -0
- package/lib/public/modules/project-activation.js +2 -2
- package/lib/public/modules/qrcode.js +12 -1
- package/lib/public/modules/right-workbench.js +68 -0
- package/lib/public/modules/search-hints.js +79 -0
- package/lib/public/modules/session-context-menu.js +35 -2
- package/lib/public/modules/session-folder-collapse.js +2 -1
- package/lib/public/modules/session-folder-context.js +24 -13
- package/lib/public/modules/session-folder-delete.js +18 -1
- package/lib/public/modules/session-folder-dnd.js +4 -1
- package/lib/public/modules/session-folder-layout.js +7 -6
- package/lib/public/modules/session-search-pane-state.js +30 -0
- package/lib/public/modules/session-search.js +53 -13
- package/lib/public/modules/sidebar-mobile.js +3 -3
- package/lib/public/modules/sidebar-projects.js +1 -0
- package/lib/public/modules/sidebar-session-folders-ui.js +3 -3
- package/lib/public/modules/sidebar-sessions.js +57 -24
- package/lib/public/modules/sidebar.js +20 -3
- package/lib/public/modules/split-group-helpers.js +1 -1
- package/lib/public/modules/split-group-restore.js +8 -11
- package/lib/public/modules/split-pair-ui.js +18 -0
- package/lib/public/modules/split-pane-header-state.js +45 -0
- package/lib/public/modules/split-pane-reconciler.js +23 -4
- package/lib/public/modules/split-pane-renderer.js +330 -23
- package/lib/public/modules/split-pane-retention.js +157 -0
- package/lib/public/modules/split-session-boundary.js +21 -0
- package/lib/public/modules/split-view-create-action.js +15 -0
- package/lib/public/modules/split-view-create-menu.js +83 -0
- package/lib/public/modules/split-view-create-request.js +23 -0
- package/lib/public/modules/split-view-layout-client.js +198 -0
- package/lib/public/modules/split-view-layout-icons.js +37 -0
- package/lib/public/modules/split-view-layout-scope.js +38 -0
- package/lib/public/modules/split-view-organization.js +73 -0
- package/lib/public/modules/split-view-presentation-close.js +35 -0
- package/lib/public/modules/split-view-presentation-drag.js +278 -0
- package/lib/public/modules/split-view-presentation-drop.js +57 -0
- package/lib/public/modules/split-view-presentation-lookup.js +44 -0
- package/lib/public/modules/split-view-presentation-renderer.js +494 -0
- package/lib/public/modules/split-view-presentations.js +498 -0
- package/lib/public/modules/split-view-shell-controls.js +28 -0
- package/lib/public/modules/split-view-sidebar-menu.js +42 -0
- package/lib/public/modules/split-view-sidebar-renderer.js +30 -0
- package/lib/public/modules/split-view-singleton.js +21 -0
- package/lib/public/modules/split-view-worker-activity.js +94 -0
- package/lib/public/modules/split-view-worker-controls.js +30 -0
- package/lib/public/modules/split-view-worker-removal.js +137 -0
- package/lib/public/modules/split-view-worker-scroll.js +57 -0
- package/lib/public/modules/split-view-worker-transcript.js +46 -0
- package/lib/public/modules/split-view-workers.js +418 -0
- package/lib/public/modules/split-view.js +61 -22
- package/lib/public/modules/sticky-notes.js +9 -2
- package/lib/public/modules/tools.js +25 -5
- package/lib/public/modules/workbench-layout-preference.js +76 -0
- package/lib/public/modules/worker-created-presentation.js +37 -0
- package/lib/public/pane-assets/app-2DRBYY3L.js +1 -0
- package/lib/public/pane-assets/chunk-4BM7UVNG.js +2 -0
- package/lib/public/pane-assets/chunk-6SM2WQJX.js +1 -0
- package/lib/public/pane-assets/chunk-7H36SUZA.js +2 -0
- package/lib/public/pane-assets/chunk-C4RVVYNT.js +1 -0
- package/lib/public/pane-assets/chunk-DGOFGEUK.js +1 -0
- package/lib/public/pane-assets/chunk-I57URYSL.js +1 -0
- package/lib/public/pane-assets/chunk-O6MQL6MR.js +167 -0
- package/lib/public/pane-assets/chunk-PO2JOHHW.js +1 -0
- package/lib/public/pane-assets/chunk-UMGHINOT.js +1 -0
- package/lib/public/pane-assets/manifest.json +553 -0
- package/lib/public/pane-assets/style-63EZCREE.css +1 -0
- package/lib/public/style.css +5 -1
- package/lib/server-owner-ui.js +6 -2
- package/lib/server-owner-view-observation.js +34 -0
- package/lib/server-palette.js +1 -0
- package/lib/server-pane-bootstrap.js +24 -0
- package/lib/server-settings.js +2 -0
- package/lib/server-static-assets.js +52 -0
- package/lib/server-workbench-layout-settings.js +56 -0
- package/lib/server.js +6 -45
- package/lib/session-activity-state.js +28 -0
- package/lib/session-list-delivery.js +17 -0
- package/lib/session-pair-turn-control.js +1 -6
- package/lib/session-search.js +3 -0
- package/lib/session-split-group-anchors.js +2 -2
- package/lib/session-split-group-roles.js +5 -8
- package/lib/session-split-group-v2-store.js +0 -3
- package/lib/sessions.js +16 -6
- package/lib/split-view-layout.js +132 -0
- package/lib/users-workbench-layout-preferences.js +33 -0
- package/lib/users.js +3 -0
- package/lib/workspace-query-service.js +1 -5
- package/lib/ws-schema.js +16 -0
- package/package.json +6 -3
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
import { store } from './store.js';
|
|
8
8
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
9
9
|
import { escapeHtml } from './utils.js';
|
|
10
|
+
import { closeSessionContextMenu, positionSessionContextMenu } from './session-context-menu.js';
|
|
10
11
|
|
|
11
12
|
var LONG_PRESS_MS = 500;
|
|
12
13
|
var MOVE_TOLERANCE = 10;
|
|
13
14
|
var POST_RELEASE_MS = 400;
|
|
14
15
|
var HOLD_MAX_MS = 15000;
|
|
15
|
-
var EDGE = 8;
|
|
16
16
|
|
|
17
17
|
function contextState() {
|
|
18
18
|
return store.get('sessionFolderContext') || { press: null, held: null, suppressUntil: 0 };
|
|
@@ -72,7 +72,7 @@ function onMenuKey(event) {
|
|
|
72
72
|
|
|
73
73
|
// Opens the menu with its top-left corner at the point, kept inside the viewport.
|
|
74
74
|
export function openFolderMenuAt(anchor, entries, point) {
|
|
75
|
-
closeFolderMenu();
|
|
75
|
+
closeFolderMenu(false); closeSessionContextMenu(false);
|
|
76
76
|
var menu = document.createElement("div");
|
|
77
77
|
menu.className = "session-ctx-menu session-folder-menu";
|
|
78
78
|
menu.setAttribute("role", "menu");
|
|
@@ -94,16 +94,12 @@ export function openFolderMenuAt(anchor, entries, point) {
|
|
|
94
94
|
});
|
|
95
95
|
menu.style.visibility = "hidden";
|
|
96
96
|
document.body.appendChild(menu);
|
|
97
|
-
|
|
98
|
-
var left = Math.min(Math.max(EDGE, point.x), Math.max(EDGE, window.innerWidth - box.width - EDGE));
|
|
99
|
-
var top = point.y;
|
|
100
|
-
if (top + box.height > window.innerHeight - EDGE) top = Math.min(point.y - box.height, window.innerHeight - box.height - EDGE);
|
|
101
|
-
top = Math.max(EDGE, top);
|
|
102
|
-
menu.style.left = left + "px";
|
|
103
|
-
menu.style.top = top + "px";
|
|
104
|
-
menu.style.right = "auto";
|
|
97
|
+
positionSessionContextMenu(menu, anchor, point);
|
|
105
98
|
menu.style.visibility = "";
|
|
106
|
-
|
|
99
|
+
var root = anchor.closest('.session-folder-folder');
|
|
100
|
+
store.set({ sessionFolderMenu: { el: menu, anchor: anchor, folderId: root && root.dataset.folderId,
|
|
101
|
+
mobile: !!(root && root.classList.contains('is-mobile')), slug: store.get('currentSlug'), viewerId: store.get('myUserId'),
|
|
102
|
+
folders: JSON.stringify((store.get('sessionFolders') || {}).folders || []) } });
|
|
107
103
|
document.addEventListener("pointerdown", onMenuOutside, true);
|
|
108
104
|
document.addEventListener("mousedown", onMenuOutside, true);
|
|
109
105
|
document.addEventListener("touchstart", onMenuOutside, true);
|
|
@@ -114,6 +110,17 @@ export function openFolderMenuAt(anchor, entries, point) {
|
|
|
114
110
|
if (first) first.focus({ preventScroll: true });
|
|
115
111
|
}
|
|
116
112
|
|
|
113
|
+
export function reconcileFolderMenu() {
|
|
114
|
+
var menu = store.get('sessionFolderMenu'); if (!menu) return;
|
|
115
|
+
if (!menu.folderId || menu.slug !== store.get('currentSlug') || menu.viewerId !== store.get('myUserId') ||
|
|
116
|
+
menu.folders !== JSON.stringify((store.get('sessionFolders') || {}).folders || [])) { closeFolderMenu(false); return; }
|
|
117
|
+
var roots = document.querySelectorAll('.session-folder-folder');
|
|
118
|
+
var root = Array.from(roots).find(function (item) { return item.dataset.folderId === menu.folderId && item.classList.contains('is-mobile') === menu.mobile; });
|
|
119
|
+
var anchor = root && root.querySelector('.session-folder-toggle');
|
|
120
|
+
if (!anchor) { closeFolderMenu(false); return; }
|
|
121
|
+
if (anchor !== menu.anchor) store.set({ sessionFolderMenu: Object.assign({}, menu, { anchor: anchor }) });
|
|
122
|
+
}
|
|
123
|
+
|
|
117
124
|
// --- Long press (touch and pen) ---
|
|
118
125
|
|
|
119
126
|
function onScrollCancel() {
|
|
@@ -164,14 +171,14 @@ export function consumeFolderClickSuppress() {
|
|
|
164
171
|
// entries when it opens, so they always reflect the current folder order.
|
|
165
172
|
export function wireFolderContext(header, anchor, entriesFor) {
|
|
166
173
|
function open(x, y) {
|
|
167
|
-
if (store.get('sessionFolderMenu')) return;
|
|
168
174
|
openFolderMenuAt(anchor, entriesFor(), { x: x, y: y });
|
|
169
175
|
}
|
|
170
176
|
|
|
171
177
|
header.addEventListener("contextmenu", function (event) {
|
|
172
178
|
event.preventDefault();
|
|
173
179
|
event.stopPropagation();
|
|
174
|
-
|
|
180
|
+
var rect = anchor.getBoundingClientRect();
|
|
181
|
+
open(event.clientX || event.clientY ? event.clientX : rect.left + 16, event.clientX || event.clientY ? event.clientY : rect.bottom);
|
|
175
182
|
});
|
|
176
183
|
|
|
177
184
|
header.addEventListener("keydown", function (event) {
|
|
@@ -213,3 +220,7 @@ export function wireFolderContext(header, anchor, entriesFor) {
|
|
|
213
220
|
header.addEventListener("pointerleave", cancelFolderPress);
|
|
214
221
|
header.addEventListener("dragstart", cancelFolderPress);
|
|
215
222
|
}
|
|
223
|
+
|
|
224
|
+
store.subscribe(function (state, previous) {
|
|
225
|
+
if (state.myUserId !== previous.myUserId || previous.connected && !state.connected) closeFolderMenu(false);
|
|
226
|
+
});
|
|
@@ -178,13 +178,28 @@ function radioRow(mode, labelText, hint, current, extraClass) {
|
|
|
178
178
|
return row;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
+
// Display context only. Session counts and deletion authority still come from the preview.
|
|
182
|
+
function folderHasSavedViews(folderId) {
|
|
183
|
+
var snapshot = store.snap();
|
|
184
|
+
if (!snapshot.connected || !snapshot.authIdentityLoaded || snapshot.dmMode || snapshot.paneMode ||
|
|
185
|
+
!snapshot.currentSlug || snapshot.activeProjectSlug !== snapshot.currentSlug || !snapshot.projectSplitViewHydrated || snapshot.projectSplitViewError ||
|
|
186
|
+
snapshot.projectSplitViewRequestSlug !== snapshot.currentSlug || snapshot.projectSplitViewRequestSocket !== getWs() ||
|
|
187
|
+
snapshot.projectSplitViewRequestOwnerId !== (snapshot.myUserId || null) ||
|
|
188
|
+
snapshot.projectSplitViewRequestGeneration !== snapshot.projectSplitViewSocketGeneration) return false;
|
|
189
|
+
return (snapshot.projectSplitViews || []).some(function (view) { return view.folderId === folderId; });
|
|
190
|
+
}
|
|
191
|
+
|
|
181
192
|
function describe(current) {
|
|
182
193
|
var preview = current.preview;
|
|
194
|
+
var hasViews = folderHasSavedViews(current.folderId);
|
|
195
|
+
var viewCopy = "Saved Split views will move to Unfiled and keep their Favorites status.";
|
|
196
|
+
if (preview.sessionCount === 0 && hasViews) return 'The folder "' + current.label + '" contains saved Split views. ' + viewCopy + " No sessions will be deleted.";
|
|
183
197
|
if (preview.sessionCount === 0) return 'The folder "' + current.label + '" is empty. Deleting it does not affect any sessions.';
|
|
184
198
|
var text = 'The folder "' + current.label + '" contains ' + phrase(preview.sessionCount, "session") + ".";
|
|
185
199
|
if (preview.workerCount > 0) {
|
|
186
200
|
text += " " + phrase(preview.workerCount, "Split Worker session") + " owned by those Drivers would be deleted with them, including hidden sessions and previous generations.";
|
|
187
201
|
}
|
|
202
|
+
if (hasViews) text += " " + viewCopy + " The choices below apply to individually filed sessions; deleting member sessions also removes them from their Split views.";
|
|
188
203
|
return text;
|
|
189
204
|
}
|
|
190
205
|
|
|
@@ -327,7 +342,9 @@ export function handleFolderDeleteState(msg) {
|
|
|
327
342
|
store.subscribe(function (next, previous) {
|
|
328
343
|
if (next.sessionFolderDelete !== previous.sessionFolderDelete) paint();
|
|
329
344
|
// Retry depends on the connection, so a change in it repaints the dialog.
|
|
330
|
-
else if (next.connected !== previous.connected
|
|
345
|
+
else if (next.sessionFolderDelete && (next.connected !== previous.connected || next.projectSplitViews !== previous.projectSplitViews ||
|
|
346
|
+
next.projectSplitViewHydrated !== previous.projectSplitViewHydrated || next.projectSplitViewError !== previous.projectSplitViewError ||
|
|
347
|
+
next.currentSlug !== previous.currentSlug || next.myUserId !== previous.myUserId)) paint();
|
|
331
348
|
var current = next.sessionFolderDelete;
|
|
332
349
|
if (next.currentSlug !== previous.currentSlug || next.dmMode !== previous.dmMode) clearDustParticles("folder-delete-dust");
|
|
333
350
|
if (!current || !previous.connected || next.connected) return;
|
|
@@ -186,8 +186,11 @@ function ghostFor(label) {
|
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
export function wireFolderDrag(header, folderId, label) {
|
|
189
|
-
|
|
189
|
+
// Keep disclosure clicks separate from the native folder-reorder gesture.
|
|
190
|
+
var handle = header.querySelector('.session-folder-label') || header;
|
|
191
|
+
handle.setAttribute("draggable", "true");
|
|
190
192
|
header.addEventListener("dragstart", function (event) {
|
|
193
|
+
if (event.target.closest && event.target.closest('.session-folder-chevron, .session-folder-new-btn')) { event.preventDefault(); return; }
|
|
191
194
|
var root = header.closest(".session-folder-folder");
|
|
192
195
|
var parent = root && root.parentElement;
|
|
193
196
|
if (!parent) return;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
// Pure projection of sidebar units into folder sections. No DOM, no store.
|
|
2
2
|
//
|
|
3
3
|
// A unit is one organizable root: a standalone session or a Driver with all of
|
|
4
|
-
// its Workers
|
|
5
|
-
//
|
|
6
|
-
// always live with the ordinary, unfiled sessions.
|
|
4
|
+
// its Workers, or a saved presentation view. Saved views own their folder and
|
|
5
|
+
// Favorite metadata. Unkeyed legacy groups and loop runs remain Unfiled.
|
|
7
6
|
|
|
8
7
|
export var FAVORITES_ID = "favorites";
|
|
9
8
|
export var UNFILED_ID = "unfiled";
|
|
@@ -48,7 +47,8 @@ export function isFavorite(state, key) {
|
|
|
48
47
|
|
|
49
48
|
export function containerOf(state, unit) {
|
|
50
49
|
if (unit.key === null) return UNFILED_ID;
|
|
51
|
-
var
|
|
50
|
+
var group = unit.item && unit.item.type === "split-group" && unit.item.group && unit.item.group.presentation ? unit.item.group : null;
|
|
51
|
+
var id = group ? group.folderId : state.assignments[unit.key];
|
|
52
52
|
for (var i = 0; i < state.folders.length; i++) if (state.folders[i].id === id) return id;
|
|
53
53
|
return UNFILED_ID;
|
|
54
54
|
}
|
|
@@ -66,7 +66,8 @@ export function buildLayout(units, state, options) {
|
|
|
66
66
|
var ordinary = [];
|
|
67
67
|
for (var i = 0; i < units.length; i++) {
|
|
68
68
|
// The same unit may appear twice: once as a favorite, once in its folder.
|
|
69
|
-
|
|
69
|
+
var group = units[i].item && units[i].item.type === "split-group" && units[i].item.group && units[i].item.group.presentation ? units[i].item.group : null;
|
|
70
|
+
if (group ? group.favorite === true : isFavorite(state, units[i].key)) favorites.push(units[i]);
|
|
70
71
|
ordinary.push(units[i]);
|
|
71
72
|
}
|
|
72
73
|
var result = {
|
|
@@ -117,7 +118,7 @@ export function containerKeys(layout, containerId) {
|
|
|
117
118
|
if (layout.sections[i].id === containerId) list = layout.sections[i].units;
|
|
118
119
|
}
|
|
119
120
|
}
|
|
120
|
-
return list.filter(function (u) { return u.key !== null; }).map(function (u) { return u.key; });
|
|
121
|
+
return list.filter(function (u) { return u.key !== null && !(u.item && u.item.type === "split-group" && u.item.group && u.item.group.presentation); }).map(function (u) { return u.key; });
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
export var SORT_OPTIONS = [
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { paneMessageBinding } from './pane-binding.js';
|
|
3
|
+
import { acceptsPaneSearchResults } from './pane-search-receipt.js';
|
|
4
|
+
|
|
5
|
+
function publishPaneSearchStatus(query, status) {
|
|
6
|
+
if (!store.get('paneMode') || window.parent === window) return;
|
|
7
|
+
window.parent.postMessage({ type: "clay-pane-search-status", paneBinding: paneMessageBinding(), query: query,
|
|
8
|
+
status: status, open: store.get('paneSearchOpen') === true }, window.location.origin);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function updatePaneSearchStatus(countElement, query, status) {
|
|
12
|
+
if (countElement) countElement.textContent = status;
|
|
13
|
+
publishPaneSearchStatus(query, status);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function beginPaneSearch(query) {
|
|
17
|
+
var binding = paneMessageBinding(), epoch = (Number(store.get('paneSearchEpoch')) || 0) + 1;
|
|
18
|
+
store.set({ paneSearchEpoch: epoch, paneSearchReceipt: binding ? { binding: binding, epoch: epoch, query: query } : null });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function endPaneSearch() {
|
|
22
|
+
store.set({ paneSearchOpen: false, paneSearchReceipt: null, paneSearchEpoch: (Number(store.get('paneSearchEpoch')) || 0) + 1 });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function acceptsSessionSearchResults(state, msg, query) {
|
|
26
|
+
if (state.paneMode && (!state.paneSearchOpen || !acceptsPaneSearchResults(state, msg))) return false;
|
|
27
|
+
return msg.query === query;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { updatePaneSearchStatus, beginPaneSearch, endPaneSearch, acceptsSessionSearchResults };
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// --- In-session search (Cmd+F / Ctrl+F) ---
|
|
2
2
|
import { escapeHtml } from './utils.js';
|
|
3
3
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
4
|
+
import { store } from './store.js';
|
|
5
|
+
import { paneMessageBinding } from './pane-binding.js';
|
|
6
|
+
import { updatePaneSearchStatus, beginPaneSearch, endPaneSearch, acceptsSessionSearchResults } from './session-search-pane-state.js';
|
|
4
7
|
|
|
5
8
|
var ctx = null;
|
|
6
9
|
var searchBarEl = null;
|
|
@@ -19,9 +22,20 @@ var serverHits = []; // full history hits from server
|
|
|
19
22
|
var serverTotal = 0;
|
|
20
23
|
var pendingScrollTarget = null;
|
|
21
24
|
|
|
25
|
+
function setSearchStatus(text) {
|
|
26
|
+
updatePaneSearchStatus(matchCountEl, currentQuery, text);
|
|
27
|
+
}
|
|
28
|
+
|
|
22
29
|
function initSessionSearch(context) {
|
|
23
30
|
ctx = context;
|
|
24
31
|
createSearchBar();
|
|
32
|
+
var searchBinding = paneMessageBinding();
|
|
33
|
+
store.subscribe(function () {
|
|
34
|
+
var nextBinding = paneMessageBinding();
|
|
35
|
+
if (searchBinding === nextBinding) return;
|
|
36
|
+
searchBinding = nextBinding;
|
|
37
|
+
if (store.get('paneSearchOpen') || currentQuery || matches.length || serverHits.length) closeSearch();
|
|
38
|
+
});
|
|
25
39
|
document.addEventListener("keydown", function (e) {
|
|
26
40
|
var isMod = e.metaKey || e.ctrlKey;
|
|
27
41
|
if (isMod && e.key === "f") {
|
|
@@ -90,11 +104,16 @@ function createSearchBar() {
|
|
|
90
104
|
}
|
|
91
105
|
|
|
92
106
|
function isSearchOpen() {
|
|
107
|
+
if (store.get('paneMode')) return store.get('paneSearchOpen') === true;
|
|
93
108
|
return searchBarEl && !searchBarEl.classList.contains("hidden");
|
|
94
109
|
}
|
|
95
110
|
|
|
96
111
|
function openSearch(prefill) {
|
|
97
112
|
if (!searchBarEl) return;
|
|
113
|
+
if (store.get('paneMode') && window.parent !== window) {
|
|
114
|
+
window.parent.postMessage({ type: "clay-pane-search-open", paneBinding: paneMessageBinding() }, window.location.origin);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
98
117
|
searchBarEl.classList.remove("hidden");
|
|
99
118
|
var btn = document.getElementById("find-in-session-btn");
|
|
100
119
|
if (btn) btn.classList.add("active");
|
|
@@ -111,6 +130,7 @@ function openSearch(prefill) {
|
|
|
111
130
|
|
|
112
131
|
function closeSearch() {
|
|
113
132
|
if (!searchBarEl) return;
|
|
133
|
+
endPaneSearch();
|
|
114
134
|
searchBarEl.classList.add("hidden");
|
|
115
135
|
var btn = document.getElementById("find-in-session-btn");
|
|
116
136
|
if (btn) btn.classList.remove("active");
|
|
@@ -121,7 +141,27 @@ function closeSearch() {
|
|
|
121
141
|
currentMatchIndex = -1;
|
|
122
142
|
serverHits = [];
|
|
123
143
|
serverTotal = 0;
|
|
124
|
-
|
|
144
|
+
setSearchStatus("");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function controlPaneSearch(action, query) {
|
|
148
|
+
if (!store.get('paneMode')) return false;
|
|
149
|
+
if (action === "close") { closeSearch(); return true; }
|
|
150
|
+
if (action === "open") {
|
|
151
|
+
store.set({ paneSearchOpen: true });
|
|
152
|
+
searchInputEl.value = String(query || "");
|
|
153
|
+
performSearch(searchInputEl.value);
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
if (!store.get('paneSearchOpen')) return false;
|
|
157
|
+
if (action === "query") {
|
|
158
|
+
searchInputEl.value = String(query || "");
|
|
159
|
+
performSearch(searchInputEl.value);
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
if (action === "next") { goToNextMatch(); return true; }
|
|
163
|
+
if (action === "previous") { goToPrevMatch(); return true; }
|
|
164
|
+
return false;
|
|
125
165
|
}
|
|
126
166
|
|
|
127
167
|
function toggleSearch() {
|
|
@@ -140,9 +180,10 @@ function performSearch(query) {
|
|
|
140
180
|
serverHits = [];
|
|
141
181
|
serverTotal = 0;
|
|
142
182
|
currentQuery = query.trim();
|
|
183
|
+
if (store.get('paneMode')) beginPaneSearch(currentQuery);
|
|
143
184
|
|
|
144
185
|
if (!currentQuery) {
|
|
145
|
-
|
|
186
|
+
setSearchStatus("");
|
|
146
187
|
return;
|
|
147
188
|
}
|
|
148
189
|
|
|
@@ -171,16 +212,15 @@ function highlightLoadedMessages() {
|
|
|
171
212
|
if (matches.length > 0) {
|
|
172
213
|
currentMatchIndex = 0;
|
|
173
214
|
setActiveMatch(0);
|
|
174
|
-
|
|
215
|
+
setSearchStatus("1 / " + matches.length);
|
|
175
216
|
} else {
|
|
176
|
-
|
|
217
|
+
setSearchStatus("Searching...");
|
|
177
218
|
}
|
|
178
219
|
}
|
|
179
220
|
|
|
180
221
|
// Handle server response with full-history search results
|
|
181
222
|
function handleFindInSessionResults(msg) {
|
|
182
|
-
if (!isSearchOpen()) return;
|
|
183
|
-
if (msg.query !== currentQuery) return; // stale
|
|
223
|
+
if (!isSearchOpen() || !acceptsSessionSearchResults(store.snap(), msg, currentQuery)) return;
|
|
184
224
|
|
|
185
225
|
serverHits = msg.hits || [];
|
|
186
226
|
serverTotal = msg.total || 0;
|
|
@@ -191,13 +231,13 @@ function handleFindInSessionResults(msg) {
|
|
|
191
231
|
clearHighlights();
|
|
192
232
|
highlightLoadedMessages();
|
|
193
233
|
if (matches.length > 0) {
|
|
194
|
-
|
|
234
|
+
setSearchStatus("1 / " + matches.length + " (" + serverHits.length + " total)");
|
|
195
235
|
} else {
|
|
196
|
-
|
|
236
|
+
setSearchStatus(serverHits.length + " matches");
|
|
197
237
|
}
|
|
198
238
|
buildTimeline();
|
|
199
239
|
} else {
|
|
200
|
-
|
|
240
|
+
setSearchStatus("No results");
|
|
201
241
|
}
|
|
202
242
|
}
|
|
203
243
|
|
|
@@ -441,16 +481,16 @@ function goToNextMatch() {
|
|
|
441
481
|
if (matches.length === 0) return;
|
|
442
482
|
currentMatchIndex = (currentMatchIndex + 1) % matches.length;
|
|
443
483
|
setActiveMatch(currentMatchIndex);
|
|
444
|
-
|
|
445
|
-
(serverHits.length > matches.length ? " (" + serverHits.length + " total)" : "");
|
|
484
|
+
setSearchStatus((currentMatchIndex + 1) + " / " + matches.length +
|
|
485
|
+
(serverHits.length > matches.length ? " (" + serverHits.length + " total)" : ""));
|
|
446
486
|
}
|
|
447
487
|
|
|
448
488
|
function goToPrevMatch() {
|
|
449
489
|
if (matches.length === 0) return;
|
|
450
490
|
currentMatchIndex = (currentMatchIndex - 1 + matches.length) % matches.length;
|
|
451
491
|
setActiveMatch(currentMatchIndex);
|
|
452
|
-
|
|
453
|
-
(serverHits.length > matches.length ? " (" + serverHits.length + " total)" : "");
|
|
492
|
+
setSearchStatus((currentMatchIndex + 1) + " / " + matches.length +
|
|
493
|
+
(serverHits.length > matches.length ? " (" + serverHits.length + " total)" : ""));
|
|
454
494
|
}
|
|
455
495
|
|
|
456
496
|
export { initSessionSearch, openSearch, closeSearch, toggleSearch, isSearchOpen, handleFindInSessionResults, onHistoryPrepended };
|
|
@@ -47,7 +47,7 @@ import { ordinarySessionListSessions } from './session-list-visibility.js';
|
|
|
47
47
|
import { formatSessionActivity } from './session-activity-tooltip.js';
|
|
48
48
|
import { bindSessionTitleMarquee } from './session-title-marquee.js';
|
|
49
49
|
import { summarizeSidebarItems } from './sidebar-session-counts.js';
|
|
50
|
-
import { cancelSessionInlineRename,
|
|
50
|
+
import { cancelSessionInlineRename, reconcileSessionContextMenu } from './session-context-menu.js';
|
|
51
51
|
|
|
52
52
|
// --- Mobile state ---
|
|
53
53
|
var mobileChatSheetOpen = false;
|
|
@@ -447,7 +447,7 @@ function createMobileSessionItem(s, options) {
|
|
|
447
447
|
el.addEventListener("contextmenu", function (event) {
|
|
448
448
|
event.preventDefault();
|
|
449
449
|
event.stopPropagation();
|
|
450
|
-
openSessionContextMenu(el, s);
|
|
450
|
+
openSessionContextMenu(el, s, event.clientX || event.clientY ? { x: event.clientX, y: event.clientY } : null);
|
|
451
451
|
});
|
|
452
452
|
|
|
453
453
|
(function (session, worker) {
|
|
@@ -775,7 +775,6 @@ export function refreshMobileChatSheet() {
|
|
|
775
775
|
var sessionListEl = sheet.querySelector(".mobile-chat-session-list");
|
|
776
776
|
if (!sessionListEl) return;
|
|
777
777
|
|
|
778
|
-
closeSessionContextMenu(false);
|
|
779
778
|
cancelSessionInlineRename(false);
|
|
780
779
|
|
|
781
780
|
// Update chips: active state and processing dots
|
|
@@ -815,6 +814,7 @@ export function refreshMobileChatSheet() {
|
|
|
815
814
|
renderMobileSessionsInto(sessionListEl);
|
|
816
815
|
|
|
817
816
|
refreshIcons();
|
|
817
|
+
reconcileSessionContextMenu();
|
|
818
818
|
}
|
|
819
819
|
|
|
820
820
|
function renderSheetMateProfile(listEl) {
|
|
@@ -1020,6 +1020,7 @@ function showWorktreeModal(parentSlug, parentName) {
|
|
|
1020
1020
|
export function renderIconStrip(projects, currentSlug) {
|
|
1021
1021
|
cachedProjectList = projects;
|
|
1022
1022
|
cachedCurrentSlug = currentSlug;
|
|
1023
|
+
if (store.get('paneMode')) return;
|
|
1023
1024
|
|
|
1024
1025
|
var container = document.getElementById("icon-strip-projects");
|
|
1025
1026
|
if (!container) return;
|
|
@@ -65,7 +65,7 @@ export function unitsFromItems(items) {
|
|
|
65
65
|
var item = items[i];
|
|
66
66
|
var data = item.type === "session" || item.type === "driver-hierarchy" ? item.data : null;
|
|
67
67
|
units.push({
|
|
68
|
-
key: data && typeof data.id === "number" ? data.id : null,
|
|
68
|
+
key: item.type === "split-group" && item.group && item.group.presentation ? "split-view:" + item.group.id : data && typeof data.id === "number" ? data.id : null,
|
|
69
69
|
title: data ? data.title : (item.type === "split-group" ? (item.group && item.group.name) : (item.type === "loop" ? item.loopId : "")),
|
|
70
70
|
createdAt: data ? data.createdAt || 0 : 0,
|
|
71
71
|
lastActivity: data ? (data.lastActivity || 0) : (item.lastActivity || 0),
|
|
@@ -275,9 +275,9 @@ function renderUnit(unit, containerId, ctx) {
|
|
|
275
275
|
wrapper.className = "session-folder-unit";
|
|
276
276
|
if (unit.key) {
|
|
277
277
|
wrapper.dataset.unitKey = String(unit.key);
|
|
278
|
-
if (ctx.surface === "desktop") wireUnitDrop(wrapper, containerId, unit.key);
|
|
278
|
+
if (ctx.surface === "desktop" && typeof unit.key === "number") wireUnitDrop(wrapper, containerId, unit.key);
|
|
279
279
|
}
|
|
280
|
-
wrapper.appendChild(ctx.renderItem(unit.item));
|
|
280
|
+
wrapper.appendChild(ctx.renderItem(unit.item, containerId, ctx.surface));
|
|
281
281
|
return wrapper;
|
|
282
282
|
}
|
|
283
283
|
|
|
@@ -13,6 +13,8 @@ import { showConfirm } from './app-misc.js';
|
|
|
13
13
|
import { getUpcomingSchedules } from './scheduler.js';
|
|
14
14
|
import { refreshMobileChatSheet } from './sidebar-mobile.js';
|
|
15
15
|
import { openGroup, openWorkerWithDriver, separateGroup } from './split-view.js';
|
|
16
|
+
import { presentationViewForSidebar } from './split-view-presentations.js';
|
|
17
|
+
import { renderPresentationSplitViewItem } from './split-view-sidebar-renderer.js';
|
|
16
18
|
import { groupedSessionIds } from './split-group-helpers.js';
|
|
17
19
|
import { resolvePreferredVendor } from './default-vendor.js';
|
|
18
20
|
import {
|
|
@@ -21,6 +23,7 @@ import {
|
|
|
21
23
|
} from './sidebar-session-folders-ui.js';
|
|
22
24
|
import { renderFolderToolbar, captureFolderInputs, folderToolbarNeedsRender } from './session-folder-toolbar.js';
|
|
23
25
|
import { captureFolderCollapseAnimations } from './session-folder-collapse.js';
|
|
26
|
+
import { closeFolderMenu, reconcileFolderMenu } from './session-folder-context.js';
|
|
24
27
|
import { getListSearchQuery, getListSearchMatchIds, applyListSearchResults, filterItemsBySearch } from './session-list-search.js';
|
|
25
28
|
import { expectCreatedSession } from './session-create.js';
|
|
26
29
|
import { isMateWorkspace } from './project-mate-navigation.js';
|
|
@@ -39,6 +42,8 @@ import {
|
|
|
39
42
|
cancelSessionInlineRename,
|
|
40
43
|
closeSessionContextMenu,
|
|
41
44
|
mountSessionContextMenu,
|
|
45
|
+
positionSessionContextMenu,
|
|
46
|
+
reconcileSessionContextMenu,
|
|
42
47
|
startSessionInlineRename
|
|
43
48
|
} from './session-context-menu.js';
|
|
44
49
|
|
|
@@ -203,6 +208,8 @@ export function initSidebarSessions() {
|
|
|
203
208
|
var b = prev.sessionCreate;
|
|
204
209
|
if ((!a !== !b || (a && b && a.folderId !== b.folderId)) && getSessionListEl()) renderSessionList(null);
|
|
205
210
|
if (state.splitPanes !== prev.splitPanes) renderSessionList(null);
|
|
211
|
+
if (state.projectSplitViews !== prev.projectSplitViews || state.projectSplitViewActiveId !== prev.projectSplitViewActiveId ||
|
|
212
|
+
state.projectSplitViewError !== prev.projectSplitViewError || state.projectSplitViewPending !== prev.projectSplitViewPending) renderSessionList(null);
|
|
206
213
|
});
|
|
207
214
|
|
|
208
215
|
// --- Resume session picker ---
|
|
@@ -234,8 +241,17 @@ export function getExpandedLoopRuns() {
|
|
|
234
241
|
|
|
235
242
|
// --- Context menu ---
|
|
236
243
|
|
|
237
|
-
function showSessionCtxMenu(anchorBtn, sessionId, title, cliSid, sessionData) {
|
|
244
|
+
function showSessionCtxMenu(anchorBtn, sessionId, title, cliSid, sessionData, point) {
|
|
238
245
|
closeSessionContextMenu(false);
|
|
246
|
+
closeFolderMenu(false);
|
|
247
|
+
var origin = sessionData && sessionData.sessionOriginId, owner = sessionData && sessionData.ownerId;
|
|
248
|
+
var rowSelector = anchorBtn.classList.contains('mobile-session-item') ? '.mobile-session-item' : '.session-item';
|
|
249
|
+
var wasFavorite = isFavoriteSession(sessionData);
|
|
250
|
+
function currentAnchor() {
|
|
251
|
+
var live = cachedSessions.find(function (item) { return item.id === sessionId && item.sessionOriginId === origin && item.ownerId === owner && item.title === title; });
|
|
252
|
+
if (!live || isFavoriteSession(live) !== wasFavorite) return null;
|
|
253
|
+
return anchorBtn.isConnected ? anchorBtn : document.querySelector(rowSelector + '[data-session-id="' + sessionId + '"]');
|
|
254
|
+
}
|
|
239
255
|
|
|
240
256
|
var menu = document.createElement("div");
|
|
241
257
|
menu.className = "session-ctx-menu";
|
|
@@ -268,8 +284,8 @@ function showSessionCtxMenu(anchorBtn, sessionId, title, cliSid, sessionData) {
|
|
|
268
284
|
renameItem.innerHTML = iconHtml("pencil") + " <span>Rename</span>";
|
|
269
285
|
renameItem.addEventListener("click", function (e) {
|
|
270
286
|
e.stopPropagation();
|
|
271
|
-
closeSessionContextMenu(false);
|
|
272
|
-
startSessionInlineRename(
|
|
287
|
+
var row = currentAnchor(); closeSessionContextMenu(false);
|
|
288
|
+
if (row) startSessionInlineRename(row, sessionData);
|
|
273
289
|
});
|
|
274
290
|
menu.appendChild(renameItem);
|
|
275
291
|
|
|
@@ -305,26 +321,13 @@ function showSessionCtxMenu(anchorBtn, sessionId, title, cliSid, sessionData) {
|
|
|
305
321
|
}
|
|
306
322
|
|
|
307
323
|
document.body.appendChild(menu);
|
|
308
|
-
mountSessionContextMenu(menu, anchorBtn, sessionId);
|
|
324
|
+
mountSessionContextMenu(menu, anchorBtn, sessionId, { resolveAnchor: currentAnchor });
|
|
309
325
|
refreshIcons();
|
|
310
|
-
|
|
311
|
-
// Position: fixed relative to the anchor button
|
|
312
|
-
requestAnimationFrame(function () {
|
|
313
|
-
var btnRect = anchorBtn.getBoundingClientRect();
|
|
314
|
-
menu.style.position = "fixed";
|
|
315
|
-
menu.style.top = (btnRect.bottom + 2) + "px";
|
|
316
|
-
menu.style.right = (window.innerWidth - btnRect.right) + "px";
|
|
317
|
-
menu.style.left = "auto";
|
|
318
|
-
// If menu overflows below viewport, flip up
|
|
319
|
-
var menuRect = menu.getBoundingClientRect();
|
|
320
|
-
if (menuRect.bottom > window.innerHeight - 8) {
|
|
321
|
-
menu.style.top = (btnRect.top - menuRect.height - 2) + "px";
|
|
322
|
-
}
|
|
323
|
-
});
|
|
326
|
+
positionSessionContextMenu(menu, anchorBtn, point);
|
|
324
327
|
}
|
|
325
328
|
|
|
326
|
-
export function openSessionContextMenu(anchor, session) {
|
|
327
|
-
showSessionCtxMenu(anchor, session.id, session.title, session.cliSessionId, session);
|
|
329
|
+
export function openSessionContextMenu(anchor, session, point) {
|
|
330
|
+
showSessionCtxMenu(anchor, session.id, session.title, session.cliSessionId, session, point);
|
|
328
331
|
}
|
|
329
332
|
|
|
330
333
|
function showSplitGroupCtxMenu(anchor, group) {
|
|
@@ -867,9 +870,14 @@ function renderSessionItem(s, options) {
|
|
|
867
870
|
return function(e) {
|
|
868
871
|
e.preventDefault();
|
|
869
872
|
e.stopPropagation();
|
|
870
|
-
showSessionCtxMenu(anchor, id, title, cliSid, sData);
|
|
873
|
+
showSessionCtxMenu(anchor, id, title, cliSid, sData, e.clientX || e.clientY ? { x: e.clientX, y: e.clientY } : null);
|
|
871
874
|
};
|
|
872
875
|
})(s.id, s.title, s.cliSessionId, el, s));
|
|
876
|
+
el.addEventListener('keydown', function (event) {
|
|
877
|
+
if (event.key !== 'ContextMenu' && !(event.shiftKey && event.key === 'F10')) return;
|
|
878
|
+
if (event.target.closest('input, textarea, [contenteditable="true"]')) return;
|
|
879
|
+
event.preventDefault(); event.stopPropagation(); showSessionCtxMenu(el, s.id, s.title, s.cliSessionId, s);
|
|
880
|
+
});
|
|
873
881
|
|
|
874
882
|
// Unread badge
|
|
875
883
|
var unreadBadge = document.createElement("span");
|
|
@@ -975,6 +983,9 @@ function sessionRenderFingerprint(sessions) {
|
|
|
975
983
|
}
|
|
976
984
|
|
|
977
985
|
export function renderSessionList(sessions) {
|
|
986
|
+
// Pane activation consumes this exact list, but its sidebar is parent-owned.
|
|
987
|
+
// Avoid building every hidden session/folder/Worker row in each iframe.
|
|
988
|
+
if (store.get('paneMode')) { if (sessions) cachedSessions = sessions; return; }
|
|
978
989
|
if (sessions) {
|
|
979
990
|
// A reconnect and a streaming turn both re-send this list unchanged;
|
|
980
991
|
// rebuilding the sidebar for an identical frame is what made it flicker.
|
|
@@ -983,7 +994,6 @@ export function renderSessionList(sessions) {
|
|
|
983
994
|
cachedSessions = sessions;
|
|
984
995
|
}
|
|
985
996
|
|
|
986
|
-
closeSessionContextMenu(false);
|
|
987
997
|
cancelSessionInlineRename(false);
|
|
988
998
|
|
|
989
999
|
// Remember the inline folder draft before either surface is rebuilt: the mobile
|
|
@@ -1007,6 +1017,26 @@ export function renderSessionList(sessions) {
|
|
|
1007
1017
|
hierarchySessionIds.forEach(function (id) { groupedIds.delete(id); });
|
|
1008
1018
|
var sessionsById = new Map();
|
|
1009
1019
|
for (var si = 0; si < visibleSessions.length; si++) sessionsById.set(visibleSessions[si].id, visibleSessions[si]);
|
|
1020
|
+
var presentationItems = [];
|
|
1021
|
+
var presentationViews = store.get('projectSplitViews') || [];
|
|
1022
|
+
for (var pvi = 0; pvi < presentationViews.length; pvi++) {
|
|
1023
|
+
var presentationView = presentationViewForSidebar(presentationViews[pvi].id);
|
|
1024
|
+
if (!presentationView || !Array.isArray(presentationView.sessions) || presentationView.sessions.length < 2 || presentationView.sessions.length > 8) continue;
|
|
1025
|
+
var presentationMembers = [], validPresentation = true, presentationActivity = 0;
|
|
1026
|
+
for (var pmi = 0; pmi < presentationView.sessions.length; pmi++) {
|
|
1027
|
+
var presentationRef = presentationView.sessions[pmi], presentationSession = null;
|
|
1028
|
+
for (var psi = 0; psi < visibleSessions.length; psi++) {
|
|
1029
|
+
if (visibleSessions[psi].id === presentationRef.id && visibleSessions[psi].sessionOriginId === presentationRef.sessionOriginId) presentationSession = visibleSessions[psi];
|
|
1030
|
+
}
|
|
1031
|
+
if (!presentationSession || presentationSession.sessionRole === "worker" || presentationSession.isMate || presentationSession.isTui ||
|
|
1032
|
+
groupedIds.has(presentationSession.id)) { validPresentation = false; break; }
|
|
1033
|
+
presentationMembers.push(presentationSession); presentationActivity = Math.max(presentationActivity, presentationSession.lastActivity || 0);
|
|
1034
|
+
}
|
|
1035
|
+
if (!validPresentation) continue;
|
|
1036
|
+
for (var pgm = 0; pgm < presentationMembers.length; pgm++) groupedIds.add(presentationMembers[pgm].id);
|
|
1037
|
+
presentationItems.push({ type: "split-group", group: Object.assign({ presentation: true }, presentationView), members: presentationMembers,
|
|
1038
|
+
lastActivity: presentationActivity });
|
|
1039
|
+
}
|
|
1010
1040
|
var loopGroups = {}; // groupKey -> [sessions]
|
|
1011
1041
|
var normalSessions = [];
|
|
1012
1042
|
for (var i = 0; i < visibleSessions.length; i++) {
|
|
@@ -1056,6 +1086,7 @@ export function renderSessionList(sessions) {
|
|
|
1056
1086
|
lastActivity: splitLastActivity,
|
|
1057
1087
|
});
|
|
1058
1088
|
}
|
|
1089
|
+
for (var pitem = 0; pitem < presentationItems.length; pitem++) items.push(presentationItems[pitem]);
|
|
1059
1090
|
var groupKeys = Object.keys(loopGroups);
|
|
1060
1091
|
for (var k = 0; k < groupKeys.length; k++) {
|
|
1061
1092
|
var gk = groupKeys[k];
|
|
@@ -1079,9 +1110,9 @@ export function renderSessionList(sessions) {
|
|
|
1079
1110
|
searching: matchIds !== null,
|
|
1080
1111
|
matchIds: matchIds,
|
|
1081
1112
|
sessions: cachedSessions,
|
|
1082
|
-
renderItem: function (it) {
|
|
1113
|
+
renderItem: function (it, containerId, surface) {
|
|
1083
1114
|
if (it.type === "loop") return renderLoopGroup(it.loopId, it.children, it.groupKey) || document.createElement("div");
|
|
1084
|
-
if (it.type === "split-group") return renderSplitGroupItem(it.group, it.members);
|
|
1115
|
+
if (it.type === "split-group") return it.group.presentation ? renderPresentationSplitViewItem(it.group, it.members, renderSessionItem, containerId, surface) : renderSplitGroupItem(it.group, it.members);
|
|
1085
1116
|
if (it.type === "driver-hierarchy") return renderDesktopDriverHierarchy(it.root, renderSessionItem, rerender, matchIds);
|
|
1086
1117
|
if (it.type === "orphan-workers") return document.createElement("div");
|
|
1087
1118
|
return renderSessionItem(it.data);
|
|
@@ -1101,6 +1132,8 @@ export function renderSessionList(sessions) {
|
|
|
1101
1132
|
renderOrdinarySections(layout, folderCtx, regularContainer);
|
|
1102
1133
|
getSessionListEl().appendChild(regularContainer);
|
|
1103
1134
|
refreshIcons();
|
|
1135
|
+
reconcileSessionContextMenu();
|
|
1136
|
+
reconcileFolderMenu();
|
|
1104
1137
|
if (updatePageTitle) updatePageTitle();
|
|
1105
1138
|
// Recorded from the sessions actually rendered, so a null-argument re-render
|
|
1106
1139
|
// driven by other state refreshes the fingerprint rather than stranding it.
|
|
@@ -63,11 +63,27 @@ export function closeSidebar() {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
export function toggleSidebarCollapse() {
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
var layout = document.getElementById("layout");
|
|
67
|
+
if (!layout && ctx) layout = ctx.$("layout");
|
|
68
|
+
if (!layout) return;
|
|
68
69
|
var collapsed = layout.classList.toggle("sidebar-collapsed");
|
|
69
70
|
try { localStorage.setItem("sidebar-collapsed", collapsed ? "1" : ""); } catch (e) {}
|
|
70
|
-
|
|
71
|
+
updateSidebarToggleState(collapsed);
|
|
72
|
+
if (ctx) setTimeout(function () { syncResizeHandles(); }, 210);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function updateSidebarToggleState(collapsed) {
|
|
76
|
+
var buttons = document.querySelectorAll("#sidebar-toggle-btn, #sidebar-expand-btn, #mate-sidebar-toggle-btn, #sidebar-shell-expand-btn");
|
|
77
|
+
for (var i = 0; i < buttons.length; i++) {
|
|
78
|
+
buttons[i].setAttribute("aria-expanded", collapsed ? "false" : "true");
|
|
79
|
+
buttons[i].title = collapsed ? "Open sidebar" : "Collapse sidebar";
|
|
80
|
+
buttons[i].setAttribute("aria-label", collapsed ? "Open sidebar" : "Collapse sidebar");
|
|
81
|
+
if (buttons[i].id === "sidebar-shell-expand-btn") {
|
|
82
|
+
var splitVisible = document.getElementById("split-host");
|
|
83
|
+
var desktop = !window.matchMedia || window.matchMedia("(min-width: 769px)").matches;
|
|
84
|
+
buttons[i].hidden = !collapsed || !desktop || !splitVisible || !splitVisible.classList.contains("visible");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
71
87
|
}
|
|
72
88
|
|
|
73
89
|
export function initSidebar(_ctx) {
|
|
@@ -106,6 +122,7 @@ export function initSidebar(_ctx) {
|
|
|
106
122
|
ctx.$("layout").classList.add("sidebar-collapsed");
|
|
107
123
|
}
|
|
108
124
|
} catch (e) {}
|
|
125
|
+
updateSidebarToggleState(ctx.$("layout").classList.contains("sidebar-collapsed"));
|
|
109
126
|
|
|
110
127
|
if (ctx.newSessionBtn) {
|
|
111
128
|
ctx.newSessionBtn.addEventListener("click", function () {
|