clay-server 5.2.0-beta.6 → 5.2.0-beta.8
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 +96 -0
- package/lib/clay-control-presentation.js +128 -0
- package/lib/clay-control-target-projection.js +26 -0
- package/lib/clay-mate-chat.js +1 -0
- package/lib/clay-mate-knowledge.js +1 -1
- package/lib/clay-product-control-tools.js +1 -1
- package/lib/clay-product-control.js +22 -12
- package/lib/clay-project-records.js +2 -1
- package/lib/clay-scheduling.js +1 -1
- package/lib/clay-screen-controls.js +13 -5
- package/lib/clay-workspace-controls.js +2 -1
- package/lib/contextual-screen-snapshot.js +38 -0
- package/lib/mate-context-service.js +138 -0
- package/lib/mate-context-tools.js +21 -0
- package/lib/project-connection.js +8 -5
- package/lib/project-control-git-view.js +12 -0
- package/lib/project-filesystem.js +5 -1
- package/lib/project-http.js +3 -0
- package/lib/project-notifications.js +5 -4
- package/lib/project-product-control.js +3 -1
- package/lib/project-scheduled-messages.js +4 -4
- package/lib/project-scheduled-task-control.js +1 -1
- package/lib/project-session-document.js +20 -5
- package/lib/project-shared-browser.js +22 -5
- package/lib/project-studio-presentation.js +58 -0
- package/lib/project-user-message.js +36 -0
- package/lib/project-workspace-query.js +11 -1
- package/lib/project.js +24 -7
- package/lib/public/app.js +24 -0
- package/lib/public/appearance-bootstrap.js +2 -0
- package/lib/public/css/contextual-clay.css +240 -0
- package/lib/public/modules/app-favicon.js +7 -7
- package/lib/public/modules/app-message-router.js +5 -1
- package/lib/public/modules/app-messages.js +14 -1
- package/lib/public/modules/app-notifications.js +10 -3
- package/lib/public/modules/app-projects.js +3 -0
- package/lib/public/modules/clay-control-activity.js +214 -0
- package/lib/public/modules/clay-control-copy.js +36 -0
- package/lib/public/modules/clay-control-detail-targets.js +44 -0
- package/lib/public/modules/clay-control-object-targets.js +52 -0
- package/lib/public/modules/clay-control-target-visibility.js +12 -0
- package/lib/public/modules/clay-control-targets.js +44 -0
- package/lib/public/modules/clay-navigation-render.js +52 -0
- package/lib/public/modules/clay-owner-navigation.js +11 -2
- package/lib/public/modules/clay-session-links.js +31 -0
- package/lib/public/modules/clay-studio-bridge.js +20 -0
- package/lib/public/modules/clay-studio-document.js +67 -0
- package/lib/public/modules/clay-studio-render.js +144 -0
- package/lib/public/modules/clay-tool-presentation.js +4 -0
- package/lib/public/modules/command-palette.js +26 -31
- package/lib/public/modules/contextual-ask-session.js +26 -0
- package/lib/public/modules/contextual-clay-geometry.js +155 -0
- package/lib/public/modules/contextual-clay-session.js +130 -0
- package/lib/public/modules/contextual-clay-workspace.js +157 -0
- package/lib/public/modules/contextual-clay.js +237 -0
- package/lib/public/modules/contextual-composer.js +91 -0
- package/lib/public/modules/contextual-mate-context.js +115 -0
- package/lib/public/modules/contextual-mate-hint.js +40 -0
- package/lib/public/modules/contextual-mate-identity.js +23 -0
- package/lib/public/modules/contextual-mate-sessions.js +134 -0
- package/lib/public/modules/contextual-search.js +137 -0
- package/lib/public/modules/contextual-session-citations.js +58 -0
- package/lib/public/modules/filebrowser.js +20 -1
- package/lib/public/modules/generation-stop.js +8 -5
- package/lib/public/modules/git-panel.js +3 -1
- package/lib/public/modules/icon-strip-tooltip.js +2 -1
- package/lib/public/modules/input.js +35 -1
- package/lib/public/modules/issues-render.js +3 -2
- package/lib/public/modules/knowledge-db-workbench.js +4 -4
- package/lib/public/modules/markdown.js +2 -34
- package/lib/public/modules/mate-knowledge-workbench.js +1 -1
- package/lib/public/modules/message-delivery.js +10 -0
- package/lib/public/modules/pane-bridge.js +2 -2
- package/lib/public/modules/pending-message-queue.js +1 -0
- package/lib/public/modules/product-review.js +26 -0
- package/lib/public/modules/project-chat-identity.js +13 -4
- package/lib/public/modules/project-logs-render.js +3 -0
- package/lib/public/modules/project-mate-navigation.js +58 -21
- package/lib/public/modules/scheduled-tasks.js +2 -0
- package/lib/public/modules/search-clay-chat.js +5 -454
- package/lib/public/modules/shared-browser.js +2 -0
- package/lib/public/modules/sidebar-mobile.js +4 -3
- package/lib/public/modules/sidebar-projects.js +1 -0
- package/lib/public/modules/sidebar-sessions.js +1 -0
- package/lib/public/modules/split-view.js +7 -16
- package/lib/public/modules/stt.js +7 -2
- package/lib/public/modules/terminal.js +9 -0
- package/lib/public/modules/tools.js +4 -1
- package/lib/public/modules/wireframe-workbench.js +5 -2
- package/lib/public/style.css +2 -0
- package/lib/sdk-bridge.js +4 -0
- package/lib/server-contextual-clay-settings.js +40 -0
- package/lib/server-contextual-mate-sessions.js +70 -0
- package/lib/server-home-chat.js +7 -1
- package/lib/server-home-clay-entry.js +23 -4
- package/lib/server-home-clay-session-links.js +12 -0
- package/lib/server-owner-ui.js +49 -8
- package/lib/server-settings.js +2 -0
- package/lib/server.js +17 -0
- package/lib/sessions.js +10 -3
- package/lib/terminal-manager.js +18 -11
- package/lib/users-contextual-clay-preferences.js +31 -0
- package/lib/users.js +3 -0
- package/lib/workspace-assignment-service.js +25 -0
- package/lib/workspace-query-mcp-server.js +1 -1
- package/lib/ws-request.js +1 -0
- package/lib/ws-schema.js +10 -0
- package/lib/yoke/claude-hook-installer.js +4 -0
- package/package.json +1 -1
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { observeNavigationRendering } from './clay-navigation-render.js';
|
|
2
|
+
import { dismissContextualClay } from './contextual-clay.js';
|
|
3
|
+
import { showClayNavigationActivity, clearClayControlActivity } from './clay-control-activity.js';
|
|
1
4
|
import { store } from './store.js';
|
|
2
5
|
import { getWs } from './ws-ref.js';
|
|
3
6
|
import { navigateToSession } from './command-palette.js';
|
|
4
7
|
function context() {
|
|
5
|
-
return JSON.stringify([store.get('currentSlug'), store.get('activeSessionId'), store.get('dmMode'), store.get('homeShellVisible'), store.get('homeChatMateId'), store.get('homeChatSessionId'), store.get('homeSubSurface')]);
|
|
8
|
+
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]);
|
|
6
9
|
}
|
|
7
10
|
export function handleOwnerNavigation(msg) {
|
|
8
11
|
if (msg.type !== 'clay_navigation_offer' && msg.type !== 'clay_navigation_apply') return false;
|
|
@@ -19,9 +22,15 @@ export function handleOwnerNavigation(msg) {
|
|
|
19
22
|
var accepted = offer && offer.id === msg.deliveryId && offer.socket === ws && offer.context === context() && !document.hidden && !store.get('paneMode');
|
|
20
23
|
var status = 'refused';
|
|
21
24
|
if (accepted && msg.target && typeof msg.target.projectSlug === 'string' && Number.isSafeInteger(msg.target.sessionId)) {
|
|
22
|
-
try {
|
|
25
|
+
try {
|
|
26
|
+
var contextual = store.get('contextualClay') || {};
|
|
27
|
+
if (msg.target.contextualSourceMateId && contextual.mateId === msg.target.contextualSourceMateId && contextual.mode === 'popover') dismissContextualClay(false);
|
|
28
|
+
showClayNavigationActivity(msg.target, false); navigateToSession(msg.target, null, msg.target.entryIndex); status = 'accepted';
|
|
29
|
+
} catch (error) { showClayNavigationActivity(msg.target, true); status = 'failed'; }
|
|
23
30
|
}
|
|
31
|
+
if (status === 'refused') clearClayControlActivity();
|
|
24
32
|
// This acknowledges navigator invocation only. Rendering may finish after reconnect.
|
|
25
33
|
if (ws.readyState === 1) ws.send(JSON.stringify({ type: 'clay_navigation_ack', deliveryId: msg.deliveryId, status: status }));
|
|
34
|
+
if (status === 'accepted') observeNavigationRendering(msg, ws);
|
|
26
35
|
return true;
|
|
27
36
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { parseClaySessionReference, renderClaySessionLink } from './clay-log-links.js';
|
|
2
|
+
|
|
3
|
+
// The prefix is a display compatibility label, never a navigation target.
|
|
4
|
+
var citationPattern = /\[([A-Za-z0-9][A-Za-z0-9_-]{0,79})\/(session:[A-Za-z0-9_-]{24}(?:#entry-[0-9]+)?)(?:\s+[\u2014-]\s+([^\]\n]{1,80}))?\]/g;
|
|
5
|
+
function matches(text) {
|
|
6
|
+
var pattern = new RegExp(citationPattern.source, 'g'), results = [], match;
|
|
7
|
+
while ((match = pattern.exec(text))) if (parseClaySessionReference(match[2])) results.push({ start: match.index, end: pattern.lastIndex, prefix: match[1], ref: match[2], label: match[3] || '' });
|
|
8
|
+
return results;
|
|
9
|
+
}
|
|
10
|
+
function button(match) {
|
|
11
|
+
var template = document.createElement('template');
|
|
12
|
+
template.innerHTML = renderClaySessionLink(match.ref, match.label);
|
|
13
|
+
return template.content.firstElementChild;
|
|
14
|
+
}
|
|
15
|
+
export function enhanceSessionCitations(root) {
|
|
16
|
+
// Historical assistants sometimes wrapped a whole project-labelled citation
|
|
17
|
+
// in inline code. Only that complete legacy token is a compatibility citation.
|
|
18
|
+
// Canonical code samples, mixed inline code and all fenced code stay literal.
|
|
19
|
+
if (root.closest('.msg-assistant')) root.querySelectorAll('code').forEach(function (code) {
|
|
20
|
+
if (code.closest('pre, a, button')) return;
|
|
21
|
+
var text = code.textContent || '', found = matches(text);
|
|
22
|
+
if (found.length === 1 && found[0].prefix !== 'clayos' && found[0].start === 0 && found[0].end === text.length) code.replaceWith(button(found[0]));
|
|
23
|
+
});
|
|
24
|
+
var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT), nodes = [], node;
|
|
25
|
+
while ((node = walker.nextNode())) if (node.parentElement && !node.parentElement.closest('code, pre, a, button') && matches(node.nodeValue || '').length) nodes.push(node);
|
|
26
|
+
nodes.forEach(function (node) {
|
|
27
|
+
var text = node.nodeValue || '', fragment = document.createDocumentFragment(), offset = 0;
|
|
28
|
+
matches(text).forEach(function (match) { fragment.appendChild(document.createTextNode(text.slice(offset, match.start))); fragment.appendChild(button(match)); offset = match.end; });
|
|
29
|
+
fragment.appendChild(document.createTextNode(text.slice(offset))); node.replaceWith(fragment);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { adoptDocumentSource, presentedDocument, initDocumentSource } from './clay-studio-document.js';
|
|
2
|
+
import { acceptStudioForward, trustedStudioFrame, initStudioReceipts } from './clay-studio-render.js';
|
|
3
|
+
import { presentMarkdownEdit, handleFileChanged } from './filebrowser.js';
|
|
4
|
+
import { openWireframe } from './wireframe-workbench.js';
|
|
5
|
+
|
|
6
|
+
export function initStudioBridge() {
|
|
7
|
+
initStudioReceipts(); initDocumentSource();
|
|
8
|
+
window.addEventListener('message', function (event) {
|
|
9
|
+
var data = event.data;
|
|
10
|
+
if (event.origin === location.origin && data && data.type === 'clay-studio-document-update' && data.message) {
|
|
11
|
+
var view = presentedDocument(data.message.path);
|
|
12
|
+
if (view && view.source === event.source && view.descriptor.token === data.token) handleFileChanged(data.message, view);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (!data || ['clay-pane-present-markdown', 'clay-pane-present-wireframe'].indexOf(data.type) < 0 || !data.message || !data.message.controlPresentation) return;
|
|
16
|
+
if (!trustedStudioFrame(event, data) || !acceptStudioForward(event, data)) return;
|
|
17
|
+
if (data.type === 'clay-pane-present-markdown') { adoptDocumentSource(event, data, trustedStudioFrame(event, data)); presentMarkdownEdit(data.message); }
|
|
18
|
+
else openWireframe(data.message);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { showToast } from './utils.js';
|
|
2
|
+
import { store } from './store.js';
|
|
3
|
+
import { getWs } from './ws-ref.js';
|
|
4
|
+
|
|
5
|
+
function live(view) { var state = store.snap(); return view && state.connected && view.socket === getWs() && view.ownerId === (state.myUserId || null) && view.slug === state.currentSlug && view.sessionId === state.activeSessionId && view.origin === state.sessionOriginId && view.runtime === state.controlRuntimeId; }
|
|
6
|
+
export function forwardDocumentSource(message) {
|
|
7
|
+
var old = store.get('studioDocumentSource');
|
|
8
|
+
if (old) clearTimeout(old.timer);
|
|
9
|
+
if (old && live(old)) old.socket.send(JSON.stringify({ type: 'fs_unwatch' }));
|
|
10
|
+
var state = store.snap(), view = { token: crypto.randomUUID(), socket: getWs(), ownerId: state.myUserId || null, slug: state.currentSlug, sessionId: state.activeSessionId, origin: state.sessionOriginId, runtime: state.controlRuntimeId, path: message.path, expires: Date.now() + 300000 };
|
|
11
|
+
view.timer = setTimeout(function () { if (store.get('studioDocumentSource') === view) { if (live(view)) view.socket.send(JSON.stringify({ type: 'fs_unwatch' })); store.set({ studioDocumentSource: null }); } }, 300000);
|
|
12
|
+
store.set({ studioDocumentSource: view });
|
|
13
|
+
if (live(view)) view.socket.send(JSON.stringify({ type: 'fs_watch', path: view.path }));
|
|
14
|
+
return Object.assign({}, message, { studioDocument: { token: view.token, sourceSlug: view.slug, sourceSessionId: view.sessionId, sourceOrigin: view.origin, runtimeId: view.runtime, expires: view.expires, path: view.path } });
|
|
15
|
+
}
|
|
16
|
+
export function adoptDocumentSource(event, data, frame) {
|
|
17
|
+
var document = data.message && data.message.studioDocument;
|
|
18
|
+
if (!frame || !document || document.path !== data.message.path || document.sourceSlug !== data.studioSource.slug || document.sourceSessionId !== data.studioSource.sessionId || document.sourceOrigin !== data.studioSource.origin || document.runtimeId !== data.studioSource.runtime) return;
|
|
19
|
+
closePresentedDocument();
|
|
20
|
+
var socket = getWs(); if (socket && socket.readyState === 1) socket.send(JSON.stringify({ type: 'fs_unwatch' }));
|
|
21
|
+
store.set({ fileReadRequest: null, pendingFileNavigation: null });
|
|
22
|
+
var view = { descriptor: document, frame: frame, document: frame.contentDocument, source: event.source, owner: store.get('myUserId'), context: store.get('currentSlug') };
|
|
23
|
+
view.timer = setTimeout(function () { if (store.get('studioDocumentView') === view) store.set({ studioDocumentView: { descriptor: view.descriptor, invalidated: true } }); }, Math.max(0, document.expires - Date.now()));
|
|
24
|
+
store.set({ studioDocumentView: view });
|
|
25
|
+
}
|
|
26
|
+
export function presentedDocument(path) {
|
|
27
|
+
var view = store.get('studioDocumentView');
|
|
28
|
+
if (!view || view.invalidated || !view.frame || Date.now() > view.descriptor.expires || view.descriptor.path !== path) return null;
|
|
29
|
+
var bridge = view.frame.clayStudioSource, identity = bridge && bridge.document === view.frame.contentDocument && bridge.window === view.source && bridge.identity();
|
|
30
|
+
return identity && identity.connected && identity.ownerId === view.owner && identity.slug === view.descriptor.sourceSlug && identity.sessionId === view.descriptor.sourceSessionId && identity.origin === view.descriptor.sourceOrigin && identity.runtime === view.descriptor.runtimeId && view.document === view.frame.contentDocument && view.source === view.frame.contentWindow && view.owner === store.get('myUserId') && view.context === store.get('currentSlug') ? view : null;
|
|
31
|
+
}
|
|
32
|
+
export function refreshPresentedDocument(path) {
|
|
33
|
+
var stored = store.get('studioDocumentView');
|
|
34
|
+
if (!stored || stored.descriptor.path !== path) return false;
|
|
35
|
+
var view = presentedDocument(path);
|
|
36
|
+
if (!view) { showToast('The source connection changed. Reopen this document from its conversation.'); return true; }
|
|
37
|
+
view.source.postMessage({ type: 'clay-studio-document-refresh', token: view.descriptor.token }, location.origin);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
export function closePresentedDocument() {
|
|
41
|
+
var view = store.get('studioDocumentView');
|
|
42
|
+
if (view) clearTimeout(view.timer);
|
|
43
|
+
if (view && view.frame && view.source === view.frame.contentWindow && view.document === view.frame.contentDocument) view.source.postMessage({ type: 'clay-studio-document-close', token: view.descriptor.token }, location.origin);
|
|
44
|
+
store.set({ studioDocumentView: null });
|
|
45
|
+
}
|
|
46
|
+
export function handleStudioDocumentMessage(message) {
|
|
47
|
+
var view = store.get('studioDocumentSource');
|
|
48
|
+
if (!live(view) || Date.now() > view.expires || message.path !== view.path) return false;
|
|
49
|
+
if (message.type !== 'fs_file_changed' && !(message.type === 'fs_read_result' && message.requestId === view.requestId)) return false;
|
|
50
|
+
window.parent.postMessage({ type: 'clay-studio-document-update', token: view.token, message: message }, location.origin);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
export function initDocumentSource() {
|
|
54
|
+
window.addEventListener('message', function (event) {
|
|
55
|
+
var data = event.data, view = store.get('studioDocumentSource');
|
|
56
|
+
if (event.origin !== location.origin || event.source !== window.parent || !data || !view || data.token !== view.token || !live(view)) return;
|
|
57
|
+
if (data.type === 'clay-studio-document-close') { clearTimeout(view.timer); view.socket.send(JSON.stringify({ type: 'fs_unwatch' })); store.set({ studioDocumentSource: null }); }
|
|
58
|
+
if (data.type === 'clay-studio-document-refresh' && Date.now() <= view.expires) {
|
|
59
|
+
view.requestId = 'studio-document-' + crypto.randomUUID();
|
|
60
|
+
view.socket.send(JSON.stringify({ type: 'fs_read', path: view.path, requestId: view.requestId, projectSlug: view.slug, sessionId: String(view.sessionId), accountId: view.ownerId }));
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
function stop() { var view = store.get('studioDocumentSource'); if (view) clearTimeout(view.timer); if (live(view)) view.socket.send(JSON.stringify({ type: 'fs_unwatch' })); store.set({ studioDocumentSource: null }); var presented = store.get('studioDocumentView'); closePresentedDocument(); if (presented) store.set({ studioDocumentView: { descriptor: presented.descriptor, invalidated: true } }); }
|
|
64
|
+
store.subscribe(function (state, old) { if (!state.connected && old.connected || state.currentSlug !== old.currentSlug || state.activeSessionId !== old.activeSessionId || state.myUserId !== old.myUserId) stop(); });
|
|
65
|
+
document.addEventListener('contextual-ask-stopped', stop, true);
|
|
66
|
+
document.addEventListener('click', function (event) { if (event.target.closest('#generation-stop, #autonomous-run-stop, #send-btn.stop')) stop(); }, true);
|
|
67
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { forwardDocumentSource } from './clay-studio-document.js';
|
|
2
|
+
import { store } from './store.js';
|
|
3
|
+
import { getWs } from './ws-ref.js';
|
|
4
|
+
import { contextualMateStamp } from './contextual-mate-identity.js';
|
|
5
|
+
import { visibleTarget } from './clay-control-target-visibility.js';
|
|
6
|
+
|
|
7
|
+
function validEvidence(evidence) {
|
|
8
|
+
var uuid = /^[a-f0-9-]{36}$/;
|
|
9
|
+
return evidence && evidence.version === 1 && uuid.test(evidence.id) && uuid.test(evidence.token) && /^activity:[a-f0-9-]{36}$/.test(evidence.activityId) && Number.isSafeInteger(evidence.deadline) &&
|
|
10
|
+
evidence.target && ['document', 'sketch', 'browser'].indexOf(evidence.target.surface) >= 0 && typeof evidence.target.key === 'string' && evidence.target.key.length <= 400 && /^[a-f0-9]{64}$/.test(evidence.target.revision);
|
|
11
|
+
}
|
|
12
|
+
function source(evidence, state) {
|
|
13
|
+
return validEvidence(evidence) && evidence.mateId === state.activeProjectMateId && evidence.sourceSlug === 'mate-' + evidence.mateId && evidence.ownerId === (state.myUserId || null) && evidence.runtimeId === state.controlRuntimeId && evidence.sourceSlug === state.currentSlug && evidence.sourceSessionId === state.activeSessionId && evidence.sourceSessionOriginId === state.sessionOriginId &&
|
|
14
|
+
(state.cachedMatesList || []).some(function (mate) { return mate.id === evidence.mateId && mate.builtinKey === 'clay' && !mate.archived && !mate.lifecycleDeletion && contextualMateStamp(mate) === evidence.sourceMateStamp; });
|
|
15
|
+
}
|
|
16
|
+
function currentActivity(evidence) {
|
|
17
|
+
var activity = store.get('clayControlActivity'), forwarded = store.get('clayControlForwarded');
|
|
18
|
+
var message = store.get('paneMode') ? forwarded : activity && activity.message;
|
|
19
|
+
return message && message.id === evidence.activityId && message.mateId === evidence.mateId && message.sourceSlug === evidence.sourceSlug && message.sourceSessionId === evidence.sourceSessionId && message.sourceSessionOriginId === evidence.sourceSessionOriginId && message.sourceMateStamp === evidence.sourceMateStamp && message.sourceQueryGeneration === evidence.sourceQueryGeneration && message.phase !== 'clear' && !(store.get('clayControlReceipts') || []).some(function (receipt) { return receipt.id === evidence.activityId && receipt.stopped; });
|
|
20
|
+
}
|
|
21
|
+
function binding() {
|
|
22
|
+
return { window: window, document: document, identity: function () { var s = store.snap(); return { ownerId: s.myUserId || null, slug: s.currentSlug, sessionId: s.activeSessionId, origin: s.sessionOriginId, connected: s.connected, runtime: s.controlRuntimeId, mateId: s.activeProjectMateId }; } };
|
|
23
|
+
}
|
|
24
|
+
export function forwardStudioPresentation(message, type) {
|
|
25
|
+
if (!store.get('paneMode') || window.parent === window) return false;
|
|
26
|
+
var frame = window.frameElement;
|
|
27
|
+
if (!frame) return false;
|
|
28
|
+
frame.clayStudioSource = binding();
|
|
29
|
+
var evidence = message.controlPresentation;
|
|
30
|
+
if (evidence && (!source(evidence, store.snap()) || !currentActivity(evidence))) return true;
|
|
31
|
+
if (type === 'clay-pane-present-markdown') message = forwardDocumentSource(message);
|
|
32
|
+
var receipts = Object.assign({}, store.get('clayStudioRelays') || {});
|
|
33
|
+
Object.keys(receipts).forEach(function (id) { if (Date.now() > receipts[id].evidence.deadline) delete receipts[id]; });
|
|
34
|
+
if (Object.keys(receipts).length >= 100) return true;
|
|
35
|
+
if (evidence) receipts[evidence.id] = { evidence: evidence, socket: getWs() };
|
|
36
|
+
store.set({ clayStudioRelays: receipts });
|
|
37
|
+
window.parent.postMessage({ type: type, message: message, studioSource: frame.clayStudioSource.identity() }, location.origin);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
export function trustedStudioFrame(event, data) {
|
|
41
|
+
if (event.origin !== location.origin || !data || !data.studioSource) return null;
|
|
42
|
+
var frame = Array.from(document.querySelectorAll('#contextual-clay iframe, .split-pane-frame')).find(function (item) { return item.contentWindow === event.source; });
|
|
43
|
+
var bridge = frame && frame.clayStudioSource, live = bridge && bridge.window === frame.contentWindow && bridge.document === frame.contentDocument && bridge.identity();
|
|
44
|
+
var expected = data.studioSource;
|
|
45
|
+
if (!live || !live.connected || live.ownerId !== (store.get('myUserId') || null) || live.ownerId !== expected.ownerId || live.slug !== expected.slug || live.sessionId !== expected.sessionId || live.origin !== expected.origin || live.runtime !== expected.runtime) return null;
|
|
46
|
+
if (new URL(frame.src, location.href).pathname !== '/p/' + encodeURIComponent(live.slug) + '/') return null;
|
|
47
|
+
if (frame.classList.contains('split-pane-frame')) {
|
|
48
|
+
var split = store.get('splitPanes');
|
|
49
|
+
if (!split || !split.panes.some(function (pane) { return pane.slug === live.slug && String(pane.sessionId) === String(live.sessionId); })) return null;
|
|
50
|
+
} else {
|
|
51
|
+
var conversation = frame.conversation, identity = conversation && conversation.document === frame.contentDocument && conversation.source === frame.contentWindow && conversation.identity();
|
|
52
|
+
if (!identity || !identity.connected || !identity.available || identity.ownerId !== live.ownerId || identity.sessionId !== live.sessionId || identity.sessionOriginId !== live.origin) return null;
|
|
53
|
+
}
|
|
54
|
+
var evidence = data.message && data.message.controlPresentation;
|
|
55
|
+
if (evidence && (!validEvidence(evidence) || evidence.sourceSlug !== 'mate-' + evidence.mateId || evidence.mateId !== live.mateId || !(store.get('cachedMatesList') || []).some(function (mate) { return mate.id === evidence.mateId && mate.builtinKey === 'clay' && !mate.archived && !mate.lifecycleDeletion && contextualMateStamp(mate) === evidence.sourceMateStamp; }) || evidence.sourceSlug !== live.slug || evidence.sourceSessionId !== live.sessionId || evidence.sourceSessionOriginId !== live.origin || evidence.ownerId !== live.ownerId || evidence.runtimeId !== live.runtime)) return null;
|
|
56
|
+
return frame;
|
|
57
|
+
}
|
|
58
|
+
export function acceptStudioForward(event, data) {
|
|
59
|
+
var frame = trustedStudioFrame(event, data), evidence = data.message && data.message.controlPresentation;
|
|
60
|
+
if (!frame || !evidence || !currentActivity(evidence) || Date.now() > evidence.deadline) return false;
|
|
61
|
+
var entries = Object.assign({}, store.get('clayStudioForwarded') || {});
|
|
62
|
+
Object.keys(entries).forEach(function (id) { if (Date.now() > entries[id].evidence.deadline) delete entries[id]; });
|
|
63
|
+
if (Object.keys(entries).length >= 100) return false;
|
|
64
|
+
entries[evidence.id] = { frame: frame, document: frame.contentDocument, window: event.source, data: data, evidence: evidence };
|
|
65
|
+
store.set({ clayStudioForwarded: entries });
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
function delivery(evidence) {
|
|
69
|
+
var socket = getWs(), owner = store.get('myUserId'), slug = store.get('currentSlug');
|
|
70
|
+
var relay = (store.get('clayStudioForwarded') || {})[evidence.id];
|
|
71
|
+
return {
|
|
72
|
+
valid: function () {
|
|
73
|
+
if (Date.now() > evidence.deadline || !currentActivity(evidence) || !store.get('connected') || owner !== store.get('myUserId') || slug !== store.get('currentSlug') || getWs() !== socket) return false;
|
|
74
|
+
return relay ? relay.document === relay.frame.contentDocument && relay.window === relay.frame.contentWindow && !!trustedStudioFrame({ origin: location.origin, source: relay.window }, relay.data) : source(evidence, store.snap());
|
|
75
|
+
},
|
|
76
|
+
send: function (visible) {
|
|
77
|
+
var message = { type: 'clay_studio_rendered', id: evidence.id, token: evidence.token, sessionOriginId: evidence.sourceSessionOriginId, revision: evidence.target.revision, visible: visible };
|
|
78
|
+
if (relay) { if (relay.document === relay.frame.contentDocument) relay.window.postMessage({ type: 'clay-studio-receipt', message: message }, location.origin); }
|
|
79
|
+
else if (getWs() === socket && socket && socket.readyState === 1) socket.send(JSON.stringify(message));
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
async function digest(value) {
|
|
84
|
+
var hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(JSON.stringify(value)));
|
|
85
|
+
return Array.from(new Uint8Array(hash)).map(function (byte) { return byte.toString(16).padStart(2, '0'); }).join('');
|
|
86
|
+
}
|
|
87
|
+
// Called only by the actual renderer after content has reached its real product surface.
|
|
88
|
+
export function registerStudioPaint(message, element, ready, contentElement) {
|
|
89
|
+
if (!element) return;
|
|
90
|
+
delete element.dataset.controlSurface;
|
|
91
|
+
var evidence = message.controlPresentation;
|
|
92
|
+
if (!validEvidence(evidence)) return;
|
|
93
|
+
var receipt = delivery(evidence), settled = false, frame, timer, stable = 0;
|
|
94
|
+
var entry = { painted: false, evidence: evidence, target: evidence.target, element: element, valid: receipt.valid };
|
|
95
|
+
element.dataset.controlSurface = evidence.id;
|
|
96
|
+
var surfaces = Object.assign({}, store.get('clayStudioSurfaces') || {});
|
|
97
|
+
Object.keys(surfaces).forEach(function (id) { if (!surfaces[id].element.isConnected || surfaces[id].element === element || Date.now() > surfaces[id].evidence.deadline + 6000) delete surfaces[id]; });
|
|
98
|
+
surfaces[evidence.id] = entry; store.set({ clayStudioSurfaces: surfaces });
|
|
99
|
+
function finish(visible) { if (settled) return; settled = true; clearTimeout(timer); cancelAnimationFrame(frame); document.removeEventListener('visibilitychange', hidden); receipt.send(visible); }
|
|
100
|
+
entry.cancel = function () { finish(false); };
|
|
101
|
+
function hidden() { if (document.hidden) finish(false); }
|
|
102
|
+
document.addEventListener('visibilitychange', hidden);
|
|
103
|
+
timer = setTimeout(function () { finish(false); }, Math.max(0, evidence.deadline - Date.now()));
|
|
104
|
+
var content = evidence.target.surface === 'document' ? { content: message.content, exists: message.exists, size: message.size } : evidence.target.surface === 'sketch' ? message.source : message.data;
|
|
105
|
+
Promise.all([Promise.resolve(ready), digest(content)]).then(function (values) {
|
|
106
|
+
if (values[1] !== evidence.target.revision) { finish(false); return; }
|
|
107
|
+
function inspect() {
|
|
108
|
+
if (settled) return;
|
|
109
|
+
if (!receipt.valid() || document.hidden || element.dataset.controlSurface !== evidence.id) { finish(false); return; }
|
|
110
|
+
stable = painted(contentElement || element) ? stable + 1 : 0;
|
|
111
|
+
if (stable >= 2) { entry.painted = true; finish(true); } else frame = requestAnimationFrame(inspect);
|
|
112
|
+
}
|
|
113
|
+
frame = requestAnimationFrame(inspect);
|
|
114
|
+
}).catch(function () { finish(false); });
|
|
115
|
+
}
|
|
116
|
+
function painted(element) {
|
|
117
|
+
if (!element || !element.isConnected || element.closest('[hidden], [inert], [aria-hidden="true"]')) return false;
|
|
118
|
+
var rect = element.getBoundingClientRect(), style = getComputedStyle(element);
|
|
119
|
+
if (style.visibility !== 'visible' || Number(style.opacity) === 0) return false;
|
|
120
|
+
for (var parent = element.parentElement; parent; parent = parent.parentElement) {
|
|
121
|
+
var ancestorStyle = getComputedStyle(parent);
|
|
122
|
+
if (ancestorStyle.visibility !== 'visible' || Number(ancestorStyle.opacity) === 0) return false;
|
|
123
|
+
}
|
|
124
|
+
var left = Math.max(0, rect.left), right = Math.min(innerWidth, rect.right), top = Math.max(0, rect.top), bottom = Math.min(innerHeight, rect.bottom);
|
|
125
|
+
if (right <= left || bottom <= top) return false;
|
|
126
|
+
return [0.2, 0.5, 0.8].some(function (x) { return [0.2, 0.5, 0.8].some(function (y) { var hit = document.elementFromPoint(left + (right - left) * x, top + (bottom - top) * y); return hit && element.contains(hit); }); });
|
|
127
|
+
}
|
|
128
|
+
export function studioControlTarget(target) {
|
|
129
|
+
var entries = store.get('clayStudioSurfaces') || {};
|
|
130
|
+
var entry = Object.values(entries).find(function (item) { return item.painted && item.target.surface === target.surface && item.target.key === target.key && item.target.revision === target.revision && item.target.runtimeId === target.runtimeId && item.target.sourceSessionOriginId === target.sourceSessionOriginId && item.target.sourceSessionId === target.sourceSessionId && item.target.sourceSlug === target.sourceSlug && item.element.dataset.controlSurface === item.evidence.id && item.valid(); });
|
|
131
|
+
return entry && entry.element;
|
|
132
|
+
}
|
|
133
|
+
export function initStudioReceipts() {
|
|
134
|
+
window.addEventListener('message', function (event) {
|
|
135
|
+
if (event.origin !== location.origin || event.source !== window.parent || !event.data || event.data.type !== 'clay-studio-receipt') return;
|
|
136
|
+
var message = event.data.message, entries = Object.assign({}, store.get('clayStudioRelays') || {}), entry = message && entries[message.id];
|
|
137
|
+
if (!entry || entry.evidence.token !== message.token || !source(entry.evidence, store.snap()) || getWs() !== entry.socket || entry.socket.readyState !== 1 || Date.now() > entry.evidence.deadline) return;
|
|
138
|
+
delete entries[message.id]; store.set({ clayStudioRelays: entries }); entry.socket.send(JSON.stringify(message));
|
|
139
|
+
});
|
|
140
|
+
function stop() { Object.values(store.get('clayStudioSurfaces') || {}).forEach(function (entry) { delete entry.element.dataset.controlSurface; if (entry.cancel) entry.cancel(); }); store.set({ clayStudioRelays: {}, clayStudioForwarded: {}, clayStudioSurfaces: {} }); }
|
|
141
|
+
document.addEventListener('contextual-ask-stopped', stop, true);
|
|
142
|
+
document.addEventListener('click', function (event) { if (event.target.closest('#generation-stop, #autonomous-run-stop, #send-btn.stop')) stop(); }, true);
|
|
143
|
+
store.subscribe(function (state, prior) { if (!state.connected && prior.connected || state.currentSlug !== prior.currentSlug || state.activeSessionId !== prior.activeSessionId || state.myUserId !== prior.myUserId) stop(); });
|
|
144
|
+
}
|
|
@@ -153,6 +153,10 @@ rows('clay-email', [
|
|
|
153
153
|
]);
|
|
154
154
|
rows('clay-history', [['search_clay_history', 'Searching conversations', 'Conversations searched', 'Conversation search', 'query'], ['read_session', 'Reading conversation', 'Conversation read', 'Conversation'], ['list_recent_decisions', 'Checking recent decisions', 'Recent decisions checked', 'Recent decisions']]);
|
|
155
155
|
rows('clay-workspace', [
|
|
156
|
+
['read_current_context', 'Checking shared context', 'Shared context checked', 'Shared context'],
|
|
157
|
+
['read_selected_context', 'Reading selected conversation', 'Selected conversation read', 'Selected conversation'],
|
|
158
|
+
['open_context_conversation', 'Requesting conversation navigation', 'Navigation request returned', 'Conversation navigation', 'why'],
|
|
159
|
+
['read_context_navigation_receipt', 'Checking navigation receipt', 'Navigation receipt checked', 'Navigation receipt'],
|
|
156
160
|
['read_owner_screen', 'Checking your screen context', 'Screen context request returned', 'Screen context'],
|
|
157
161
|
['inspect_owner_screen_change', 'Inspecting screen change', 'Screen change inspected', 'Screen control'],
|
|
158
162
|
['control_owner_screen', 'Requesting screen change', 'Screen change request returned', 'Screen control', 'why'],
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { forwardContextualSessionCitation, validContextualCitation, contextualCitationLink } from './contextual-session-citations.js';
|
|
2
|
+
import { dismissContextualClay } from './contextual-clay.js';
|
|
1
3
|
import { mateAvatarUrl } from './avatar.js';
|
|
2
4
|
import { escapeHtml } from './utils.js';
|
|
3
5
|
import { refreshIcons } from './icons.js';
|
|
4
6
|
import { openSearch as openSessionSearch } from './session-search.js';
|
|
5
|
-
import { startSearchClayChat,
|
|
7
|
+
import { startSearchClayChat, retrySearchClayAsk } from './search-clay-chat.js';
|
|
6
8
|
import { getWs } from './ws-ref.js';
|
|
7
9
|
import { openProjectLog } from './project-logs.js';
|
|
8
10
|
import { store } from './store.js';
|
|
@@ -29,7 +31,7 @@ var paletteEl = null, inputEl = null, resultsEl = null, footerEl = null;
|
|
|
29
31
|
var activeIndex = -1, items = [], debounceTimer = null, abortCtrl = null;
|
|
30
32
|
var pendingNav = null, cachedHomeData = [], cachedVersion = null;
|
|
31
33
|
var pendingLogNav = null;
|
|
32
|
-
var searchResults = [], searchQuery = "", searchPending = false
|
|
34
|
+
var searchResults = [], searchQuery = "", searchPending = false;
|
|
33
35
|
|
|
34
36
|
export function initCommandPalette(value) {
|
|
35
37
|
ctx = value;
|
|
@@ -62,6 +64,10 @@ function handleClaySessionLinkClick(event) {
|
|
|
62
64
|
openProjectLog(link.dataset.logRef);
|
|
63
65
|
return;
|
|
64
66
|
}
|
|
67
|
+
if (link.classList.contains('clayos-session-link') && forwardContextualSessionCitation(link)) return;
|
|
68
|
+
requestClayRecordLink(link);
|
|
69
|
+
}
|
|
70
|
+
export function requestClayRecordLink(link, citation) {
|
|
65
71
|
var ws = getWs();
|
|
66
72
|
if (!ws || ws.readyState !== 1) {
|
|
67
73
|
link.classList.add("is-error");
|
|
@@ -74,7 +80,7 @@ function handleClaySessionLinkClick(event) {
|
|
|
74
80
|
else link.dataset.sessionRequest = requestId;
|
|
75
81
|
if (!isLog) store.set({ sessionNavigationRequestId: requestId, sessionNavigationRequestContext: {
|
|
76
82
|
projectSlug: store.get('currentSlug'),
|
|
77
|
-
sessionId: store.get('activeSessionId')
|
|
83
|
+
sessionId: store.get('activeSessionId'), citation: citation || null
|
|
78
84
|
} });
|
|
79
85
|
link.classList.remove("is-error");
|
|
80
86
|
link.classList.add("is-loading");
|
|
@@ -85,19 +91,24 @@ function handleClaySessionLinkClick(event) {
|
|
|
85
91
|
} else {
|
|
86
92
|
request.type = "home_clay_session_resolve";
|
|
87
93
|
request.sessionRef = link.dataset.sessionRef;
|
|
94
|
+
if (citation) request.contextualSource = { mateId: citation.mateId, sessionId: citation.sessionId, sessionOriginId: citation.originId, mateStamp: citation.stamp };
|
|
88
95
|
if (link.dataset.sessionEntryIndex !== undefined) request.entryIndex = Number(link.dataset.sessionEntryIndex);
|
|
89
96
|
}
|
|
90
97
|
ws.send(JSON.stringify(request));
|
|
98
|
+
return true;
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
export function handleClaySessionTarget(msg) {
|
|
94
102
|
if (!msg || (msg.type !== "home_clay_session_target" && msg.type !== "home_clay_log_target")) return false;
|
|
95
103
|
var isLog = msg.type === "home_clay_log_target";
|
|
96
104
|
var links = document.querySelectorAll(isLog ? ".clayos-log-link[data-log-request]" : ".clayos-session-link[data-session-request]");
|
|
97
|
-
var
|
|
105
|
+
var sourceContext = store.get('sessionNavigationRequestContext');
|
|
106
|
+
var citation = !isLog && sourceContext && sourceContext.citation;
|
|
107
|
+
var link = !isLog ? contextualCitationLink(citation, msg.requestId) : null;
|
|
98
108
|
for (var i = 0; i < links.length; i++) {
|
|
99
109
|
if ((isLog ? links[i].dataset.logRequest : links[i].dataset.sessionRequest) === msg.requestId) link = links[i];
|
|
100
110
|
}
|
|
111
|
+
if (!isLog && link && link.ownerDocument !== document && !citation) { link.classList.remove('is-loading'); link.classList.add('is-error'); link.title = 'The view changed. Click again from the current conversation.'; delete link.dataset.sessionRequest; return true; }
|
|
101
112
|
if (!isLog && store.get('sessionNavigationRequestId') !== msg.requestId) {
|
|
102
113
|
if (link) {
|
|
103
114
|
link.classList.remove("is-loading");
|
|
@@ -107,6 +118,7 @@ export function handleClaySessionTarget(msg) {
|
|
|
107
118
|
}
|
|
108
119
|
if (!isLog) {
|
|
109
120
|
var requestContext = store.get('sessionNavigationRequestContext');
|
|
121
|
+
if (citation && (!validContextualCitation(citation) || msg.sessionRef !== citation.ref)) { if (link) { link.classList.remove('is-loading'); link.classList.add('is-error'); link.title = 'The view changed. Click again from the current conversation.'; delete link.dataset.sessionRequest; } store.set({ pendingSessionEntryNavigation: null, sessionNavigationRequestContext: null }); return true; }
|
|
110
122
|
if (requestContext && (String(store.get('currentSlug')) !== String(requestContext.projectSlug) || String(store.get('activeSessionId')) !== String(requestContext.sessionId))) {
|
|
111
123
|
if (link) {
|
|
112
124
|
link.classList.remove("is-loading");
|
|
@@ -134,6 +146,7 @@ export function handleClaySessionTarget(msg) {
|
|
|
134
146
|
navigateToLog(msg.target);
|
|
135
147
|
return true;
|
|
136
148
|
}
|
|
149
|
+
if (citation) { dismissContextualClay(false); if ((store.get('contextualClay') || {}).mode !== 'closed') return true; }
|
|
137
150
|
navigateToSession({ projectSlug: msg.target.projectSlug, sessionId: msg.target.sessionId }, null, msg.target.entryIndex);
|
|
138
151
|
return true;
|
|
139
152
|
}
|
|
@@ -146,11 +159,6 @@ export function openCommandPalette() {
|
|
|
146
159
|
var trigger = document.getElementById("cmd-palette-btn");
|
|
147
160
|
if (trigger) trigger.style.visibility = "hidden";
|
|
148
161
|
updateFooter();
|
|
149
|
-
if (attachSearchClayChat(resultsEl, showSearch, closeCommandPalette)) {
|
|
150
|
-
chatMode = true;
|
|
151
|
-
paletteEl.classList.add("is-chatting");
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
162
|
showSearch();
|
|
155
163
|
fetchHomeData();
|
|
156
164
|
}
|
|
@@ -162,7 +170,6 @@ export function closeCommandPalette() {
|
|
|
162
170
|
if (trigger) trigger.style.visibility = "";
|
|
163
171
|
if (debounceTimer) { clearTimeout(debounceTimer); debounceTimer = null; }
|
|
164
172
|
if (abortCtrl) { abortCtrl.abort(); abortCtrl = null; }
|
|
165
|
-
detachSearchClayChat();
|
|
166
173
|
}
|
|
167
174
|
|
|
168
175
|
export function setPaletteVersion(version) { cachedVersion = version; }
|
|
@@ -289,8 +296,6 @@ function buildDOM() {
|
|
|
289
296
|
}
|
|
290
297
|
|
|
291
298
|
function showSearch() {
|
|
292
|
-
chatMode = false;
|
|
293
|
-
paletteEl.classList.remove("is-chatting");
|
|
294
299
|
inputEl.value = searchQuery;
|
|
295
300
|
inputEl.disabled = false;
|
|
296
301
|
inputEl.focus({ preventScroll: true });
|
|
@@ -354,7 +359,6 @@ function sessionIcon(session) {
|
|
|
354
359
|
}
|
|
355
360
|
|
|
356
361
|
function renderSearch() {
|
|
357
|
-
if (chatMode) return;
|
|
358
362
|
var query = searchQuery.toLowerCase();
|
|
359
363
|
var sessions = query ? cachedHomeData.filter(function (item) {
|
|
360
364
|
return (item.sessionTitle || "").toLowerCase().indexOf(query) !== -1 || (item.projectTitle || "").toLowerCase().indexOf(query) !== -1;
|
|
@@ -364,11 +368,10 @@ function renderSearch() {
|
|
|
364
368
|
for (var r = 0; r < searchResults.length; r++) if (!seen[sessionKey(searchResults[r])]) { sessions.push(searchResults[r]); seen[sessionKey(searchResults[r])] = true; }
|
|
365
369
|
items = [];
|
|
366
370
|
var html = "", index = 0;
|
|
367
|
-
var
|
|
368
|
-
if (
|
|
369
|
-
items.push({ type:
|
|
370
|
-
html += '<
|
|
371
|
-
html += renderItem(index++, '<img src="/clay-studio-symbol.png" width="22" height="22" alt="">', "Return to Clay", escapeHtml(clayChat.processing ? "Clay is responding" : "Continue your current conversation"), clayChat.preview ? escapeHtml(clayChat.preview) : null, clayChat.updatedAt, " is-resume-clay");
|
|
371
|
+
var pendingAsk = store.get('contextualSearchAsk');
|
|
372
|
+
if (!query && pendingAsk && pendingAsk.ownerId === store.get('myUserId') && !pendingAsk.clientMessageId && /^(error|stopped)$/.test(pendingAsk.status)) {
|
|
373
|
+
items.push({ type: 'retry-clay-ask' });
|
|
374
|
+
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');
|
|
372
375
|
}
|
|
373
376
|
if (sessions.length) {
|
|
374
377
|
html += '<div class="cmd-palette-group-label">' + (query ? "Conversations" : "Recent conversations") + '</div>';
|
|
@@ -383,8 +386,8 @@ function renderSearch() {
|
|
|
383
386
|
if (query) {
|
|
384
387
|
items.push({ type: "ask-clay", query: searchQuery });
|
|
385
388
|
if (!sessions.length && !searchPending) html += '<div class="cmd-palette-exact-empty">No exact matches. Clay can search by meaning.</div>';
|
|
386
|
-
html += '<div class="cmd-palette-group-label">
|
|
387
|
-
html += renderItem(index, '<img src="/clay-studio-symbol.png" width="22" height="22" alt="">',
|
|
389
|
+
html += '<div class="cmd-palette-group-label">Ask Clay</div>';
|
|
390
|
+
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");
|
|
388
391
|
}
|
|
389
392
|
if (!html) html = '<div class="cmd-palette-empty">No conversations yet.</div>';
|
|
390
393
|
resultsEl.innerHTML = html;
|
|
@@ -417,20 +420,12 @@ function setActive(index, skipScroll) {
|
|
|
417
420
|
}
|
|
418
421
|
|
|
419
422
|
function beginClayChat(query) {
|
|
420
|
-
if (
|
|
421
|
-
|
|
422
|
-
paletteEl.classList.add("is-chatting");
|
|
423
|
-
startSearchClayChat(query, resultsEl, showSearch, closeCommandPalette);
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
function resumeClayChat() {
|
|
427
|
-
if (!attachSearchClayChat(resultsEl, showSearch, closeCommandPalette)) return;
|
|
428
|
-
chatMode = true;
|
|
429
|
-
paletteEl.classList.add("is-chatting");
|
|
423
|
+
if (!isCommandPaletteOpen()) return;
|
|
424
|
+
if (startSearchClayChat(query)) closeCommandPalette();
|
|
430
425
|
}
|
|
431
426
|
|
|
432
427
|
function activateItem(entry) {
|
|
433
|
-
if (entry.type ===
|
|
428
|
+
if (entry.type === 'retry-clay-ask') { if (retrySearchClayAsk()) closeCommandPalette(); return; }
|
|
434
429
|
if (entry.type === "ask-clay") { beginClayChat(entry.query); return; }
|
|
435
430
|
if (entry.type === "mate-session" || entry.type === "session") navigateToSession(entry.data, entry.query);
|
|
436
431
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { sendTextMessage } from './input.js';
|
|
3
|
+
import { pausePendingMessage } from './message-delivery.js';
|
|
4
|
+
import { stopGeneration } from './generation-stop.js';
|
|
5
|
+
|
|
6
|
+
export function submitContextualAsk(request) {
|
|
7
|
+
if (!request || typeof request.requestId !== 'string' || typeof request.text !== 'string') return null;
|
|
8
|
+
var current = store.get('contextualAskSubmission');
|
|
9
|
+
if (current && current.requestId === request.requestId) return current;
|
|
10
|
+
if (!request || request.ownerId !== store.get('myUserId') || request.sessionId !== store.get('paneSessionId') || request.sessionOriginId !== store.get('sessionOriginId') || !store.get('contextualSessionReady') || !store.get('connected') || store.get('contextualWriterSuspended') || !window.frameElement || window.frameElement.dataset.canonical !== 'true') return null;
|
|
11
|
+
// A fresh session has no user draft to replace. Refuse any ambiguous target.
|
|
12
|
+
if (document.getElementById('input').value || (store.get('pendingOutboundMessages') || []).length) return null;
|
|
13
|
+
store.set({ contextualAskSubmission: { requestId: request.requestId, status: 'sending' } });
|
|
14
|
+
sendTextMessage(request.text);
|
|
15
|
+
var outbound = (store.get('pendingOutboundMessages') || []).find(function (item) { return item.payload.text === request.text && item.sessionId === request.sessionId; });
|
|
16
|
+
var submission = { requestId: request.requestId, status: outbound ? 'sent' : 'unconfirmed', clientMessageId: outbound && outbound.payload.clientMessageId };
|
|
17
|
+
store.set({ contextualAskSubmission: submission });
|
|
18
|
+
return submission;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function pauseContextualAsk() {
|
|
22
|
+
var current = store.get('contextualAskSubmission');
|
|
23
|
+
if (current && current.clientMessageId) pausePendingMessage(current.clientMessageId);
|
|
24
|
+
if (current) store.set({ contextualAskSubmission: Object.assign({}, current, { status: 'stopped' }) });
|
|
25
|
+
}
|
|
26
|
+
export function stopContextualAsk() { pauseContextualAsk(); return stopGeneration(); }
|