clay-server 3.8.1 → 4.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/lib/ask-user-mcp-server.js +5 -119
  2. package/lib/builtin-mates.js +6 -6
  3. package/lib/claude-hook-installer.js +15 -0
  4. package/lib/clay-history-mcp-server.js +62 -297
  5. package/lib/daemon.js +1 -1
  6. package/lib/debate-model-selection.js +119 -0
  7. package/lib/home-debate-tool-policy.js +164 -0
  8. package/lib/home-response-notifications.js +26 -0
  9. package/lib/mate-creation-mcp-server.js +55 -0
  10. package/lib/mate-ready-creation.js +95 -0
  11. package/lib/mates.js +2 -0
  12. package/lib/notes.js +2 -1
  13. package/lib/project-ask-user.js +64 -0
  14. package/lib/project-capsule-catalog.js +101 -0
  15. package/lib/project-connection.js +4 -0
  16. package/lib/project-debate-proposal.js +133 -17
  17. package/lib/project-debate.js +214 -110
  18. package/lib/project-delegated-follow-up.js +84 -0
  19. package/lib/project-delegated-session.js +53 -0
  20. package/lib/project-filesystem.js +7 -9
  21. package/lib/project-knowledge.js +1 -0
  22. package/lib/project-mate-creation-proposal.js +120 -0
  23. package/lib/project-memory.js +14 -7
  24. package/lib/project-models.js +157 -32
  25. package/lib/project-session-notes.js +2 -2
  26. package/lib/project-session-pair.js +8 -8
  27. package/lib/project-sessions.js +66 -51
  28. package/lib/project-vendor-login.js +397 -0
  29. package/lib/project-worker-proposal.js +25 -25
  30. package/lib/project-workspace-query.js +144 -0
  31. package/lib/project.js +349 -102
  32. package/lib/public/app.js +85 -42
  33. package/lib/public/css/avatar-imprints.css +0 -1
  34. package/lib/public/css/base.css +4 -0
  35. package/lib/public/css/capsule-ui.css +389 -0
  36. package/lib/public/css/command-palette.css +306 -6
  37. package/lib/public/css/home-capsule-library.css +243 -0
  38. package/lib/public/css/home-capsule-source.css +192 -0
  39. package/lib/public/css/home-debate-live.css +146 -0
  40. package/lib/public/css/home-debate-planning.css +125 -0
  41. package/lib/public/css/home-debates-archive.css +281 -0
  42. package/lib/public/css/home-hub.css +896 -405
  43. package/lib/public/css/home-mate-creation.css +34 -0
  44. package/lib/public/css/home-mate-model-picker.css +131 -0
  45. package/lib/public/css/home-mate-settings.css +344 -0
  46. package/lib/public/css/home-session-actions.css +265 -0
  47. package/lib/public/css/home-sidebar.css +667 -0
  48. package/lib/public/css/icon-strip.css +1 -2
  49. package/lib/public/css/mates.css +7 -34
  50. package/lib/public/css/pane.css +1 -1
  51. package/lib/public/css/playbook.css +0 -80
  52. package/lib/public/css/sticky-notes.css +53 -117
  53. package/lib/public/css/tui-attention.css +0 -44
  54. package/lib/public/css/workspace-assignment.css +45 -0
  55. package/lib/public/fonts/source-serif-4/LICENSE.md +93 -0
  56. package/lib/public/fonts/source-serif-4/SourceSerif4Caption-Semibold.ttf.woff2 +0 -0
  57. package/lib/public/index.html +93 -74
  58. package/lib/public/modules/app-connection.js +10 -1
  59. package/lib/public/modules/app-dm.js +43 -160
  60. package/lib/public/modules/app-home-hub.js +328 -585
  61. package/lib/public/modules/app-message-cards.js +242 -0
  62. package/lib/public/modules/app-message-router.js +84 -0
  63. package/lib/public/modules/app-messages.js +102 -37
  64. package/lib/public/modules/app-notifications.js +8 -119
  65. package/lib/public/modules/app-projects.js +9 -1
  66. package/lib/public/modules/app-rendering.js +21 -602
  67. package/lib/public/modules/avatar-imprint.js +11 -8
  68. package/lib/public/modules/chat-bubble-renderer.js +152 -0
  69. package/lib/public/modules/chat-render-runtime.js +198 -0
  70. package/lib/public/modules/command-palette.js +225 -436
  71. package/lib/public/modules/dm-render.js +82 -0
  72. package/lib/public/modules/home-capsule-creation-intent.js +31 -0
  73. package/lib/public/modules/home-capsule-library.js +146 -0
  74. package/lib/public/modules/home-capsule-source.js +383 -0
  75. package/lib/public/modules/home-chat-identity.js +35 -0
  76. package/lib/public/modules/home-chat-scroll.js +77 -0
  77. package/lib/public/modules/home-chat-stream-state.js +34 -0
  78. package/lib/public/modules/home-composer-focus.js +25 -0
  79. package/lib/public/modules/home-conversations-sheet.js +179 -0
  80. package/lib/public/modules/home-debate-activity.js +48 -0
  81. package/lib/public/modules/home-debate-controls.js +295 -0
  82. package/lib/public/modules/home-debate-launch.js +41 -0
  83. package/lib/public/modules/home-debate-live.js +284 -0
  84. package/lib/public/modules/home-debate-models.js +73 -0
  85. package/lib/public/modules/home-debate-planning.js +335 -0
  86. package/lib/public/modules/home-debates-archive.js +279 -0
  87. package/lib/public/modules/home-dock-resize.js +74 -0
  88. package/lib/public/modules/home-dock.js +446 -0
  89. package/lib/public/modules/home-mate-chat.js +496 -0
  90. package/lib/public/modules/home-mate-creation.js +109 -0
  91. package/lib/public/modules/home-mate-model-picker.js +281 -0
  92. package/lib/public/modules/home-mate-selection.js +38 -0
  93. package/lib/public/modules/home-mate-settings-menu.js +131 -0
  94. package/lib/public/modules/home-mate-settings.js +383 -0
  95. package/lib/public/modules/home-session-actions.js +373 -0
  96. package/lib/public/modules/home-session-model-confirmation.js +13 -0
  97. package/lib/public/modules/home-shell.js +9 -0
  98. package/lib/public/modules/home-sidebar-chat-list.js +63 -0
  99. package/lib/public/modules/home-sidebar.js +158 -0
  100. package/lib/public/modules/home-sub-surface.js +19 -0
  101. package/lib/public/modules/home-surface-boot.js +68 -0
  102. package/lib/public/modules/home-surface.js +141 -0
  103. package/lib/public/modules/home-tools.js +271 -0
  104. package/lib/public/modules/markdown.js +50 -2
  105. package/lib/public/modules/mate-management.js +86 -0
  106. package/lib/public/modules/mate-wizard.js +0 -7
  107. package/lib/public/modules/mention.js +17 -19
  108. package/lib/public/modules/pane-bridge.js +9 -0
  109. package/lib/public/modules/profile.js +4 -5
  110. package/lib/public/modules/project-settings.js +34 -28
  111. package/lib/public/modules/project-switcher.js +11 -5
  112. package/lib/public/modules/project-workspace-assignment.js +23 -0
  113. package/lib/public/modules/search-clay-chat.js +402 -0
  114. package/lib/public/modules/server-settings.js +14 -12
  115. package/lib/public/modules/session-actions.js +1 -1
  116. package/lib/public/modules/sidebar-mates.js +56 -552
  117. package/lib/public/modules/sidebar-mobile.js +7 -64
  118. package/lib/public/modules/sidebar-presence.js +37 -0
  119. package/lib/public/modules/split-pair-ui.js +13 -13
  120. package/lib/public/modules/split-view.js +7 -0
  121. package/lib/public/modules/sticky-notes.js +7 -6
  122. package/lib/public/modules/tool-input-composition.js +48 -0
  123. package/lib/public/modules/tool-llm-status.js +167 -0
  124. package/lib/public/modules/tool-renderer-advanced.js +285 -0
  125. package/lib/public/modules/tool-renderer-chart.js +158 -0
  126. package/lib/public/modules/tool-renderer-semantics.js +35 -0
  127. package/lib/public/modules/tool-renderer.js +464 -0
  128. package/lib/public/modules/tool-runtime.js +309 -0
  129. package/lib/public/modules/tool-ui-evaluator.js +97 -0
  130. package/lib/public/modules/tool-ui-tree.js +21 -0
  131. package/lib/public/modules/tui-attention.js +10 -0
  132. package/lib/public/modules/user-settings.js +16 -23
  133. package/lib/public/modules/vendor-login.js +287 -0
  134. package/lib/public/modules/worker-proposal.js +2 -2
  135. package/lib/public/modules/workspace-assignment-card.js +149 -0
  136. package/lib/public/modules/workspace-assignment-routing.js +4 -0
  137. package/lib/public/style.css +13 -2
  138. package/lib/runtime-env.js +65 -0
  139. package/lib/sdk-bridge.js +120 -32
  140. package/lib/sdk-message-processor.js +10 -6
  141. package/lib/server-home-capsule-creation.js +36 -0
  142. package/lib/server-home-chat-events.js +287 -0
  143. package/lib/server-home-chat.js +518 -0
  144. package/lib/server-home-clay-entry.js +65 -0
  145. package/lib/server-home-clay-session-links.js +26 -0
  146. package/lib/server-home-debate-planning.js +214 -0
  147. package/lib/server-home-debates.js +137 -0
  148. package/lib/server-home-mate-creation.js +261 -0
  149. package/lib/server-home-models.js +241 -0
  150. package/lib/server-home-preferences.js +61 -0
  151. package/lib/server-mates.js +37 -0
  152. package/lib/server-tools.js +377 -0
  153. package/lib/server.js +111 -59
  154. package/lib/session-pair-mcp-server.js +6 -6
  155. package/lib/sessions.js +18 -3
  156. package/lib/tool-capsule-source.js +142 -0
  157. package/lib/tool-control-mcp-server.js +126 -0
  158. package/lib/tool-llm.js +48 -0
  159. package/lib/tool-storage.js +77 -0
  160. package/lib/tool-ui-spec-advanced.js +82 -0
  161. package/lib/tool-ui-spec.js +261 -0
  162. package/lib/tools-registry.js +286 -0
  163. package/lib/users-home-dock-preferences.js +77 -0
  164. package/lib/users-home-surface-preferences.js +99 -0
  165. package/lib/users-preferences.js +4 -7
  166. package/lib/users.js +12 -0
  167. package/lib/whats-new-content.js +3 -3
  168. package/lib/workspace-assignment-service.js +420 -0
  169. package/lib/workspace-query-mcp-server.js +154 -0
  170. package/lib/workspace-query-service.js +492 -0
  171. package/lib/ws-schema.js +93 -12
  172. package/lib/yoke/adapters/acp.js +2 -1
  173. package/lib/yoke/adapters/antigravity.js +2 -1
  174. package/lib/yoke/adapters/claude-worker.js +11 -4
  175. package/lib/yoke/adapters/claude.js +73 -43
  176. package/lib/yoke/adapters/codex.js +40 -10
  177. package/lib/yoke/adapters/kiro.js +2 -1
  178. package/lib/yoke/claude-user-input.js +38 -0
  179. package/lib/yoke/codex-app-server.js +20 -2
  180. package/lib/yoke/codex-user-input.js +64 -0
  181. package/lib/yoke/complete-once.js +164 -0
  182. package/lib/yoke/index.js +5 -1
  183. package/lib/yoke/interface.js +19 -0
  184. package/lib/yoke/mcp-bridge-server.js +2 -6
  185. package/lib/yoke/user-input.js +254 -0
  186. package/package.json +3 -2
  187. package/lib/public/css/home-chat.css +0 -380
  188. package/lib/public/modules/home-chat.js +0 -438
  189. package/lib/server-clay-home.js +0 -245
