clay-server 5.2.1-beta.2 → 5.3.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/clay-control-presentation-binding.js +5 -3
- package/lib/clay-product-control-tools.js +3 -3
- package/lib/clay-product-control.js +4 -2
- package/lib/clay-screen-controls.js +18 -3
- package/lib/clay-split-view-awareness.js +35 -0
- package/lib/clay-split-view-controls.js +122 -0
- package/lib/clay-split-view-input.js +20 -0
- package/lib/clay-split-view-render-proof.js +22 -0
- package/lib/pane-asset-manifest.js +63 -0
- package/lib/project-connection.js +19 -10
- package/lib/project-session-actions.js +1 -0
- package/lib/project-session-delete.js +2 -2
- package/lib/project-session-folders.js +1 -0
- package/lib/project-sessions.js +6 -1
- package/lib/project-split-view-document.js +33 -0
- package/lib/project-split-view-names.js +67 -0
- package/lib/project-split-view-organization.js +23 -0
- package/lib/project-split-views.js +479 -0
- package/lib/project-worker-conversation.js +222 -0
- package/lib/project-worker-creation-proposal.js +3 -4
- package/lib/project-worker-proposal.js +1 -1
- package/lib/project-worker-removal.js +101 -0
- package/lib/project.js +28 -1
- package/lib/public/app.js +32 -13
- package/lib/public/css/base.css +9 -0
- package/lib/public/css/filebrowser.css +0 -1
- package/lib/public/css/pane-header-controls.css +100 -0
- package/lib/public/css/pane.css +24 -41
- package/lib/public/css/popup-chat.css +123 -0
- package/lib/public/css/right-workbench.css +50 -18
- package/lib/public/css/session-folders.css +1 -1
- package/lib/public/css/split-view-layout.css +463 -0
- package/lib/public/css/split-view-worker-activity.css +97 -0
- package/lib/public/css/sticky-notes.css +13 -0
- package/lib/public/index.html +3 -3
- package/lib/public/modules/app-connection.js +4 -1
- package/lib/public/modules/app-favicon.js +1 -1
- package/lib/public/modules/app-messages.js +31 -9
- package/lib/public/modules/chat-render-runtime.js +54 -0
- package/lib/public/modules/clay-control-copy.js +3 -0
- package/lib/public/modules/clay-navigation-render.js +1 -1
- package/lib/public/modules/clay-owner-navigation.js +15 -5
- package/lib/public/modules/clay-split-view-screen.js +74 -0
- package/lib/public/modules/clay-tool-activity.js +5 -0
- package/lib/public/modules/command-palette.js +30 -28
- package/lib/public/modules/contextual-clay-session.js +26 -2
- package/lib/public/modules/contextual-clay.js +5 -2
- package/lib/public/modules/contextual-mate-sessions.js +2 -0
- package/lib/public/modules/filebrowser.js +6 -2
- package/lib/public/modules/generation-stop.js +7 -1
- package/lib/public/modules/knowledge-workbench-entry.js +43 -0
- package/lib/public/modules/markdown-view-mode.js +34 -0
- package/lib/public/modules/markdown.js +11 -0
- package/lib/public/modules/mate-knowledge-workbench.js +2 -7
- package/lib/public/modules/mate-sidebar.js +1 -1
- package/lib/public/modules/palette-clay-conversation.js +87 -0
- package/lib/public/modules/palette-render-helpers.js +25 -0
- package/lib/public/modules/pane-activation.js +46 -7
- package/lib/public/modules/pane-binding.js +12 -2
- package/lib/public/modules/pane-bridge.js +12 -3
- package/lib/public/modules/pane-deferred-bootstrap.js +32 -0
- package/lib/public/modules/pane-feature-assets.js +35 -0
- package/lib/public/modules/pane-file-bridge.js +8 -3
- package/lib/public/modules/pane-search-receipt.js +12 -0
- package/lib/public/modules/permission-control.js +6 -6
- package/lib/public/modules/popup-chat-rendering.js +244 -0
- package/lib/public/modules/project-activation.js +2 -2
- package/lib/public/modules/qrcode.js +12 -1
- package/lib/public/modules/right-workbench.js +68 -0
- package/lib/public/modules/search-hints.js +79 -0
- package/lib/public/modules/session-context-menu.js +35 -2
- package/lib/public/modules/session-folder-collapse.js +2 -1
- package/lib/public/modules/session-folder-context.js +24 -13
- package/lib/public/modules/session-folder-delete.js +18 -1
- package/lib/public/modules/session-folder-dnd.js +4 -1
- package/lib/public/modules/session-folder-layout.js +7 -6
- package/lib/public/modules/session-search-pane-state.js +30 -0
- package/lib/public/modules/session-search.js +53 -13
- package/lib/public/modules/sidebar-mobile.js +3 -3
- package/lib/public/modules/sidebar-projects.js +1 -0
- package/lib/public/modules/sidebar-session-folders-ui.js +3 -3
- package/lib/public/modules/sidebar-sessions.js +57 -24
- package/lib/public/modules/sidebar.js +20 -3
- package/lib/public/modules/split-group-helpers.js +1 -1
- package/lib/public/modules/split-group-restore.js +8 -11
- package/lib/public/modules/split-pair-ui.js +18 -0
- package/lib/public/modules/split-pane-header-state.js +45 -0
- package/lib/public/modules/split-pane-reconciler.js +23 -4
- package/lib/public/modules/split-pane-renderer.js +330 -23
- package/lib/public/modules/split-pane-retention.js +157 -0
- package/lib/public/modules/split-session-boundary.js +21 -0
- package/lib/public/modules/split-view-create-action.js +15 -0
- package/lib/public/modules/split-view-create-menu.js +83 -0
- package/lib/public/modules/split-view-create-request.js +23 -0
- package/lib/public/modules/split-view-layout-client.js +198 -0
- package/lib/public/modules/split-view-layout-icons.js +37 -0
- package/lib/public/modules/split-view-layout-scope.js +38 -0
- package/lib/public/modules/split-view-organization.js +73 -0
- package/lib/public/modules/split-view-presentation-close.js +35 -0
- package/lib/public/modules/split-view-presentation-drag.js +278 -0
- package/lib/public/modules/split-view-presentation-drop.js +57 -0
- package/lib/public/modules/split-view-presentation-lookup.js +44 -0
- package/lib/public/modules/split-view-presentation-renderer.js +494 -0
- package/lib/public/modules/split-view-presentations.js +498 -0
- package/lib/public/modules/split-view-shell-controls.js +28 -0
- package/lib/public/modules/split-view-sidebar-menu.js +42 -0
- package/lib/public/modules/split-view-sidebar-renderer.js +30 -0
- package/lib/public/modules/split-view-singleton.js +21 -0
- package/lib/public/modules/split-view-worker-activity.js +94 -0
- package/lib/public/modules/split-view-worker-controls.js +30 -0
- package/lib/public/modules/split-view-worker-removal.js +137 -0
- package/lib/public/modules/split-view-worker-scroll.js +57 -0
- package/lib/public/modules/split-view-worker-transcript.js +46 -0
- package/lib/public/modules/split-view-workers.js +418 -0
- package/lib/public/modules/split-view.js +61 -22
- package/lib/public/modules/sticky-notes.js +9 -2
- package/lib/public/modules/tools.js +25 -5
- package/lib/public/modules/workbench-layout-preference.js +76 -0
- package/lib/public/modules/worker-created-presentation.js +37 -0
- package/lib/public/pane-assets/app-2DRBYY3L.js +1 -0
- package/lib/public/pane-assets/chunk-4BM7UVNG.js +2 -0
- package/lib/public/pane-assets/chunk-6SM2WQJX.js +1 -0
- package/lib/public/pane-assets/chunk-7H36SUZA.js +2 -0
- package/lib/public/pane-assets/chunk-C4RVVYNT.js +1 -0
- package/lib/public/pane-assets/chunk-DGOFGEUK.js +1 -0
- package/lib/public/pane-assets/chunk-I57URYSL.js +1 -0
- package/lib/public/pane-assets/chunk-O6MQL6MR.js +167 -0
- package/lib/public/pane-assets/chunk-PO2JOHHW.js +1 -0
- package/lib/public/pane-assets/chunk-UMGHINOT.js +1 -0
- package/lib/public/pane-assets/manifest.json +553 -0
- package/lib/public/pane-assets/style-63EZCREE.css +1 -0
- package/lib/public/style.css +5 -1
- package/lib/server-owner-ui.js +6 -2
- package/lib/server-owner-view-observation.js +34 -0
- package/lib/server-palette.js +1 -0
- package/lib/server-pane-bootstrap.js +24 -0
- package/lib/server-settings.js +2 -0
- package/lib/server-static-assets.js +52 -0
- package/lib/server-workbench-layout-settings.js +56 -0
- package/lib/server.js +6 -45
- package/lib/session-activity-state.js +28 -0
- package/lib/session-list-delivery.js +17 -0
- package/lib/session-pair-turn-control.js +1 -6
- package/lib/session-search.js +3 -0
- package/lib/session-split-group-anchors.js +2 -2
- package/lib/session-split-group-roles.js +5 -8
- package/lib/session-split-group-v2-store.js +0 -3
- package/lib/sessions.js +16 -6
- package/lib/split-view-layout.js +132 -0
- package/lib/users-workbench-layout-preferences.js +33 -0
- package/lib/users.js +3 -0
- package/lib/workspace-query-service.js +1 -5
- package/lib/ws-schema.js +16 -0
- package/package.json +6 -3
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { showCreatedWorkerPresentation } from './worker-created-presentation.js';
|
|
2
|
+
import { handleWorkerRemovalResult } from './split-view-worker-removal.js';
|
|
1
3
|
import { handleStudioDocumentMessage } from './clay-studio-document.js';
|
|
2
4
|
import { saveSessionRichDraft, restoreSessionRichDraft } from './session-rich-drafts.js';
|
|
3
5
|
import { refreshPaneActivation, primePaneSession } from './pane-activation.js';
|
|
@@ -48,11 +50,13 @@ import { handleDefaultVendorMessage } from './default-vendor.js';
|
|
|
48
50
|
import { handleContextViewMessage } from './context-view-preference.js';
|
|
49
51
|
import { handleHomeMateMemoryState, handleHomeMateKnowledgeState, syncHomeMateSettingsTarget, handleHomeMateAvatarError } from './home-mate-settings.js';
|
|
50
52
|
import { renderKnowledgeList, handleKnowledgeContent, handleKnowledgeMutation } from './mate-knowledge.js';
|
|
51
|
-
import { handleKnowledgeWorkbenchMessage } from './
|
|
53
|
+
import { handleKnowledgeWorkbenchMessage } from './knowledge-workbench-entry.js';
|
|
52
54
|
import { renderMemoryList } from './mate-memory.js';
|
|
53
55
|
import { handlePaletteSessionSwitch, handlePaletteHistoryDone, handlePaletteHistoryPrepended, setPaletteVersion } from './command-palette.js';
|
|
54
56
|
import { handleFindInSessionResults } from './session-search.js';
|
|
55
57
|
import { syncPaneTitles, maybeRestoreSplitGroup, openGroup } from './split-view.js';
|
|
58
|
+
import { handleProjectSplitViews, handleProjectSplitViewResult, requestProjectSplitViews } from './split-view-presentations.js';
|
|
59
|
+
import { handleSplitWorkerConversation, handleWorkerSessionActivity, handleWorkerConversationChanged } from './split-view-workers.js';
|
|
56
60
|
import { showPairDialog, handlePairCreated, handleSplitDelegation, showWorkerDelegationNotice, hideWorkerDelegationNotice } from './split-pair-ui.js';
|
|
57
61
|
import { handleSessionActionMessage } from './session-actions.js';
|
|
58
62
|
import { renderWorkerProposal, updateWorkerProposal } from './worker-proposal.js';
|
|
@@ -95,7 +99,7 @@ import { clearPairResultStatus, handlePairResultStatus } from './pair-result-sta
|
|
|
95
99
|
import { scrollToBottom, addToMessages, addUserMessage, addSystemMessage, removeMatePreThinking, resetStreamBuffer, appendDelta, flushStreamBuffer, finalizeAssistantBlock, addConflictMessage, addContextOverflowMessage, showSuggestionChips, armStickyBottom, VENDOR_AVATARS, VENDOR_NAMES } from './app-rendering.js';
|
|
96
100
|
import { setActivity, startUrgentBlink, stopUrgentBlink, blinkSessionDot, updateCrossProjectBlink } from './app-favicon.js';
|
|
97
101
|
import { setStatus } from './app-connection.js';
|
|
98
|
-
import {
|
|
102
|
+
import { resolveSwitchedVendor } from './pane-session.js';
|
|
99
103
|
import { markProjectSessionListHydrated, applyProjectSplitGroups, clearProjectSplitState } from './split-session-boundary.js';
|
|
100
104
|
import { selectDefaultVendorForBlankSession } from './vendor-selection.js';
|
|
101
105
|
import { getModelInfoUpdate, modelEntryValue, modelEntryMatches, handleModelSelectionResult, requestVendorModels } from './model-picker.js';
|
|
@@ -440,6 +444,7 @@ export function processMessage(msg) {
|
|
|
440
444
|
if (msg.projectOwnerId !== undefined) store.set({ currentProjectOwnerId: msg.projectOwnerId });
|
|
441
445
|
if (msg.ownerLocked !== undefined) store.set({ ownerLocked: !!msg.ownerLocked });
|
|
442
446
|
if (msg.osUsers !== undefined) store.set({ isOsUsers: !!msg.osUsers });
|
|
447
|
+
store.set({ paneOsIdentity: msg.paneOsIdentity || null });
|
|
443
448
|
if (msg.lanHost) window.__lanHost = msg.lanHost;
|
|
444
449
|
store.set({ skipPermsEnabled: msg.dangerouslySkipPermissions === true });
|
|
445
450
|
var spBanner = document.getElementById("skip-perms-pill");
|
|
@@ -785,19 +790,31 @@ export function processMessage(msg) {
|
|
|
785
790
|
renderMateSessionList(msg.sessions || []);
|
|
786
791
|
renderSessionList(msg.sessions || []);
|
|
787
792
|
store.set({ paneBindingSessions: msg.sessions || [] });
|
|
793
|
+
requestProjectSplitViews();
|
|
788
794
|
refreshPaneActivation();
|
|
789
795
|
syncGithubSessions(msg.sessions || []);
|
|
790
796
|
syncPaneTitles();
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
}
|
|
797
|
+
if (store.get('paneMode')) {
|
|
798
|
+
// A pane pin must use the authenticated activation binding. Sending
|
|
799
|
+
// this startup URL's session as a plain switch can race a parent
|
|
800
|
+
// retarget and restore the iframe's stale initial session.
|
|
801
|
+
primePaneSession();
|
|
797
802
|
}
|
|
798
803
|
handlePaletteSessionSwitch();
|
|
799
804
|
maybeRestoreSplitGroup();
|
|
800
805
|
break;
|
|
806
|
+
case "project_split_views":
|
|
807
|
+
handleProjectSplitViews(msg);
|
|
808
|
+
break;
|
|
809
|
+
case "project_split_view_result":
|
|
810
|
+
handleProjectSplitViewResult(msg);
|
|
811
|
+
break;
|
|
812
|
+
case "split_worker_conversation_changed":
|
|
813
|
+
handleWorkerConversationChanged(msg);
|
|
814
|
+
break;
|
|
815
|
+
case "split_worker_conversation":
|
|
816
|
+
handleSplitWorkerConversation(msg);
|
|
817
|
+
break;
|
|
801
818
|
case "session_github_status":
|
|
802
819
|
applyGithubPersonalReviewStatus(msg);
|
|
803
820
|
break;
|
|
@@ -813,6 +830,10 @@ export function processMessage(msg) {
|
|
|
813
830
|
if (!msg.ok && msg.error) showToast(msg.error, "error");
|
|
814
831
|
break;
|
|
815
832
|
|
|
833
|
+
case "split_worker_remove_result":
|
|
834
|
+
handleWorkerRemovalResult(msg);
|
|
835
|
+
break;
|
|
836
|
+
|
|
816
837
|
case "split_worker_close_result":
|
|
817
838
|
if (!msg.ok && msg.error) showToast(msg.error, "error");
|
|
818
839
|
break;
|
|
@@ -823,7 +844,7 @@ export function processMessage(msg) {
|
|
|
823
844
|
|
|
824
845
|
case "pair_session_created":
|
|
825
846
|
var createdPairGroup = handlePairCreated(msg);
|
|
826
|
-
if (createdPairGroup)
|
|
847
|
+
if (createdPairGroup) showCreatedWorkerPresentation(createdPairGroup);
|
|
827
848
|
break;
|
|
828
849
|
|
|
829
850
|
case "session_handoff_result":
|
|
@@ -898,6 +919,7 @@ export function processMessage(msg) {
|
|
|
898
919
|
|
|
899
920
|
case "session_io":
|
|
900
921
|
blinkSessionDot(msg.id);
|
|
922
|
+
handleWorkerSessionActivity(msg);
|
|
901
923
|
break;
|
|
902
924
|
|
|
903
925
|
case "session_unread":
|
|
@@ -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");
|
|
@@ -162,6 +205,7 @@ export function addToMessages(element) {
|
|
|
162
205
|
if (prependAnchor) messagesEl.insertBefore(element, prependAnchor);
|
|
163
206
|
else messagesEl.appendChild(element);
|
|
164
207
|
keepActiveThinkingAtTail();
|
|
208
|
+
if (stickyBottomResizeObs && stickyBottom) stickyBottomResizeObs.observe(element);
|
|
165
209
|
}
|
|
166
210
|
|
|
167
211
|
export function addHistoryIndex(element, historyIndex) {
|
|
@@ -200,9 +244,15 @@ export function keepActiveThinkingAtTail() {
|
|
|
200
244
|
}
|
|
201
245
|
|
|
202
246
|
export function scrollToBottom() {
|
|
247
|
+
if (layoutScrollLock()) return;
|
|
203
248
|
if (prependAnchor || store.get('replayingHistory')) return;
|
|
204
249
|
var messagesEl = getMessagesEl();
|
|
205
250
|
if (!messagesEl) return;
|
|
251
|
+
if (stickyBottom) {
|
|
252
|
+
pinToBottomNow();
|
|
253
|
+
requestAnimationFrame(function () { if (stickyBottom && !layoutScrollLock()) pinToBottomNow(); });
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
206
256
|
var distFromBottom = messagesEl.scrollHeight - messagesEl.scrollTop - messagesEl.clientHeight;
|
|
207
257
|
if (distFromBottom > 150 || isUserScrolledUp) {
|
|
208
258
|
if (distFromBottom > 150) isUserScrolledUp = true;
|
|
@@ -214,6 +264,7 @@ export function scrollToBottom() {
|
|
|
214
264
|
return;
|
|
215
265
|
}
|
|
216
266
|
requestAnimationFrame(function () {
|
|
267
|
+
if (layoutScrollLock()) return;
|
|
217
268
|
var dist = messagesEl.scrollHeight - messagesEl.scrollTop - messagesEl.clientHeight;
|
|
218
269
|
if (dist > 150) {
|
|
219
270
|
isUserScrolledUp = true;
|
|
@@ -225,7 +276,10 @@ export function scrollToBottom() {
|
|
|
225
276
|
|
|
226
277
|
export function forceScrollToBottom() {
|
|
227
278
|
if (prependAnchor || store.get('replayingHistory')) return;
|
|
279
|
+
// An explicit new turn supersedes a layout-only scroll receipt.
|
|
280
|
+
if (layoutScrollLock()) store.set({ paneLayoutScrollLock: null });
|
|
228
281
|
armStickyBottom(750);
|
|
282
|
+
requestAnimationFrame(function () { if (stickyBottom && !layoutScrollLock()) pinToBottomNow(); });
|
|
229
283
|
}
|
|
230
284
|
|
|
231
285
|
export function getMsgTime() {
|
|
@@ -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) {
|