clay-server 5.2.1-beta.2 → 5.3.0-beta.1
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 +158 -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 -5
- package/lib/public/css/popup-chat.css +123 -0
- package/lib/public/css/right-workbench.css +50 -18
- package/lib/public/css/split-view-layout.css +460 -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-messages.js +25 -8
- package/lib/public/modules/chat-render-runtime.js +45 -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/generation-stop.js +7 -1
- package/lib/public/modules/knowledge-workbench-entry.js +43 -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-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-context.js +24 -13
- package/lib/public/modules/session-folder-delete.js +18 -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 +254 -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 +495 -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 +352 -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/pane-assets/app-OJEQXBBS.js +1 -0
- package/lib/public/pane-assets/chunk-4SMIVAB4.js +1 -0
- package/lib/public/pane-assets/chunk-623V5CEO.js +1 -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-DGOFGEUK.js +1 -0
- package/lib/public/pane-assets/chunk-GML2R5JE.js +167 -0
- package/lib/public/pane-assets/chunk-I57URYSL.js +1 -0
- package/lib/public/pane-assets/chunk-I5P3MWQG.js +1 -0
- package/lib/public/pane-assets/chunk-UVGOPFEX.js +2 -0
- package/lib/public/pane-assets/manifest.json +551 -0
- package/lib/public/pane-assets/style-GN7R6XKV.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 +15 -0
- package/package.json +6 -3
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { getWs } from './ws-ref.js';
|
|
3
|
+
import { refreshIcons } from './icons.js';
|
|
4
|
+
import { showToast } from './utils.js';
|
|
5
|
+
import { ensurePaneFeatureAssets } from './pane-feature-assets.js';
|
|
6
|
+
|
|
7
|
+
var knowledgeWorkbenchModule = null;
|
|
8
|
+
|
|
9
|
+
export function ensureKnowledgeButton() {
|
|
10
|
+
var button = document.getElementById('knowledge-workbench-btn'); if (button) return button;
|
|
11
|
+
var tools = document.getElementById('session-actions'); if (!tools) return null;
|
|
12
|
+
button = document.createElement('button'); button.id = 'knowledge-workbench-btn'; button.type = 'button'; button.className = 'palette-tile'; button.title = 'Knowledge'; button.setAttribute('aria-label', 'Knowledge'); button.innerHTML = '<i data-lucide="book-open"></i><span class="tool-btn-label">Knowledge</span>';
|
|
13
|
+
var fixed = tools.querySelector('[data-fixed="true"]');
|
|
14
|
+
tools.insertBefore(button, fixed ? fixed.nextSibling : tools.firstChild); refreshIcons(button); return button;
|
|
15
|
+
}
|
|
16
|
+
function sameContext(receipt) {
|
|
17
|
+
var state = store.snap();
|
|
18
|
+
return receipt.owner === state.myUserId && receipt.slug === state.currentSlug && receipt.socket === getWs() &&
|
|
19
|
+
receipt.generation === state.projectSplitViewSocketGeneration && receipt.binding === state.paneCommittedBinding && state.connected;
|
|
20
|
+
}
|
|
21
|
+
export function initMateKnowledgeWorkbench() {
|
|
22
|
+
var button = ensureKnowledgeButton(); if (!button || store.get('knowledgeWorkbenchEntryInitialized')) return;
|
|
23
|
+
store.set({ knowledgeWorkbenchEntryInitialized: true });
|
|
24
|
+
button.addEventListener('click', open);
|
|
25
|
+
function open(event) {
|
|
26
|
+
event.stopImmediatePropagation();
|
|
27
|
+
if (button.disabled) return;
|
|
28
|
+
var state = store.snap(), receipt = { owner: state.myUserId, slug: state.currentSlug, socket: getWs(), generation: state.projectSplitViewSocketGeneration, binding: state.paneCommittedBinding };
|
|
29
|
+
button.disabled = true;
|
|
30
|
+
Promise.all([import('./mate-knowledge-workbench.js'), ensurePaneFeatureAssets('editor')]).then(function (loaded) {
|
|
31
|
+
var module = loaded[0];
|
|
32
|
+
if (!knowledgeWorkbenchModule) {
|
|
33
|
+
module.initMateKnowledgeWorkbench(); knowledgeWorkbenchModule = module;
|
|
34
|
+
button.removeEventListener('click', open);
|
|
35
|
+
}
|
|
36
|
+
if (sameContext(receipt)) module.openKnowledgeWorkbench();
|
|
37
|
+
}).catch(function (error) { if (sameContext(receipt)) showToast(error.message, 'error'); }).finally(function () { button.disabled = false; });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function handleKnowledgeWorkbenchMessage(message) {
|
|
41
|
+
var module = knowledgeWorkbenchModule;
|
|
42
|
+
return module ? module.handleKnowledgeWorkbenchMessage(message) : false;
|
|
43
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ensurePaneFeatureAssets } from './pane-feature-assets.js';
|
|
1
2
|
import { enhanceSessionCitations } from './clay-session-links.js';
|
|
2
3
|
import { renderBrowserLink, enhanceBrowserLinks } from './clay-browser-links.js';
|
|
3
4
|
import { renderWireframeBlocks } from './wireframe-chat.js';
|
|
@@ -41,6 +42,7 @@ marked.use({
|
|
|
41
42
|
var mermaidInitialized = false;
|
|
42
43
|
|
|
43
44
|
export function updateMermaidTheme(vars) {
|
|
45
|
+
if (typeof mermaid === "undefined") return;
|
|
44
46
|
mermaidInitialized = true;
|
|
45
47
|
mermaid.initialize({
|
|
46
48
|
startOnLoad: false,
|
|
@@ -140,6 +142,15 @@ export function renderMermaidBlocks(el) {
|
|
|
140
142
|
if (store.get('replayingHistory')) return;
|
|
141
143
|
renderWireframeBlocks(el);
|
|
142
144
|
var blocks = el.querySelectorAll("pre code.language-mermaid");
|
|
145
|
+
if (blocks.length && typeof mermaid === 'undefined') {
|
|
146
|
+
var binding = store.get('panePendingBinding') || store.get('paneCommittedBinding'), origin = store.get('sessionOriginId');
|
|
147
|
+
var owner = store.get('myUserId'), slug = store.get('currentSlug'), generation = store.get('projectSplitViewSocketGeneration');
|
|
148
|
+
ensurePaneFeatureAssets('mermaid').then(function () {
|
|
149
|
+
if (el.isConnected && binding === (store.get('panePendingBinding') || store.get('paneCommittedBinding')) && origin === store.get('sessionOriginId') &&
|
|
150
|
+
owner === store.get('myUserId') && slug === store.get('currentSlug') && generation === store.get('projectSplitViewSocketGeneration')) renderMermaidBlocks(el);
|
|
151
|
+
}).catch(function () { /* Preserve readable source when diagram assets fail. */ });
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
143
154
|
blocks.forEach(function (codeEl) {
|
|
144
155
|
var pre = codeEl.parentElement;
|
|
145
156
|
if (!pre || pre.dataset.mermaidProcessed) return;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ensureKnowledgeButton as ensureButton } from './knowledge-workbench-entry.js';
|
|
1
2
|
import { store } from './store.js';
|
|
2
3
|
import { getWs } from './ws-ref.js';
|
|
3
4
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
@@ -93,13 +94,7 @@ function ensurePanel() {
|
|
|
93
94
|
var newButton = panel.querySelector('[data-knowledge-new]'); newButton.insertAdjacentHTML('beforebegin', '<button type="button" data-knowledge-back title="Back" aria-label="Previous document">' + iconHtml('arrow-left') + '</button><button type="button" data-knowledge-forward title="Forward" aria-label="Next document">' + iconHtml('arrow-right') + '</button>');
|
|
94
95
|
host.appendChild(panel); bindPanel(); refreshIcons(panel);
|
|
95
96
|
}
|
|
96
|
-
|
|
97
|
-
var button = document.getElementById('knowledge-workbench-btn'); if (button) return button;
|
|
98
|
-
var tools = document.getElementById('session-actions'); if (!tools) return null;
|
|
99
|
-
button = document.createElement('button'); button.id = 'knowledge-workbench-btn'; button.type = 'button'; button.className = 'palette-tile'; button.title = 'Knowledge'; button.setAttribute('aria-label', 'Knowledge'); button.innerHTML = '<i data-lucide="book-open"></i><span class="tool-btn-label">Knowledge</span>';
|
|
100
|
-
var fixed = tools.querySelector('[data-fixed="true"]');
|
|
101
|
-
tools.insertBefore(button, fixed ? fixed.nextSibling : tools.firstChild); refreshIcons(button); return button;
|
|
102
|
-
}
|
|
97
|
+
|
|
103
98
|
function bindPanel() {
|
|
104
99
|
panel.querySelector('[data-knowledge-close]').addEventListener('click', closeKnowledgeWorkbench);
|
|
105
100
|
panel.querySelector('[data-knowledge-new]').addEventListener('click', function () { showKnowledgeCreateMenu(panel); });
|
|
@@ -406,7 +406,7 @@ function appendMateSessionTitleLine(el, textSpan) {
|
|
|
406
406
|
|
|
407
407
|
export function renderMateSessionList(sessions) {
|
|
408
408
|
if (sessions) cachedSessions = sessions;
|
|
409
|
-
if (!listEl) return;
|
|
409
|
+
if (!listEl || (store.get('paneMode') && !store.get('activeProjectMateId') && !store.get('dmMode') && !store.get('paneActivationUnsupported') && window.frameElement && window.frameElement.clayPane && !new URLSearchParams(location.search).has('contextual'))) return;
|
|
410
410
|
listEl.innerHTML = "";
|
|
411
411
|
|
|
412
412
|
if (cachedSessions.length === 0) {
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Palette selection resolves one existing private conversation before presentation.
|
|
2
|
+
import { store } from './store.js';
|
|
3
|
+
import { getWs } from './ws-ref.js';
|
|
4
|
+
import { contextualMate, contextualMateStamp, contextualFrame, contextualTrigger } from './contextual-mate-identity.js';
|
|
5
|
+
import { selectContextualConversation, enterComposer } from './contextual-clay.js';
|
|
6
|
+
import { pauseContextualSearchForSelection } from './contextual-search.js';
|
|
7
|
+
import { closeCommandPalette } from './command-palette.js';
|
|
8
|
+
import { showToast } from './utils.js';
|
|
9
|
+
|
|
10
|
+
var socket = null, timer = null, surface = null;
|
|
11
|
+
function current() { return store.get('paletteClayConversation'); }
|
|
12
|
+
function parentView() { return JSON.stringify([store.get('currentSlug'), store.get('activeSessionId'), store.get('projectSplitViewActiveId')]); }
|
|
13
|
+
function selection() { return JSON.stringify(store.get('contextualClay') || null); }
|
|
14
|
+
function warn(message) { showToast(message || 'This conversation is unavailable. Refresh search and try again.', 'warn'); }
|
|
15
|
+
function clear() {
|
|
16
|
+
clearTimeout(timer); timer = null; socket = null;
|
|
17
|
+
if (surface) { surface.removeEventListener('contextual-clay-ready', ready); surface.removeEventListener('contextual-ask-stopped', stopped); }
|
|
18
|
+
surface = null; store.set({ paletteClayConversation: null });
|
|
19
|
+
}
|
|
20
|
+
function stopped() { if (current()) { clear(); warn('Opening was stopped. Select the conversation again to resume.'); } }
|
|
21
|
+
function live(request) {
|
|
22
|
+
var mate = contextualMate(request.mateId);
|
|
23
|
+
return store.get('connected') && socket === getWs() && socket && socket.readyState === 1 && request.ownerId === store.get('myUserId') && mate && !mate.lifecycleDeletion && mate.builtinKey === 'clay' && mate.createdBy === request.ownerId && contextualMateStamp(mate) === request.stamp && parentView() === request.view;
|
|
24
|
+
}
|
|
25
|
+
function guard() {
|
|
26
|
+
var action = store.get('contextualConversationRequest'), popup = store.get('contextualClay') || {};
|
|
27
|
+
if (action && action.status === 'loading' || popup.startingAsk || popup.mode === 'opening') { warn('Wait for the current conversation request to finish.'); return false; }
|
|
28
|
+
try { var frame = contextualFrame(); if (frame && frame.conversation) frame.conversation.checkDraft(); return true; }
|
|
29
|
+
catch (error) { warn(error.message); return false; }
|
|
30
|
+
}
|
|
31
|
+
export function openPaletteClayConversation(item, query, entryIndex) {
|
|
32
|
+
var mateId = item.mateId || String(item.projectSlug || '').slice(5);
|
|
33
|
+
var mate = contextualMate(mateId), owner = store.get('myUserId');
|
|
34
|
+
if (!mate || mate.lifecycleDeletion || item.projectSlug !== 'mate-' + mateId) { warn(); return true; }
|
|
35
|
+
if (mate.builtinKey !== 'clay') return false;
|
|
36
|
+
if (!owner || mate.createdBy !== owner || !Number.isSafeInteger(item.sessionId) || item.sessionId <= 0 || typeof item.sessionOriginId !== 'string' || !item.sessionOriginId) { warn(); return true; }
|
|
37
|
+
if (current()) { warn('Wait for the current conversation request to finish.'); return true; }
|
|
38
|
+
if (!guard()) return true;
|
|
39
|
+
socket = getWs();
|
|
40
|
+
if (!store.get('connected') || !socket || socket.readyState !== 1) { socket = null; warn('Reconnect before opening this conversation.'); return true; }
|
|
41
|
+
var request = { requestId: 'mate-conversations-' + crypto.randomUUID(), phase: 'resolving', mateId: mate.id, stamp: contextualMateStamp(mate), ownerId: owner,
|
|
42
|
+
sessionId: item.sessionId, sessionOriginId: item.sessionOriginId, view: parentView(), selection: selection(), query: query || null,
|
|
43
|
+
entryIndex: entryIndex != null && Number.isSafeInteger(Number(entryIndex)) && Number(entryIndex) >= 0 ? Number(entryIndex) : null };
|
|
44
|
+
store.set({ paletteClayConversation: request });
|
|
45
|
+
surface = document.getElementById('contextual-clay');
|
|
46
|
+
if (surface) surface.addEventListener('contextual-ask-stopped', stopped);
|
|
47
|
+
timer = setTimeout(function () { if (current() && current().requestId === request.requestId) { clear(); warn('The conversation did not become ready. Select it again to retry.'); } }, 15000);
|
|
48
|
+
try { socket.send(JSON.stringify({ type: 'home_mate_session_open', presentation: 'contextual', requestId: request.requestId, mateId: request.mateId, mateStamp: request.stamp, sessionId: 'local:' + request.sessionId, sessionOriginId: request.sessionOriginId })); }
|
|
49
|
+
catch (error) { clear(); warn('The connection changed. Select the conversation again after reconnecting.'); }
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
export function handlePaletteClayConversation(msg) {
|
|
53
|
+
var request = current();
|
|
54
|
+
if (msg.type !== 'contextual_mate_sessions_result' || !request || msg.requestId !== request.requestId) return false;
|
|
55
|
+
if (request.phase !== 'resolving') return true;
|
|
56
|
+
if (!live(request) || request.selection !== selection() || msg.mateId !== request.mateId || msg.mateStamp !== request.stamp || !msg.ok || msg.sessionId !== request.sessionId || msg.sessionOriginId !== request.sessionOriginId) { clear(); warn(); return true; }
|
|
57
|
+
if (!guard()) { clear(); return true; }
|
|
58
|
+
pauseContextualSearchForSelection();
|
|
59
|
+
store.set({ paletteClayConversation: Object.assign({}, request, { phase: 'selecting' }) });
|
|
60
|
+
var selected = false;
|
|
61
|
+
try { selected = selectContextualConversation(contextualMate(request.mateId), request.sessionId, request.sessionOriginId); } catch (error) { /* An unavailable child must not fall back to native navigation. */ }
|
|
62
|
+
if (!selected || !current() || !live(request)) { clear(); warn('This conversation could not be opened. Your draft is retained.'); return true; }
|
|
63
|
+
store.set({ paletteClayConversation: Object.assign({}, request, { phase: 'opening', selection: selection() }) });
|
|
64
|
+
closeCommandPalette();
|
|
65
|
+
if (surface) surface.removeEventListener('contextual-ask-stopped', stopped);
|
|
66
|
+
surface = document.getElementById('contextual-clay');
|
|
67
|
+
if (surface) { var trigger = contextualTrigger(request.mateId); if (trigger) { surface.dataset.focusEntry = 'true'; trigger.focus({ preventScroll: true }); enterComposer(); } surface.addEventListener('contextual-clay-ready', ready); surface.addEventListener('contextual-ask-stopped', stopped); }
|
|
68
|
+
ready();
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
function ready() {
|
|
72
|
+
var request = current();
|
|
73
|
+
if (!request || request.phase !== 'opening') return;
|
|
74
|
+
if (!live(request) || request.selection !== selection()) { clear(); warn('Your view changed. Select the conversation again.'); return; }
|
|
75
|
+
var frame = contextualFrame(request.mateId, request.sessionId), controller = frame && frame.conversation;
|
|
76
|
+
if (!controller || controller.source !== frame.contentWindow || frame.hidden || frame.inert) return;
|
|
77
|
+
var identity = controller.identity();
|
|
78
|
+
if (!identity.ready) return;
|
|
79
|
+
if (identity.ownerId !== request.ownerId || identity.mateId !== request.mateId || identity.sessionId !== request.sessionId || identity.sessionOriginId !== request.sessionOriginId || !identity.connected || identity.available === false) { clear(); warn(); return; }
|
|
80
|
+
if ((request.query || request.entryIndex !== null) && (!controller.navigateEntry || !controller.navigateEntry(request))) { clear(); warn('The conversation opened, but the requested passage could not be located.'); return; }
|
|
81
|
+
clear();
|
|
82
|
+
}
|
|
83
|
+
store.subscribe(function () {
|
|
84
|
+
var request = current();
|
|
85
|
+
if (!request && socket) { clear(); return; }
|
|
86
|
+
if (request && (!live(request) || request.phase !== 'selecting' && request.selection !== selection())) { clear(); warn('Your view or connection changed. Select the conversation again.'); }
|
|
87
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function formatRelativeDate(ts) {
|
|
2
|
+
if (!ts) return "";
|
|
3
|
+
var diff = Math.max(0, Date.now() - ts);
|
|
4
|
+
var min = Math.floor(diff / 60000);
|
|
5
|
+
if (min < 1) return "just now";
|
|
6
|
+
if (min < 60) return min + "m ago";
|
|
7
|
+
var hr = Math.floor(min / 60);
|
|
8
|
+
if (hr < 24) return hr + "h ago";
|
|
9
|
+
var days = Math.floor(hr / 24);
|
|
10
|
+
if (days < 7) return days + "d ago";
|
|
11
|
+
if (days < 30) return Math.floor(days / 7) + "w ago";
|
|
12
|
+
var d = new Date(ts);
|
|
13
|
+
var months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
|
|
14
|
+
return months[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
export function paletteSelectionIndex(previous, items, query) {
|
|
19
|
+
var index = previous ? items.findIndex(function (item) {
|
|
20
|
+
if (item.type !== previous.type || item.query !== previous.query) return false;
|
|
21
|
+
if (!item.data || !previous.data) return !item.data && !previous.data;
|
|
22
|
+
return item.data.projectSlug === previous.data.projectSlug && item.data.sessionId === previous.data.sessionId && item.data.sessionOriginId === previous.data.sessionOriginId;
|
|
23
|
+
}) : -1;
|
|
24
|
+
return index >= 0 ? index : query ? 0 : -1;
|
|
25
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { disarmStickyBottom } from './chat-render-runtime.js';
|
|
1
2
|
import { store } from './store.js';
|
|
2
3
|
import { getWs } from './ws-ref.js';
|
|
3
4
|
import { getCachedSessions } from './sidebar-sessions.js';
|
|
@@ -15,34 +16,58 @@ function block(value) {
|
|
|
15
16
|
document.documentElement.dataset.paneReady = value ? 'false' : 'true';
|
|
16
17
|
}
|
|
17
18
|
function draftKey() { return String(store.get('myUserId')) + ':' + store.get('sessionOriginId'); }
|
|
19
|
+
function rememberPanePosition() {
|
|
20
|
+
var messages = document.getElementById('messages'), key = draftKey();
|
|
21
|
+
if (!messages || !store.get('sessionOriginId') || store.get('panePendingBinding')) return;
|
|
22
|
+
var positions = Object.assign({}, store.get('paneRetainedPositions') || {});
|
|
23
|
+
delete positions[key];
|
|
24
|
+
positions[key] = { top: messages.scrollTop, height: messages.scrollHeight, viewport: messages.clientHeight };
|
|
25
|
+
var keys = Object.keys(positions);
|
|
26
|
+
while (keys.length > 8) delete positions[keys.shift()];
|
|
27
|
+
store.set({ paneRetainedPositions: positions });
|
|
28
|
+
}
|
|
29
|
+
function suspendPane() {
|
|
30
|
+
if (!store.get('paneSuspended')) rememberPanePosition();
|
|
31
|
+
clearDeadline();
|
|
32
|
+
store.set({ paneSuspended: true, paneReady: false, panePendingBinding: null });
|
|
33
|
+
block(true);
|
|
34
|
+
}
|
|
18
35
|
function clearDeadline() {
|
|
19
36
|
if (store.get('paneActivationTimer')) clearTimeout(store.get('paneActivationTimer'));
|
|
20
37
|
store.set({ paneActivationTimer: null });
|
|
21
38
|
}
|
|
22
|
-
function failed(requestId) {
|
|
39
|
+
function failed(requestId, reason) {
|
|
23
40
|
var pending = store.get('panePendingBinding');
|
|
24
41
|
if (!pending || requestId !== pending.requestId) return;
|
|
25
42
|
clearDeadline();
|
|
26
43
|
store.set({ paneReady: false, panePendingBinding: null });
|
|
27
44
|
block(true);
|
|
28
45
|
var frame = window.frameElement;
|
|
29
|
-
if (frame)
|
|
46
|
+
if (frame) {
|
|
47
|
+
if (reason) frame.dataset.paneActivationFailure = reason;
|
|
48
|
+
frame.dispatchEvent(new window.parent.CustomEvent('clay-pane-activation-failed'));
|
|
49
|
+
}
|
|
30
50
|
}
|
|
31
51
|
|
|
32
52
|
// Claim ordinary initial pins before the legacy session-list handler runs.
|
|
33
53
|
// Authentication, list and group metadata can arrive in either order; the
|
|
34
54
|
// subscription retries when those authoritative prerequisites become ready.
|
|
35
55
|
export function primePaneSession() {
|
|
36
|
-
if (!enabled() || !window.frameElement || !window.frameElement.clayPane) return false;
|
|
37
|
-
var
|
|
56
|
+
if (store.get('paneSuspended') || !enabled() || !window.frameElement || !window.frameElement.clayPane) return false;
|
|
57
|
+
var frame = window.frameElement;
|
|
58
|
+
var id = Number(frame.dataset.requestedSessionId || frame.dataset.sessionId || store.get('paneSessionId'));
|
|
59
|
+
if (!Number.isSafeInteger(id) || id < 1) return false;
|
|
38
60
|
var session = (getCachedSessions() || []).find(function (item) { return item.id === id; });
|
|
39
61
|
if (!session || session.isMate || (session.runtimeMode || session.mode || 'gui') !== 'gui' || session.tuiSuspended) return false;
|
|
40
|
-
|
|
62
|
+
var target = paneTarget(store.snap(), getCachedSessions(), id);
|
|
63
|
+
var existing = store.get('panePendingBinding') || store.get('paneCommittedBinding');
|
|
64
|
+
activatePaneSession(id, store.get('paneSessionId') !== id || !samePaneTarget(existing, target), target);
|
|
41
65
|
return true;
|
|
42
66
|
}
|
|
43
67
|
|
|
44
68
|
export function activatePaneSession(id, force, expectedTarget) {
|
|
45
69
|
var state = store.snap(), ws = getWs();
|
|
70
|
+
if (window.frameElement && window.frameElement.closest && window.frameElement.closest('.split-pane-cached')) return false;
|
|
46
71
|
if (state.authIdentityLoaded === false) return false;
|
|
47
72
|
if (state.isMultiUserMode && !state.myUserId) return false;
|
|
48
73
|
if (!enabled() || !state.connected || !ws || ws.readyState !== 1 || state.splitGroupsProjectSlug !== state.currentSlug) return false;
|
|
@@ -54,6 +79,7 @@ export function activatePaneSession(id, force, expectedTarget) {
|
|
|
54
79
|
var old = state.paneCommittedBinding, pending = state.panePendingBinding;
|
|
55
80
|
if (!force && !state.paneSuspended && samePaneTarget(pending || old, target)) return true;
|
|
56
81
|
if (!pending && state.sessionOriginId) {
|
|
82
|
+
if (!state.paneSuspended) rememberPanePosition();
|
|
57
83
|
var draft;
|
|
58
84
|
try { draft = takeComposerDraft(); } catch (error) { return false; }
|
|
59
85
|
var drafts = Object.assign({}, state.paneRichDrafts || {});
|
|
@@ -67,9 +93,11 @@ export function activatePaneSession(id, force, expectedTarget) {
|
|
|
67
93
|
var path = new URL(store.get('wsPath'), location.origin);
|
|
68
94
|
path.searchParams.set('session', String(id));
|
|
69
95
|
store.set({ paneActivationGeneration: generation, panePendingBinding: target, paneReady: false, paneSuspended: false,
|
|
96
|
+
paneLayoutScrollLock: null, paneLastUserScrollTop: null, paneUserScrollIntent: null, paneUserScrollRevision: 0,
|
|
70
97
|
paneSessionId: id, panePinPending: false, wsPath: path.pathname + path.search });
|
|
71
98
|
block(true);
|
|
72
99
|
store.set({ paneActivationTimer: setTimeout(function () { failed(target.requestId); }, 12000) });
|
|
100
|
+
if (typeof performance !== 'undefined' && performance.mark) { performance.clearMarks('clay:activation-request'); performance.mark('clay:activation-request'); }
|
|
73
101
|
ws.send(JSON.stringify({ type: 'switch_session', id: id, paneActivation: target }));
|
|
74
102
|
return true;
|
|
75
103
|
}
|
|
@@ -87,6 +115,7 @@ export function filterPaneActivation(message) {
|
|
|
87
115
|
if (message.type === 'pane_activation_started') return true;
|
|
88
116
|
if (message.type === 'pane_activation_snapshot_done') {
|
|
89
117
|
if (!pending) return true;
|
|
118
|
+
if (typeof performance !== 'undefined' && performance.mark) { performance.clearMarks('clay:snapshot-received'); performance.mark('clay:snapshot-received'); }
|
|
90
119
|
var draft = (store.get('paneRichDrafts') || {})[String(store.get('myUserId')) + ':' + expected.sessionOriginId];
|
|
91
120
|
restoreComposerDraft(draft || { text: '', start: 0, end: 0 });
|
|
92
121
|
function paint() {
|
|
@@ -95,10 +124,20 @@ export function filterPaneActivation(message) {
|
|
|
95
124
|
var messages = document.getElementById('messages');
|
|
96
125
|
if (!samePaneTarget(current, expected) ||
|
|
97
126
|
store.get('activeSessionId') !== expected.sessionId || store.get('sessionOriginId') !== expected.sessionOriginId || store.get('replayingHistory') ||
|
|
98
|
-
!messages || messages.dataset.controlSessionId !== String(expected.sessionId) || messages.dataset.controlSessionOrigin !== expected.sessionOriginId) {
|
|
127
|
+
!messages || messages.dataset.controlSessionId !== String(expected.sessionId) || messages.dataset.controlSessionOrigin !== expected.sessionOriginId) {
|
|
128
|
+
failed(expected.requestId, 'snapshot-state-mismatch: active=' + store.get('activeSessionId') + ', origin=' + store.get('sessionOriginId') + ', messages=' + (messages && messages.dataset.controlSessionId) + ', messageOrigin=' + (messages && messages.dataset.controlSessionOrigin));
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
var position = (store.get('paneRetainedPositions') || {})[String(store.get('myUserId')) + ':' + expected.sessionOriginId];
|
|
132
|
+
if (position && Number.isFinite(position.top)) {
|
|
133
|
+
disarmStickyBottom();
|
|
134
|
+
messages.scrollTop = position.top;
|
|
135
|
+
store.set({ paneLastUserScrollTop: position.top });
|
|
136
|
+
}
|
|
99
137
|
clearDeadline();
|
|
100
138
|
store.set({ paneCommittedBinding: expected, panePendingBinding: null, paneReady: true });
|
|
101
139
|
block(false);
|
|
140
|
+
if (typeof performance !== 'undefined' && performance.mark) { performance.clearMarks('clay:pane-ready'); performance.mark('clay:pane-ready'); }
|
|
102
141
|
var frame = window.frameElement;
|
|
103
142
|
if (frame) frame.dispatchEvent(new window.parent.CustomEvent('clay-pane-ready'));
|
|
104
143
|
updateContextPanel();
|
|
@@ -167,7 +206,7 @@ export function initPaneActivation() {
|
|
|
167
206
|
},
|
|
168
207
|
activate: activatePaneSession,
|
|
169
208
|
checkDraft: function () { if (!store.get('panePendingBinding')) { var draft = takeComposerDraft(); restoreComposerDraft(draft); } },
|
|
170
|
-
suspend:
|
|
209
|
+
suspend: suspendPane,
|
|
171
210
|
};
|
|
172
211
|
// A pointer that began on the previous conversation cannot become a Stop or
|
|
173
212
|
// approval click on the replacement conversation after hydration.
|
|
@@ -56,14 +56,23 @@ export function paneTarget(state, sessions, id) {
|
|
|
56
56
|
// Ordinary reusable panes have a live document binding; legacy/canonical frames
|
|
57
57
|
// retain their existing URL and receipt checks in their own bridge paths.
|
|
58
58
|
export function livePaneBinding(frame, message) {
|
|
59
|
-
if (!frame || !frame.clayPane) return false;
|
|
59
|
+
if (!frame || !frame.clayPane || frame.closest && frame.closest('.split-pane-cached')) return false;
|
|
60
60
|
var api = frame.clayPane, live = api.identity();
|
|
61
61
|
var state = store.snap(), split = state.splitPanes;
|
|
62
|
+
if (!state.connected) return false;
|
|
62
63
|
var target = live && paneTarget(state, state.paneBindingSessions || [], live.sessionId);
|
|
64
|
+
var inLegacySplit = !!(live && split && split.panes.some(function (pane) { return pane.slug === live.projectSlug && pane.sessionId === live.sessionId; }));
|
|
65
|
+
var activePresentation = (state.projectSplitViews || []).find(function (view) { return view.id === state.projectSplitViewActiveId; });
|
|
66
|
+
var inPresentation = !!(live && live.binding && !split && activePresentation && activePresentation.sessions.some(function (session) {
|
|
67
|
+
return session.id === live.sessionId && session.sessionOriginId === live.binding.sessionOriginId;
|
|
68
|
+
}));
|
|
69
|
+
var inSingletonPresentation = !!(live && live.binding && !split && !activePresentation &&
|
|
70
|
+
state.projectSplitViewSingletonOrigin === live.binding.sessionOriginId &&
|
|
71
|
+
live.sessionId === Number(frame.dataset.sessionId) && live.binding.sessionOriginId === frame.dataset.sessionOriginId);
|
|
63
72
|
return api.source === frame.contentWindow && api.document === frame.contentDocument && live && live.ready &&
|
|
64
73
|
samePaneTarget(target, live.binding) &&
|
|
65
74
|
live.ownerId === (state.myUserId || null) && live.projectSlug === state.currentSlug &&
|
|
66
|
-
|
|
75
|
+
(inLegacySplit || inPresentation || inSingletonPresentation) &&
|
|
67
76
|
(!message || samePaneBinding(live.binding, message.paneBinding));
|
|
68
77
|
}
|
|
69
78
|
|
|
@@ -77,6 +86,7 @@ export function currentPaneReview(container) {
|
|
|
77
86
|
}
|
|
78
87
|
|
|
79
88
|
export function paneCallbackCurrent(binding) {
|
|
89
|
+
if (store.get('paneSuspended')) return false;
|
|
80
90
|
var current = store.get('panePendingBinding') || store.get('paneCommittedBinding');
|
|
81
91
|
return current ? samePaneBinding(current, binding) : !binding;
|
|
82
92
|
}
|
|
@@ -5,9 +5,11 @@ import { forwardStudioPresentation } from './clay-studio-render.js';
|
|
|
5
5
|
// Same-origin messages only, both directions.
|
|
6
6
|
|
|
7
7
|
import { store } from './store.js';
|
|
8
|
+
import { beginPaneLayoutScrollLock, endPaneLayoutScrollLock } from './chat-render-runtime.js';
|
|
8
9
|
import { paneMessageBinding, samePaneBinding } from './pane-binding.js';
|
|
9
10
|
import { getEffectiveContextView } from './context-view-preference.js';
|
|
10
11
|
import { forceExternalLinkToNewTab } from './pane-links.js';
|
|
12
|
+
import { toggleSearch, controlPaneSearch } from './session-search.js';
|
|
11
13
|
|
|
12
14
|
export function reportPaneContext(data) {
|
|
13
15
|
if (!store.get('paneMode') || window.parent === window) return;
|
|
@@ -71,8 +73,15 @@ export function initPaneBridge() {
|
|
|
71
73
|
window.addEventListener("message", function (event) {
|
|
72
74
|
if (event.origin !== window.location.origin) return;
|
|
73
75
|
var msg = event.data;
|
|
74
|
-
if (!msg || msg.type !== "clay-pane-toggle-context") return;
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
if (!msg || (msg.type !== "clay-pane-toggle-context" && msg.type !== "clay-pane-toggle-search" && msg.type !== "clay-pane-search-command" && msg.type !== "clay-pane-layout-resize")) return;
|
|
77
|
+
var committed = store.get('paneCommittedBinding');
|
|
78
|
+
if (event.source !== window.parent || !committed || !paneMessageBinding() ||
|
|
79
|
+
!samePaneBinding(committed, msg.paneBinding) || !samePaneBinding(paneMessageBinding(), msg.paneBinding)) return;
|
|
80
|
+
if (msg.type === "clay-pane-layout-resize") {
|
|
81
|
+
if (msg.phase === 'begin') beginPaneLayoutScrollLock(msg.token, msg.scrollTop);
|
|
82
|
+
else if (msg.phase === 'end') endPaneLayoutScrollLock(msg.token);
|
|
83
|
+
} else if (msg.type === "clay-pane-toggle-context") togglePaneContextPanel();
|
|
84
|
+
else if (msg.type === "clay-pane-toggle-search") toggleSearch();
|
|
85
|
+
else controlPaneSearch(msg.action, msg.query);
|
|
77
86
|
});
|
|
78
87
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
|
|
3
|
+
// Executable initializers belong to this module; the store holds only IDs.
|
|
4
|
+
var initializers = [];
|
|
5
|
+
|
|
6
|
+
// Parent-owned workbench chrome is unnecessary for a pane's first transcript.
|
|
7
|
+
// Keep the same initializers, but run them one task at a time after its exact
|
|
8
|
+
// authenticated activation has made the conversation interactive.
|
|
9
|
+
function drain() {
|
|
10
|
+
var state = store.snap(), boot = state.paneDeferredBootstrap;
|
|
11
|
+
if (!boot || boot.scheduled || !boot.jobs.length || !state.connected || !state.paneReady || state.paneSuspended) return;
|
|
12
|
+
store.set({ paneDeferredBootstrap: Object.assign({}, boot, { scheduled: true }) });
|
|
13
|
+
setTimeout(function () {
|
|
14
|
+
var latest = store.snap(), pending = latest.paneDeferredBootstrap;
|
|
15
|
+
if (!pending) return;
|
|
16
|
+
var jobs = pending.jobs.slice(), job = latest.connected && latest.paneReady && !latest.paneSuspended ? jobs.shift() : null;
|
|
17
|
+
store.set({ paneDeferredBootstrap: Object.assign({}, pending, { scheduled: false, jobs: jobs }) });
|
|
18
|
+
try { if (job !== null && initializers[job]) { var initialize = initializers[job]; initializers[job] = null; initialize(); } } finally { drain(); }
|
|
19
|
+
}, 0);
|
|
20
|
+
}
|
|
21
|
+
export function initializeAfterPaneReady(jobs) {
|
|
22
|
+
if (!store.get('paneMode')) { jobs.forEach(function (job) { job(); }); return; }
|
|
23
|
+
var boot = store.get('paneDeferredBootstrap');
|
|
24
|
+
if (!boot) {
|
|
25
|
+
boot = { jobs: [], scheduled: false };
|
|
26
|
+
store.subscribe(function (next, previous) {
|
|
27
|
+
if (next.paneReady !== previous.paneReady || next.connected !== previous.connected || next.paneSuspended !== previous.paneSuspended) drain();
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
store.set({ paneDeferredBootstrap: Object.assign({}, boot, { jobs: boot.jobs.concat(jobs.map(function (job) { var id = initializers.length; initializers.push(job); return id; })) }) });
|
|
31
|
+
drain();
|
|
32
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// In-flight promises are module-owned resources, never store data.
|
|
2
|
+
var featureRequests = {};
|
|
3
|
+
|
|
4
|
+
// Only server-authored, inert feature tags are eligible. Parent documents retain
|
|
5
|
+
// their normal vendor loading; pane documents load these groups on first use.
|
|
6
|
+
export function ensurePaneFeatureAssets(group) {
|
|
7
|
+
if (['editor', 'mermaid', 'qr'].indexOf(group) === -1) return Promise.reject(new Error('Unknown feature assets'));
|
|
8
|
+
var existing = featureRequests[group];
|
|
9
|
+
if (existing) return existing;
|
|
10
|
+
var tags = Array.from(document.querySelectorAll('script[data-pane-asset-group="' + group + '"]'));
|
|
11
|
+
var promise = tags.reduce(function (previous, tag) {
|
|
12
|
+
return previous.then(function () {
|
|
13
|
+
if (tag.dataset.loaded === 'true') return;
|
|
14
|
+
return new Promise(function (resolve, reject) {
|
|
15
|
+
var script = document.createElement('script'), timer;
|
|
16
|
+
function failed() {
|
|
17
|
+
clearTimeout(timer); script.onload = null; script.onerror = null; script.remove();
|
|
18
|
+
reject(new Error('Could not load ' + group + ' assets. Try again.'));
|
|
19
|
+
}
|
|
20
|
+
script.src = tag.dataset.src; script.async = false;
|
|
21
|
+
script.onload = function () { clearTimeout(timer); tag.dataset.loaded = 'true'; resolve(); };
|
|
22
|
+
script.onerror = failed; timer = setTimeout(failed, 15000);
|
|
23
|
+
tag.after(script);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}, Promise.resolve());
|
|
27
|
+
promise = promise.then(function () {
|
|
28
|
+
var value = group === 'editor' ? window.CodeMirror : group === 'mermaid' ? window.mermaid : window.qrcode;
|
|
29
|
+
if (!value) throw new Error('Could not load ' + group + ' assets. Try again.');
|
|
30
|
+
}).catch(function (error) {
|
|
31
|
+
delete featureRequests[group]; throw error;
|
|
32
|
+
});
|
|
33
|
+
featureRequests[group] = promise;
|
|
34
|
+
return promise;
|
|
35
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { samePaneBinding } from './pane-binding.js';
|
|
2
|
+
|
|
3
|
+
function acceptsPaneSearchResults(state, msg) {
|
|
4
|
+
var binding = state.paneCommittedBinding;
|
|
5
|
+
var receipt = state.paneSearchReceipt;
|
|
6
|
+
return !!(state.paneReady && !state.panePendingBinding && !state.paneSuspended && binding && receipt &&
|
|
7
|
+
receipt.epoch === state.paneSearchEpoch && samePaneBinding(receipt.binding, binding) &&
|
|
8
|
+
Number(binding.sessionId) === Number(msg.sessionId) && Number(binding.sessionId) === Number(state.activeSessionId) &&
|
|
9
|
+
receipt.query === msg.query);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { acceptsPaneSearchResults };
|
|
@@ -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) {
|