@@ -4,8 +4,15 @@ var PAPER = "#f1efe8";
4
4
  var INK = "#1b1b1a";
5
5
  var LIGHT_BASE = "#e7e5de";
6
6
  var LIGHT_FIELD = "#333330";
7
- var REGISTRATION_INDIGO = "#5857fc";
8
- var BRAND_GREEN = "#07e5a3";
7
+ var MUTED_INDIGO = "#72778f";
8
+ var MATE_MARK_PALETTE = [
9
+ { background: "#6f7489", foreground: PAPER },
10
+ { background: "#78816c", foreground: PAPER },
11
+ { background: "#92756d", foreground: PAPER },
12
+ { background: "#6f8382", foreground: PAPER },
13
+ { background: "#887b91", foreground: PAPER },
14
+ { background: "#8a806a", foreground: PAPER },
15
+ ];
9
16
 
10
17
  function hashString(value) {
11
18
  var text = String(value || "anonymous");
@@ -71,7 +78,7 @@ export function imprintSvg(options) {
71
78
  var darkBase = random() > 0.52;
72
79
  var base = darkBase ? INK : LIGHT_BASE;
73
80
  var field = darkBase ? PAPER : LIGHT_FIELD;
74
- var accent = REGISTRATION_INDIGO;
81
+ var accent = MUTED_INDIGO;
75
82
  var rotation = Math.floor(random() * 4) * 90;
76
83
  var bandWidth = between(random, 37, 56);
77
84
  var secondaryWidth = between(random, 14, 24);
@@ -114,11 +121,7 @@ export function mateMarkSvg(options) {
114
121
  var size = Math.max(12, Math.min(256, Number(settings.size) || 64));
115
122
  var identity = String(settings.seed || "M").trim();
116
123
  var initial = Array.from(identity || "M")[0].toUpperCase();
117
- var palette = [
118
- { background: REGISTRATION_INDIGO, foreground: PAPER },
119
- { background: BRAND_GREEN, foreground: INK },
120
- { background: PAPER, foreground: REGISTRATION_INDIGO },
121
- ][initial.codePointAt(0) % 3];
124
+ var palette = MATE_MARK_PALETTE[initial.codePointAt(0) % MATE_MARK_PALETTE.length];
122
125
  var clipId = "mate-mark";
123
126
 
124
127
  return '<svg xmlns="http://www.w3.org/2000/svg" width="' + size + '" height="' + size +
@@ -0,0 +1,152 @@
1
+ // chat-bubble-renderer.js - Stateless project-style chat bubble rendering.
2
+
3
+ import { copyToClipboard } from './utils.js';
4
+ import { renderMarkdown, highlightCodeBlocks, renderMermaidBlocks, enhanceClaySessionLinks } from './markdown.js';
5
+
6
+ var copyCleanups = new WeakMap();
7
+
8
+ function appendIdentity(row, options, avatarClass) {
9
+ if (options.avatarUrl) {
10
+ var avatar = document.createElement("img");
11
+ avatar.className = "dm-bubble-avatar " + avatarClass;
12
+ avatar.src = options.avatarUrl;
13
+ avatar.alt = "";
14
+ row.appendChild(avatar);
15
+ }
16
+
17
+ var content = document.createElement("div");
18
+ content.className = "dm-bubble-content";
19
+ if (options.name || options.time) {
20
+ var header = document.createElement("div");
21
+ header.className = "dm-bubble-header";
22
+ if (options.name) {
23
+ var name = document.createElement("span");
24
+ name.className = "dm-bubble-name";
25
+ name.textContent = options.name;
26
+ header.appendChild(name);
27
+ }
28
+ if (options.time) {
29
+ var time = document.createElement("span");
30
+ time.className = "dm-bubble-time";
31
+ time.textContent = options.time;
32
+ header.appendChild(time);
33
+ }
34
+ content.appendChild(header);
35
+ }
36
+ row.appendChild(content);
37
+ return content;
38
+ }
39
+
40
+ export function createAssistantBubble(options) {
41
+ options = options || {};
42
+ var row = document.createElement("div");
43
+ row.className = "msg-assistant";
44
+ if (options.turn !== undefined && options.turn !== null) row.dataset.turn = options.turn;
45
+ var contentWrap = appendIdentity(row, options, "dm-bubble-avatar-mate");
46
+ var content = document.createElement("div");
47
+ content.className = "md-content";
48
+ content.dir = "auto";
49
+ contentWrap.appendChild(content);
50
+ return row;
51
+ }
52
+
53
+ export function createUserBubble(options) {
54
+ options = options || {};
55
+ var row = document.createElement("div");
56
+ row.className = "msg-user";
57
+ if (options.turn !== undefined && options.turn !== null) row.dataset.turn = options.turn;
58
+ var contentWrap = appendIdentity(row, options, "dm-bubble-avatar-me");
59
+ var bubble = document.createElement("div");
60
+ bubble.className = "bubble";
61
+ bubble.dir = "auto";
62
+ var text = document.createElement("span");
63
+ text.textContent = options.text || "";
64
+ bubble.appendChild(text);
65
+ contentWrap.appendChild(bubble);
66
+ return row;
67
+ }
68
+
69
+ function assistantContent(row) {
70
+ return row ? row.querySelector(".md-content") : null;
71
+ }
72
+
73
+ export function renderAssistantBubbleText(row, text, highlight) {
74
+ var content = assistantContent(row);
75
+ if (!content) return null;
76
+ content.innerHTML = renderMarkdown(text || "");
77
+ enhanceClaySessionLinks(content);
78
+ if (highlight) highlightCodeBlocks(content);
79
+ return content;
80
+ }
81
+
82
+ export function enhanceAssistantBubble(row, renderMermaid) {
83
+ var content = assistantContent(row);
84
+ if (!content) return;
85
+ enhanceClaySessionLinks(content);
86
+ highlightCodeBlocks(content);
87
+ if (renderMermaid) renderMermaidBlocks(content);
88
+ }
89
+
90
+ export function addAssistantCopyHandler(row, rawText) {
91
+ if (!row || !rawText || row.dataset.copyBound === "true") return;
92
+ row.dataset.copyBound = "true";
93
+ var primed = false;
94
+ var resetTimer = null;
95
+ var isTouchDevice = "ontouchstart" in window;
96
+ var hint = document.createElement("div");
97
+ hint.className = "msg-copy-hint";
98
+ hint.textContent = (isTouchDevice ? "Tap" : "Click") + " to grab this";
99
+ row.appendChild(hint);
100
+
101
+ function reset() {
102
+ primed = false;
103
+ row.classList.remove("copy-primed", "copy-done");
104
+ hint.textContent = (isTouchDevice ? "Tap" : "Click") + " to grab this";
105
+ }
106
+
107
+ row.addEventListener("click", function (event) {
108
+ if (event.target.closest("a, pre, code")) return;
109
+ var selection = window.getSelection();
110
+ if (selection && selection.toString().length > 0) return;
111
+ if (!primed) {
112
+ primed = true;
113
+ row.classList.add("copy-primed");
114
+ hint.textContent = isTouchDevice ? "Tap again to grab" : "Click again to grab";
115
+ clearTimeout(resetTimer);
116
+ resetTimer = setTimeout(reset, 3000);
117
+ return;
118
+ }
119
+ clearTimeout(resetTimer);
120
+ copyToClipboard(rawText).then(function () {
121
+ row.classList.remove("copy-primed");
122
+ row.classList.add("copy-done");
123
+ hint.textContent = "Grabbed!";
124
+ resetTimer = setTimeout(reset, 1500);
125
+ });
126
+ });
127
+
128
+ function handleOutsideClick(event) {
129
+ if (primed && !row.contains(event.target)) reset();
130
+ }
131
+ document.addEventListener("click", handleOutsideClick);
132
+ copyCleanups.set(row, function () {
133
+ clearTimeout(resetTimer);
134
+ document.removeEventListener("click", handleOutsideClick);
135
+ copyCleanups.delete(row);
136
+ });
137
+ }
138
+
139
+ export function finalizeAssistantBubble(row, rawText, enableCopy) {
140
+ renderAssistantBubbleText(row, rawText, false);
141
+ enhanceAssistantBubble(row, true);
142
+ if (enableCopy) addAssistantCopyHandler(row, rawText);
143
+ }
144
+
145
+ export function disposeChatBubbleTree(root) {
146
+ if (!root) return;
147
+ var rows = root.querySelectorAll('.msg-assistant[data-copy-bound="true"]');
148
+ for (var i = 0; i < rows.length; i++) {
149
+ var cleanup = copyCleanups.get(rows[i]);
150
+ if (cleanup) cleanup();
151
+ }
152
+ }
@@ -0,0 +1,198 @@
1
+ // chat-render-runtime.js - Shared project transcript state and scrolling.
2
+
3
+ import { store } from './store.js';
4
+ import { getMessagesEl } from './dom-refs.js';
5
+
6
+ export var VENDOR_AVATARS = {
7
+ claude: "/claude-code-avatar.png",
8
+ codex: "/codex-avatar.png",
9
+ antigravity: "/antigravity-avatar.png",
10
+ opencode: "/opencode-avatar.svg",
11
+ kimi: "/kimi-avatar.svg",
12
+ grok: "/grok-avatar.svg",
13
+ copilot: "/copilot-avatar.svg",
14
+ qwen: "/qwen-avatar.svg",
15
+ junie: "/junie-avatar.svg",
16
+ kiro: "/kiro-avatar.svg",
17
+ };
18
+
19
+ export var VENDOR_NAMES = {
20
+ claude: "Claude Code",
21
+ codex: "Codex",
22
+ antigravity: "Antigravity CLI",
23
+ opencode: "OpenCode",
24
+ kimi: "Kimi Code",
25
+ grok: "Grok Build",
26
+ copilot: "GitHub Copilot CLI",
27
+ qwen: "Qwen Code",
28
+ junie: "Junie CLI",
29
+ kiro: "Kiro CLI",
30
+ };
31
+
32
+ export var VENDOR_HOMEPAGES = {
33
+ claude: "https://claude.com/product/claude-code",
34
+ codex: "https://openai.com/codex/",
35
+ antigravity: "https://antigravity.google/product/antigravity-cli",
36
+ opencode: "https://opencode.ai/",
37
+ kimi: "https://www.kimi.com/code/",
38
+ grok: "https://docs.x.ai/build/overview",
39
+ copilot: "https://github.com/features/copilot/cli",
40
+ qwen: "https://qwenlm.github.io/qwen-code-docs/",
41
+ junie: "https://junie.jetbrains.com/",
42
+ kiro: "https://kiro.dev/",
43
+ };
44
+
45
+ var NEW_MSG_BTN_DEFAULT = "\u2193 Latest";
46
+ var NEW_MSG_BTN_ACTIVITY = "\u2193 New activity";
47
+ var turnCounter = 0;
48
+ var prependAnchor = null;
49
+ var activityEl = null;
50
+ var isUserScrolledUp = false;
51
+ var stickyBottom = false;
52
+ var stickyBottomQuietTimer = null;
53
+ var stickyBottomCeilingTimer = null;
54
+ var stickyBottomQuietMs = 750;
55
+ var stickyBottomCeilingMs = 8000;
56
+ var stickyBottomResizeObs = null;
57
+ var stickyBottomInputBound = false;
58
+
59
+ export function getStickyBottom() { return stickyBottom; }
60
+
61
+ function pinToBottomNow() {
62
+ var messagesEl = getMessagesEl();
63
+ if (!messagesEl) return;
64
+ messagesEl.scrollTop = messagesEl.scrollHeight;
65
+ }
66
+
67
+ function ensureStickyInfrastructure() {
68
+ var messagesEl = getMessagesEl();
69
+ if (!messagesEl) return;
70
+ if (!stickyBottomResizeObs && typeof ResizeObserver !== "undefined") {
71
+ stickyBottomResizeObs = new ResizeObserver(function () {
72
+ if (!stickyBottom) return;
73
+ pinToBottomNow();
74
+ if (stickyBottomQuietTimer) clearTimeout(stickyBottomQuietTimer);
75
+ stickyBottomQuietTimer = setTimeout(disarmStickyBottom, stickyBottomQuietMs);
76
+ });
77
+ stickyBottomResizeObs.observe(messagesEl);
78
+ var kids = messagesEl.children;
79
+ for (var i = 0; i < kids.length; i++) stickyBottomResizeObs.observe(kids[i]);
80
+ }
81
+ if (!stickyBottomInputBound) {
82
+ stickyBottomInputBound = true;
83
+ var disarmOnUserScroll = function () { disarmStickyBottom(); };
84
+ messagesEl.addEventListener("wheel", disarmOnUserScroll, { passive: true });
85
+ messagesEl.addEventListener("touchmove", disarmOnUserScroll, { passive: true });
86
+ document.addEventListener("keydown", function (event) {
87
+ if (!stickyBottom) return;
88
+ if (event.key === "PageUp" || event.key === "Home" || event.key === "ArrowUp") disarmStickyBottom();
89
+ });
90
+ }
91
+ }
92
+
93
+ export function armStickyBottom(durationMs) {
94
+ if (prependAnchor) return;
95
+ ensureStickyInfrastructure();
96
+ stickyBottom = true;
97
+ isUserScrolledUp = false;
98
+ var newMsgBtn = document.getElementById("new-msg-btn");
99
+ if (newMsgBtn) {
100
+ newMsgBtn.classList.add("hidden");
101
+ newMsgBtn.textContent = NEW_MSG_BTN_DEFAULT;
102
+ }
103
+ pinToBottomNow();
104
+ if (stickyBottomResizeObs) {
105
+ var messagesEl = getMessagesEl();
106
+ if (messagesEl) {
107
+ var kids = messagesEl.children;
108
+ for (var i = 0; i < kids.length; i++) {
109
+ try { stickyBottomResizeObs.observe(kids[i]); } catch (e) {}
110
+ }
111
+ }
112
+ }
113
+ stickyBottomQuietMs = durationMs || 750;
114
+ if (stickyBottomQuietTimer) clearTimeout(stickyBottomQuietTimer);
115
+ stickyBottomQuietTimer = setTimeout(disarmStickyBottom, stickyBottomQuietMs);
116
+ if (stickyBottomCeilingTimer) clearTimeout(stickyBottomCeilingTimer);
117
+ stickyBottomCeilingTimer = setTimeout(disarmStickyBottom, stickyBottomCeilingMs);
118
+ }
119
+
120
+ export function disarmStickyBottom() {
121
+ stickyBottom = false;
122
+ if (stickyBottomQuietTimer) { clearTimeout(stickyBottomQuietTimer); stickyBottomQuietTimer = null; }
123
+ if (stickyBottomCeilingTimer) { clearTimeout(stickyBottomCeilingTimer); stickyBottomCeilingTimer = null; }
124
+ }
125
+
126
+ export function initRendering() {
127
+ store.subscribe(function (state, prev) {
128
+ if (state.currentVendor !== prev.currentVendor) {
129
+ var inputEl = document.getElementById("input");
130
+ if (inputEl) inputEl.placeholder = "Message " + (VENDOR_NAMES[state.currentVendor] || VENDOR_NAMES.claude) + "...";
131
+ }
132
+ });
133
+ }
134
+
135
+ export function getTurnCounter() { return turnCounter; }
136
+ export function setTurnCounter(value) { turnCounter = value; }
137
+ export function getPrependAnchor() { return prependAnchor; }
138
+ export function setPrependAnchor(value) { prependAnchor = value; }
139
+ export function getActivityEl() { return activityEl; }
140
+ export function setActivityEl(value) { activityEl = value; }
141
+ export function getIsUserScrolledUp() { return isUserScrolledUp; }
142
+ export function setIsUserScrolledUp(value) { isUserScrolledUp = value; }
143
+
144
+ export function addToMessages(element) {
145
+ var messagesEl = getMessagesEl();
146
+ if (prependAnchor) messagesEl.insertBefore(element, prependAnchor);
147
+ else messagesEl.appendChild(element);
148
+ var scheduled = document.getElementById("scheduled-msg-bubble");
149
+ if (scheduled && element !== scheduled && scheduled.parentNode === messagesEl) messagesEl.appendChild(scheduled);
150
+ }
151
+
152
+ export function scrollToBottom() {
153
+ if (prependAnchor) return;
154
+ var messagesEl = getMessagesEl();
155
+ if (!messagesEl) return;
156
+ var distFromBottom = messagesEl.scrollHeight - messagesEl.scrollTop - messagesEl.clientHeight;
157
+ if (distFromBottom > 150 || isUserScrolledUp) {
158
+ if (distFromBottom > 150) isUserScrolledUp = true;
159
+ var newMsgBtn = document.getElementById("new-msg-btn");
160
+ if (newMsgBtn) {
161
+ newMsgBtn.textContent = NEW_MSG_BTN_ACTIVITY;
162
+ newMsgBtn.classList.remove("hidden");
163
+ }
164
+ return;
165
+ }
166
+ requestAnimationFrame(function () {
167
+ var dist = messagesEl.scrollHeight - messagesEl.scrollTop - messagesEl.clientHeight;
168
+ if (dist > 150) {
169
+ isUserScrolledUp = true;
170
+ return;
171
+ }
172
+ messagesEl.scrollTop = messagesEl.scrollHeight;
173
+ });
174
+ }
175
+
176
+ export function forceScrollToBottom() {
177
+ if (prependAnchor) return;
178
+ armStickyBottom(750);
179
+ }
180
+
181
+ export function getMsgTime() {
182
+ var timestamp = store.get('currentMsgTs');
183
+ var date = timestamp ? new Date(timestamp) : new Date();
184
+ var time = String(date.getHours()).padStart(2, "0") + ":" + String(date.getMinutes()).padStart(2, "0");
185
+ var now = new Date();
186
+ if (date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth() && date.getDate() === now.getDate()) return time;
187
+ return (date.getMonth() + 1) + "/" + date.getDate() + " " + time;
188
+ }
189
+
190
+ export function shouldGroupMessage(senderClass) {
191
+ var state = store.snap();
192
+ if (state.replayingHistory && !state.currentMsgTs) return false;
193
+ var prev = getMessagesEl().lastElementChild;
194
+ if (!prev || !prev.classList.contains(senderClass)) return false;
195
+ var prevTime = prev.querySelector(".dm-bubble-time");
196
+ if (!prevTime) return false;
197
+ return prevTime.textContent === getMsgTime();
198
+ }