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.
- package/lib/ask-user-mcp-server.js +5 -119
- package/lib/builtin-mates.js +6 -6
- package/lib/claude-hook-installer.js +15 -0
- package/lib/clay-history-mcp-server.js +62 -297
- package/lib/daemon.js +1 -1
- package/lib/debate-model-selection.js +119 -0
- package/lib/home-debate-tool-policy.js +164 -0
- package/lib/home-response-notifications.js +26 -0
- package/lib/mate-creation-mcp-server.js +55 -0
- package/lib/mate-ready-creation.js +95 -0
- package/lib/mates.js +2 -0
- package/lib/notes.js +2 -1
- package/lib/project-ask-user.js +64 -0
- package/lib/project-capsule-catalog.js +101 -0
- package/lib/project-connection.js +4 -0
- package/lib/project-debate-proposal.js +133 -17
- package/lib/project-debate.js +214 -110
- package/lib/project-delegated-follow-up.js +84 -0
- package/lib/project-delegated-session.js +53 -0
- package/lib/project-filesystem.js +7 -9
- package/lib/project-knowledge.js +1 -0
- package/lib/project-mate-creation-proposal.js +120 -0
- package/lib/project-memory.js +14 -7
- package/lib/project-models.js +157 -32
- package/lib/project-session-notes.js +2 -2
- package/lib/project-session-pair.js +8 -8
- package/lib/project-sessions.js +66 -51
- package/lib/project-vendor-login.js +397 -0
- package/lib/project-worker-proposal.js +25 -25
- package/lib/project-workspace-query.js +144 -0
- package/lib/project.js +349 -102
- package/lib/public/app.js +85 -42
- package/lib/public/css/avatar-imprints.css +0 -1
- package/lib/public/css/base.css +4 -0
- package/lib/public/css/capsule-ui.css +389 -0
- package/lib/public/css/command-palette.css +306 -6
- package/lib/public/css/home-capsule-library.css +243 -0
- package/lib/public/css/home-capsule-source.css +192 -0
- package/lib/public/css/home-debate-live.css +146 -0
- package/lib/public/css/home-debate-planning.css +125 -0
- package/lib/public/css/home-debates-archive.css +281 -0
- package/lib/public/css/home-hub.css +896 -405
- package/lib/public/css/home-mate-creation.css +34 -0
- package/lib/public/css/home-mate-model-picker.css +131 -0
- package/lib/public/css/home-mate-settings.css +344 -0
- package/lib/public/css/home-session-actions.css +265 -0
- package/lib/public/css/home-sidebar.css +667 -0
- package/lib/public/css/icon-strip.css +1 -2
- package/lib/public/css/mates.css +7 -34
- package/lib/public/css/pane.css +1 -1
- package/lib/public/css/playbook.css +0 -80
- package/lib/public/css/sticky-notes.css +53 -117
- package/lib/public/css/tui-attention.css +0 -44
- package/lib/public/css/workspace-assignment.css +45 -0
- package/lib/public/fonts/source-serif-4/LICENSE.md +93 -0
- package/lib/public/fonts/source-serif-4/SourceSerif4Caption-Semibold.ttf.woff2 +0 -0
- package/lib/public/index.html +93 -74
- package/lib/public/modules/app-connection.js +10 -1
- package/lib/public/modules/app-dm.js +43 -160
- package/lib/public/modules/app-home-hub.js +328 -585
- package/lib/public/modules/app-message-cards.js +242 -0
- package/lib/public/modules/app-message-router.js +84 -0
- package/lib/public/modules/app-messages.js +102 -37
- package/lib/public/modules/app-notifications.js +8 -119
- package/lib/public/modules/app-projects.js +9 -1
- package/lib/public/modules/app-rendering.js +21 -602
- package/lib/public/modules/avatar-imprint.js +11 -8
- package/lib/public/modules/chat-bubble-renderer.js +152 -0
- package/lib/public/modules/chat-render-runtime.js +198 -0
- package/lib/public/modules/command-palette.js +225 -436
- package/lib/public/modules/dm-render.js +82 -0
- package/lib/public/modules/home-capsule-creation-intent.js +31 -0
- package/lib/public/modules/home-capsule-library.js +146 -0
- package/lib/public/modules/home-capsule-source.js +383 -0
- package/lib/public/modules/home-chat-identity.js +35 -0
- package/lib/public/modules/home-chat-scroll.js +77 -0
- package/lib/public/modules/home-chat-stream-state.js +34 -0
- package/lib/public/modules/home-composer-focus.js +25 -0
- package/lib/public/modules/home-conversations-sheet.js +179 -0
- package/lib/public/modules/home-debate-activity.js +48 -0
- package/lib/public/modules/home-debate-controls.js +295 -0
- package/lib/public/modules/home-debate-launch.js +41 -0
- package/lib/public/modules/home-debate-live.js +284 -0
- package/lib/public/modules/home-debate-models.js +73 -0
- package/lib/public/modules/home-debate-planning.js +335 -0
- package/lib/public/modules/home-debates-archive.js +279 -0
- package/lib/public/modules/home-dock-resize.js +74 -0
- package/lib/public/modules/home-dock.js +446 -0
- package/lib/public/modules/home-mate-chat.js +496 -0
- package/lib/public/modules/home-mate-creation.js +109 -0
- package/lib/public/modules/home-mate-model-picker.js +281 -0
- package/lib/public/modules/home-mate-selection.js +38 -0
- package/lib/public/modules/home-mate-settings-menu.js +131 -0
- package/lib/public/modules/home-mate-settings.js +383 -0
- package/lib/public/modules/home-session-actions.js +373 -0
- package/lib/public/modules/home-session-model-confirmation.js +13 -0
- package/lib/public/modules/home-shell.js +9 -0
- package/lib/public/modules/home-sidebar-chat-list.js +63 -0
- package/lib/public/modules/home-sidebar.js +158 -0
- package/lib/public/modules/home-sub-surface.js +19 -0
- package/lib/public/modules/home-surface-boot.js +68 -0
- package/lib/public/modules/home-surface.js +141 -0
- package/lib/public/modules/home-tools.js +271 -0
- package/lib/public/modules/markdown.js +50 -2
- package/lib/public/modules/mate-management.js +86 -0
- package/lib/public/modules/mate-wizard.js +0 -7
- package/lib/public/modules/mention.js +17 -19
- package/lib/public/modules/pane-bridge.js +9 -0
- package/lib/public/modules/profile.js +4 -5
- package/lib/public/modules/project-settings.js +34 -28
- package/lib/public/modules/project-switcher.js +11 -5
- package/lib/public/modules/project-workspace-assignment.js +23 -0
- package/lib/public/modules/search-clay-chat.js +402 -0
- package/lib/public/modules/server-settings.js +14 -12
- package/lib/public/modules/session-actions.js +1 -1
- package/lib/public/modules/sidebar-mates.js +56 -552
- package/lib/public/modules/sidebar-mobile.js +7 -64
- package/lib/public/modules/sidebar-presence.js +37 -0
- package/lib/public/modules/split-pair-ui.js +13 -13
- package/lib/public/modules/split-view.js +7 -0
- package/lib/public/modules/sticky-notes.js +7 -6
- package/lib/public/modules/tool-input-composition.js +48 -0
- package/lib/public/modules/tool-llm-status.js +167 -0
- package/lib/public/modules/tool-renderer-advanced.js +285 -0
- package/lib/public/modules/tool-renderer-chart.js +158 -0
- package/lib/public/modules/tool-renderer-semantics.js +35 -0
- package/lib/public/modules/tool-renderer.js +464 -0
- package/lib/public/modules/tool-runtime.js +309 -0
- package/lib/public/modules/tool-ui-evaluator.js +97 -0
- package/lib/public/modules/tool-ui-tree.js +21 -0
- package/lib/public/modules/tui-attention.js +10 -0
- package/lib/public/modules/user-settings.js +16 -23
- package/lib/public/modules/vendor-login.js +287 -0
- package/lib/public/modules/worker-proposal.js +2 -2
- package/lib/public/modules/workspace-assignment-card.js +149 -0
- package/lib/public/modules/workspace-assignment-routing.js +4 -0
- package/lib/public/style.css +13 -2
- package/lib/runtime-env.js +65 -0
- package/lib/sdk-bridge.js +120 -32
- package/lib/sdk-message-processor.js +10 -6
- package/lib/server-home-capsule-creation.js +36 -0
- package/lib/server-home-chat-events.js +287 -0
- package/lib/server-home-chat.js +518 -0
- package/lib/server-home-clay-entry.js +65 -0
- package/lib/server-home-clay-session-links.js +26 -0
- package/lib/server-home-debate-planning.js +214 -0
- package/lib/server-home-debates.js +137 -0
- package/lib/server-home-mate-creation.js +261 -0
- package/lib/server-home-models.js +241 -0
- package/lib/server-home-preferences.js +61 -0
- package/lib/server-mates.js +37 -0
- package/lib/server-tools.js +377 -0
- package/lib/server.js +111 -59
- package/lib/session-pair-mcp-server.js +6 -6
- package/lib/sessions.js +18 -3
- package/lib/tool-capsule-source.js +142 -0
- package/lib/tool-control-mcp-server.js +126 -0
- package/lib/tool-llm.js +48 -0
- package/lib/tool-storage.js +77 -0
- package/lib/tool-ui-spec-advanced.js +82 -0
- package/lib/tool-ui-spec.js +261 -0
- package/lib/tools-registry.js +286 -0
- package/lib/users-home-dock-preferences.js +77 -0
- package/lib/users-home-surface-preferences.js +99 -0
- package/lib/users-preferences.js +4 -7
- package/lib/users.js +12 -0
- package/lib/whats-new-content.js +3 -3
- package/lib/workspace-assignment-service.js +420 -0
- package/lib/workspace-query-mcp-server.js +154 -0
- package/lib/workspace-query-service.js +492 -0
- package/lib/ws-schema.js +93 -12
- package/lib/yoke/adapters/acp.js +2 -1
- package/lib/yoke/adapters/antigravity.js +2 -1
- package/lib/yoke/adapters/claude-worker.js +11 -4
- package/lib/yoke/adapters/claude.js +73 -43
- package/lib/yoke/adapters/codex.js +40 -10
- package/lib/yoke/adapters/kiro.js +2 -1
- package/lib/yoke/claude-user-input.js +38 -0
- package/lib/yoke/codex-app-server.js +20 -2
- package/lib/yoke/codex-user-input.js +64 -0
- package/lib/yoke/complete-once.js +164 -0
- package/lib/yoke/index.js +5 -1
- package/lib/yoke/interface.js +19 -0
- package/lib/yoke/mcp-bridge-server.js +2 -6
- package/lib/yoke/user-input.js +254 -0
- package/package.json +3 -2
- package/lib/public/css/home-chat.css +0 -380
- package/lib/public/modules/home-chat.js +0 -438
- 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
|
|
8
|
-
var
|
|
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 =
|
|
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
|
+
}
|