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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { handleWorkerRemovalResult } from './split-view-worker-removal.js';
|
|
1
2
|
import { handleStudioDocumentMessage } from './clay-studio-document.js';
|
|
2
3
|
import { saveSessionRichDraft, restoreSessionRichDraft } from './session-rich-drafts.js';
|
|
3
4
|
import { refreshPaneActivation, primePaneSession } from './pane-activation.js';
|
|
@@ -48,11 +49,13 @@ import { handleDefaultVendorMessage } from './default-vendor.js';
|
|
|
48
49
|
import { handleContextViewMessage } from './context-view-preference.js';
|
|
49
50
|
import { handleHomeMateMemoryState, handleHomeMateKnowledgeState, syncHomeMateSettingsTarget, handleHomeMateAvatarError } from './home-mate-settings.js';
|
|
50
51
|
import { renderKnowledgeList, handleKnowledgeContent, handleKnowledgeMutation } from './mate-knowledge.js';
|
|
51
|
-
import { handleKnowledgeWorkbenchMessage } from './
|
|
52
|
+
import { handleKnowledgeWorkbenchMessage } from './knowledge-workbench-entry.js';
|
|
52
53
|
import { renderMemoryList } from './mate-memory.js';
|
|
53
54
|
import { handlePaletteSessionSwitch, handlePaletteHistoryDone, handlePaletteHistoryPrepended, setPaletteVersion } from './command-palette.js';
|
|
54
55
|
import { handleFindInSessionResults } from './session-search.js';
|
|
55
56
|
import { syncPaneTitles, maybeRestoreSplitGroup, openGroup } from './split-view.js';
|
|
57
|
+
import { handleProjectSplitViews, handleProjectSplitViewResult, requestProjectSplitViews } from './split-view-presentations.js';
|
|
58
|
+
import { handleSplitWorkerConversation } from './split-view-workers.js';
|
|
56
59
|
import { showPairDialog, handlePairCreated, handleSplitDelegation, showWorkerDelegationNotice, hideWorkerDelegationNotice } from './split-pair-ui.js';
|
|
57
60
|
import { handleSessionActionMessage } from './session-actions.js';
|
|
58
61
|
import { renderWorkerProposal, updateWorkerProposal } from './worker-proposal.js';
|
|
@@ -95,7 +98,7 @@ import { clearPairResultStatus, handlePairResultStatus } from './pair-result-sta
|
|
|
95
98
|
import { scrollToBottom, addToMessages, addUserMessage, addSystemMessage, removeMatePreThinking, resetStreamBuffer, appendDelta, flushStreamBuffer, finalizeAssistantBlock, addConflictMessage, addContextOverflowMessage, showSuggestionChips, armStickyBottom, VENDOR_AVATARS, VENDOR_NAMES } from './app-rendering.js';
|
|
96
99
|
import { setActivity, startUrgentBlink, stopUrgentBlink, blinkSessionDot, updateCrossProjectBlink } from './app-favicon.js';
|
|
97
100
|
import { setStatus } from './app-connection.js';
|
|
98
|
-
import {
|
|
101
|
+
import { resolveSwitchedVendor } from './pane-session.js';
|
|
99
102
|
import { markProjectSessionListHydrated, applyProjectSplitGroups, clearProjectSplitState } from './split-session-boundary.js';
|
|
100
103
|
import { selectDefaultVendorForBlankSession } from './vendor-selection.js';
|
|
101
104
|
import { getModelInfoUpdate, modelEntryValue, modelEntryMatches, handleModelSelectionResult, requestVendorModels } from './model-picker.js';
|
|
@@ -440,6 +443,7 @@ export function processMessage(msg) {
|
|
|
440
443
|
if (msg.projectOwnerId !== undefined) store.set({ currentProjectOwnerId: msg.projectOwnerId });
|
|
441
444
|
if (msg.ownerLocked !== undefined) store.set({ ownerLocked: !!msg.ownerLocked });
|
|
442
445
|
if (msg.osUsers !== undefined) store.set({ isOsUsers: !!msg.osUsers });
|
|
446
|
+
store.set({ paneOsIdentity: msg.paneOsIdentity || null });
|
|
443
447
|
if (msg.lanHost) window.__lanHost = msg.lanHost;
|
|
444
448
|
store.set({ skipPermsEnabled: msg.dangerouslySkipPermissions === true });
|
|
445
449
|
var spBanner = document.getElementById("skip-perms-pill");
|
|
@@ -785,19 +789,28 @@ export function processMessage(msg) {
|
|
|
785
789
|
renderMateSessionList(msg.sessions || []);
|
|
786
790
|
renderSessionList(msg.sessions || []);
|
|
787
791
|
store.set({ paneBindingSessions: msg.sessions || [] });
|
|
792
|
+
requestProjectSplitViews();
|
|
788
793
|
refreshPaneActivation();
|
|
789
794
|
syncGithubSessions(msg.sessions || []);
|
|
790
795
|
syncPaneTitles();
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
}
|
|
796
|
+
if (store.get('paneMode')) {
|
|
797
|
+
// A pane pin must use the authenticated activation binding. Sending
|
|
798
|
+
// this startup URL's session as a plain switch can race a parent
|
|
799
|
+
// retarget and restore the iframe's stale initial session.
|
|
800
|
+
primePaneSession();
|
|
797
801
|
}
|
|
798
802
|
handlePaletteSessionSwitch();
|
|
799
803
|
maybeRestoreSplitGroup();
|
|
800
804
|
break;
|
|
805
|
+
case "project_split_views":
|
|
806
|
+
handleProjectSplitViews(msg);
|
|
807
|
+
break;
|
|
808
|
+
case "project_split_view_result":
|
|
809
|
+
handleProjectSplitViewResult(msg);
|
|
810
|
+
break;
|
|
811
|
+
case "split_worker_conversation":
|
|
812
|
+
handleSplitWorkerConversation(msg);
|
|
813
|
+
break;
|
|
801
814
|
case "session_github_status":
|
|
802
815
|
applyGithubPersonalReviewStatus(msg);
|
|
803
816
|
break;
|
|
@@ -813,6 +826,10 @@ export function processMessage(msg) {
|
|
|
813
826
|
if (!msg.ok && msg.error) showToast(msg.error, "error");
|
|
814
827
|
break;
|
|
815
828
|
|
|
829
|
+
case "split_worker_remove_result":
|
|
830
|
+
handleWorkerRemovalResult(msg);
|
|
831
|
+
break;
|
|
832
|
+
|
|
816
833
|
case "split_worker_close_result":
|
|
817
834
|
if (!msg.ok && msg.error) showToast(msg.error, "error");
|
|
818
835
|
break;
|
|
@@ -60,6 +60,32 @@ var stickyBottomCeilingMs = 8000;
|
|
|
60
60
|
var stickyBottomResizeObs = null;
|
|
61
61
|
var stickyBottomInputBound = false;
|
|
62
62
|
|
|
63
|
+
function layoutScrollLock() { return store.get('paneLayoutScrollLock'); }
|
|
64
|
+
|
|
65
|
+
export function beginPaneLayoutScrollLock(token, scrollTop) {
|
|
66
|
+
var messagesEl = getMessagesEl();
|
|
67
|
+
if (!messagesEl || !token) return false;
|
|
68
|
+
disarmStickyBottom();
|
|
69
|
+
var userPosition = store.get('paneLastUserScrollTop');
|
|
70
|
+
var position = Number.isFinite(userPosition) ? userPosition : Number.isFinite(scrollTop) ? Math.max(0, scrollTop) : messagesEl.scrollTop;
|
|
71
|
+
store.set({ paneLayoutScrollLock: { token: token, scrollTop: position } });
|
|
72
|
+
messagesEl.scrollTop = position;
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function endPaneLayoutScrollLock(token) {
|
|
77
|
+
var state = layoutScrollLock(), messagesEl = getMessagesEl();
|
|
78
|
+
if (!state || state.token !== token || !messagesEl) return false;
|
|
79
|
+
requestAnimationFrame(function () {
|
|
80
|
+
var current = layoutScrollLock();
|
|
81
|
+
if (!current || current.token !== token || !messagesEl.isConnected) return;
|
|
82
|
+
disarmStickyBottom();
|
|
83
|
+
messagesEl.scrollTop = current.scrollTop;
|
|
84
|
+
store.set({ paneLayoutScrollLock: null });
|
|
85
|
+
});
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
|
|
63
89
|
export function getStickyBottom() { return stickyBottom; }
|
|
64
90
|
|
|
65
91
|
function pinToBottomNow() {
|
|
@@ -95,6 +121,7 @@ function ensureStickyInfrastructure() {
|
|
|
95
121
|
}
|
|
96
122
|
|
|
97
123
|
export function armStickyBottom(durationMs) {
|
|
124
|
+
if (layoutScrollLock()) return;
|
|
98
125
|
if (prependAnchor) return;
|
|
99
126
|
ensureStickyInfrastructure();
|
|
100
127
|
stickyBottom = true;
|
|
@@ -128,6 +155,22 @@ export function disarmStickyBottom() {
|
|
|
128
155
|
}
|
|
129
156
|
|
|
130
157
|
export function initRendering() {
|
|
158
|
+
var messagesEl = getMessagesEl();
|
|
159
|
+
if (messagesEl && !messagesEl.__paneLayoutScrollLockListener) {
|
|
160
|
+
messagesEl.__paneLayoutScrollLockListener = true;
|
|
161
|
+
messagesEl.addEventListener('wheel', function () {
|
|
162
|
+
var revision = (store.get('paneUserScrollRevision') || 0) + 1;
|
|
163
|
+
store.set({ paneUserScrollRevision: revision, paneUserScrollIntent: revision });
|
|
164
|
+
requestAnimationFrame(function () {
|
|
165
|
+
if (store.get('paneUserScrollIntent') === revision) store.set({ paneUserScrollIntent: null });
|
|
166
|
+
});
|
|
167
|
+
}, { passive: true });
|
|
168
|
+
messagesEl.addEventListener('scroll', function () {
|
|
169
|
+
var state = layoutScrollLock();
|
|
170
|
+
if (state) store.set({ paneLayoutScrollLock: Object.assign({}, state, { scrollTop: messagesEl.scrollTop }) });
|
|
171
|
+
if (store.get('paneUserScrollIntent') != null) store.set({ paneLastUserScrollTop: messagesEl.scrollTop, paneUserScrollIntent: null });
|
|
172
|
+
}, { passive: true });
|
|
173
|
+
}
|
|
131
174
|
var lastAssistantPlaceholder = '';
|
|
132
175
|
function updateInputPlaceholder(state) {
|
|
133
176
|
var inputEl = document.getElementById("input");
|
|
@@ -200,6 +243,7 @@ export function keepActiveThinkingAtTail() {
|
|
|
200
243
|
}
|
|
201
244
|
|
|
202
245
|
export function scrollToBottom() {
|
|
246
|
+
if (layoutScrollLock()) return;
|
|
203
247
|
if (prependAnchor || store.get('replayingHistory')) return;
|
|
204
248
|
var messagesEl = getMessagesEl();
|
|
205
249
|
if (!messagesEl) return;
|
|
@@ -214,6 +258,7 @@ export function scrollToBottom() {
|
|
|
214
258
|
return;
|
|
215
259
|
}
|
|
216
260
|
requestAnimationFrame(function () {
|
|
261
|
+
if (layoutScrollLock()) return;
|
|
217
262
|
var dist = messagesEl.scrollHeight - messagesEl.scrollTop - messagesEl.clientHeight;
|
|
218
263
|
if (dist > 150) {
|
|
219
264
|
isUserScrolledUp = true;
|
|
@@ -8,6 +8,7 @@ var actions = {
|
|
|
8
8
|
runtime: 'return this conversation to GUI chat', queue_add: 'queue the reviewed message for the Driver',
|
|
9
9
|
queue_edit: 'update the reviewed queued message', queue_cancel: 'cancel the reviewed queued message',
|
|
10
10
|
queue_reorder: 'reorder the reviewed message queue', navigate: 'open the reviewed conversation so we can continue there',
|
|
11
|
+
split_view: 'change the reviewed Split view',
|
|
11
12
|
dismiss_notifications: 'dismiss the reviewed notifications', profile: 'update the reviewed Mate profile',
|
|
12
13
|
send: 'deliver the reviewed message to this Mate', assign: 'prepare the requested Driver work for your review', follow_up: 'prepare a follow-up for the existing Driver', document: 'open the requested document for editing', sketch: 'present the requested sketch', browser: 'operate the requested shared browser tab'
|
|
13
14
|
};
|
|
@@ -20,6 +21,8 @@ export function activityCopy(msg) {
|
|
|
20
21
|
rendered: 'The reviewed conversation is visibly open in the selected window.',
|
|
21
22
|
cancelled: 'I left the change unapplied.', failed: 'I could not finish this change. Please check the result before trying again.',
|
|
22
23
|
partial: 'Part of this change took effect, but the operation failed. Please check the result.', unknown: 'I cannot confirm the outcome. Please check before trying again.' };
|
|
24
|
+
if (msg.domain === 'screen' && msg.action === 'split_view' && msg.phase === 'acknowledged') return 'I started opening the reviewed Split view. Its visible result is still unconfirmed.';
|
|
25
|
+
if (msg.domain === 'screen' && msg.action === 'split_view' && msg.phase === 'rendered') return 'The reviewed Split view and ready panes are visibly open in the selected window.';
|
|
23
26
|
if (msg.domain === 'assignment') { var work = { review: 'I prepared the Driver work. Please review its proposal before it starts.', queued: 'I submitted the approved Driver work for dispatch.', running: 'The Driver is working on the approved request.', completed: 'The Driver finished this request.', cancelled: 'I left this Driver request unapplied.', unknown: 'I cannot confirm the Driver outcome. Please inspect the work before retrying.' }; return work[msg.phase] || endings[msg.phase] || null; }
|
|
24
27
|
if (msg.domain === 'studio' && msg.phase === 'rendered') return 'The requested workbench content is visibly rendered in this window.';
|
|
25
28
|
if (msg.domain === 'studio' && msg.phase === 'render_unknown') return 'The service returned, but I could not confirm that the workbench content is visible.';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { store } from './store.js';
|
|
2
2
|
import { getWs } from './ws-ref.js';
|
|
3
3
|
|
|
4
|
-
function visible(element) {
|
|
4
|
+
export function visible(element) {
|
|
5
5
|
if (!element || !element.isConnected || element.closest('[hidden], [inert], [aria-hidden="true"]')) return false;
|
|
6
6
|
var box = element.getBoundingClientRect(), style = getComputedStyle(element);
|
|
7
7
|
if (style.visibility !== 'visible' || Number(style.opacity) === 0 || box.width <= 0 || box.height <= 0) return false;
|
|
@@ -1,26 +1,34 @@
|
|
|
1
|
+
import { handleSplitViewProbe, splitViewScreenSnapshot, applySplitViewNavigation, observeSplitViewRendering } from './clay-split-view-screen.js';
|
|
1
2
|
import { observeNavigationRendering } from './clay-navigation-render.js';
|
|
2
3
|
import { showClayNavigationActivity, clearClayControlActivity } from './clay-control-activity.js';
|
|
3
4
|
import { store } from './store.js';
|
|
4
5
|
import { getWs } from './ws-ref.js';
|
|
5
6
|
import { navigateToSession } from './command-palette.js';
|
|
7
|
+
var offerSocket = null;
|
|
6
8
|
function context() {
|
|
7
|
-
return JSON.stringify([store.get('currentSlug'), store.get('activeSessionId'), store.get('dmMode'), store.get('homeShellVisible'), store.get('homeChatMateId'), store.get('homeChatSessionId'), store.get('homeSubSurface'), (store.get('contextualClay') || {}).mode]);
|
|
9
|
+
return JSON.stringify([store.get('currentSlug'), store.get('activeSessionId'), store.get('dmMode'), store.get('homeShellVisible'), store.get('homeChatMateId'), store.get('homeChatSessionId'), store.get('homeSubSurface'), (store.get('contextualClay') || {}).mode, splitViewScreenSnapshot()]);
|
|
8
10
|
}
|
|
9
11
|
export function handleOwnerNavigation(msg) {
|
|
12
|
+
if (handleSplitViewProbe(msg)) return true;
|
|
10
13
|
if (msg.type !== 'clay_navigation_offer' && msg.type !== 'clay_navigation_apply') return false;
|
|
11
14
|
var ws = getWs();
|
|
12
15
|
if (!ws || ws.readyState !== 1) return true;
|
|
13
16
|
if (msg.type === 'clay_navigation_offer') {
|
|
14
17
|
if (document.hidden || store.get('paneMode')) return true;
|
|
15
|
-
|
|
18
|
+
offerSocket = ws;
|
|
19
|
+
store.set({ clayNavigationOffer: { id: msg.deliveryId, context: context() } });
|
|
16
20
|
ws.send(JSON.stringify({ type: 'clay_navigation_claim', deliveryId: msg.deliveryId }));
|
|
17
21
|
return true;
|
|
18
22
|
}
|
|
19
23
|
var offer = store.get('clayNavigationOffer');
|
|
20
24
|
store.set({ clayNavigationOffer: null });
|
|
21
|
-
var accepted = offer && offer.id === msg.deliveryId &&
|
|
25
|
+
var accepted = offer && offer.id === msg.deliveryId && offerSocket === ws && offer.context === context() && !document.hidden && !store.get('paneMode');
|
|
26
|
+
offerSocket = null;
|
|
22
27
|
var status = 'refused';
|
|
23
|
-
if (accepted && msg.target &&
|
|
28
|
+
if (accepted && msg.target && msg.target.surface === 'split_view') {
|
|
29
|
+
try { status = applySplitViewNavigation(msg.target) ? 'accepted' : 'refused'; } catch (error) { status = 'failed'; }
|
|
30
|
+
}
|
|
31
|
+
if (accepted && msg.target && msg.target.surface !== 'split_view' && typeof msg.target.projectSlug === 'string' && Number.isSafeInteger(msg.target.sessionId)) {
|
|
24
32
|
try {
|
|
25
33
|
showClayNavigationActivity(msg.target, false); navigateToSession(msg.target, null, msg.target.entryIndex); status = 'accepted';
|
|
26
34
|
} catch (error) { showClayNavigationActivity(msg.target, true); status = 'failed'; }
|
|
@@ -28,6 +36,8 @@ export function handleOwnerNavigation(msg) {
|
|
|
28
36
|
if (status === 'refused') clearClayControlActivity();
|
|
29
37
|
// This acknowledges navigator invocation only. Rendering may finish after reconnect.
|
|
30
38
|
if (ws.readyState === 1) ws.send(JSON.stringify({ type: 'clay_navigation_ack', deliveryId: msg.deliveryId, status: status }));
|
|
31
|
-
if (status === 'accepted')
|
|
39
|
+
if (status === 'accepted') {
|
|
40
|
+
if (msg.target.surface === 'split_view') observeSplitViewRendering(msg, ws); else observeNavigationRendering(msg, ws);
|
|
41
|
+
}
|
|
32
42
|
return true;
|
|
33
43
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { getWs } from './ws-ref.js';
|
|
3
|
+
import { livePaneBinding } from './pane-binding.js';
|
|
4
|
+
import { applyReviewedSplitView } from './split-view-presentations.js';
|
|
5
|
+
import { visible } from './clay-navigation-render.js';
|
|
6
|
+
|
|
7
|
+
var attempts = new Map();
|
|
8
|
+
export function splitViewScreenSnapshot() {
|
|
9
|
+
var state = store.snap();
|
|
10
|
+
var view = (state.projectSplitViews || []).find(function (item) { return item.id === state.projectSplitViewActiveId; });
|
|
11
|
+
var hidden = !!(document.hidden || state.paneMode || !state.connected || state.dmMode || state.homeShellVisible);
|
|
12
|
+
return { projectSlug: state.currentSlug || null, ownerId: state.myUserId || null, hidden: hidden,
|
|
13
|
+
viewId: view ? view.id : null, revision: view ? view.revision : 0, tree: view ? view.tree : null,
|
|
14
|
+
focusOrigin: state.projectSplitViewFocusedOrigin || null, singletonOrigin: state.projectSplitViewSingletonOrigin || null,
|
|
15
|
+
activeSessionId: state.activeSessionId || null,
|
|
16
|
+
panes: Array.from(document.querySelectorAll('.split-pane-frame')).slice(0, 32).map(function (frame) {
|
|
17
|
+
var live = frame.clayPane && frame.clayPane.identity(), binding = live && live.binding;
|
|
18
|
+
var ready = !!livePaneBinding(frame), cached = !!frame.closest('.split-pane-cached');
|
|
19
|
+
return { origin: frame.dataset.sessionOriginId || '', sessionId: Number(frame.dataset.sessionId) || 0,
|
|
20
|
+
visible: !hidden && !cached && visible(frame), cached: cached, ready: ready,
|
|
21
|
+
binding: ready && binding ? { requestId: binding.requestId, generation: binding.generation, sessionId: binding.sessionId, sessionOriginId: binding.sessionOriginId } : null };
|
|
22
|
+
}) };
|
|
23
|
+
}
|
|
24
|
+
function stableObservation(key, socket, deadline, complete, ready) {
|
|
25
|
+
if (attempts.has(key)) return;
|
|
26
|
+
if (attempts.size >= 32) return;
|
|
27
|
+
var animation, timer, previous, stable = 0, owner = store.get('myUserId'), slug = store.get('currentSlug'), ended = false;
|
|
28
|
+
function finish(snapshot) {
|
|
29
|
+
if (ended) return;
|
|
30
|
+
ended = true; clearTimeout(timer); cancelAnimationFrame(animation); attempts.delete(key);
|
|
31
|
+
document.removeEventListener('click', stop, true); document.removeEventListener('visibilitychange', hidden);
|
|
32
|
+
if (getWs() === socket && socket.readyState === 1) complete(snapshot);
|
|
33
|
+
}
|
|
34
|
+
function stop(event) { if (event.target.closest('#generation-stop, #autonomous-run-stop, #send-btn.stop')) finish(null); }
|
|
35
|
+
function hidden() { if (document.hidden) finish(null); }
|
|
36
|
+
document.addEventListener('click', stop, true); document.addEventListener('visibilitychange', hidden);
|
|
37
|
+
attempts.set(key, finish);
|
|
38
|
+
timer = setTimeout(function () { finish(null); }, Math.max(0, Math.min(5000, deadline - Date.now())));
|
|
39
|
+
function inspect() {
|
|
40
|
+
if (ended) return;
|
|
41
|
+
if (getWs() !== socket || !store.get('connected') || store.get('myUserId') !== owner || store.get('currentSlug') !== slug) { finish(null); return; }
|
|
42
|
+
var snapshot = splitViewScreenSnapshot(), serialized = JSON.stringify(snapshot);
|
|
43
|
+
stable = previous === serialized ? stable + 1 : 1; previous = serialized;
|
|
44
|
+
if (stable >= 2 && (!ready || ready(snapshot))) finish(snapshot); else animation = requestAnimationFrame(inspect);
|
|
45
|
+
}
|
|
46
|
+
animation = requestAnimationFrame(inspect);
|
|
47
|
+
}
|
|
48
|
+
export function handleSplitViewProbe(msg) {
|
|
49
|
+
if (msg.type !== 'clay_view_probe') return false;
|
|
50
|
+
var socket = getWs();
|
|
51
|
+
if (!socket || socket.readyState !== 1 || store.get('paneMode')) return true;
|
|
52
|
+
stableObservation(msg.probeId, socket, Date.now() + 4500, function (snapshot) {
|
|
53
|
+
socket.send(JSON.stringify({ type: 'clay_view_observed', probeId: msg.probeId, snapshot: snapshot }));
|
|
54
|
+
});
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
export function applySplitViewNavigation(target) {
|
|
58
|
+
if (JSON.stringify(splitViewScreenSnapshot()) !== JSON.stringify(target.expectedScreen)) return false;
|
|
59
|
+
return applyReviewedSplitView(target);
|
|
60
|
+
}
|
|
61
|
+
function renderedTarget(snapshot, target) {
|
|
62
|
+
var exact = snapshot && !snapshot.hidden && snapshot.viewId === target.view.id && snapshot.revision === target.view.revision &&
|
|
63
|
+
snapshot.focusOrigin === target.focusOrigin && JSON.stringify(snapshot.tree) === JSON.stringify(target.view.tree);
|
|
64
|
+
var expected = target.view.sessions.filter(function (session) { return !target.focusOrigin || session.sessionOriginId === target.focusOrigin; });
|
|
65
|
+
var shown = snapshot ? snapshot.panes.filter(function (pane) { return pane.visible; }) : [];
|
|
66
|
+
return !!(exact && shown.length === expected.length && expected.every(function (session) { return shown.some(function (pane) { return pane.origin === session.sessionOriginId && pane.sessionId === session.id && pane.ready && !pane.cached; }); }));
|
|
67
|
+
}
|
|
68
|
+
export function observeSplitViewRendering(msg, socket) {
|
|
69
|
+
var target = msg.target;
|
|
70
|
+
stableObservation(msg.deliveryId, socket, msg.renderDeadline, function (snapshot) {
|
|
71
|
+
socket.send(JSON.stringify({ type: 'clay_navigation_rendered', deliveryId: msg.deliveryId, renderToken: msg.renderToken,
|
|
72
|
+
surface: 'split_view', visible: renderedTarget(snapshot, target), snapshot: snapshot || splitViewScreenSnapshot() }));
|
|
73
|
+
}, function (snapshot) { return renderedTarget(snapshot, target); });
|
|
74
|
+
}
|
|
@@ -17,6 +17,11 @@ export function syncToolGroupDisclosure(el) {
|
|
|
17
17
|
var expanded = !el.classList.contains('collapsed');
|
|
18
18
|
el.querySelector('.tool-group-header').setAttribute('aria-expanded', String(expanded));
|
|
19
19
|
var items = el.querySelector('.tool-group-items');
|
|
20
|
+
if (el.classList.contains('popup-chat-activity') && typeof document !== 'undefined' && document.body && document.body.classList.contains('popup-chat-rendering')) {
|
|
21
|
+
items.inert = false;
|
|
22
|
+
items.setAttribute('aria-hidden', 'false');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
20
25
|
items.inert = !expanded;
|
|
21
26
|
items.setAttribute('aria-hidden', String(!expanded));
|
|
22
27
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { formatRelativeDate, paletteSelectionIndex } from './palette-render-helpers.js';
|
|
1
2
|
import { forwardContextualSessionCitation, validContextualCitation, contextualCitationLink } from './contextual-session-citations.js';
|
|
2
3
|
import { mateAvatarUrl } from './avatar.js';
|
|
3
4
|
import { escapeHtml } from './utils.js';
|
|
@@ -7,23 +8,10 @@ import { startSearchClayChat, retrySearchClayAsk } from './search-clay-chat.js';
|
|
|
7
8
|
import { getWs } from './ws-ref.js';
|
|
8
9
|
import { openProjectLog } from './project-logs.js';
|
|
9
10
|
import { store } from './store.js';
|
|
11
|
+
import { initSearchHints, setSearchHintsPaletteOpen } from './search-hints.js';
|
|
12
|
+
import { openPaletteClayConversation } from './palette-clay-conversation.js';
|
|
10
13
|
import { disarmStickyBottom } from './chat-render-runtime.js';
|
|
11
14
|
|
|
12
|
-
function formatRelativeDate(ts) {
|
|
13
|
-
if (!ts) return "";
|
|
14
|
-
var diff = Math.max(0, Date.now() - ts);
|
|
15
|
-
var min = Math.floor(diff / 60000);
|
|
16
|
-
if (min < 1) return "just now";
|
|
17
|
-
if (min < 60) return min + "m ago";
|
|
18
|
-
var hr = Math.floor(min / 60);
|
|
19
|
-
if (hr < 24) return hr + "h ago";
|
|
20
|
-
var days = Math.floor(hr / 24);
|
|
21
|
-
if (days < 7) return days + "d ago";
|
|
22
|
-
if (days < 30) return Math.floor(days / 7) + "w ago";
|
|
23
|
-
var d = new Date(ts);
|
|
24
|
-
var months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
|
|
25
|
-
return months[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear();
|
|
26
|
-
}
|
|
27
15
|
|
|
28
16
|
var ctx;
|
|
29
17
|
var paletteEl = null, inputEl = null, resultsEl = null, footerEl = null;
|
|
@@ -34,12 +22,14 @@ var searchResults = [], searchQuery = "", searchPending = false;
|
|
|
34
22
|
|
|
35
23
|
export function initCommandPalette(value) {
|
|
36
24
|
ctx = value;
|
|
25
|
+
if (paletteEl) return;
|
|
37
26
|
buildDOM();
|
|
38
27
|
// The palette dialog stops bubbling so backdrop clicks cannot close it.
|
|
39
28
|
// Capture session controls before that boundary; the same handler also
|
|
40
29
|
// serves links rendered in the full Home transcript.
|
|
41
30
|
document.addEventListener("click", handleClaySessionLinkClick, true);
|
|
42
31
|
var trigger = document.getElementById("cmd-palette-btn");
|
|
32
|
+
initSearchHints(trigger, inputEl);
|
|
43
33
|
if (trigger) {
|
|
44
34
|
var isMac = navigator.platform.indexOf("Mac") !== -1;
|
|
45
35
|
var kbd = trigger.querySelector(".cmd-palette-searchbar-kbd");
|
|
@@ -154,6 +144,7 @@ export function isCommandPaletteOpen() { return paletteEl && !paletteEl.classLis
|
|
|
154
144
|
export function openCommandPalette() {
|
|
155
145
|
if (!paletteEl) return;
|
|
156
146
|
paletteEl.classList.remove("hidden");
|
|
147
|
+
setSearchHintsPaletteOpen(true);
|
|
157
148
|
var trigger = document.getElementById("cmd-palette-btn");
|
|
158
149
|
if (trigger) trigger.style.visibility = "hidden";
|
|
159
150
|
updateFooter();
|
|
@@ -162,8 +153,10 @@ export function openCommandPalette() {
|
|
|
162
153
|
}
|
|
163
154
|
|
|
164
155
|
export function closeCommandPalette() {
|
|
156
|
+
if ((store.get('paletteClayConversation') || {}).phase === 'resolving') store.set({ paletteClayConversation: null });
|
|
165
157
|
if (!paletteEl) return;
|
|
166
158
|
paletteEl.classList.add("hidden");
|
|
159
|
+
setSearchHintsPaletteOpen(false);
|
|
167
160
|
var trigger = document.getElementById("cmd-palette-btn");
|
|
168
161
|
if (trigger) trigger.style.visibility = "";
|
|
169
162
|
if (debounceTimer) { clearTimeout(debounceTimer); debounceTimer = null; }
|
|
@@ -297,7 +290,7 @@ function buildDOM() {
|
|
|
297
290
|
'<div class="cmd-palette-input-row">' +
|
|
298
291
|
'<img class="cmd-palette-input-brand" src="/clay-studio-symbol.png" width="22" height="22" alt="">' +
|
|
299
292
|
'<i data-lucide="search" aria-hidden="true"></i>' +
|
|
300
|
-
'<input class="cmd-palette-input" type="text" placeholder="Search
|
|
293
|
+
'<input class="cmd-palette-input" type="text" aria-label="Search or ask Clay" placeholder="Search or ask Clay, like “What was I working on recently?”" autocomplete="off" spellcheck="false" />' +
|
|
301
294
|
'<button type="button" class="cmd-palette-kbd" id="cmd-palette-close" aria-label="Close search"><i data-lucide="x"></i></button>' +
|
|
302
295
|
'</div>' +
|
|
303
296
|
'<div class="cmd-palette-results"></div>' +
|
|
@@ -313,11 +306,13 @@ function buildDOM() {
|
|
|
313
306
|
inputEl.addEventListener("input", function () {
|
|
314
307
|
var query = inputEl.value.trim();
|
|
315
308
|
searchQuery = query;
|
|
309
|
+
if (abortCtrl) { abortCtrl.abort(); abortCtrl = null; }
|
|
316
310
|
searchResults = [];
|
|
317
311
|
searchPending = !!query;
|
|
318
312
|
renderSearch();
|
|
319
313
|
if (debounceTimer) clearTimeout(debounceTimer);
|
|
320
314
|
if (query) debounceTimer = setTimeout(function () { fetchHomeSearchResults(query); }, 250);
|
|
315
|
+
else fetchHomeData();
|
|
321
316
|
});
|
|
322
317
|
inputEl.addEventListener("keydown", function (event) {
|
|
323
318
|
if (event.key === "Escape") { event.preventDefault(); closeCommandPalette(); return; }
|
|
@@ -347,13 +342,15 @@ function updateFooter() {
|
|
|
347
342
|
function fetchHomeData() {
|
|
348
343
|
if (cachedHomeData.length) { renderSearch(); return; }
|
|
349
344
|
if (abortCtrl) abortCtrl.abort();
|
|
350
|
-
abortCtrl = new AbortController();
|
|
345
|
+
var request = abortCtrl = new AbortController();
|
|
351
346
|
fetch("/api/palette/search", { signal: abortCtrl.signal }).then(function (res) { return res.json(); }).then(function (data) {
|
|
347
|
+
if (abortCtrl !== request) return;
|
|
352
348
|
abortCtrl = null;
|
|
353
349
|
cachedHomeData = data.results || [];
|
|
354
350
|
renderSearch();
|
|
355
351
|
}).catch(function (error) {
|
|
356
352
|
if (error.name === "AbortError") return;
|
|
353
|
+
if (abortCtrl !== request) return;
|
|
357
354
|
abortCtrl = null;
|
|
358
355
|
cachedHomeData = [];
|
|
359
356
|
renderSearch();
|
|
@@ -362,8 +359,9 @@ function fetchHomeData() {
|
|
|
362
359
|
|
|
363
360
|
function fetchHomeSearchResults(query) {
|
|
364
361
|
if (abortCtrl) abortCtrl.abort();
|
|
365
|
-
abortCtrl = new AbortController();
|
|
362
|
+
var request = abortCtrl = new AbortController();
|
|
366
363
|
fetch("/api/palette/search?q=" + encodeURIComponent(query), { signal: abortCtrl.signal }).then(function (res) { return res.json(); }).then(function (data) {
|
|
364
|
+
if (abortCtrl !== request) return;
|
|
367
365
|
abortCtrl = null;
|
|
368
366
|
if (searchQuery !== query) return;
|
|
369
367
|
searchResults = data.results || [];
|
|
@@ -371,6 +369,7 @@ function fetchHomeSearchResults(query) {
|
|
|
371
369
|
renderSearch();
|
|
372
370
|
}).catch(function (error) {
|
|
373
371
|
if (error.name === "AbortError") return;
|
|
372
|
+
if (abortCtrl !== request) return;
|
|
374
373
|
abortCtrl = null;
|
|
375
374
|
if (searchQuery !== query) return;
|
|
376
375
|
searchPending = false;
|
|
@@ -396,6 +395,7 @@ function sessionIcon(session) {
|
|
|
396
395
|
}
|
|
397
396
|
|
|
398
397
|
function renderSearch() {
|
|
398
|
+
var previous = items[activeIndex];
|
|
399
399
|
var query = searchQuery.toLowerCase();
|
|
400
400
|
var sessions = query ? cachedHomeData.filter(function (item) {
|
|
401
401
|
return (item.sessionTitle || "").toLowerCase().indexOf(query) !== -1 || (item.projectTitle || "").toLowerCase().indexOf(query) !== -1;
|
|
@@ -410,6 +410,11 @@ function renderSearch() {
|
|
|
410
410
|
items.push({ type: 'retry-clay-ask' });
|
|
411
411
|
html += renderItem(index++, '<img src="/clay-studio-symbol.png" width="22" height="22" alt="">', 'Retry previous question', escapeHtml(pendingAsk.text), null, null, ' is-retry-clay-ask');
|
|
412
412
|
}
|
|
413
|
+
if (query) {
|
|
414
|
+
items.push({ type: "ask-clay", query: searchQuery });
|
|
415
|
+
html += '<div class="cmd-palette-group-label">Ask Clay</div>';
|
|
416
|
+
html += renderItem(index++, '<img src="/clay-studio-symbol.png" width="22" height="22" alt="">', 'Ask Clay about “' + escapeHtml(searchQuery) + '”', "Start a new Clay conversation", null, null, " is-ask-clay");
|
|
417
|
+
}
|
|
413
418
|
if (sessions.length) {
|
|
414
419
|
html += '<div class="cmd-palette-group-label">' + (query ? "Conversations" : "Recent conversations") + '</div>';
|
|
415
420
|
for (var s = 0; s < sessions.length; s++) {
|
|
@@ -420,17 +425,13 @@ function renderSearch() {
|
|
|
420
425
|
html += renderItem(index++, sessionIcon(session), escapeHtml(session.sessionTitle || "New Chat"), project, snippet, session.lastActivity, "");
|
|
421
426
|
}
|
|
422
427
|
}
|
|
423
|
-
if (query)
|
|
424
|
-
items.push({ type: "ask-clay", query: searchQuery });
|
|
425
|
-
if (!sessions.length && !searchPending) html += '<div class="cmd-palette-exact-empty">No exact matches. Clay can search by meaning.</div>';
|
|
426
|
-
html += '<div class="cmd-palette-group-label">Ask Clay</div>';
|
|
427
|
-
html += renderItem(index, '<img src="/clay-studio-symbol.png" width="22" height="22" alt="">', 'Ask Clay about “' + escapeHtml(searchQuery) + '”', "Start a new Clay conversation", null, null, " is-ask-clay");
|
|
428
|
-
}
|
|
428
|
+
if (query && !sessions.length && !searchPending) html += '<div class="cmd-palette-exact-empty">No exact matches. Clay can search by meaning.</div>';
|
|
429
429
|
if (!html) html = '<div class="cmd-palette-empty">No conversations yet.</div>';
|
|
430
430
|
resultsEl.innerHTML = html;
|
|
431
|
-
activeIndex =
|
|
431
|
+
activeIndex = paletteSelectionIndex(previous, items, query);
|
|
432
432
|
refreshIcons();
|
|
433
433
|
bindItemEvents();
|
|
434
|
+
if (activeIndex >= 0) setActive(activeIndex, true);
|
|
434
435
|
}
|
|
435
436
|
|
|
436
437
|
function renderItem(index, icon, title, desc, snippet, timestamp, extraClass) {
|
|
@@ -455,16 +456,17 @@ function setActive(index, skipScroll) {
|
|
|
455
456
|
for (var i = 0; i < elements.length; i++) elements[i].classList.toggle("active", i === index);
|
|
456
457
|
if (!skipScroll && elements[index]) elements[index].scrollIntoView({ block: "nearest" });
|
|
457
458
|
}
|
|
458
|
-
|
|
459
459
|
function beginClayChat(query) {
|
|
460
460
|
if (!isCommandPaletteOpen()) return;
|
|
461
461
|
if (startSearchClayChat(query)) closeCommandPalette();
|
|
462
462
|
}
|
|
463
|
-
|
|
464
463
|
function activateItem(entry) {
|
|
465
464
|
if (entry.type === 'retry-clay-ask') { if (retrySearchClayAsk()) closeCommandPalette(); return; }
|
|
466
465
|
if (entry.type === "ask-clay") { beginClayChat(entry.query); return; }
|
|
467
|
-
if (entry.type === "mate-session" || entry.type === "session")
|
|
466
|
+
if (entry.type === "mate-session" || entry.type === "session") {
|
|
467
|
+
if (!store.get('paneMode') && (entry.data.isMate || /^mate-/.test(entry.data.projectSlug)) && openPaletteClayConversation(entry.data, entry.query, entry.data.entryIndex)) return;
|
|
468
|
+
navigateToSession(entry.data, entry.query, entry.data.entryIndex);
|
|
469
|
+
}
|
|
468
470
|
}
|
|
469
471
|
|
|
470
472
|
export function navigateToSession(item, query, entryIndex) {
|
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
// Session identity remains pinned across transport recovery and presentation.
|
|
2
|
+
import { navigateToSession } from './command-palette.js';
|
|
2
3
|
import { autoResize, takeComposerDraft, restoreComposerDraft } from './input.js';
|
|
3
4
|
import { store } from './store.js';
|
|
4
5
|
import { withdrawPaneControlActivity } from './clay-control-activity.js';
|
|
5
6
|
import { initContextualComposer } from './contextual-composer.js';
|
|
6
7
|
import { submitContextualAsk, pauseContextualAsk, stopContextualAsk } from './contextual-ask-session.js';
|
|
8
|
+
import { resolveProjectAssistantIdentity } from './project-chat-identity.js';
|
|
9
|
+
import { setPopupChatRendering, popupChatContextIsReady } from './popup-chat-rendering.js';
|
|
10
|
+
import { syncPopupChatToolGroups } from './tools.js';
|
|
7
11
|
|
|
8
12
|
// Session replay and authenticated account bootstrap complete independently.
|
|
9
13
|
function submissionReady() { var expected = window.frameElement && window.frameElement.dataset.expectedSessionOrigin; return (!expected || expected === store.get('sessionOriginId')) && !!(store.get('contextualSessionReady') && store.get('connected') && store.get('myUserId') && store.get('sessionOriginId') && store.get('activeSessionId') === store.get('paneSessionId')); }
|
|
10
14
|
function enabled() { return store.get('paneMode') && new URLSearchParams(location.search).get('contextual') === '1'; }
|
|
11
|
-
function renderSessionAvailability() {
|
|
15
|
+
function renderSessionAvailability(notifyReady) {
|
|
12
16
|
var input = document.getElementById('input');
|
|
13
17
|
var frame = window.frameElement;
|
|
18
|
+
var state = store.snap();
|
|
19
|
+
var identity = resolveProjectAssistantIdentity();
|
|
20
|
+
if (state.contextualSessionReady && frame && !frame.hidden) frame.dataset.sessionId = String(state.paneSessionId);
|
|
21
|
+
var popupSurface = null;
|
|
22
|
+
try { popupSurface = window.parent.document.getElementById('contextual-clay'); } catch (error) { /* Popup rendering stays native outside its authenticated parent surface. */ }
|
|
23
|
+
var popupReady = popupChatContextIsReady(document.body, state, frame, identity, popupSurface);
|
|
24
|
+
setPopupChatRendering(popupReady);
|
|
25
|
+
syncPopupChatToolGroups(popupReady);
|
|
14
26
|
var mate = frame && (store.get('cachedMatesList') || []).find(function (item) { return item.id === frame.dataset.mateId; });
|
|
15
27
|
var name = mate && ((mate.profile || {}).displayName || mate.displayName || mate.name);
|
|
16
28
|
if (input && name) { input.setAttribute('aria-label', 'Message ' + name); if (name.length > 32) input.placeholder = 'Message this Mate…'; }
|
|
@@ -24,7 +36,7 @@ function renderSessionAvailability() {
|
|
|
24
36
|
if (surface.dataset.mode === 'popover' && window.parent.document.activeElement === anchor) document.getElementById('input').focus({ preventScroll: true });
|
|
25
37
|
}
|
|
26
38
|
} catch (error) { /* Cross-origin authentication owns focus. */ }
|
|
27
|
-
if (store.get('contextualSessionReady') && window.frameElement && !window.frameElement.hidden) {
|
|
39
|
+
if (notifyReady !== false && store.get('contextualSessionReady') && window.frameElement && !window.frameElement.hidden) {
|
|
28
40
|
window.frameElement.dataset.sessionId = String(store.get('paneSessionId'));
|
|
29
41
|
var parentSurface = window.parent.document.getElementById('contextual-clay');
|
|
30
42
|
if (parentSurface) parentSurface.dispatchEvent(new window.parent.CustomEvent('contextual-clay-ready'));
|
|
@@ -67,8 +79,20 @@ export function initContextualSession() {
|
|
|
67
79
|
} catch (error) { /* Authentication navigation owns its document. */ }
|
|
68
80
|
});
|
|
69
81
|
var surfaceBridge = window.parent.document.getElementById('contextual-clay');
|
|
82
|
+
if (window.frameElement && surfaceBridge && typeof MutationObserver !== 'undefined') {
|
|
83
|
+
// Visibility changes update presentation and focus, never session readiness.
|
|
84
|
+
// A ready notification renders the parent, which can mutate these attributes.
|
|
85
|
+
var popupVisibilityObserver = new MutationObserver(function () { renderSessionAvailability(false); });
|
|
86
|
+
popupVisibilityObserver.observe(window.frameElement, { attributes: true, attributeFilter: ['hidden', 'inert'] });
|
|
87
|
+
popupVisibilityObserver.observe(surfaceBridge, { attributes: true, attributeFilter: ['data-mode'] });
|
|
88
|
+
}
|
|
70
89
|
if (surfaceBridge && window.frameElement && surfaceBridge.contains(window.frameElement)) window.frameElement.conversation = {
|
|
71
90
|
source: window, document: document,
|
|
91
|
+
navigateEntry: function (target) {
|
|
92
|
+
if (!submissionReady() || target.ownerId !== store.get('myUserId') || target.mateId !== store.get('activeProjectMateId') || target.sessionId !== store.get('paneSessionId') || target.sessionOriginId !== store.get('sessionOriginId')) return false;
|
|
93
|
+
navigateToSession({ projectSlug: store.get('currentSlug'), sessionId: target.sessionId }, target.query, target.entryIndex);
|
|
94
|
+
return true;
|
|
95
|
+
},
|
|
72
96
|
submitAsk: submitContextualAsk,
|
|
73
97
|
pauseAsk: pauseContextualAsk,
|
|
74
98
|
stopAsk: stopContextualAsk,
|
|
@@ -61,7 +61,7 @@ function render() {
|
|
|
61
61
|
renderContextualWorkspaceBack();
|
|
62
62
|
if (!surface) return;
|
|
63
63
|
surface.hidden = mode === 'closed' || mode === 'workspace';
|
|
64
|
-
surface.dataset.mode = mode;
|
|
64
|
+
if (surface.dataset.mode !== mode) surface.dataset.mode = mode;
|
|
65
65
|
positionContextualClay();
|
|
66
66
|
surface.querySelector('[data-clay-expand]').hidden = mode !== 'popover';
|
|
67
67
|
surface.querySelector('[data-clay-close]').hidden = mode !== 'popover' && mode !== 'opening';
|
|
@@ -100,7 +100,10 @@ function render() {
|
|
|
100
100
|
var selectedFrame = contextualFrame();
|
|
101
101
|
var connection = selectedFrame && selectedFrame.conversation;
|
|
102
102
|
surface.querySelector('[data-clay-loading]').hidden = !!(connection && connection.identity().ready) || surface.dataset.askPending === 'true';
|
|
103
|
-
surface.querySelectorAll('iframe').forEach(function (frame) {
|
|
103
|
+
surface.querySelectorAll('iframe').forEach(function (frame) {
|
|
104
|
+
var hidden = frame !== selectedFrame;
|
|
105
|
+
if (frame.hidden !== hidden) frame.hidden = hidden;
|
|
106
|
+
});
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
function createFrame(mate, sessionId) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Saved conversations are independent of which compact renderers are mounted.
|
|
2
|
+
import { handlePaletteClayConversation } from './palette-clay-conversation.js';
|
|
2
3
|
import { store } from './store.js';
|
|
3
4
|
import { getWs } from './ws-ref.js';
|
|
4
5
|
import { contextualMate, contextualMateStamp, contextualFrame } from './contextual-mate-identity.js';
|
|
@@ -111,6 +112,7 @@ export function attachContextualConversations(surface) {
|
|
|
111
112
|
refreshIcons(); render();
|
|
112
113
|
}
|
|
113
114
|
export function handleContextualConversations(msg) {
|
|
115
|
+
if (handlePaletteClayConversation(msg)) return true;
|
|
114
116
|
if (msg.type !== 'contextual_mate_sessions_result') return false;
|
|
115
117
|
var kind = requestState('list') && requestState('list').requestId === msg.requestId ? 'list' : 'action';
|
|
116
118
|
var request = requestState(kind);
|
|
@@ -5,7 +5,13 @@ import { isDriverOperatedView } from './worker-pane-lock.js';
|
|
|
5
5
|
export function stopGeneration() {
|
|
6
6
|
var ws = getWs();
|
|
7
7
|
if (!store.get('processing') || !store.get('connected') || !ws || ws.readyState !== 1) return false;
|
|
8
|
-
|
|
8
|
+
var message = { type: 'stop' };
|
|
9
|
+
if (store.get('paneMode')) {
|
|
10
|
+
var binding = store.get('paneCommittedBinding');
|
|
11
|
+
if (!store.get('paneReady') || !binding || binding.sessionId !== store.get('activeSessionId') || binding.sessionOriginId !== store.get('sessionOriginId')) return false;
|
|
12
|
+
message.paneBinding = binding;
|
|
13
|
+
}
|
|
14
|
+
ws.send(JSON.stringify(message));
|
|
9
15
|
return true;
|
|
10
16
|
}
|
|
11
17
|
|