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
|
@@ -48,7 +48,7 @@ export function createPermissionControl(className, ariaLabel) {
|
|
|
48
48
|
control.innerHTML = '<div class="session-permission-segmented">' +
|
|
49
49
|
'<button type="button" data-permission-mode="default" aria-label="Ask permissions" title="Ask permissions"><span class="permission-label">Ask</span><span class="permission-spinner" aria-hidden="true"></span></button>' +
|
|
50
50
|
'<button type="button" data-permission-mode="auto" aria-label="Auto permissions" title="Auto permissions"><span class="permission-label">Auto</span><span class="permission-spinner" aria-hidden="true"></span></button>' +
|
|
51
|
-
'<button type="button" data-permission-mode="bypassPermissions" aria-label="
|
|
51
|
+
'<button type="button" data-permission-mode="bypassPermissions" aria-label="YOLO permissions" title="YOLO permissions"><span class="permission-label">YOLO</span><span class="permission-spinner" aria-hidden="true"></span></button>' +
|
|
52
52
|
'</div><span class="session-permission-status" aria-live="polite"></span>';
|
|
53
53
|
control.querySelector(".session-permission-status").setAttribute("aria-atomic", "true");
|
|
54
54
|
return control;
|
|
@@ -56,12 +56,12 @@ export function createPermissionControl(className, ariaLabel) {
|
|
|
56
56
|
|
|
57
57
|
function modeLabel(mode) {
|
|
58
58
|
var labels = { default: "Ask", plan: "Plan", acceptEdits: "Auto-accept edits", dontAsk: "Don't ask",
|
|
59
|
-
bypassPermissions: "
|
|
59
|
+
bypassPermissions: "YOLO permissions", auto: "Auto" };
|
|
60
60
|
return labels[mode] || mode;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
export function permissionStatusText(state, pending, autoSupported, autoVisible) {
|
|
64
|
-
if (state.globalPermissionModeForced) return "
|
|
64
|
+
if (state.globalPermissionModeForced) return "YOLO permissions forced by the server setting";
|
|
65
65
|
if (pending) return "Applying…";
|
|
66
66
|
if (state.permissionMode === "plan") return "Plan workflow";
|
|
67
67
|
if (state.permissionMode === "acceptEdits") return "Auto-accept edits workflow";
|
|
@@ -75,7 +75,7 @@ export function permissionStatusText(state, pending, autoSupported, autoVisible)
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
function compactPermissionStatusText(state, pending, autoSupported, autoVisible, fullText) {
|
|
78
|
-
if (state.globalPermissionModeForced) return "Forced
|
|
78
|
+
if (state.globalPermissionModeForced) return "Forced YOLO";
|
|
79
79
|
if (pending) return "Applying…";
|
|
80
80
|
if (state.permissionMode === "plan") return "Plan";
|
|
81
81
|
if (state.permissionMode === "acceptEdits") return "Auto-accept";
|
|
@@ -102,7 +102,7 @@ export function renderPermissionControl(control, state) {
|
|
|
102
102
|
control.classList.toggle("permission-globally-forced", globallyForced);
|
|
103
103
|
control.classList.toggle("permission-runtime-fallback", state.permissionMode === "auto" && !!state.effectivePermissionMode && state.effectivePermissionMode !== "auto");
|
|
104
104
|
control.setAttribute("aria-busy", pending ? "true" : "false");
|
|
105
|
-
var buttonTitles = { default: "Ask permissions", auto: "Auto permissions", bypassPermissions: "
|
|
105
|
+
var buttonTitles = { default: "Ask permissions", auto: "Auto permissions", bypassPermissions: "YOLO permissions" };
|
|
106
106
|
var buttons = control.querySelectorAll("[data-permission-mode]");
|
|
107
107
|
for (var i = 0; i < buttons.length; i++) {
|
|
108
108
|
var mode = buttons[i].dataset.permissionMode;
|
|
@@ -147,7 +147,7 @@ export function bindPermissionControl(control, getState) {
|
|
|
147
147
|
export function sendPermissionMode(projectSlug, sessionId, mode) {
|
|
148
148
|
var ws = getWs();
|
|
149
149
|
if (store.get('skipPermsEnabled') === true) {
|
|
150
|
-
showToast("
|
|
150
|
+
showToast("YOLO permissions are forced by the server setting.", "error");
|
|
151
151
|
return false;
|
|
152
152
|
}
|
|
153
153
|
if (!projectSlug || !sessionId || !ws || ws.readyState !== 1) {
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// Popup transcript presentation hooks. Tool identity, lifecycle and evidence
|
|
2
|
+
// stay owned by tools.js; this module only controls the compact activity view.
|
|
3
|
+
import { iconHtml } from './icons.js';
|
|
4
|
+
import { syncToolGroupDisclosure } from './clay-tool-activity.js';
|
|
5
|
+
|
|
6
|
+
// Mounted transcript nodes own completed records without retaining removed history.
|
|
7
|
+
var mountedToolGroups = new WeakMap();
|
|
8
|
+
export function rememberMountedToolGroup(group) { mountedToolGroups.set(group.el, group); }
|
|
9
|
+
export function mountedToolGroup(element) { return mountedToolGroups.get(element) || null; }
|
|
10
|
+
export function transcriptToolGroups(root) {
|
|
11
|
+
return root ? Array.from(root.querySelectorAll('.tool-group')).map(mountedToolGroup).filter(function (group) { return !!group; }) : [];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function setPopupChatRendering(enabled) {
|
|
15
|
+
if (typeof document === 'undefined' || !document.body) return false;
|
|
16
|
+
document.body.classList.toggle('popup-chat-rendering', enabled === true);
|
|
17
|
+
if (!enabled) {
|
|
18
|
+
var rows = document.querySelectorAll('.popup-chat-activity-row[hidden]');
|
|
19
|
+
for (var i = 0; i < rows.length; i++) rows[i].hidden = false;
|
|
20
|
+
var groups = document.querySelectorAll('.popup-chat-activity');
|
|
21
|
+
for (var j = 0; j < groups.length; j++) {
|
|
22
|
+
var items = groups[j].querySelector('.tool-group-items');
|
|
23
|
+
if (items) {
|
|
24
|
+
var expanded = !groups[j].classList.contains('collapsed');
|
|
25
|
+
items.inert = !expanded;
|
|
26
|
+
items.setAttribute('aria-hidden', String(!expanded));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return enabled === true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function shouldEnablePopupChatRendering(body, state, frame, identity) {
|
|
34
|
+
return !!(body && body.classList && body.classList.contains('contextual-chat-pane') && state && state.paneMode && frame &&
|
|
35
|
+
identity && identity.kind === 'mate' && identity.mateId && identity.mateId === frame.dataset.mateId &&
|
|
36
|
+
state.myUserId && state.sessionOriginId && state.paneSessionId && state.activeSessionId === state.paneSessionId &&
|
|
37
|
+
(!frame.dataset.expectedSessionOrigin || frame.dataset.expectedSessionOrigin === state.sessionOriginId));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function popupChatContextIsReady(body, state, frame, identity, surface) {
|
|
41
|
+
if (!body || !body.classList || !body.classList.contains('contextual-chat-pane')) return false;
|
|
42
|
+
if (!state || !state.paneMode || !state.connected || !state.myUserId || !state.contextualSessionReady) return false;
|
|
43
|
+
if (!state.sessionOriginId || state.activeSessionId !== state.paneSessionId) return false;
|
|
44
|
+
if (!frame || !frame.dataset || !frame.dataset.mateId || frame.dataset.ownerId !== state.myUserId) return false;
|
|
45
|
+
if (frame.hidden || frame.inert || !surface || surface.dataset.mode !== 'popover' || !surface.contains(frame)) return false;
|
|
46
|
+
if (String(frame.dataset.sessionId || '') !== String(state.paneSessionId || '')) return false;
|
|
47
|
+
if (frame.dataset.expectedSessionOrigin && frame.dataset.expectedSessionOrigin !== state.sessionOriginId) return false;
|
|
48
|
+
if (state.activeProjectMateId !== frame.dataset.mateId) return false;
|
|
49
|
+
if (!identity || identity.kind !== 'mate' || identity.mateId !== frame.dataset.mateId) return false;
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function popupChatRenderingEnabled(identity) {
|
|
54
|
+
return !!(identity && identity.kind === 'mate' && typeof document !== 'undefined' &&
|
|
55
|
+
document.body && document.body.classList.contains('popup-chat-rendering'));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function popupChatRenderingActive() {
|
|
59
|
+
return typeof document !== 'undefined' && document.body && document.body.classList.contains('popup-chat-rendering');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function reconcilePopupToolGroups(groups, active, activate, deactivate) {
|
|
63
|
+
for (var i = 0; i < groups.length; i++) {
|
|
64
|
+
var group = groups[i];
|
|
65
|
+
if (!group) continue;
|
|
66
|
+
if (active) {
|
|
67
|
+
group.popupChat = true;
|
|
68
|
+
activate(group);
|
|
69
|
+
} else if (group.popupChat) {
|
|
70
|
+
deactivate(group);
|
|
71
|
+
group.popupChat = false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function decorateTool(tool) {
|
|
77
|
+
if (!tool || !tool.el || tool.popupOriginalName !== undefined) return;
|
|
78
|
+
var name = tool.el.querySelector('.tool-name');
|
|
79
|
+
tool.popupOriginalName = name.textContent;
|
|
80
|
+
tool.popupOriginalHidden = tool.el.hidden;
|
|
81
|
+
tool.popupOriginalInert = tool.el.inert;
|
|
82
|
+
tool.el.classList.add('popup-chat-activity-row');
|
|
83
|
+
// Native descriptions own reference nodes and their handlers throughout mode changes.
|
|
84
|
+
if (!tool.clayActivity) {
|
|
85
|
+
tool.popupPresentedName = popupToolActionLabel(tool.name);
|
|
86
|
+
name.textContent = tool.popupPresentedName;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function decorateGroup(group) {
|
|
91
|
+
if (!group || !group.el || group.popupOriginalState) return;
|
|
92
|
+
var header = group.el.querySelector('.tool-group-header');
|
|
93
|
+
var label = group.el.querySelector('.tool-group-label');
|
|
94
|
+
group.popupOriginalState = {
|
|
95
|
+
label: label.textContent,
|
|
96
|
+
collapsed: group.el.classList.contains('collapsed'),
|
|
97
|
+
userToggled: !!group.userToggled,
|
|
98
|
+
groupDisplay: group.el.style.display,
|
|
99
|
+
headerDisplay: header.style.display,
|
|
100
|
+
headerAriaExpanded: header.getAttribute('aria-expanded'),
|
|
101
|
+
headerAriaLabel: header.getAttribute('aria-label'),
|
|
102
|
+
headerTitle: header.title
|
|
103
|
+
};
|
|
104
|
+
group.el.classList.add('popup-chat-activity');
|
|
105
|
+
var icon = document.createElement('span');
|
|
106
|
+
icon.className = 'popup-chat-activity-icon';
|
|
107
|
+
icon.setAttribute('aria-hidden', 'true');
|
|
108
|
+
icon.innerHTML = iconHtml('activity');
|
|
109
|
+
var heading = document.createElement('span');
|
|
110
|
+
heading.className = 'popup-chat-activity-heading';
|
|
111
|
+
heading.textContent = 'Activity';
|
|
112
|
+
label.textContent = '';
|
|
113
|
+
label.appendChild(heading);
|
|
114
|
+
var count = document.createElement('span');
|
|
115
|
+
count.className = 'popup-chat-activity-count';
|
|
116
|
+
count.setAttribute('aria-label', '0 recorded events');
|
|
117
|
+
header.insertBefore(icon, label);
|
|
118
|
+
header.insertBefore(count, header.querySelector('.tool-group-status-icon'));
|
|
119
|
+
for (var i = 0; i < group.toolIds.length; i++) decorateTool(group.toolRecords[group.toolIds[i]]);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function restoreGroup(group) {
|
|
123
|
+
if (!group || !group.el) return;
|
|
124
|
+
for (var i = 0; i < group.toolIds.length; i++) {
|
|
125
|
+
var tool = group.toolRecords[group.toolIds[i]];
|
|
126
|
+
if (!tool || !tool.el || tool.popupOriginalName === undefined) continue;
|
|
127
|
+
var name = tool.el.querySelector('.tool-name');
|
|
128
|
+
if (!tool.clayActivity && name.textContent === tool.popupPresentedName) name.textContent = tool.popupOriginalName;
|
|
129
|
+
tool.el.hidden = tool.popupOriginalHidden;
|
|
130
|
+
tool.el.inert = tool.popupOriginalInert;
|
|
131
|
+
tool.el.classList.remove('popup-chat-activity-row');
|
|
132
|
+
delete tool.popupOriginalName;
|
|
133
|
+
delete tool.popupPresentedName;
|
|
134
|
+
delete tool.popupOriginalHidden;
|
|
135
|
+
delete tool.popupOriginalInert;
|
|
136
|
+
}
|
|
137
|
+
var original = group.popupOriginalState;
|
|
138
|
+
group.popupOriginalState = null;
|
|
139
|
+
if (!original) return;
|
|
140
|
+
var label = group.el.querySelector('.tool-group-label');
|
|
141
|
+
var header = group.el.querySelector('.tool-group-header');
|
|
142
|
+
label.textContent = original.label;
|
|
143
|
+
group.el.style.display = original.groupDisplay;
|
|
144
|
+
header.style.display = original.headerDisplay;
|
|
145
|
+
group.userToggled = original.userToggled;
|
|
146
|
+
group.el.classList.toggle('collapsed', original.collapsed);
|
|
147
|
+
if (original.headerAriaLabel === null) header.removeAttribute('aria-label');
|
|
148
|
+
else header.setAttribute('aria-label', original.headerAriaLabel);
|
|
149
|
+
header.title = original.headerTitle;
|
|
150
|
+
var icon = header.querySelector('.popup-chat-activity-icon');
|
|
151
|
+
var count = header.querySelector('.popup-chat-activity-count');
|
|
152
|
+
if (icon) icon.remove();
|
|
153
|
+
if (count) count.remove();
|
|
154
|
+
group.el.classList.remove('popup-chat-activity');
|
|
155
|
+
syncToolGroupDisclosure(group.el);
|
|
156
|
+
if (original.headerAriaExpanded !== null) header.setAttribute('aria-expanded', original.headerAriaExpanded);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function syncPopupChatToolGroups(enabled, groups, identity) {
|
|
160
|
+
var active = !!(enabled && popupChatRenderingActive() && identity && identity.kind === 'mate');
|
|
161
|
+
reconcilePopupToolGroups(groups, active, function (group) {
|
|
162
|
+
decorateGroup(group);
|
|
163
|
+
for (var i = 0; i < group.toolIds.length; i++) decorateTool(group.toolRecords[group.toolIds[i]]);
|
|
164
|
+
group.el.style.display = '';
|
|
165
|
+
group.el.querySelector('.tool-group-header').style.display = '';
|
|
166
|
+
if (!group.userToggled) group.el.classList.add('collapsed');
|
|
167
|
+
syncToolGroupDisclosure(group.el);
|
|
168
|
+
syncPopupActivityGroup(group);
|
|
169
|
+
}, restoreGroup);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function hasIndependentControl(row) {
|
|
173
|
+
if (!row || !row.querySelectorAll) return false;
|
|
174
|
+
var controls = row.querySelectorAll('button, a[href], input, select, textarea, [role="button"], [role="menuitem"], [role="option"]');
|
|
175
|
+
var disclosure = row.querySelector('.tool-header');
|
|
176
|
+
if (row.querySelector('.tool-desc-link')) return true;
|
|
177
|
+
for (var i = 0; i < controls.length; i++) {
|
|
178
|
+
if (controls[i] !== disclosure) return true;
|
|
179
|
+
}
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function syncPopupActivityGroup(group) {
|
|
184
|
+
if (!group || !group.popupChat || !group.el) return false;
|
|
185
|
+
var header = group.el.querySelector('.tool-group-header');
|
|
186
|
+
var items = group.el.querySelector('.tool-group-items');
|
|
187
|
+
var count = group.el.querySelector('.popup-chat-activity-count');
|
|
188
|
+
if (!header || !items) return false;
|
|
189
|
+
|
|
190
|
+
var expanded = !group.el.classList.contains('collapsed');
|
|
191
|
+
var rows = items.children;
|
|
192
|
+
var firstVisible = Math.max(0, rows.length - 2);
|
|
193
|
+
var pinned = 0;
|
|
194
|
+
for (var i = 0; i < rows.length; i++) {
|
|
195
|
+
var row = rows[i];
|
|
196
|
+
var keepVisible = hasIndependentControl(row);
|
|
197
|
+
if (keepVisible) pinned++;
|
|
198
|
+
row.classList.add('popup-chat-activity-row');
|
|
199
|
+
row.hidden = !expanded && i < firstVisible && !keepVisible;
|
|
200
|
+
row.inert = row.hidden;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// The native group disclosure remains the one control for revealing older
|
|
204
|
+
// activity. In popup mode the visible recent rows stay reachable even while
|
|
205
|
+
// the rest of the group is collapsed.
|
|
206
|
+
items.inert = false;
|
|
207
|
+
items.setAttribute('aria-hidden', 'false');
|
|
208
|
+
header.setAttribute('aria-expanded', String(expanded));
|
|
209
|
+
header.setAttribute('aria-label', 'Activity, ' + group.toolCount + ' recorded events. ' + (expanded ? 'Hide older activity' : 'Show older activity'));
|
|
210
|
+
header.title = expanded ? 'Hide older activity' : 'Show older activity';
|
|
211
|
+
if (count) {
|
|
212
|
+
count.textContent = String(group.toolCount);
|
|
213
|
+
count.setAttribute('aria-label', group.toolCount + ' recorded events');
|
|
214
|
+
count.dataset.pinnedCount = String(pinned);
|
|
215
|
+
}
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function popupToolActionLabel(name) {
|
|
220
|
+
var labels = {
|
|
221
|
+
Read: 'Read', Edit: 'Edit', Write: 'Write', Bash: 'Command',
|
|
222
|
+
Grep: 'Search files', Glob: 'Find files', WebSearch: 'Web search',
|
|
223
|
+
WebFetch: 'Fetch page', Task: 'Task'
|
|
224
|
+
};
|
|
225
|
+
return labels[name] || name || 'Tool activity';
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function popupToolDescription(name, input) {
|
|
229
|
+
input = input && typeof input === 'object' ? input : {};
|
|
230
|
+
if (name === 'Read' || name === 'Edit' || name === 'Write') {
|
|
231
|
+
var filePath = typeof input.file_path === 'string' ? input.file_path : '';
|
|
232
|
+
var parts = filePath.split('/');
|
|
233
|
+
return parts.length > 3 ? '…/' + parts.slice(-3).join('/') : filePath;
|
|
234
|
+
}
|
|
235
|
+
if (name === 'Bash') return typeof input.description === 'string' ? input.description.slice(0, 100) : '';
|
|
236
|
+
if (name === 'Grep') return typeof input.pattern === 'string' ? input.pattern.slice(0, 100) : '';
|
|
237
|
+
if (name === 'Glob') return typeof input.pattern === 'string' ? input.pattern.slice(0, 100) : '';
|
|
238
|
+
if (name === 'WebSearch') return typeof input.query === 'string' ? input.query.slice(0, 100) : '';
|
|
239
|
+
if (name === 'WebFetch') {
|
|
240
|
+
try { return new URL(input.url).hostname; } catch (error) { return ''; }
|
|
241
|
+
}
|
|
242
|
+
if (name === 'Task') return typeof input.description === 'string' ? input.description.slice(0, 100) : '';
|
|
243
|
+
return '';
|
|
244
|
+
}
|
|
@@ -70,7 +70,7 @@ export function isProjectSessionReady(state, socket) {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
export function splitPanesMatchProject(split, slug) {
|
|
73
|
-
if (!split || !Array.isArray(split.panes) || split.panes.length < 2 ||
|
|
73
|
+
if (!split || !Array.isArray(split.panes) || split.panes.length < 2 || !slug) return false;
|
|
74
74
|
for (var i = 0; i < split.panes.length; i++) if (split.panes[i].slug !== slug) return false;
|
|
75
75
|
return true;
|
|
76
76
|
}
|
|
@@ -78,7 +78,7 @@ export function splitPanesMatchProject(split, slug) {
|
|
|
78
78
|
export function hasCurrentSplitGroup(split, groups) {
|
|
79
79
|
if (!split || !split.groupId) return true;
|
|
80
80
|
var members = split.panes && split.panes.map(function (pane) { return pane.sessionId; });
|
|
81
|
-
if (!members || members.length < 2
|
|
81
|
+
if (!members || members.length < 2) return false;
|
|
82
82
|
var available = Array.isArray(groups) ? groups : [];
|
|
83
83
|
for (var i = 0; i < available.length; i++) {
|
|
84
84
|
var group = available[i];
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ensurePaneFeatureAssets } from './pane-feature-assets.js';
|
|
2
|
+
import { store } from './store.js';
|
|
3
|
+
import { showToast, copyToClipboard } from './utils.js';
|
|
2
4
|
|
|
3
5
|
var activeShareUrl = null;
|
|
4
6
|
var activeShareTitle = null;
|
|
@@ -32,6 +34,15 @@ export function triggerShare(options) {
|
|
|
32
34
|
return;
|
|
33
35
|
}
|
|
34
36
|
|
|
37
|
+
if (typeof qrcode === 'undefined') {
|
|
38
|
+
var owner = store.get('myUserId'), slug = store.get('currentSlug'), generation = store.get('projectSplitViewSocketGeneration');
|
|
39
|
+
ensurePaneFeatureAssets('qr').then(function () {
|
|
40
|
+
if (owner === store.get('myUserId') && slug === store.get('currentSlug') && generation === store.get('projectSplitViewSocketGeneration')) triggerShare(options);
|
|
41
|
+
}).catch(function (error) {
|
|
42
|
+
if (owner === store.get('myUserId') && slug === store.get('currentSlug') && generation === store.get('projectSplitViewSocketGeneration')) showToast(error.message, 'error');
|
|
43
|
+
});
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
35
46
|
// Show QR overlay
|
|
36
47
|
var qrOverlay = document.getElementById("qr-overlay");
|
|
37
48
|
var qrCanvas = document.getElementById("qr-canvas");
|
|
@@ -1,8 +1,73 @@
|
|
|
1
1
|
// Single arbitration point for the mutually exclusive right-side workbench.
|
|
2
2
|
|
|
3
3
|
import { store } from './store.js';
|
|
4
|
+
import { iconHtml, refreshIcons } from './icons.js';
|
|
5
|
+
import { showToast } from './utils.js';
|
|
4
6
|
|
|
5
7
|
var closers = {};
|
|
8
|
+
var panelIds = { terminal: 'terminal-container', browser: 'shared-browser-panel', 'file-viewer': 'file-viewer',
|
|
9
|
+
'project-logs': 'project-logs-panel', you: 'you-panel', issues: 'issues-panel', linear: 'linear-panel',
|
|
10
|
+
'notes-browser': 'notes-browser', 'scheduled-tasks': 'scheduled-tasks-panel', debates: 'debates-workbench',
|
|
11
|
+
'mcp-skills': 'mcp-skills-workbench', 'mate-knowledge': 'mate-knowledge-workbench', wireframe: 'wireframe-panel' };
|
|
12
|
+
|
|
13
|
+
// Pane startup does not need account settings for a closed workbench.
|
|
14
|
+
function ensureLayoutPreference() {
|
|
15
|
+
var account = store.get('myUserId'), project = store.get('currentSlug');
|
|
16
|
+
return import('./workbench-layout-preference.js').then(function (api) {
|
|
17
|
+
if (account !== store.get('myUserId') || project !== store.get('currentSlug')) return false;
|
|
18
|
+
return api.ensureWorkbenchLayoutPreference();
|
|
19
|
+
}).catch(function () {
|
|
20
|
+
if (account !== store.get('myUserId') || project !== store.get('currentSlug')) return false;
|
|
21
|
+
store.set({ workbenchLayoutPreference: Object.assign({}, store.get('workbenchLayoutPreference') || {}, {
|
|
22
|
+
loading: false, saving: false, error: 'Workbench layout could not be loaded. Try again.'
|
|
23
|
+
}) });
|
|
24
|
+
return false;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function renderLayoutControls() {
|
|
29
|
+
if (typeof document === 'undefined') return;
|
|
30
|
+
var main = document.getElementById('main-panels');
|
|
31
|
+
if (!main) return;
|
|
32
|
+
var state = store.get('workbenchLayoutPreference') || {}, docked = state.mode === 'docked';
|
|
33
|
+
main.dataset.workbenchMode = docked ? 'docked' : 'overlay';
|
|
34
|
+
var panel = document.getElementById(panelIds[store.get('rightWorkbenchOwner')]);
|
|
35
|
+
if (!panel) return;
|
|
36
|
+
var header = panel.querySelector(':scope > header, :scope > .file-viewer-header .file-viewer-tabbar, :scope > .terminal-header');
|
|
37
|
+
if (!header) return;
|
|
38
|
+
var button = panel.querySelector('.workbench-layout-toggle');
|
|
39
|
+
if (!button) {
|
|
40
|
+
var actions = header.querySelector('[class*="window-actions"], .terminal-header-actions, .shared-browser-window, :scope.mcp-skills-header > div') || header;
|
|
41
|
+
button = document.createElement('button'); button.type = 'button'; button.className = 'workbench-layout-toggle';
|
|
42
|
+
button.addEventListener('click', function () {
|
|
43
|
+
var current = store.get('workbenchLayoutPreference') || {};
|
|
44
|
+
if (!current.ready || current.loading || current.saving) { ensureLayoutPreference(); return; }
|
|
45
|
+
var owner = store.get('myUserId'), project = store.get('currentSlug');
|
|
46
|
+
function currentTarget() { return button.isConnected && owner === store.get('myUserId') && project === store.get('currentSlug'); }
|
|
47
|
+
import('./workbench-layout-preference.js').then(function (api) {
|
|
48
|
+
var fresh = store.get('workbenchLayoutPreference') || {};
|
|
49
|
+
if (!currentTarget() || fresh.mode !== current.mode || fresh.revision !== current.revision || fresh.loading || fresh.saving || !fresh.ready) return false;
|
|
50
|
+
return api.saveWorkbenchLayoutMode(current.mode === 'docked' ? 'overlay' : 'docked');
|
|
51
|
+
}).then(function (saved) {
|
|
52
|
+
if (saved === false && currentTarget()) showToast((store.get('workbenchLayoutPreference') || {}).error || 'Workbench layout could not be saved. Try again.', 'error');
|
|
53
|
+
}).catch(function (error) {
|
|
54
|
+
if (currentTarget()) showToast(error.message || 'Workbench layout could not be saved. Try again.', 'error');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
var anchor = actions === header ? header.querySelector('[data-knowledge-wide]') : actions.firstElementChild;
|
|
58
|
+
actions.insertBefore(button, anchor || null);
|
|
59
|
+
}
|
|
60
|
+
if (button.dataset.mode !== (docked ? 'docked' : 'overlay')) {
|
|
61
|
+
button.dataset.mode = docked ? 'docked' : 'overlay';
|
|
62
|
+
button.innerHTML = iconHtml(docked ? 'layers-2' : 'panel-right');
|
|
63
|
+
refreshIcons();
|
|
64
|
+
}
|
|
65
|
+
button.setAttribute('aria-label', docked ? 'Float workbench over sessions' : 'Dock workbench beside sessions');
|
|
66
|
+
button.title = state.error || button.getAttribute('aria-label');
|
|
67
|
+
button.setAttribute('aria-pressed', docked ? 'true' : 'false');
|
|
68
|
+
button.disabled = !!state.loading || !!state.saving;
|
|
69
|
+
button.setAttribute('aria-busy', state.loading || state.saving ? 'true' : 'false');
|
|
70
|
+
}
|
|
6
71
|
|
|
7
72
|
export function registerRightWorkbench(name, close) {
|
|
8
73
|
if (!name || typeof close !== "function") return;
|
|
@@ -10,6 +75,7 @@ export function registerRightWorkbench(name, close) {
|
|
|
10
75
|
}
|
|
11
76
|
|
|
12
77
|
export function claimRightWorkbench(name) {
|
|
78
|
+
if (typeof document !== 'undefined') ensureLayoutPreference();
|
|
13
79
|
var previousRevision = store.get("rightWorkbenchRevision") || 0;
|
|
14
80
|
var nextRevision = previousRevision + 1;
|
|
15
81
|
storeSet(name, nextRevision);
|
|
@@ -18,6 +84,7 @@ export function claimRightWorkbench(name) {
|
|
|
18
84
|
if (keys[i] === name) continue;
|
|
19
85
|
try { closers[keys[i]](); } catch (error) { /* one panel must not block another */ }
|
|
20
86
|
}
|
|
87
|
+
if (typeof requestAnimationFrame === 'function') requestAnimationFrame(renderLayoutControls);
|
|
21
88
|
return nextRevision;
|
|
22
89
|
}
|
|
23
90
|
|
|
@@ -39,6 +106,7 @@ export function resetRightWorkbench() {
|
|
|
39
106
|
}
|
|
40
107
|
|
|
41
108
|
store.subscribe(function (state, previous) {
|
|
109
|
+
if (state.workbenchLayoutPreference !== previous.workbenchLayoutPreference || state.rightWorkbenchOwner !== previous.rightWorkbenchOwner) renderLayoutControls();
|
|
42
110
|
if (state.currentSlug === previous.currentSlug && state.myUserId === previous.myUserId) return;
|
|
43
111
|
resetRightWorkbench();
|
|
44
112
|
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
|
|
3
|
+
export var SEARCH_HINT_INTERVAL = 180000;
|
|
4
|
+
export var SEARCH_HINT_QUESTIONS = Object.freeze([
|
|
5
|
+
'What was I working on recently?',
|
|
6
|
+
'Where did I leave off yesterday?',
|
|
7
|
+
'What did I review recently?',
|
|
8
|
+
"Find the session I said I'd return to.",
|
|
9
|
+
'What work did I leave unfinished?',
|
|
10
|
+
'What did we decide about this project?',
|
|
11
|
+
'Why did we choose this approach?',
|
|
12
|
+
'Find the conversation about this bug.',
|
|
13
|
+
'Which sessions need my attention?',
|
|
14
|
+
"What's happening across this view?",
|
|
15
|
+
'What are my Workers doing?',
|
|
16
|
+
'Which sessions are waiting for approval?'
|
|
17
|
+
]);
|
|
18
|
+
export function searchHintText(index) {
|
|
19
|
+
return 'Search or ask Clay, like “' + SEARCH_HINT_QUESTIONS[index % SEARCH_HINT_QUESTIONS.length] + '”';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
var trigger = null, input = null, timer = null, unsubscribe = null;
|
|
23
|
+
function relevantFocus(target) { return !!target && (target === input || trigger && trigger.contains(target)); }
|
|
24
|
+
function available() {
|
|
25
|
+
return trigger && input && !document.hidden && !store.get('paneMode') && window === window.top &&
|
|
26
|
+
!store.get('searchHintsPaletteOpen') && !store.get('searchHintsHovered') && !store.get('searchHintsFocused');
|
|
27
|
+
}
|
|
28
|
+
function cancelTimer() { if (timer !== null) { clearTimeout(timer); timer = null; } }
|
|
29
|
+
function paint() {
|
|
30
|
+
var label = trigger.querySelector('.cmd-palette-searchbar-text');
|
|
31
|
+
var text = searchHintText(store.get('searchHintIndex') || 0);
|
|
32
|
+
if (label) label.textContent = text;
|
|
33
|
+
input.placeholder = text;
|
|
34
|
+
}
|
|
35
|
+
function schedule() {
|
|
36
|
+
if (!available()) { cancelTimer(); return; }
|
|
37
|
+
if (timer !== null) return;
|
|
38
|
+
timer = setTimeout(function () {
|
|
39
|
+
timer = null;
|
|
40
|
+
if (!available()) return;
|
|
41
|
+
store.set({ searchHintIndex: ((store.get('searchHintIndex') || 0) + 1) % SEARCH_HINT_QUESTIONS.length });
|
|
42
|
+
paint(); schedule();
|
|
43
|
+
}, SEARCH_HINT_INTERVAL);
|
|
44
|
+
}
|
|
45
|
+
function visibilityChanged() { cancelTimer(); schedule(); }
|
|
46
|
+
function enter() { store.set({ searchHintsHovered: true }); }
|
|
47
|
+
function leave() { store.set({ searchHintsHovered: false }); }
|
|
48
|
+
function focusIn(event) { store.set({ searchHintsFocused: relevantFocus(event.target) }); }
|
|
49
|
+
function focusOut(event) { store.set({ searchHintsFocused: relevantFocus(event.relatedTarget) }); }
|
|
50
|
+
export function setSearchHintsPaletteOpen(open) { store.set({ searchHintsPaletteOpen: open === true }); }
|
|
51
|
+
export function disposeSearchHints() {
|
|
52
|
+
cancelTimer();
|
|
53
|
+
if (!trigger) return;
|
|
54
|
+
trigger.removeEventListener('mouseenter', enter); trigger.removeEventListener('mouseleave', leave);
|
|
55
|
+
document.removeEventListener('focusin', focusIn); document.removeEventListener('focusout', focusOut);
|
|
56
|
+
document.removeEventListener('visibilitychange', visibilityChanged);
|
|
57
|
+
if (unsubscribe) unsubscribe();
|
|
58
|
+
trigger = null; input = null; unsubscribe = null;
|
|
59
|
+
}
|
|
60
|
+
export function initSearchHints(button, field) {
|
|
61
|
+
if (button) button.removeAttribute('title');
|
|
62
|
+
if (trigger === button && input === field) return;
|
|
63
|
+
disposeSearchHints();
|
|
64
|
+
if (!button || !field) return;
|
|
65
|
+
trigger = button; input = field;
|
|
66
|
+
store.set({ searchHintIndex: store.get('searchHintIndex') || 0, searchHintsPaletteOpen: false,
|
|
67
|
+
searchHintsHovered: trigger.matches(':hover'), searchHintsFocused: relevantFocus(document.activeElement) });
|
|
68
|
+
if (!store.get('searchHintsFocused')) paint();
|
|
69
|
+
trigger.addEventListener('mouseenter', enter); trigger.addEventListener('mouseleave', leave);
|
|
70
|
+
document.addEventListener('focusin', focusIn); document.addEventListener('focusout', focusOut);
|
|
71
|
+
document.addEventListener('visibilitychange', visibilityChanged);
|
|
72
|
+
unsubscribe = store.subscribe(function (state, previous) {
|
|
73
|
+
if (state.paneMode !== previous.paneMode || state.searchHintsPaletteOpen !== previous.searchHintsPaletteOpen ||
|
|
74
|
+
state.searchHintsHovered !== previous.searchHintsHovered || state.searchHintsFocused !== previous.searchHintsFocused) {
|
|
75
|
+
cancelTimer(); schedule();
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
schedule();
|
|
79
|
+
}
|
|
@@ -22,6 +22,7 @@ export function closeSessionContextMenu(restoreFocus) {
|
|
|
22
22
|
document.removeEventListener("keydown", onMenuKey, true);
|
|
23
23
|
window.removeEventListener("blur", onWindowBlur);
|
|
24
24
|
if (state.anchor) state.anchor.setAttribute("aria-expanded", "false");
|
|
25
|
+
state.el.hidden = true;
|
|
25
26
|
if (state.el && state.el.isConnected) state.el.remove();
|
|
26
27
|
if (restoreFocus !== false && state.anchor && state.anchor.isConnected) {
|
|
27
28
|
state.anchor.focus({ preventScroll: true });
|
|
@@ -54,10 +55,40 @@ function onMenuKey(event) {
|
|
|
54
55
|
event.preventDefault();
|
|
55
56
|
event.stopPropagation();
|
|
56
57
|
closeSessionContextMenu(true);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
var items = Array.from(state.el.querySelectorAll('button:not([disabled])')), index = items.indexOf(document.activeElement), next = null;
|
|
61
|
+
if (!items.length) return;
|
|
62
|
+
if (event.key === 'ArrowDown' || event.key === 'Tab') next = (index + (event.shiftKey ? -1 : 1) + items.length) % items.length;
|
|
63
|
+
else if (event.key === 'ArrowUp') next = (index - 1 + items.length) % items.length;
|
|
64
|
+
else if (event.key === 'Home') next = 0;
|
|
65
|
+
else if (event.key === 'End') next = items.length - 1;
|
|
66
|
+
if (next !== null) { event.preventDefault(); event.stopPropagation(); items[next].focus({ preventScroll: true }); }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function positionSessionContextMenu(menu, anchor, point) {
|
|
70
|
+
var rect = anchor.getBoundingClientRect(), box = menu.getBoundingClientRect();
|
|
71
|
+
var x = point ? point.x : rect.left, y = point ? point.y : rect.bottom + 2;
|
|
72
|
+
menu.style.left = Math.max(8, Math.min(x, window.innerWidth - box.width - 8)) + 'px';
|
|
73
|
+
menu.style.top = Math.max(8, Math.min(y, window.innerHeight - box.height - 8)) + 'px';
|
|
74
|
+
menu.style.right = 'auto';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function reconcileSessionContextMenu() {
|
|
78
|
+
var state = menuState(); if (!state) return;
|
|
79
|
+
var anchor = state.resolveAnchor ? state.resolveAnchor() : state.anchor;
|
|
80
|
+
if (!anchor || !anchor.isConnected || state.slug !== store.get('currentSlug') || state.viewerId !== store.get('myUserId') || state.ws !== getWs()) {
|
|
81
|
+
closeSessionContextMenu(false); return;
|
|
57
82
|
}
|
|
83
|
+
if (anchor === state.anchor) return;
|
|
84
|
+
state.anchor.setAttribute('aria-expanded', 'false');
|
|
85
|
+
var hadTabIndex = anchor.hasAttribute('tabindex'), tabIndex = anchor.getAttribute('tabindex');
|
|
86
|
+
anchor.setAttribute('aria-haspopup', 'menu'); anchor.setAttribute('aria-expanded', 'true');
|
|
87
|
+
if (!hadTabIndex) anchor.setAttribute('tabindex', '-1');
|
|
88
|
+
store.set({ sessionContextMenu: Object.assign({}, state, { anchor: anchor, anchorHadTabIndex: hadTabIndex, anchorTabIndex: tabIndex }) });
|
|
58
89
|
}
|
|
59
90
|
|
|
60
|
-
export function mountSessionContextMenu(menu, anchor, sessionId) {
|
|
91
|
+
export function mountSessionContextMenu(menu, anchor, sessionId, options) {
|
|
61
92
|
closeSessionContextMenu(false);
|
|
62
93
|
menu.setAttribute("role", "menu");
|
|
63
94
|
var items = menu.querySelectorAll("button");
|
|
@@ -76,6 +107,8 @@ export function mountSessionContextMenu(menu, anchor, sessionId) {
|
|
|
76
107
|
anchorHadTabIndex: anchorHadTabIndex,
|
|
77
108
|
anchorTabIndex: anchorTabIndex,
|
|
78
109
|
sessionId: sessionId || null,
|
|
110
|
+
resolveAnchor: options && options.resolveAnchor || null,
|
|
111
|
+
slug: store.get('currentSlug'), viewerId: store.get('myUserId'), ws: getWs(),
|
|
79
112
|
} });
|
|
80
113
|
document.addEventListener("pointerdown", onOutsidePress, true);
|
|
81
114
|
document.addEventListener("mousedown", onOutsidePress, true);
|
|
@@ -205,7 +238,7 @@ export function startHeaderSessionInlineRename(title, control, session) {
|
|
|
205
238
|
}
|
|
206
239
|
|
|
207
240
|
store.subscribe(function (state, previous) {
|
|
208
|
-
if (state.currentSlug !== previous.currentSlug || state.dmMode !== previous.dmMode ||
|
|
241
|
+
if (state.currentSlug !== previous.currentSlug || state.myUserId !== previous.myUserId || state.dmMode !== previous.dmMode ||
|
|
209
242
|
state.sessionActionEpoch !== previous.sessionActionEpoch || state.panePendingBinding !== previous.panePendingBinding ||
|
|
210
243
|
(previous.connected && !state.connected)) {
|
|
211
244
|
closeSessionContextMenu(false);
|
|
@@ -89,7 +89,8 @@ function setAccessibleState(root, body, toggle, open) {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
function finish(key, token, root, body, toggle, open) {
|
|
92
|
-
if (currentRecord(key, token)
|
|
92
|
+
if (!currentRecord(key, token) || !root.isConnected) return;
|
|
93
|
+
putRecord(key, null);
|
|
93
94
|
cleanBody(body);
|
|
94
95
|
setAccessibleState(root, body, toggle, open);
|
|
95
96
|
body.hidden = !open;
|