clay-server 5.2.0-beta.6 → 5.2.0-beta.7

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.
Files changed (108) hide show
  1. package/lib/clay-control-presentation-binding.js +96 -0
  2. package/lib/clay-control-presentation.js +128 -0
  3. package/lib/clay-control-target-projection.js +26 -0
  4. package/lib/clay-mate-chat.js +1 -0
  5. package/lib/clay-mate-knowledge.js +1 -1
  6. package/lib/clay-product-control-tools.js +1 -1
  7. package/lib/clay-product-control.js +22 -12
  8. package/lib/clay-project-records.js +2 -1
  9. package/lib/clay-scheduling.js +1 -1
  10. package/lib/clay-screen-controls.js +13 -5
  11. package/lib/clay-workspace-controls.js +2 -1
  12. package/lib/contextual-screen-snapshot.js +38 -0
  13. package/lib/mate-context-service.js +138 -0
  14. package/lib/mate-context-tools.js +21 -0
  15. package/lib/project-connection.js +8 -5
  16. package/lib/project-control-git-view.js +12 -0
  17. package/lib/project-filesystem.js +5 -1
  18. package/lib/project-http.js +3 -0
  19. package/lib/project-notifications.js +5 -4
  20. package/lib/project-product-control.js +3 -1
  21. package/lib/project-scheduled-messages.js +4 -4
  22. package/lib/project-scheduled-task-control.js +1 -1
  23. package/lib/project-session-document.js +20 -5
  24. package/lib/project-shared-browser.js +22 -5
  25. package/lib/project-studio-presentation.js +58 -0
  26. package/lib/project-user-message.js +36 -0
  27. package/lib/project-workspace-query.js +11 -1
  28. package/lib/project.js +24 -7
  29. package/lib/public/app.js +24 -0
  30. package/lib/public/appearance-bootstrap.js +2 -0
  31. package/lib/public/css/contextual-clay.css +240 -0
  32. package/lib/public/modules/app-favicon.js +7 -7
  33. package/lib/public/modules/app-message-router.js +5 -1
  34. package/lib/public/modules/app-messages.js +14 -1
  35. package/lib/public/modules/app-notifications.js +10 -3
  36. package/lib/public/modules/app-projects.js +3 -0
  37. package/lib/public/modules/clay-control-activity.js +214 -0
  38. package/lib/public/modules/clay-control-copy.js +36 -0
  39. package/lib/public/modules/clay-control-detail-targets.js +44 -0
  40. package/lib/public/modules/clay-control-object-targets.js +52 -0
  41. package/lib/public/modules/clay-control-target-visibility.js +12 -0
  42. package/lib/public/modules/clay-control-targets.js +44 -0
  43. package/lib/public/modules/clay-navigation-render.js +52 -0
  44. package/lib/public/modules/clay-owner-navigation.js +11 -2
  45. package/lib/public/modules/clay-session-links.js +31 -0
  46. package/lib/public/modules/clay-studio-bridge.js +20 -0
  47. package/lib/public/modules/clay-studio-document.js +67 -0
  48. package/lib/public/modules/clay-studio-render.js +144 -0
  49. package/lib/public/modules/clay-tool-presentation.js +4 -0
  50. package/lib/public/modules/command-palette.js +26 -31
  51. package/lib/public/modules/contextual-ask-session.js +26 -0
  52. package/lib/public/modules/contextual-clay-geometry.js +155 -0
  53. package/lib/public/modules/contextual-clay-session.js +130 -0
  54. package/lib/public/modules/contextual-clay-workspace.js +157 -0
  55. package/lib/public/modules/contextual-clay.js +237 -0
  56. package/lib/public/modules/contextual-composer.js +91 -0
  57. package/lib/public/modules/contextual-mate-context.js +115 -0
  58. package/lib/public/modules/contextual-mate-hint.js +40 -0
  59. package/lib/public/modules/contextual-mate-identity.js +23 -0
  60. package/lib/public/modules/contextual-mate-sessions.js +134 -0
  61. package/lib/public/modules/contextual-search.js +137 -0
  62. package/lib/public/modules/contextual-session-citations.js +58 -0
  63. package/lib/public/modules/filebrowser.js +20 -1
  64. package/lib/public/modules/generation-stop.js +8 -5
  65. package/lib/public/modules/git-panel.js +3 -1
  66. package/lib/public/modules/icon-strip-tooltip.js +2 -1
  67. package/lib/public/modules/input.js +35 -1
  68. package/lib/public/modules/issues-render.js +3 -2
  69. package/lib/public/modules/knowledge-db-workbench.js +4 -4
  70. package/lib/public/modules/markdown.js +2 -34
  71. package/lib/public/modules/mate-knowledge-workbench.js +1 -1
  72. package/lib/public/modules/message-delivery.js +10 -0
  73. package/lib/public/modules/pane-bridge.js +2 -2
  74. package/lib/public/modules/pending-message-queue.js +1 -0
  75. package/lib/public/modules/product-review.js +26 -0
  76. package/lib/public/modules/project-chat-identity.js +13 -4
  77. package/lib/public/modules/project-logs-render.js +3 -0
  78. package/lib/public/modules/project-mate-navigation.js +58 -21
  79. package/lib/public/modules/scheduled-tasks.js +2 -0
  80. package/lib/public/modules/search-clay-chat.js +5 -454
  81. package/lib/public/modules/shared-browser.js +2 -0
  82. package/lib/public/modules/sidebar-mobile.js +4 -3
  83. package/lib/public/modules/sidebar-projects.js +1 -0
  84. package/lib/public/modules/sidebar-sessions.js +1 -0
  85. package/lib/public/modules/split-view.js +7 -16
  86. package/lib/public/modules/terminal.js +9 -0
  87. package/lib/public/modules/tools.js +4 -1
  88. package/lib/public/modules/wireframe-workbench.js +5 -2
  89. package/lib/public/style.css +2 -0
  90. package/lib/sdk-bridge.js +4 -0
  91. package/lib/server-contextual-clay-settings.js +40 -0
  92. package/lib/server-contextual-mate-sessions.js +70 -0
  93. package/lib/server-home-chat.js +7 -1
  94. package/lib/server-home-clay-entry.js +23 -4
  95. package/lib/server-home-clay-session-links.js +12 -0
  96. package/lib/server-owner-ui.js +49 -8
  97. package/lib/server-settings.js +2 -0
  98. package/lib/server.js +17 -0
  99. package/lib/sessions.js +8 -1
  100. package/lib/terminal-manager.js +18 -11
  101. package/lib/users-contextual-clay-preferences.js +31 -0
  102. package/lib/users.js +3 -0
  103. package/lib/workspace-assignment-service.js +25 -0
  104. package/lib/workspace-query-mcp-server.js +1 -1
  105. package/lib/ws-request.js +1 -0
  106. package/lib/ws-schema.js +10 -0
  107. package/lib/yoke/claude-hook-installer.js +4 -0
  108. 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
+ }