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,3 +1,7 @@
|
|
|
1
|
+
import { handleStudioDocumentMessage } from './clay-studio-document.js';
|
|
2
|
+
import { handleClayControlActivity } from './clay-control-activity.js';
|
|
3
|
+
import { handleContextualWorkspaceMessage } from './contextual-clay-workspace.js';
|
|
4
|
+
import { handleContextualSessionMessage } from './contextual-clay-session.js';
|
|
1
5
|
import { handleOwnerNavigation } from './clay-owner-navigation.js';
|
|
2
6
|
import { leaveRevokedProject } from './project-access-recovery.js';
|
|
3
7
|
import { handlePersonalSettingsMessage } from './user-settings-preferences.js';
|
|
@@ -62,7 +66,7 @@ import { handleProjectOwnerChanged } from './project-settings.js';
|
|
|
62
66
|
import { handleProjectEnv, handleProjectEnvSaved } from './project-settings-environment.js';
|
|
63
67
|
import { handleProjectSessionVisibilityDefault, refreshProjectSessionVisibilitySettings } from './project-session-visibility-settings.js';
|
|
64
68
|
import { updateSettingsStats, updateDaemonConfig, handleSetPinResult, handleKeepAwakeChanged, handleAutoContinueChanged, handleRestartResult, handleShutdownResult, handleSharedEnv, handleSharedEnvSaved, handleServerSettingResult } from './server-settings.js';
|
|
65
|
-
import { handleTermList, handleTermCreated, sendTerminalCommand, handleTermOutput, handleTermResized, handleTermExited, handleTermClosed } from './terminal.js';
|
|
69
|
+
import { handleTerminalProjection, handleTermList, handleTermCreated, sendTerminalCommand, handleTermOutput, handleTermResized, handleTermExited, handleTermClosed } from './terminal.js';
|
|
66
70
|
import { attachTuiView, detachTuiView, setTuiSuspendedView, tuiHandleTermOutput, tuiHandleTermResized, tuiHandleTermExited, tuiHandleTermClosed } from './session-tui-view.js';
|
|
67
71
|
import { handleTuiTranscriptState } from './tui-grab.js';
|
|
68
72
|
import { tuiModalHandleTermOutput, tuiModalHandleTermResized, tuiModalHandleTermExited, tuiModalHandleTermClosed } from './tui-attention.js';
|
|
@@ -121,6 +125,8 @@ var connectOverlay = document.getElementById("connect-overlay");
|
|
|
121
125
|
initMessageDeliveryUi();
|
|
122
126
|
|
|
123
127
|
export function processMessage(msg) {
|
|
128
|
+
if (handleStudioDocumentMessage(msg)) return;
|
|
129
|
+
if (handleClayControlActivity(msg)) return;
|
|
124
130
|
if (handleOwnerNavigation(msg)) return;
|
|
125
131
|
if (["update_available", "up_to_date", "update_started", "update_check_failed"].indexOf(msg.type) !== -1) handleServerUpdate(msg);
|
|
126
132
|
if (/^(set_pin|set_keep_awake|set_inherit_groups|set_image_retention|set_update_channel|restart_server|shutdown_server)_result$/.test(msg.type)) handleServerSettingResult(msg);
|
|
@@ -230,6 +236,8 @@ export function processMessage(msg) {
|
|
|
230
236
|
|
|
231
237
|
store.set({ currentHistoryIndex: Number.isSafeInteger(msg._historyIndex) && msg._historyIndex >= 0 ? msg._historyIndex : null,
|
|
232
238
|
currentHistoryIndexes: Array.isArray(msg._historyIndexes) ? msg._historyIndexes : null });
|
|
239
|
+
if (handleContextualSessionMessage(msg)) return;
|
|
240
|
+
handleContextualWorkspaceMessage(msg);
|
|
233
241
|
switch (msg.type) {
|
|
234
242
|
case "message_ack":
|
|
235
243
|
var acknowledgedEntry = acknowledgeMessage(msg.clientMessageId);
|
|
@@ -260,6 +268,7 @@ export function processMessage(msg) {
|
|
|
260
268
|
break;
|
|
261
269
|
|
|
262
270
|
case "history_done":
|
|
271
|
+
if (messagesEl && msg.sessionId === store.get("activeSessionId") && msg.sessionOriginId === store.get("sessionOriginId")) { messagesEl.dataset.controlSessionId = String(msg.sessionId); messagesEl.dataset.controlSessionOrigin = msg.sessionOriginId; messagesEl.dataset.controlHistoryRevision = msg.controlHistoryRevision || ""; }
|
|
263
272
|
store.set({ replayingHistory: false });
|
|
264
273
|
resumeThinkingAfterReplay(store.get('sessionIsProcessing'));
|
|
265
274
|
// Batched syntax highlight + mermaid pass for the entire replayed
|
|
@@ -377,6 +386,7 @@ export function processMessage(msg) {
|
|
|
377
386
|
break;
|
|
378
387
|
|
|
379
388
|
case "info":
|
|
389
|
+
store.set({ controlRuntimeId: msg.controlRuntimeId || null });
|
|
380
390
|
if (msg.text && !msg.project && !msg.cwd) {
|
|
381
391
|
addSystemMessage(msg.text, false);
|
|
382
392
|
break;
|
|
@@ -1693,6 +1703,9 @@ export function processMessage(msg) {
|
|
|
1693
1703
|
handleFileAt(msg);
|
|
1694
1704
|
break;
|
|
1695
1705
|
|
|
1706
|
+
case "term_control_projection":
|
|
1707
|
+
handleTerminalProjection(msg);
|
|
1708
|
+
break;
|
|
1696
1709
|
case "term_list":
|
|
1697
1710
|
handleTermList(msg);
|
|
1698
1711
|
updateTerminalList(msg.terminals);
|
|
@@ -134,7 +134,7 @@ function showBanner(notif, autoDismissMs) {
|
|
|
134
134
|
|
|
135
135
|
var banner = document.createElement("div");
|
|
136
136
|
banner.className = "notif-banner" + (isAuthRequired ? " notif-banner-update notif-banner-auth" : isPermission ? " notif-banner-permission" : isUserMention ? " notif-banner-user-mention" : "");
|
|
137
|
-
if (!isEmpty) banner.setAttribute("data-notif-id", notif.id);
|
|
137
|
+
if (!isEmpty) { banner.setAttribute("data-notif-id", notif.id); banner.dataset.controlRevision = notif.controlRevision || ""; }
|
|
138
138
|
if (isAuthRequired) banner.setAttribute("data-auth-banner", "true");
|
|
139
139
|
|
|
140
140
|
var iconHtmlStr = userMentionAvatarSrc
|
|
@@ -469,7 +469,12 @@ export function getPendingTuiAttention(slug) {
|
|
|
469
469
|
}
|
|
470
470
|
|
|
471
471
|
export function handleNotificationsState(msg) {
|
|
472
|
-
notifications = msg.notifications || [];
|
|
472
|
+
notifications = msg.notifications || []; store.set({ clayControlNotifications: notifications });
|
|
473
|
+
// Authoritative snapshots also withdraw banners removed by reviewed canonical actions.
|
|
474
|
+
if (bannerContainer) Array.from(bannerContainer.querySelectorAll('[data-notif-id]')).forEach(function (banner) {
|
|
475
|
+
var id = banner.dataset.notifId;
|
|
476
|
+
if (id.charAt(0) !== '_' && !notifications.some(function (item) { return item.id === id; })) removeBanner(banner);
|
|
477
|
+
});
|
|
473
478
|
unreadCount = msg.unreadCount || 0;
|
|
474
479
|
updateBadge();
|
|
475
480
|
|
|
@@ -500,6 +505,7 @@ export function handleNotificationsState(msg) {
|
|
|
500
505
|
|
|
501
506
|
export function handleNotificationCreated(msg) {
|
|
502
507
|
var notif = msg.notification;
|
|
508
|
+
store.set({ clayControlNotifications: [notif].concat((store.get("clayControlNotifications") || []).filter(function (item) { return item.id !== notif.id; })) });
|
|
503
509
|
|
|
504
510
|
// Auto-dismiss if it's for the session the user is currently viewing
|
|
505
511
|
var activeSession = store.get('activeSessionId') || null;
|
|
@@ -510,6 +516,7 @@ export function handleNotificationCreated(msg) {
|
|
|
510
516
|
}
|
|
511
517
|
|
|
512
518
|
notifications.unshift(notif);
|
|
519
|
+
store.set({ clayControlNotifications: notifications });
|
|
513
520
|
unreadCount = msg.unreadCount;
|
|
514
521
|
updateBadge();
|
|
515
522
|
|
|
@@ -567,7 +574,7 @@ export function handleNotificationDismissed(msg) {
|
|
|
567
574
|
}
|
|
568
575
|
|
|
569
576
|
export function handleNotificationDismissedAll() {
|
|
570
|
-
notifications = [];
|
|
577
|
+
notifications = []; store.set({ clayControlNotifications: [] });
|
|
571
578
|
unreadCount = 0;
|
|
572
579
|
updateBadge();
|
|
573
580
|
if (bannerContainer) bannerContainer.innerHTML = "";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { prepareContextualWorkspaceDeparture } from './contextual-clay-workspace.js';
|
|
1
2
|
// app-projects.js - Project list, switching, add/remove project modals
|
|
2
3
|
// Extracted from app.js (PR-29)
|
|
3
4
|
|
|
@@ -311,6 +312,7 @@ export function renderProjectList() {
|
|
|
311
312
|
}).map(function (p) {
|
|
312
313
|
return {
|
|
313
314
|
slug: p.slug,
|
|
315
|
+
controlRuntimeId: p.controlRuntimeId || null,
|
|
314
316
|
name: p.title || p.project,
|
|
315
317
|
icon: p.icon || null,
|
|
316
318
|
isProcessing: p.isProcessing,
|
|
@@ -389,6 +391,7 @@ export function resetClientState() {
|
|
|
389
391
|
|
|
390
392
|
export function switchProject(slug) {
|
|
391
393
|
if (!slug) return;
|
|
394
|
+
if (prepareContextualWorkspaceDeparture(slug) === false) return;
|
|
392
395
|
var target = cachedProjects.find(function (project) { return project.slug === slug; });
|
|
393
396
|
if (target && target.contentAccess === false && target.administrativeAccess === true) {
|
|
394
397
|
window.location.assign("/p/" + encodeURIComponent(slug) + "/");
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
// Owner-authenticated presentation only; this module never invokes a product action.
|
|
2
|
+
import { contextualMateStamp } from './contextual-mate-identity.js';
|
|
3
|
+
import { store } from './store.js';
|
|
4
|
+
import { mateAvatarUrl } from './avatar.js';
|
|
5
|
+
import { activityCopy, validActivity } from './clay-control-copy.js';
|
|
6
|
+
import { controlTarget, activityPlacement, visibleTarget } from './clay-control-targets.js';
|
|
7
|
+
|
|
8
|
+
function forward(message) {
|
|
9
|
+
if (!store.get('paneMode') || window.parent === window) return false;
|
|
10
|
+
store.set({ clayControlForwarded: message });
|
|
11
|
+
window.parent.postMessage({ type: 'clay-control-presentation', message: message }, location.origin);
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
export function clearClayControlActivity() {
|
|
15
|
+
var activity = store.get('clayControlActivity');
|
|
16
|
+
if (activity) {
|
|
17
|
+
clearTimeout(activity.timer);
|
|
18
|
+
cancelAnimationFrame(activity.frame);
|
|
19
|
+
if (activity.animation) activity.animation.cancel();
|
|
20
|
+
if (activity.observer) activity.observer.disconnect();
|
|
21
|
+
if (activity.resize) activity.resize.disconnect();
|
|
22
|
+
}
|
|
23
|
+
store.set({ clayControlActivity: null });
|
|
24
|
+
var element = document.getElementById('clay-control-activity');
|
|
25
|
+
if (element) element.remove();
|
|
26
|
+
}
|
|
27
|
+
function layout() {
|
|
28
|
+
var activity = store.get('clayControlActivity'), element = document.getElementById('clay-control-activity');
|
|
29
|
+
if (!activity || !element) return;
|
|
30
|
+
var surface = document.getElementById('contextual-clay');
|
|
31
|
+
var narrow = window.matchMedia('(max-width: 768px)').matches;
|
|
32
|
+
var workbench = narrow && Array.from(document.querySelectorAll('#file-viewer, #mate-knowledge-workbench, #project-logs-panel, #issues-panel, #scheduled-tasks-panel')).find(function (item) { return visibleTarget(item); });
|
|
33
|
+
var host = workbench || surface;
|
|
34
|
+
var inline = narrow && host && !host.hidden && host.getClientRects().length;
|
|
35
|
+
element.classList.toggle('is-inline', !!inline);
|
|
36
|
+
if (inline) {
|
|
37
|
+
if (element.parentNode !== host) {
|
|
38
|
+
if (host === surface) host.insertBefore(element, surface.querySelector('.contextual-clay-body'));
|
|
39
|
+
else host.insertBefore(element, host.children[1] || null);
|
|
40
|
+
}
|
|
41
|
+
element.classList.remove('has-target', 'is-flipped', 'is-reversed');
|
|
42
|
+
element.dataset.targeted = 'false'; element.style.visibility = 'visible';
|
|
43
|
+
element.style.marginTop = '6px';
|
|
44
|
+
var inlineBox = element.getBoundingClientRect(), offset = 0;
|
|
45
|
+
Array.from(document.querySelectorAll('.notif-banner.show')).forEach(function (banner) { var rect = banner.getBoundingClientRect(); if (rect.left < inlineBox.right && rect.right > inlineBox.left && rect.bottom > inlineBox.top && rect.top < inlineBox.bottom) offset = Math.max(offset, rect.bottom - inlineBox.top + 8); });
|
|
46
|
+
if (offset) element.style.marginTop = (6 + offset) + 'px';
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (element.parentNode !== document.body) document.body.appendChild(element);
|
|
50
|
+
element.style.removeProperty("margin-top");
|
|
51
|
+
var contextual = store.get('contextualClay') || {};
|
|
52
|
+
var background = activity.fromPane && (contextual.mode !== 'popover' || contextual.mateId !== activity.message.mateId);
|
|
53
|
+
var row = !background && !window.matchMedia('(pointer: coarse), (max-width: 768px)').matches && controlTarget(activity.message.target);
|
|
54
|
+
if (activity.targetNode !== row) {
|
|
55
|
+
if (activity.targetNode) activity.resize.unobserve(activity.targetNode);
|
|
56
|
+
activity.targetNode = row;
|
|
57
|
+
if (row) activity.resize.observe(row, { box: 'border-box' });
|
|
58
|
+
}
|
|
59
|
+
var obstacles = Array.from(document.querySelectorAll('#input-area, #contextual-clay, .ask-user-container:not(.answered), [role="dialog"], .notif-banner.show, #file-viewer-body, .wireframe-workbench-canvas')).filter(function (item) {
|
|
60
|
+
return (!row || !item.contains(row) || item.id === 'input-area') && !item.closest('details:not([open]), [hidden], [inert], [aria-hidden="true"]') && item.getClientRects().length && !item.hidden && getComputedStyle(item).visibility !== 'hidden' && Number(getComputedStyle(item).opacity) > 0;
|
|
61
|
+
}).map(function (item) { return item.getBoundingClientRect(); });
|
|
62
|
+
var box = element.getBoundingClientRect();
|
|
63
|
+
var place = activityPlacement(row, box.width, box.height, obstacles);
|
|
64
|
+
element.style.visibility = place ? 'visible' : 'hidden';
|
|
65
|
+
if (!place) { if (activity.animation) activity.animation.cancel(); element.dataset.targeted = 'false'; element.classList.remove('has-target', 'is-flipped', 'is-reversed'); element.style.removeProperty('--tip-x'); element.style.removeProperty('--tip-y'); return; }
|
|
66
|
+
var targeted = place.tipX !== undefined;
|
|
67
|
+
if (!targeted && activity.animation) { activity.animation.cancel(); activity.animation = null; }
|
|
68
|
+
element.classList.toggle('has-target', targeted);
|
|
69
|
+
element.classList.toggle('is-flipped', !!place.flip);
|
|
70
|
+
element.classList.toggle('is-reversed', !!place.reverse);
|
|
71
|
+
element.style.left = place.left + 'px'; element.style.top = place.top + 'px';
|
|
72
|
+
if (targeted) {
|
|
73
|
+
element.style.setProperty('--tip-x', (place.tipX - place.left) + 'px');
|
|
74
|
+
element.style.setProperty('--tip-y', (place.tipY - place.top) + 'px');
|
|
75
|
+
}
|
|
76
|
+
element.dataset.targeted = String(targeted);
|
|
77
|
+
if (document.getAnimations().some(function (animation) {
|
|
78
|
+
var node = animation.effect && animation.effect.target;
|
|
79
|
+
if (animation.playState !== 'running' || !(node instanceof Element) || element.contains(node) ||
|
|
80
|
+
!(row && (node === row || node.contains(row)) || node.matches('.notif-banner.show'))) return false;
|
|
81
|
+
if (!activity.motionSeen) activity.motionSeen = new WeakSet();
|
|
82
|
+
if (!activity.motionSeen.has(animation)) { activity.motionSeen.add(animation); activity.motionUntil = Date.now() + 5000; }
|
|
83
|
+
return Date.now() < activity.motionUntil;
|
|
84
|
+
})) scheduleLayout();
|
|
85
|
+
}
|
|
86
|
+
function scheduleLayout() {
|
|
87
|
+
var activity = store.get('clayControlActivity');
|
|
88
|
+
if (!activity || activity.frame) return;
|
|
89
|
+
activity.frame = requestAnimationFrame(function () { activity.frame = null; layout(); });
|
|
90
|
+
}
|
|
91
|
+
function render(message, mate, copy, fromPane) {
|
|
92
|
+
var existing = store.get('clayControlActivity');
|
|
93
|
+
if (existing && existing.message.id === message.id) {
|
|
94
|
+
existing.message = message;
|
|
95
|
+
existing.fromPane = !!fromPane;
|
|
96
|
+
clearTimeout(existing.timer);
|
|
97
|
+
existing.timer = setTimeout(clearClayControlActivity, message.terminal === false || ['review', 'running'].indexOf(message.phase) >= 0 ? 300000 : 6000);
|
|
98
|
+
document.querySelector('#clay-control-activity .clay-control-bubble').textContent = copy;
|
|
99
|
+
scheduleLayout(); return;
|
|
100
|
+
}
|
|
101
|
+
var previous = document.getElementById('clay-control-activity');
|
|
102
|
+
var previousRect = previous && previous.classList.contains('has-target') && previous.getBoundingClientRect();
|
|
103
|
+
clearClayControlActivity();
|
|
104
|
+
var element = document.createElement('div'); element.id = 'clay-control-activity';
|
|
105
|
+
element.setAttribute('role', 'status'); element.setAttribute('aria-live', 'polite'); element.setAttribute('aria-atomic', 'true');
|
|
106
|
+
var identity = document.createElement('span'); identity.className = 'clay-control-identity';
|
|
107
|
+
var avatar = document.createElement('img'); avatar.alt = ''; avatar.width = 20; avatar.height = 20;
|
|
108
|
+
avatar.addEventListener('error', function () { avatar.hidden = true; }, { once: true });
|
|
109
|
+
avatar.src = mateAvatarUrl(mate, 20);
|
|
110
|
+
var name = document.createElement('span'); name.className = 'clay-control-name';
|
|
111
|
+
name.textContent = (mate.profile || {}).displayName || mate.displayName || mate.name || (mate.builtinKey === 'clay' ? 'Clay' : 'Mate');
|
|
112
|
+
identity.append(avatar, name);
|
|
113
|
+
var bubble = document.createElement('span'); bubble.className = 'clay-control-bubble'; bubble.textContent = copy;
|
|
114
|
+
var pointer = document.createElement('span'); pointer.className = 'clay-control-pointer'; pointer.setAttribute('aria-hidden', 'true');
|
|
115
|
+
element.append(pointer, identity, bubble); document.body.appendChild(element);
|
|
116
|
+
var observer = new MutationObserver(function (changes) {
|
|
117
|
+
if (changes.some(function (change) { return !element.contains(change.target); })) scheduleLayout();
|
|
118
|
+
});
|
|
119
|
+
observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['class', 'hidden', 'inert', 'data-session-id', 'data-control-session-origin', 'data-control-revision', 'data-ref', 'data-task-id', 'data-open-issue', 'data-control-runtime', 'data-control-mate-stamp'] });
|
|
120
|
+
var resize = new ResizeObserver(scheduleLayout); resize.observe(document.body); resize.observe(element);
|
|
121
|
+
var activity = { message: message, fromPane: !!fromPane, observer: observer, resize: resize, frame: null,
|
|
122
|
+
timer: setTimeout(clearClayControlActivity, message.terminal === false || ['review', 'running'].indexOf(message.phase) >= 0 ? 300000 : 6000) };
|
|
123
|
+
store.set({ clayControlActivity: activity }); layout();
|
|
124
|
+
if (previousRect && element.classList.contains('has-target') && !window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
|
125
|
+
var next = element.getBoundingClientRect(), dx = previousRect.left - next.left, dy = previousRect.top - next.top;
|
|
126
|
+
if (Math.hypot(dx, dy) <= 600 && (dx || dy)) activity.animation = element.animate([
|
|
127
|
+
{ transform: 'translate(' + dx + 'px, ' + dy + 'px)' }, { transform: 'translate(0, 0)' }
|
|
128
|
+
], { duration: 140, easing: 'ease-out' });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
export function handleClayControlActivity(message) {
|
|
132
|
+
if (message && message.type === 'clay_control_viewer') { store.set({ clayControlViewerRef: message.viewerRef }); return true; }
|
|
133
|
+
if (!message || message.type !== 'clay_control_activity') return false;
|
|
134
|
+
if (!validActivity(message) || message.recipientViewerRef !== store.get('clayControlViewerRef')) return true;
|
|
135
|
+
if (store.get('paneMode') && (message.sourceSessionOriginId !== store.get('sessionOriginId') || message.sourceSessionId !== store.get('activeSessionId') || message.mateId !== store.get('activeProjectMateId'))) return true;
|
|
136
|
+
if (!sourceMate(message)) return true;
|
|
137
|
+
if (forward(message)) return true;
|
|
138
|
+
acceptActivity(message);
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
function sourceMate(message) {
|
|
142
|
+
return (store.get('cachedMatesList') || []).find(function (mate) {
|
|
143
|
+
return !mate.archived && !mate.lifecycleDeletion && mate.id === message.mateId && mate.builtinKey === 'clay' && contextualMateStamp(mate) === message.sourceMateStamp;
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
function acceptActivity(message, fromPane) {
|
|
147
|
+
if ((message.ownerId || null) !== (store.get('myUserId') || null) || !store.get('connected')) return;
|
|
148
|
+
if (!sourceMate(message)) return;
|
|
149
|
+
var active = store.get('clayControlActivity');
|
|
150
|
+
var receipts = store.get('clayControlReceipts') || [];
|
|
151
|
+
var old = receipts.find(function (entry) { return entry.id === message.id; });
|
|
152
|
+
var order = store.get('clayControlOrder') || 0;
|
|
153
|
+
if (message.order < order || old && message.sequence <= old.sequence) return;
|
|
154
|
+
if (old && old.stopped) return;
|
|
155
|
+
receipts = receipts.filter(function (entry) { return entry.id !== message.id && Date.now() - entry.time < 300000; }).slice(-99);
|
|
156
|
+
receipts.push({ id: message.id, sequence: message.sequence, time: Date.now(), stopped: message.phase === 'clear' });
|
|
157
|
+
store.set({ clayControlOrder: message.order, clayControlReceipts: receipts });
|
|
158
|
+
if (message.phase === 'clear') { if (active && active.message.id === message.id) clearClayControlActivity(); return; }
|
|
159
|
+
var mate = (store.get('cachedMatesList') || []).find(function (item) { return !item.archived && !item.lifecycleDeletion && item.id === message.mateId && item.builtinKey === 'clay'; });
|
|
160
|
+
if (!mate) return;
|
|
161
|
+
render(message, mate, activityCopy(message), fromPane);
|
|
162
|
+
}
|
|
163
|
+
// Ordinary Mates retain their existing restricted navigation invocation feedback.
|
|
164
|
+
export function showClayNavigationActivity(target, failed) {
|
|
165
|
+
if (!target || !target.contextualSourceMateId) return;
|
|
166
|
+
var mate = (store.get('cachedMatesList') || []).find(function (item) { return !item.archived && item.id === target.contextualSourceMateId; });
|
|
167
|
+
if (!mate) return;
|
|
168
|
+
render({ id: 'ordinary-navigation', target: Object.assign({ kind: 'session' }, target) }, mate,
|
|
169
|
+
failed ? 'I could not start that navigation.' : 'I’m opening the reviewed conversation.');
|
|
170
|
+
}
|
|
171
|
+
function stopPresentation() {
|
|
172
|
+
var active = store.get('clayControlActivity');
|
|
173
|
+
if (active) {
|
|
174
|
+
var receipts = (store.get('clayControlReceipts') || []).slice();
|
|
175
|
+
receipts.push({ id: active.message.id, sequence: Number.MAX_SAFE_INTEGER, stopped: true, time: Date.now() });
|
|
176
|
+
store.set({ clayControlReceipts: receipts.slice(-100) });
|
|
177
|
+
}
|
|
178
|
+
clearClayControlActivity();
|
|
179
|
+
}
|
|
180
|
+
export function initClayControlActivity() {
|
|
181
|
+
store.subscribe(function (value, previous) {
|
|
182
|
+
if (!value.connected && previous.connected) store.set({ clayControlOrder: 0, clayControlViewerRef: null });
|
|
183
|
+
if (!value.clayControlActivity) return;
|
|
184
|
+
if (!value.connected || value.myUserId !== previous.myUserId || value.currentSlug !== previous.currentSlug || value.activeSessionId !== previous.activeSessionId) { stopPresentation(); return; }
|
|
185
|
+
if (value.cachedMatesList !== previous.cachedMatesList) {
|
|
186
|
+
var actor = value.clayControlActivity.message.mateId;
|
|
187
|
+
if (actor && !sourceMate(value.clayControlActivity.message)) stopPresentation();
|
|
188
|
+
}
|
|
189
|
+
scheduleLayout();
|
|
190
|
+
});
|
|
191
|
+
['resize', 'scroll'].forEach(function (name) { window.addEventListener(name, scheduleLayout, true); });
|
|
192
|
+
['transitionrun', 'transitionend', 'transitioncancel', 'animationstart', 'animationend', 'animationcancel'].forEach(function (name) { document.addEventListener(name, scheduleLayout, true); });
|
|
193
|
+
document.addEventListener('visibilitychange', function () { if (document.hidden) stopPresentation(); });
|
|
194
|
+
document.addEventListener('click', function (event) {
|
|
195
|
+
if (event.target.closest('#generation-stop, #autonomous-run-stop, #send-btn.stop')) stopPresentation();
|
|
196
|
+
}, true);
|
|
197
|
+
window.addEventListener('message', function (event) {
|
|
198
|
+
if (event.origin !== location.origin || !event.data || event.data.type !== 'clay-control-presentation') return;
|
|
199
|
+
var host = document.getElementById('contextual-clay'), message = event.data.message;
|
|
200
|
+
if (!validActivity(message)) return;
|
|
201
|
+
var frame = host && Array.from(host.querySelectorAll('iframe[data-canonical="true"]')).find(function (item) { return item.contentWindow === event.source; });
|
|
202
|
+
var bridge = frame && frame.conversation;
|
|
203
|
+
var identity = bridge && bridge.document === frame.contentDocument && bridge.source === frame.contentWindow && bridge.identity();
|
|
204
|
+
if (!identity || message.phase !== 'clear' && (!identity.connected || !identity.available) || identity.ownerId !== store.get('myUserId') || identity.mateId !== message.mateId || identity.sessionId !== message.sourceSessionId || identity.sessionOriginId !== message.sourceSessionOriginId || identity.viewerRef !== message.recipientViewerRef || !sourceMate(message)) return;
|
|
205
|
+
if (new URL(frame.src, location.href).pathname !== '/p/' + encodeURIComponent(message.sourceSlug) + '/') return;
|
|
206
|
+
acceptActivity(message, true);
|
|
207
|
+
});
|
|
208
|
+
document.addEventListener('contextual-ask-stopped', stopPresentation, true);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function withdrawPaneControlActivity() {
|
|
212
|
+
var message = store.get('clayControlForwarded');
|
|
213
|
+
if (message) forward(Object.assign({}, message, { phase: 'clear', sequence: message.sequence + 1 }));
|
|
214
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
var domains = { session: 'conversation', project: 'project', mate: 'Mate profile', knowledge: 'Mate Knowledge', records: 'project record', schedule: 'schedule', conversation: 'Mate conversation', workspace: 'workspace', screen: 'screen', assignment: 'Driver work', studio: 'Studio workbench' };
|
|
2
|
+
var actions = {
|
|
3
|
+
rename: 'rename this conversation so it is easier to find', stop: 'request Stop so this work can settle',
|
|
4
|
+
model: 'change the model for this conversation', effort: 'adjust reasoning effort for this conversation',
|
|
5
|
+
permission: 'change the reviewed permission mode', delete: 'remove the reviewed item',
|
|
6
|
+
fork: 'fork the conversation at the reviewed checkpoint', rewind: 'restore the reviewed conversation checkpoint',
|
|
7
|
+
vendor: 'select the provider for this conversation', vendor_new_session: 'prepare a new conversation with the reviewed provider',
|
|
8
|
+
runtime: 'return this conversation to GUI chat', queue_add: 'queue the reviewed message for the Driver',
|
|
9
|
+
queue_edit: 'update the reviewed queued message', queue_cancel: 'cancel the reviewed queued message',
|
|
10
|
+
queue_reorder: 'reorder the reviewed message queue', navigate: 'open the reviewed conversation so we can continue there',
|
|
11
|
+
dismiss_notifications: 'dismiss the reviewed notifications', profile: 'update the reviewed Mate profile',
|
|
12
|
+
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
|
+
export function activityCopy(msg) {
|
|
15
|
+
var purpose = (['session', 'conversation', 'screen', 'mate', 'assignment', 'studio'].indexOf(msg.domain) >= 0 && Object.hasOwn(actions, msg.action) && actions[msg.action]) || 'apply the reviewed change to this ' + (domains[msg.domain] || 'item');
|
|
16
|
+
var endings = { review: 'I’m waiting for your review before I ' + purpose + '.', running: 'I’m working to ' + purpose + '.',
|
|
17
|
+
proposed: 'I saved the task proposal. Use Create task in Scheduled Tasks to create it; execution has not started.',
|
|
18
|
+
persisted: 'I saved the reviewed change.', completed: 'The service finished the reviewed change.', queued: 'I queued the request. The work has not finished yet.',
|
|
19
|
+
delivered: 'I sent the navigation request. I cannot yet confirm what is visible.', acknowledged: 'I started opening the conversation. Its visible result is still unconfirmed.',
|
|
20
|
+
rendered: 'The reviewed conversation is visibly open in the selected window.',
|
|
21
|
+
cancelled: 'I left the change unapplied.', failed: 'I could not finish this change. Please check the result before trying again.',
|
|
22
|
+
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.' };
|
|
23
|
+
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
|
+
if (msg.domain === 'studio' && msg.phase === 'rendered') return 'The requested workbench content is visibly rendered in this window.';
|
|
25
|
+
if (msg.domain === 'studio' && msg.phase === 'render_unknown') return 'The service returned, but I could not confirm that the workbench content is visible.';
|
|
26
|
+
if (msg.domain === 'studio' && msg.phase === 'delivered') return 'I sent the requested content to the workbench. Its visible result is unconfirmed.';
|
|
27
|
+
if (msg.domain === 'studio' && msg.phase === 'completed') return 'I finished the requested workbench operation.';
|
|
28
|
+
return endings[msg.phase] || null;
|
|
29
|
+
}
|
|
30
|
+
export function validActivity(msg) {
|
|
31
|
+
return msg && msg.version === 1 && typeof msg.id === 'string' && /^activity:[\w-]{36}$/.test(msg.id) &&
|
|
32
|
+
typeof msg.sourceSessionOriginId === 'string' && !!msg.sourceSessionOriginId && typeof msg.sourceMateStamp === 'string' &&
|
|
33
|
+
typeof msg.recipientViewerRef === 'string' && /^viewer:[\w-]{36}$/.test(msg.recipientViewerRef) && Number.isSafeInteger(msg.sourceQueryGeneration) &&
|
|
34
|
+
Number.isSafeInteger(msg.order) && msg.order > 0 && Number.isSafeInteger(msg.sequence) && msg.sequence > 0 &&
|
|
35
|
+
(msg.phase === 'clear' || Object.hasOwn(domains, msg.domain) && !!activityCopy(msg));
|
|
36
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { visibleTarget } from './clay-control-target-visibility.js';
|
|
2
|
+
import { store } from './store.js';
|
|
3
|
+
|
|
4
|
+
function node(selector, id, revision, field) {
|
|
5
|
+
return Array.from(document.querySelectorAll(selector)).find(function (item) {
|
|
6
|
+
return item.dataset[field] === id && item.dataset.controlRevision === String(revision) && item.dataset.controlRuntime === store.get('controlRuntimeId');
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
// Fixed object adapters verify authoritative client state before locating rendered identities.
|
|
10
|
+
export function detailedControlTarget(target) {
|
|
11
|
+
if (target.kind === 'notifications') {
|
|
12
|
+
var notifications = store.get('clayControlNotifications') || [];
|
|
13
|
+
var nodes = target.items.map(function (item) {
|
|
14
|
+
if (!notifications.some(function (saved) { return saved.id === item.id && saved.controlRevision === item.revision; })) return null;
|
|
15
|
+
return Array.from(document.querySelectorAll('.notif-banner[data-notif-id]')).find(function (el) { return el.dataset.notifId === item.id && el.dataset.controlRevision === item.revision; });
|
|
16
|
+
});
|
|
17
|
+
return nodes.every(function (item) { return visibleTarget(item); }) ? nodes.length === 1 ? nodes[0] : nodes[0].parentElement : null;
|
|
18
|
+
}
|
|
19
|
+
if (target.projectSlug !== store.get('currentSlug') || target.runtimeId !== store.get('controlRuntimeId')) return null;
|
|
20
|
+
if (target.kind === 'git-set') {
|
|
21
|
+
var git = store.get('clayControlGitView');
|
|
22
|
+
if (!git || git.repository !== target.repository || git.runtimeId !== target.runtimeId || git.projectSlug !== target.projectSlug) return null;
|
|
23
|
+
var rows = target.files.map(function (file) { return git.files.some(function (saved) { return saved.path === file.path && saved.revision === file.revision; }) && node('.git-file-row', file.path, file.revision, 'controlGitPath'); });
|
|
24
|
+
if (!rows.every(function (item) { return visibleTarget(item); })) return null;
|
|
25
|
+
return document.querySelector('#git-panel-body');
|
|
26
|
+
}
|
|
27
|
+
if (target.kind !== 'knowledge' || !target.objectId) return null;
|
|
28
|
+
var main = (store.get('mateKnowledgeWorkspaces') || {})[target.projectSlug];
|
|
29
|
+
if (!main) return null;
|
|
30
|
+
if (target.object === 'document') {
|
|
31
|
+
var file = main.files.concat(main.deletedFiles || []).find(function (item) { return !item.common && item.id === target.objectId && item.name === target.name && String(item.revision) === target.revision; });
|
|
32
|
+
return file && node('.knowledge-file-row', 'local:' + file.id, target.revision, 'openDocument');
|
|
33
|
+
}
|
|
34
|
+
if (target.object === 'database') {
|
|
35
|
+
var database = main.files.find(function (item) { return !item.common && item.id === target.databaseId && String(item.revision) === target.revision; });
|
|
36
|
+
return database && node('.knowledge-file-row', 'local:' + target.databaseId, target.revision, 'openDocument');
|
|
37
|
+
}
|
|
38
|
+
var db = ((store.get('mateKnowledgeDatabaseWorkspaces') || {})[target.projectSlug] || {}).databases;
|
|
39
|
+
var view = db && db[target.databaseId];
|
|
40
|
+
if (!view || !view.database || String(view.database.revision) !== target.databaseRevision) return null;
|
|
41
|
+
if (target.object === 'database') return node('.knowledge-file-row', 'local:' + target.databaseId, target.revision, 'openDocument');
|
|
42
|
+
var record = (view.records || []).concat(view.recordDetail && view.recordDetail.record || []).find(function (item) { return item.id === target.objectId && String(item.revision) === target.revision; });
|
|
43
|
+
return record && Array.from(document.querySelectorAll('[data-db-record][data-db-database]')).find(function (item) { return item.dataset.dbRecord === record.id && item.dataset.dbDatabase === target.databaseId && item.dataset.controlRevision === target.revision && item.dataset.controlRuntime === target.runtimeId; });
|
|
44
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { studioControlTarget } from './clay-studio-render.js';
|
|
2
|
+
import { store } from './store.js';
|
|
3
|
+
import { detailedControlTarget } from './clay-control-detail-targets.js';
|
|
4
|
+
import { contextualMateStamp } from './contextual-mate-identity.js';
|
|
5
|
+
|
|
6
|
+
// Server projections identify the object; fixed adapters only locate its rendered view.
|
|
7
|
+
export function objectControlTarget(target) {
|
|
8
|
+
if (target.kind === 'surface') return studioControlTarget(target);
|
|
9
|
+
var detailed = detailedControlTarget(target);
|
|
10
|
+
if (['knowledge', 'notifications', 'git-set'].indexOf(target.kind) >= 0) return detailed;
|
|
11
|
+
if (target.kind === 'mate') {
|
|
12
|
+
var mate = (store.get('cachedMatesList') || []).find(function (item) { return item.id === target.mateId && !item.archived && !item.lifecycleDeletion && contextualMateStamp(item) === target.mateStamp; });
|
|
13
|
+
if (!mate) return null;
|
|
14
|
+
return Array.from(document.querySelectorAll('.icon-strip-project-mate[data-mate-id], .icon-strip-brand')).find(function (item) {
|
|
15
|
+
return item.dataset.mateId === mate.id && item.dataset.controlMateStamp === target.mateStamp || mate.builtinKey === 'clay' && item.classList.contains('icon-strip-brand');
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
var project = (store.get('projectsHubList') || []).find(function (item) { return item.slug === target.projectSlug && item.controlRuntimeId === target.runtimeId && item.contentAccess !== false; });
|
|
19
|
+
if (target.kind === 'project') {
|
|
20
|
+
if (!project) return null;
|
|
21
|
+
return Array.from(document.querySelectorAll('#icon-strip-projects .icon-strip-item[href]')).find(function (item) { return item.dataset.controlRuntime === target.runtimeId && new URL(item.href, location.href).pathname === '/p/' + encodeURIComponent(target.projectSlug) + '/'; });
|
|
22
|
+
}
|
|
23
|
+
if (target.projectSlug !== store.get('currentSlug') || target.runtimeId !== store.get('controlRuntimeId')) return null;
|
|
24
|
+
if (target.kind === 'record') {
|
|
25
|
+
var selector = target.area === 'issues' ? '.issue-row[data-open-issue], .issue-detail[data-control-ref]' : '.project-log-row[data-ref]';
|
|
26
|
+
return Array.from(document.querySelectorAll(selector)).find(function (item) {
|
|
27
|
+
return (item.dataset.openIssue || item.dataset.controlRef || item.dataset.ref) === target.ref && item.dataset.controlRevision === target.revision && item.dataset.controlRuntime === target.runtimeId;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (target.kind === 'file') {
|
|
31
|
+
var file = store.get('clayControlFileView');
|
|
32
|
+
if (!file || file.path !== target.path || file.revision !== target.revision || file.projectSlug !== target.projectSlug || file.runtimeId !== target.runtimeId) return null;
|
|
33
|
+
return document.querySelector('#file-viewer .file-viewer-header');
|
|
34
|
+
}
|
|
35
|
+
if (target.kind === 'git') {
|
|
36
|
+
var git = store.get('clayControlGitView');
|
|
37
|
+
if (!git || git.projectSlug !== target.projectSlug || git.runtimeId !== target.runtimeId || git.repository !== target.repository || !git.files.some(function (item) { return item.path === target.path && item.revision === target.revision; })) return null;
|
|
38
|
+
return Array.from(document.querySelectorAll('.git-file-row[data-control-git-path]')).find(function (item) { return item.dataset.controlGitPath === target.path && item.dataset.controlRevision === target.revision && item.dataset.controlRuntime === target.runtimeId; });
|
|
39
|
+
}
|
|
40
|
+
if (target.kind === 'terminal') {
|
|
41
|
+
var terminal = (store.get('clayControlTerminalViews') || []).find(function (item) { return item.id === target.id && item.identity === target.identity && item.revision === target.revision && item.runtimeId === target.runtimeId; });
|
|
42
|
+
if (!terminal) return null;
|
|
43
|
+
return Array.from(document.querySelectorAll('.terminal-tab[data-control-terminal-id]')).find(function (item) { return item.dataset.controlTerminalId === String(target.id) && item.dataset.controlRuntime === target.runtimeId; });
|
|
44
|
+
}
|
|
45
|
+
if (target.kind === 'schedule') {
|
|
46
|
+
if (target.sessionId !== store.get('activeSessionId') || target.sessionOriginId !== store.get('sessionOriginId')) return null;
|
|
47
|
+
var queue = store.get('pendingMessageQueue');
|
|
48
|
+
var logical = queue && (queue.items || []).some(function (item) { return item.schedule && item.schedule.logicalScheduleId === target.id && item.schedule.logicalRevision === target.revision; });
|
|
49
|
+
return Array.from(document.querySelectorAll('.scheduled-task-row[data-task-id], .pending-message-chip[data-control-schedule-id]')).find(function (item) { return (item.dataset.taskId === target.id || logical && item.dataset.controlScheduleId === target.id) && item.dataset.controlRevision === target.revision && item.dataset.controlRuntime === target.runtimeId; });
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function visibleTarget(element) {
|
|
2
|
+
if (!element || !element.isConnected || element.closest('[hidden], [inert], [aria-hidden="true"]')) return false;
|
|
3
|
+
var rect = element.getBoundingClientRect(), style = getComputedStyle(element);
|
|
4
|
+
if (style.visibility !== 'visible' || Number(style.opacity) === 0 || !rect.width || !rect.height || rect.left < 0 || rect.top < 0 || rect.right > innerWidth || rect.bottom > innerHeight) return false;
|
|
5
|
+
for (var parent = element.parentElement; parent; parent = parent.parentElement) {
|
|
6
|
+
if (Number(getComputedStyle(parent).opacity) === 0) return false;
|
|
7
|
+
}
|
|
8
|
+
return [rect.left + 2, rect.left + rect.width / 2, rect.right - 2].every(function (x) {
|
|
9
|
+
var hit = document.elementFromPoint(x, rect.top + rect.height / 2);
|
|
10
|
+
return hit && element.contains(hit);
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { visibleTarget } from './clay-control-target-visibility.js';
|
|
2
|
+
export { visibleTarget };
|
|
3
|
+
import { objectControlTarget } from './clay-control-object-targets.js';
|
|
4
|
+
import { store } from './store.js';
|
|
5
|
+
|
|
6
|
+
// Stable product adapters only. Activity data never supplies a CSS selector.
|
|
7
|
+
export function controlTarget(target) {
|
|
8
|
+
if (!target) return null;
|
|
9
|
+
if (target.kind !== 'session') { var object = objectControlTarget(target); return visibleTarget(object) ? object : null; }
|
|
10
|
+
if (target.projectSlug !== store.get('currentSlug')) return null;
|
|
11
|
+
if (target.kind === 'session' && Number.isSafeInteger(target.sessionId) && typeof target.sessionOriginId === 'string' && target.sessionOriginId) {
|
|
12
|
+
return Array.from(document.querySelectorAll('.session-item[data-session-id]')).find(function (row) {
|
|
13
|
+
return Number(row.dataset.sessionId) === target.sessionId && row.dataset.controlSessionOrigin === target.sessionOriginId && visibleTarget(row);
|
|
14
|
+
}) || null;
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function overlaps(a, b) { return a.left < b.right && a.right > b.left && a.top < b.bottom && a.bottom > b.top; }
|
|
20
|
+
export function activityPlacement(row, width, height, obstacles) {
|
|
21
|
+
var margin = 12, rect = row && row.getBoundingClientRect();
|
|
22
|
+
var candidates = [];
|
|
23
|
+
if (rect) {
|
|
24
|
+
// Keep the cursor shaft seated in the identity pill in either vertical direction.
|
|
25
|
+
candidates.push({ left: rect.right + 8, top: rect.top + rect.height / 2 + 20, tipX: rect.right - 2, tipY: rect.top + rect.height / 2 });
|
|
26
|
+
candidates.push({ left: rect.right + 8, top: rect.top + rect.height / 2 - 34, tipX: rect.right - 2, tipY: rect.top + rect.height / 2, flip: true });
|
|
27
|
+
candidates.push({ left: rect.left - width - 8, top: rect.top + rect.height / 2 + 20, tipX: rect.left + 2, tipY: rect.top + rect.height / 2, reverse: true });
|
|
28
|
+
}
|
|
29
|
+
candidates.push({ left: innerWidth - width - margin, top: 64 });
|
|
30
|
+
candidates.push({ left: margin, top: 64 });
|
|
31
|
+
candidates.push({ left: innerWidth - width - margin, top: innerHeight - height - 80 });
|
|
32
|
+
candidates.push({ left: (innerWidth - width) / 2, top: 64 });
|
|
33
|
+
obstacles.slice(0, 12).forEach(function (obstacle) {
|
|
34
|
+
candidates.push({ left: innerWidth - width - margin, top: obstacle.bottom + 8 });
|
|
35
|
+
candidates.push({ left: margin, top: obstacle.bottom + 8 });
|
|
36
|
+
candidates.push({ left: (innerWidth - width) / 2, top: obstacle.bottom + 8 });
|
|
37
|
+
});
|
|
38
|
+
return candidates.find(function (candidate) {
|
|
39
|
+
var box = { left: candidate.left, right: candidate.left + width, top: candidate.top, bottom: candidate.top + height };
|
|
40
|
+
if (box.left < margin || box.right > innerWidth - margin || box.top < margin || box.bottom > innerHeight - margin) return false;
|
|
41
|
+
if (rect && overlaps(box, rect)) return false;
|
|
42
|
+
return !obstacles.some(function (obstacle) { return overlaps(box, obstacle); });
|
|
43
|
+
}) || null;
|
|
44
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { getWs } from './ws-ref.js';
|
|
3
|
+
|
|
4
|
+
function visible(element) {
|
|
5
|
+
if (!element || !element.isConnected || element.closest('[hidden], [inert], [aria-hidden="true"]')) return false;
|
|
6
|
+
var box = element.getBoundingClientRect(), style = getComputedStyle(element);
|
|
7
|
+
if (style.visibility !== 'visible' || Number(style.opacity) === 0 || box.width <= 0 || box.height <= 0) return false;
|
|
8
|
+
for (var parent = element.parentElement; parent; parent = parent.parentElement) if (getComputedStyle(parent).visibility !== 'visible' || Number(getComputedStyle(parent).opacity) === 0) return false;
|
|
9
|
+
var left = Math.max(0, box.left), right = Math.min(innerWidth, box.right), top = Math.max(0, box.top), bottom = Math.min(innerHeight, box.bottom);
|
|
10
|
+
return right > left && bottom > top && [0.2, 0.5, 0.8].some(function (x) {
|
|
11
|
+
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); });
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
// Bounded evidence from the exact authenticated document, never navigation receipt.
|
|
15
|
+
export function observeNavigationRendering(message, socket) {
|
|
16
|
+
var previous = store.get('clayNavigationRendering');
|
|
17
|
+
if (previous) previous.finish(false);
|
|
18
|
+
var target = message.target, owner = store.get('myUserId'), settled = false, stable = 0, frame, timer;
|
|
19
|
+
function finish(rendered) {
|
|
20
|
+
if (settled) return;
|
|
21
|
+
settled = true; cancelAnimationFrame(frame); clearTimeout(timer); unsubscribe();
|
|
22
|
+
document.removeEventListener('visibilitychange', hidden); document.removeEventListener('click', stop, true);
|
|
23
|
+
if (store.get('clayNavigationRendering') === attempt) store.set({ clayNavigationRendering: null });
|
|
24
|
+
if (getWs() === socket && socket.readyState === 1) socket.send(JSON.stringify({ type: 'clay_navigation_rendered', deliveryId: message.deliveryId,
|
|
25
|
+
renderToken: message.renderToken, visible: rendered, projectSlug: target.projectSlug, sessionId: target.sessionId,
|
|
26
|
+
sessionOriginId: target.sessionOriginId, surface: 'conversation', entryIndex: target.entryIndex }));
|
|
27
|
+
}
|
|
28
|
+
var attempt = { finish: finish };
|
|
29
|
+
store.set({ clayNavigationRendering: attempt });
|
|
30
|
+
function hidden() { if (document.hidden) finish(false); }
|
|
31
|
+
var unsubscribe = store.subscribe(function (value, old) {
|
|
32
|
+
if (!value.connected || value.myUserId !== owner || getWs() !== socket || value.currentSlug !== old.currentSlug && value.currentSlug !== target.projectSlug || value.activeSessionId !== old.activeSessionId && value.currentSlug === target.projectSlug && value.activeSessionId !== target.sessionId) finish(false);
|
|
33
|
+
});
|
|
34
|
+
function stop(event) { if (event.target.closest('#generation-stop, #autonomous-run-stop, #send-btn.stop')) finish(false); }
|
|
35
|
+
document.addEventListener('click', stop, true);
|
|
36
|
+
document.addEventListener('visibilitychange', hidden);
|
|
37
|
+
timer = setTimeout(function () { finish(false); }, Math.max(0, Math.min(5000, message.renderDeadline - Date.now())));
|
|
38
|
+
function inspect() {
|
|
39
|
+
if (settled) return;
|
|
40
|
+
if (document.hidden || getWs() !== socket || !store.get('connected') || store.get('myUserId') !== owner) { finish(false); return; }
|
|
41
|
+
var transcript = document.getElementById('messages');
|
|
42
|
+
var exact = store.get('currentSlug') === target.projectSlug && store.get('activeSessionId') === target.sessionId && store.get('sessionOriginId') === target.sessionOriginId &&
|
|
43
|
+
!store.get('paneMode') && !store.get('dmMode') && !store.get('homeShellVisible') && !store.get('replayingHistory') && transcript &&
|
|
44
|
+
transcript.dataset.controlSessionId === String(target.sessionId) && transcript.dataset.controlSessionOrigin === target.sessionOriginId && visible(transcript);
|
|
45
|
+
if (exact && target.entryIndex !== undefined) exact = transcript.dataset.controlHistoryRevision === target.historyRevision && Array.from(transcript.querySelectorAll('[data-history-index], [data-history-indexes]')).some(function (item) {
|
|
46
|
+
return (item.dataset.historyIndex === String(target.entryIndex) || (item.dataset.historyIndexes || '').split(',').indexOf(String(target.entryIndex)) >= 0) && visible(item);
|
|
47
|
+
});
|
|
48
|
+
stable = exact ? stable + 1 : 0;
|
|
49
|
+
if (stable >= 2) finish(true); else frame = requestAnimationFrame(inspect);
|
|
50
|
+
}
|
|
51
|
+
frame = requestAnimationFrame(inspect);
|
|
52
|
+
}
|