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
|
@@ -2,317 +2,36 @@
|
|
|
2
2
|
// Extracted from app.js (PR-28)
|
|
3
3
|
|
|
4
4
|
import { store } from './store.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { renderMarkdown, highlightCodeBlocks, renderMermaidBlocks } from './markdown.js';
|
|
9
|
-
import { iconHtml, refreshIcons } from './icons.js';
|
|
10
|
-
import { userAvatarUrl } from './avatar.js';
|
|
5
|
+
import { escapeHtml } from './utils.js';
|
|
6
|
+
import { createAssistantBubble, renderAssistantBubbleText, enhanceAssistantBubble, addAssistantCopyHandler, finalizeAssistantBubble } from './chat-bubble-renderer.js';
|
|
7
|
+
import { refreshIcons } from './icons.js';
|
|
11
8
|
import { closeToolGroup } from './tools.js';
|
|
12
|
-
import { showImageModal, showPasteModal } from './app-misc.js';
|
|
13
9
|
import { sendMessage, hasSendableContent } from './input.js';
|
|
14
10
|
import { getChatLayout } from './theme.js';
|
|
15
|
-
import {
|
|
11
|
+
import { addToMessages, scrollToBottom, forceScrollToBottom, getMsgTime, shouldGroupMessage, getTurnCounter, getActivityEl, VENDOR_AVATARS, VENDOR_NAMES } from './chat-render-runtime.js';
|
|
16
12
|
export { VENDOR_ORDER, EXPERIMENTAL_VENDORS, isExperimentalVendor } from './vendor-priority.js';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// browser cannot import the server's CommonJS registry directly.
|
|
20
|
-
export var VENDOR_AVATARS = {
|
|
21
|
-
claude: "/claude-code-avatar.png",
|
|
22
|
-
codex: "/codex-avatar.png",
|
|
23
|
-
antigravity: "/antigravity-avatar.png",
|
|
24
|
-
opencode: "/opencode-avatar.svg",
|
|
25
|
-
kimi: "/kimi-avatar.svg",
|
|
26
|
-
grok: "/grok-avatar.svg",
|
|
27
|
-
copilot: "/copilot-avatar.svg",
|
|
28
|
-
qwen: "/qwen-avatar.svg",
|
|
29
|
-
junie: "/junie-avatar.svg",
|
|
30
|
-
kiro: "/kiro-avatar.svg",
|
|
31
|
-
};
|
|
32
|
-
export var VENDOR_NAMES = {
|
|
33
|
-
claude: "Claude Code",
|
|
34
|
-
codex: "Codex",
|
|
35
|
-
antigravity: "Antigravity CLI",
|
|
36
|
-
opencode: "OpenCode",
|
|
37
|
-
kimi: "Kimi Code",
|
|
38
|
-
grok: "Grok Build",
|
|
39
|
-
copilot: "GitHub Copilot CLI",
|
|
40
|
-
qwen: "Qwen Code",
|
|
41
|
-
junie: "Junie CLI",
|
|
42
|
-
kiro: "Kiro CLI",
|
|
43
|
-
};
|
|
44
|
-
// Display order for every vendor Clay knows about, installed or not. Pickers
|
|
45
|
-
// render the full list so a missing CLI reads as "not installed yet" rather
|
|
46
|
-
// than "Clay doesn't support it".
|
|
47
|
-
// Where to send the user when they pick a vendor whose CLI isn't installed.
|
|
48
|
-
export var VENDOR_HOMEPAGES = {
|
|
49
|
-
claude: "https://claude.com/product/claude-code",
|
|
50
|
-
codex: "https://openai.com/codex/",
|
|
51
|
-
antigravity: "https://antigravity.google/product/antigravity-cli",
|
|
52
|
-
opencode: "https://opencode.ai/",
|
|
53
|
-
kimi: "https://www.kimi.com/code/",
|
|
54
|
-
grok: "https://docs.x.ai/build/overview",
|
|
55
|
-
copilot: "https://github.com/features/copilot/cli",
|
|
56
|
-
qwen: "https://qwenlm.github.io/qwen-code-docs/",
|
|
57
|
-
junie: "https://junie.jetbrains.com/",
|
|
58
|
-
kiro: "https://kiro.dev/",
|
|
59
|
-
};
|
|
60
|
-
var NEW_MSG_BTN_DEFAULT = "\u2193 Latest";
|
|
61
|
-
var NEW_MSG_BTN_ACTIVITY = "\u2193 New activity";
|
|
13
|
+
export { initRendering, addToMessages, scrollToBottom, forceScrollToBottom, getMsgTime, shouldGroupMessage, getTurnCounter, setTurnCounter, getPrependAnchor, setPrependAnchor, getActivityEl, setActivityEl, getIsUserScrolledUp, setIsUserScrolledUp, getStickyBottom, armStickyBottom, disarmStickyBottom, VENDOR_AVATARS, VENDOR_NAMES, VENDOR_HOMEPAGES } from './chat-render-runtime.js';
|
|
14
|
+
export { addUserMessage, addSystemMessage, addConflictMessage, addContextOverflowMessage } from './app-message-cards.js';
|
|
62
15
|
|
|
63
16
|
// --- Module-owned state (not in store) ---
|
|
64
|
-
var turnCounter = 0;
|
|
65
|
-
var prependAnchor = null;
|
|
66
|
-
var activityEl = null;
|
|
67
17
|
var matePreThinkingTimer = null;
|
|
68
18
|
var highlightTimer = null;
|
|
69
19
|
var streamBuffer = "";
|
|
70
20
|
var streamDrainTimer = null;
|
|
71
|
-
var isUserScrolledUp = false;
|
|
72
|
-
var scrollThreshold = 150;
|
|
73
|
-
|
|
74
|
-
// --- Sticky-bottom mode ---
|
|
75
|
-
// While armed, a ResizeObserver re-pins #messages to scrollHeight on every
|
|
76
|
-
// height change so deferred content (tools, syntax highlighting, images,
|
|
77
|
-
// IntersectionObserver-driven reflows) doesn't strand the user mid-page.
|
|
78
|
-
// The scroll listener in app.js consults getStickyBottom() and ignores
|
|
79
|
-
// growth-induced scroll events while armed.
|
|
80
|
-
//
|
|
81
|
-
// Disarm rules:
|
|
82
|
-
// - Real user input (wheel / touchmove / PageUp / Home / ArrowUp): immediate.
|
|
83
|
-
// - Quiet detector: armStickyBottom(durationMs) treats durationMs as the
|
|
84
|
-
// QUIET WINDOW, not a hard timer. Each ResizeObserver callback resets
|
|
85
|
-
// a debounce timer; sticky-bottom only disarms after no resize for
|
|
86
|
-
// durationMs. Long-settling sessions (large todo widgets, slow code
|
|
87
|
-
// highlighting) keep extending the window naturally.
|
|
88
|
-
// - Hard ceiling: a separate cap prevents pathological lock-in.
|
|
89
|
-
var stickyBottom = false;
|
|
90
|
-
var stickyBottomQuietTimer = null;
|
|
91
|
-
var stickyBottomCeilingTimer = null;
|
|
92
|
-
var stickyBottomQuietMs = 750;
|
|
93
|
-
var stickyBottomCeilingMs = 8000;
|
|
94
|
-
var stickyBottomResizeObs = null;
|
|
95
|
-
var stickyBottomInputBound = false;
|
|
96
|
-
|
|
97
|
-
export function getStickyBottom() { return stickyBottom; }
|
|
98
|
-
|
|
99
|
-
function pinToBottomNow() {
|
|
100
|
-
var messagesEl = getMessagesEl();
|
|
101
|
-
if (!messagesEl) return;
|
|
102
|
-
messagesEl.scrollTop = messagesEl.scrollHeight;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function ensureStickyInfrastructure() {
|
|
106
|
-
var messagesEl = getMessagesEl();
|
|
107
|
-
if (!messagesEl) return;
|
|
108
|
-
if (!stickyBottomResizeObs && typeof ResizeObserver !== "undefined") {
|
|
109
|
-
stickyBottomResizeObs = new ResizeObserver(function () {
|
|
110
|
-
if (!stickyBottom) return;
|
|
111
|
-
// Re-pin on every layout change while armed.
|
|
112
|
-
pinToBottomNow();
|
|
113
|
-
// Reset the quiet timer — settling has not finished yet.
|
|
114
|
-
if (stickyBottomQuietTimer) clearTimeout(stickyBottomQuietTimer);
|
|
115
|
-
stickyBottomQuietTimer = setTimeout(disarmStickyBottom, stickyBottomQuietMs);
|
|
116
|
-
});
|
|
117
|
-
stickyBottomResizeObs.observe(messagesEl);
|
|
118
|
-
// Also observe direct children so child-size changes (image loads, code
|
|
119
|
-
// block highlighting, expanding tool groups) trigger a re-pin even when
|
|
120
|
-
// they don't change the scroller's own size.
|
|
121
|
-
var kids = messagesEl.children;
|
|
122
|
-
for (var i = 0; i < kids.length; i++) stickyBottomResizeObs.observe(kids[i]);
|
|
123
|
-
}
|
|
124
|
-
if (!stickyBottomInputBound) {
|
|
125
|
-
stickyBottomInputBound = true;
|
|
126
|
-
var disarmOnUserScroll = function () { disarmStickyBottom(); };
|
|
127
|
-
messagesEl.addEventListener("wheel", disarmOnUserScroll, { passive: true });
|
|
128
|
-
messagesEl.addEventListener("touchmove", disarmOnUserScroll, { passive: true });
|
|
129
|
-
document.addEventListener("keydown", function (e) {
|
|
130
|
-
if (!stickyBottom) return;
|
|
131
|
-
if (e.key === "PageUp" || e.key === "Home" || e.key === "ArrowUp") {
|
|
132
|
-
disarmStickyBottom();
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export function armStickyBottom(durationMs) {
|
|
139
|
-
if (prependAnchor) return; // never fight pagination
|
|
140
|
-
ensureStickyInfrastructure();
|
|
141
|
-
stickyBottom = true;
|
|
142
|
-
isUserScrolledUp = false;
|
|
143
|
-
var newMsgBtn = document.getElementById("new-msg-btn");
|
|
144
|
-
if (newMsgBtn) {
|
|
145
|
-
newMsgBtn.classList.add("hidden");
|
|
146
|
-
newMsgBtn.textContent = NEW_MSG_BTN_DEFAULT;
|
|
147
|
-
}
|
|
148
|
-
pinToBottomNow();
|
|
149
|
-
// After children may have been replaced since last arm, re-observe.
|
|
150
|
-
if (stickyBottomResizeObs) {
|
|
151
|
-
var messagesEl = getMessagesEl();
|
|
152
|
-
if (messagesEl) {
|
|
153
|
-
var kids = messagesEl.children;
|
|
154
|
-
for (var i = 0; i < kids.length; i++) {
|
|
155
|
-
try { stickyBottomResizeObs.observe(kids[i]); } catch (e) {}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
// Quiet window: callers pass intended quiet duration; ResizeObserver
|
|
160
|
-
// resets this each time layout changes, so the actual armed duration
|
|
161
|
-
// stretches to "no resize for durationMs".
|
|
162
|
-
stickyBottomQuietMs = durationMs || 750;
|
|
163
|
-
if (stickyBottomQuietTimer) clearTimeout(stickyBottomQuietTimer);
|
|
164
|
-
stickyBottomQuietTimer = setTimeout(disarmStickyBottom, stickyBottomQuietMs);
|
|
165
|
-
// Hard ceiling so we never lock the scroller indefinitely if some
|
|
166
|
-
// animation/observer keeps firing forever.
|
|
167
|
-
if (stickyBottomCeilingTimer) clearTimeout(stickyBottomCeilingTimer);
|
|
168
|
-
stickyBottomCeilingTimer = setTimeout(disarmStickyBottom, stickyBottomCeilingMs);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export function disarmStickyBottom() {
|
|
172
|
-
stickyBottom = false;
|
|
173
|
-
if (stickyBottomQuietTimer) { clearTimeout(stickyBottomQuietTimer); stickyBottomQuietTimer = null; }
|
|
174
|
-
if (stickyBottomCeilingTimer) { clearTimeout(stickyBottomCeilingTimer); stickyBottomCeilingTimer = null; }
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
export function initRendering() {
|
|
178
|
-
// Update input placeholder when vendor changes
|
|
179
|
-
store.subscribe(function (state, prev) {
|
|
180
|
-
if (state.currentVendor !== prev.currentVendor) {
|
|
181
|
-
var inputEl = document.getElementById("input");
|
|
182
|
-
if (inputEl) {
|
|
183
|
-
inputEl.placeholder = "Message " + (VENDOR_NAMES[state.currentVendor] || VENDOR_NAMES.claude) + "...";
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// --- State accessors (module-local, not in store) ---
|
|
190
|
-
export function getTurnCounter() { return turnCounter; }
|
|
191
|
-
export function setTurnCounter(v) { turnCounter = v; }
|
|
192
|
-
export function getPrependAnchor() { return prependAnchor; }
|
|
193
|
-
export function setPrependAnchor(v) { prependAnchor = v; }
|
|
194
|
-
export function getActivityEl() { return activityEl; }
|
|
195
|
-
export function setActivityEl(v) { activityEl = v; }
|
|
196
|
-
export function getIsUserScrolledUp() { return isUserScrolledUp; }
|
|
197
|
-
export function setIsUserScrolledUp(v) { isUserScrolledUp = v; }
|
|
198
|
-
|
|
199
|
-
// --- Rendering functions ---
|
|
200
|
-
|
|
201
|
-
export function addToMessages(el) {
|
|
202
|
-
var messagesEl = getMessagesEl();
|
|
203
|
-
if (prependAnchor) messagesEl.insertBefore(el, prependAnchor);
|
|
204
|
-
else messagesEl.appendChild(el);
|
|
205
|
-
var _sme = getScheduledMsgEl();
|
|
206
|
-
if (_sme && el !== _sme && _sme.parentNode === messagesEl) {
|
|
207
|
-
messagesEl.appendChild(_sme);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
export function scrollToBottom() {
|
|
212
|
-
if (prependAnchor) return;
|
|
213
|
-
var messagesEl = getMessagesEl();
|
|
214
|
-
if (!messagesEl) return;
|
|
215
|
-
// Compute the user's current scroll position from the DOM rather than
|
|
216
|
-
// the cached isUserScrolledUp flag. The flag is updated by an async
|
|
217
|
-
// scroll-event listener; if a delta arrives between the user's wheel
|
|
218
|
-
// input and that listener firing, the cached flag is still false and
|
|
219
|
-
// we'd snap them back to the bottom against their intent. Reading
|
|
220
|
-
// scrollTop/scrollHeight here is synchronous and race-free.
|
|
221
|
-
var distFromBottom = messagesEl.scrollHeight - messagesEl.scrollTop - messagesEl.clientHeight;
|
|
222
|
-
if (distFromBottom > 150 || isUserScrolledUp) {
|
|
223
|
-
// Keep the flag and the "New activity" button in sync even when the
|
|
224
|
-
// scroll-listener hasn't fired yet, so other call sites observing
|
|
225
|
-
// isUserScrolledUp see the truth too.
|
|
226
|
-
if (distFromBottom > 150) isUserScrolledUp = true;
|
|
227
|
-
var newMsgBtn = document.getElementById("new-msg-btn");
|
|
228
|
-
if (newMsgBtn) {
|
|
229
|
-
newMsgBtn.textContent = NEW_MSG_BTN_ACTIVITY;
|
|
230
|
-
newMsgBtn.classList.remove("hidden");
|
|
231
|
-
}
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
requestAnimationFrame(function () {
|
|
235
|
-
// Re-check just before the actual write — the user may have scrolled
|
|
236
|
-
// up during the frame between the synchronous gate above and this rAF
|
|
237
|
-
// firing. Without this re-check the snap-back race re-emerges at a
|
|
238
|
-
// single-frame granularity.
|
|
239
|
-
var dist = messagesEl.scrollHeight - messagesEl.scrollTop - messagesEl.clientHeight;
|
|
240
|
-
if (dist > 150) {
|
|
241
|
-
isUserScrolledUp = true;
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
messagesEl.scrollTop = messagesEl.scrollHeight;
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
export function forceScrollToBottom() {
|
|
249
|
-
if (prependAnchor) return;
|
|
250
|
-
// Arm sticky-bottom mode so deferred layout (tool widgets, code highlighting,
|
|
251
|
-
// image loads) can't strand the user partway down — single-rAF pin captures
|
|
252
|
-
// a stale scrollHeight, then growth below pushes the bottom further away.
|
|
253
|
-
// The quiet detector extends the window automatically while layout shifts.
|
|
254
|
-
armStickyBottom(750);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
export function getMsgTime() {
|
|
258
|
-
var _ts = store.get('currentMsgTs');
|
|
259
|
-
var d = _ts ? new Date(_ts) : new Date();
|
|
260
|
-
var time = String(d.getHours()).padStart(2, "0") + ":" + String(d.getMinutes()).padStart(2, "0");
|
|
261
|
-
var now = new Date();
|
|
262
|
-
if (d.getFullYear() === now.getFullYear() && d.getMonth() === now.getMonth() && d.getDate() === now.getDate()) {
|
|
263
|
-
return time;
|
|
264
|
-
}
|
|
265
|
-
return (d.getMonth() + 1) + "/" + d.getDate() + " " + time;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
export function shouldGroupMessage(senderClass) {
|
|
269
|
-
var _s = store.snap();
|
|
270
|
-
if (_s.replayingHistory && !_s.currentMsgTs) return false;
|
|
271
|
-
var prev = getMessagesEl().lastElementChild;
|
|
272
|
-
if (!prev || !prev.classList.contains(senderClass)) return false;
|
|
273
|
-
var prevTime = prev.querySelector(".dm-bubble-time");
|
|
274
|
-
if (!prevTime) return false;
|
|
275
|
-
return prevTime.textContent === getMsgTime();
|
|
276
|
-
}
|
|
277
21
|
|
|
278
22
|
export function ensureAssistantBlock() {
|
|
279
23
|
var _el = store.get('currentMsgEl');
|
|
280
24
|
if (!_el) {
|
|
281
|
-
_el = document.createElement("div");
|
|
282
|
-
_el.className = "msg-assistant";
|
|
283
|
-
_el.dataset.turn = turnCounter;
|
|
284
|
-
|
|
285
|
-
var grouped = shouldGroupMessage("msg-assistant");
|
|
286
|
-
if (grouped) _el.classList.add("grouped");
|
|
287
|
-
|
|
288
25
|
var _isDm2 = document.body.classList.contains("mate-dm-active") && document.body.dataset.mateAvatarUrl;
|
|
289
26
|
var vendor = store.get('currentVendor') || "claude";
|
|
290
|
-
var avi = document.createElement("img");
|
|
291
|
-
avi.className = "dm-bubble-avatar dm-bubble-avatar-mate";
|
|
292
|
-
avi.src = _isDm2 ? document.body.dataset.mateAvatarUrl : (VENDOR_AVATARS[vendor] || VENDOR_AVATARS.claude);
|
|
293
|
-
_el.appendChild(avi);
|
|
294
|
-
|
|
295
|
-
var contentWrap = document.createElement("div");
|
|
296
|
-
contentWrap.className = "dm-bubble-content";
|
|
297
|
-
|
|
298
|
-
var header = document.createElement("div");
|
|
299
|
-
header.className = "dm-bubble-header";
|
|
300
|
-
var nameSpan = document.createElement("span");
|
|
301
|
-
nameSpan.className = "dm-bubble-name";
|
|
302
27
|
var dmTarget = store.get('dmTargetUser');
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
var mdDiv = document.createElement("div");
|
|
312
|
-
mdDiv.className = "md-content";
|
|
313
|
-
mdDiv.dir = "auto";
|
|
314
|
-
contentWrap.appendChild(mdDiv);
|
|
315
|
-
_el.appendChild(contentWrap);
|
|
28
|
+
_el = createAssistantBubble({
|
|
29
|
+
turn: getTurnCounter(),
|
|
30
|
+
avatarUrl: _isDm2 ? document.body.dataset.mateAvatarUrl : (VENDOR_AVATARS[vendor] || VENDOR_AVATARS.claude),
|
|
31
|
+
name: _isDm2 ? ((dmTarget && dmTarget.displayName) || "Mate") : (VENDOR_NAMES[vendor] || VENDOR_NAMES.claude),
|
|
32
|
+
time: getMsgTime(),
|
|
33
|
+
});
|
|
34
|
+
if (shouldGroupMessage("msg-assistant")) _el.classList.add("grouped");
|
|
316
35
|
addToMessages(_el);
|
|
317
36
|
store.set({ currentMsgEl: _el, currentFullText: "" });
|
|
318
37
|
}
|
|
@@ -320,47 +39,7 @@ export function ensureAssistantBlock() {
|
|
|
320
39
|
}
|
|
321
40
|
|
|
322
41
|
export function addCopyHandler(msgEl, rawText) {
|
|
323
|
-
|
|
324
|
-
var resetTimer = null;
|
|
325
|
-
|
|
326
|
-
var isTouchDevice = "ontouchstart" in window;
|
|
327
|
-
|
|
328
|
-
var hint = document.createElement("div");
|
|
329
|
-
hint.className = "msg-copy-hint";
|
|
330
|
-
hint.textContent = (isTouchDevice ? "Tap" : "Click") + " to grab this";
|
|
331
|
-
msgEl.appendChild(hint);
|
|
332
|
-
|
|
333
|
-
function reset() {
|
|
334
|
-
primed = false;
|
|
335
|
-
msgEl.classList.remove("copy-primed", "copy-done");
|
|
336
|
-
hint.textContent = (isTouchDevice ? "Tap" : "Click") + " to grab this";
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
msgEl.addEventListener("click", function (e) {
|
|
340
|
-
if (e.target.closest("a, pre, code")) return;
|
|
341
|
-
var sel = window.getSelection();
|
|
342
|
-
if (sel && sel.toString().length > 0) return;
|
|
343
|
-
|
|
344
|
-
if (!primed) {
|
|
345
|
-
primed = true;
|
|
346
|
-
msgEl.classList.add("copy-primed");
|
|
347
|
-
hint.textContent = isTouchDevice ? "Tap again to grab" : "Click again to grab";
|
|
348
|
-
clearTimeout(resetTimer);
|
|
349
|
-
resetTimer = setTimeout(reset, 3000);
|
|
350
|
-
} else {
|
|
351
|
-
clearTimeout(resetTimer);
|
|
352
|
-
copyToClipboard(rawText).then(function () {
|
|
353
|
-
msgEl.classList.remove("copy-primed");
|
|
354
|
-
msgEl.classList.add("copy-done");
|
|
355
|
-
hint.textContent = "Grabbed!";
|
|
356
|
-
resetTimer = setTimeout(reset, 1500);
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
document.addEventListener("click", function (e) {
|
|
362
|
-
if (primed && !msgEl.contains(e.target)) reset();
|
|
363
|
-
});
|
|
42
|
+
addAssistantCopyHandler(msgEl, rawText);
|
|
364
43
|
}
|
|
365
44
|
|
|
366
45
|
export function appendDelta(text) {
|
|
@@ -389,12 +68,11 @@ function drainStreamTick() {
|
|
|
389
68
|
var newText = _s.currentFullText + chunk;
|
|
390
69
|
store.set({ currentFullText: newText });
|
|
391
70
|
|
|
392
|
-
|
|
393
|
-
contentEl.innerHTML = renderMarkdown(newText);
|
|
71
|
+
renderAssistantBubbleText(_s.currentMsgEl, newText, false);
|
|
394
72
|
|
|
395
73
|
if (highlightTimer) clearTimeout(highlightTimer);
|
|
396
74
|
highlightTimer = setTimeout(function () {
|
|
397
|
-
|
|
75
|
+
enhanceAssistantBubble(_s.currentMsgEl, false);
|
|
398
76
|
}, 150);
|
|
399
77
|
|
|
400
78
|
scrollToBottom();
|
|
@@ -414,8 +92,7 @@ export function flushStreamBuffer() {
|
|
|
414
92
|
if (_s.currentMsgEl) {
|
|
415
93
|
var contentEl = _s.currentMsgEl.querySelector(".md-content");
|
|
416
94
|
if (contentEl) {
|
|
417
|
-
|
|
418
|
-
highlightCodeBlocks(contentEl);
|
|
95
|
+
renderAssistantBubbleText(_s.currentMsgEl, _s.currentFullText, true);
|
|
419
96
|
}
|
|
420
97
|
}
|
|
421
98
|
}
|
|
@@ -424,271 +101,12 @@ export function finalizeAssistantBlock() {
|
|
|
424
101
|
flushStreamBuffer();
|
|
425
102
|
var _s = store.snap();
|
|
426
103
|
if (_s.currentMsgEl) {
|
|
427
|
-
|
|
428
|
-
if (contentEl) {
|
|
429
|
-
highlightCodeBlocks(contentEl);
|
|
430
|
-
renderMermaidBlocks(contentEl);
|
|
431
|
-
}
|
|
432
|
-
if (_s.currentFullText) {
|
|
433
|
-
addCopyHandler(_s.currentMsgEl, _s.currentFullText);
|
|
434
|
-
}
|
|
104
|
+
finalizeAssistantBubble(_s.currentMsgEl, _s.currentFullText, !!_s.currentFullText);
|
|
435
105
|
closeToolGroup();
|
|
436
106
|
}
|
|
437
107
|
store.set({ currentMsgEl: null, currentFullText: "" });
|
|
438
108
|
}
|
|
439
109
|
|
|
440
|
-
export function addUserMessage(text, images, pastes, fromUserId, fromUserName, delegatedMeta) {
|
|
441
|
-
if (!text && (!images || images.length === 0) && (!pastes || pastes.length === 0)) return;
|
|
442
|
-
var myUserId = store.get('myUserId');
|
|
443
|
-
var isDelegated = !!delegatedMeta;
|
|
444
|
-
var isOtherUser = isDelegated || (fromUserId && fromUserId !== myUserId);
|
|
445
|
-
var div = document.createElement("div");
|
|
446
|
-
div.className = "msg-user" + (isOtherUser ? " msg-user-other" : "");
|
|
447
|
-
if (isDelegated) div.classList.add("msg-user-delegated");
|
|
448
|
-
div.dataset.turn = ++turnCounter;
|
|
449
|
-
if (shouldGroupMessage("msg-user")) div.classList.add("grouped");
|
|
450
|
-
var bubble = document.createElement("div");
|
|
451
|
-
bubble.className = "bubble";
|
|
452
|
-
bubble.dir = "auto";
|
|
453
|
-
|
|
454
|
-
// Delegated work orders render as a self-contained card: identity lives
|
|
455
|
-
// in an in-card header strip (the generic bubble avatar/header are hidden
|
|
456
|
-
// via CSS) and the body is clean markdown on a neutral background.
|
|
457
|
-
if (isDelegated) {
|
|
458
|
-
var cardHead = document.createElement("div");
|
|
459
|
-
cardHead.className = "delegated-card-head";
|
|
460
|
-
var cardAvi = document.createElement("img");
|
|
461
|
-
cardAvi.className = "delegated-card-avatar";
|
|
462
|
-
cardAvi.src = VENDOR_AVATARS[delegatedMeta.delegatedByVendor] || VENDOR_AVATARS.claude;
|
|
463
|
-
cardAvi.alt = "";
|
|
464
|
-
cardHead.appendChild(cardAvi);
|
|
465
|
-
var cardHeadText = document.createElement("div");
|
|
466
|
-
cardHeadText.className = "delegated-card-headtext";
|
|
467
|
-
var eyebrow = document.createElement("span");
|
|
468
|
-
eyebrow.className = "delegated-card-eyebrow";
|
|
469
|
-
eyebrow.textContent = "Delegated task";
|
|
470
|
-
cardHeadText.appendChild(eyebrow);
|
|
471
|
-
var cardFrom = document.createElement("span");
|
|
472
|
-
cardFrom.className = "delegated-card-from";
|
|
473
|
-
cardFrom.textContent = delegatedMeta.delegatedByTitle || "Driver";
|
|
474
|
-
cardHeadText.appendChild(cardFrom);
|
|
475
|
-
cardHead.appendChild(cardHeadText);
|
|
476
|
-
var cardTime = document.createElement("span");
|
|
477
|
-
cardTime.className = "delegated-card-time";
|
|
478
|
-
cardTime.textContent = getMsgTime();
|
|
479
|
-
cardHead.appendChild(cardTime);
|
|
480
|
-
bubble.appendChild(cardHead);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
if (images && images.length > 0) {
|
|
484
|
-
var imgRow = document.createElement("div");
|
|
485
|
-
imgRow.className = "bubble-images";
|
|
486
|
-
for (var i = 0; i < images.length; i++) {
|
|
487
|
-
var img = document.createElement("img");
|
|
488
|
-
if (images[i].url) {
|
|
489
|
-
img.src = images[i].url;
|
|
490
|
-
} else if (images[i].data) {
|
|
491
|
-
img.src = "data:" + images[i].mediaType + ";base64," + images[i].data;
|
|
492
|
-
}
|
|
493
|
-
img.loading = "lazy";
|
|
494
|
-
img.className = "bubble-img";
|
|
495
|
-
img.addEventListener("click", function () { showImageModal(this.src); });
|
|
496
|
-
img.addEventListener("error", function () {
|
|
497
|
-
var placeholder = document.createElement("div");
|
|
498
|
-
placeholder.className = "bubble-img-expired";
|
|
499
|
-
placeholder.textContent = "Image deleted";
|
|
500
|
-
this.parentNode.replaceChild(placeholder, this);
|
|
501
|
-
});
|
|
502
|
-
imgRow.appendChild(img);
|
|
503
|
-
}
|
|
504
|
-
bubble.appendChild(imgRow);
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
if (pastes && pastes.length > 0) {
|
|
508
|
-
var pasteRow = document.createElement("div");
|
|
509
|
-
pasteRow.className = "bubble-pastes";
|
|
510
|
-
for (var p = 0; p < pastes.length; p++) {
|
|
511
|
-
(function (pasteText) {
|
|
512
|
-
var chip = document.createElement("div");
|
|
513
|
-
chip.className = "bubble-paste";
|
|
514
|
-
var preview = pasteText.substring(0, 60).replace(/\n/g, " ");
|
|
515
|
-
if (pasteText.length > 60) preview += "...";
|
|
516
|
-
chip.innerHTML = '<span class="bubble-paste-preview">' + escapeHtml(preview) + '</span><span class="bubble-paste-label">PASTED</span>';
|
|
517
|
-
chip.addEventListener("click", function (e) {
|
|
518
|
-
e.stopPropagation();
|
|
519
|
-
showPasteModal(pasteText);
|
|
520
|
-
});
|
|
521
|
-
pasteRow.appendChild(chip);
|
|
522
|
-
})(pastes[p]);
|
|
523
|
-
}
|
|
524
|
-
bubble.appendChild(pasteRow);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
if (text) {
|
|
528
|
-
if (isDelegated) {
|
|
529
|
-
// Delegated instructions are agent-authored work orders: render them
|
|
530
|
-
// as markdown (headings, lists, code) instead of a plain text blob.
|
|
531
|
-
var mdEl = document.createElement("div");
|
|
532
|
-
mdEl.className = "md-content delegated-brief";
|
|
533
|
-
mdEl.innerHTML = renderMarkdown(text);
|
|
534
|
-
highlightCodeBlocks(mdEl);
|
|
535
|
-
bubble.appendChild(mdEl);
|
|
536
|
-
} else {
|
|
537
|
-
var textEl = document.createElement("span");
|
|
538
|
-
textEl.textContent = text;
|
|
539
|
-
bubble.appendChild(textEl);
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
var cachedAllUsers = store.get('cachedAllUsers');
|
|
545
|
-
var _targetUser;
|
|
546
|
-
var _displayName;
|
|
547
|
-
if (isDelegated) {
|
|
548
|
-
_targetUser = null;
|
|
549
|
-
_displayName = "from " + (delegatedMeta.delegatedByTitle || "Driver");
|
|
550
|
-
} else if (isOtherUser) {
|
|
551
|
-
_targetUser = cachedAllUsers.find(function (u) { return u.id === fromUserId; });
|
|
552
|
-
_displayName = fromUserName || (_targetUser && (_targetUser.displayName || _targetUser.username)) || "User";
|
|
553
|
-
} else {
|
|
554
|
-
_targetUser = cachedAllUsers.find(function (u) { return u.id === myUserId; });
|
|
555
|
-
_displayName = document.body.dataset.myDisplayName || "";
|
|
556
|
-
if (!_displayName) {
|
|
557
|
-
_displayName = (_targetUser && (_targetUser.displayName || _targetUser.username)) || "Me";
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
var avi = document.createElement("img");
|
|
562
|
-
avi.className = "dm-bubble-avatar" + (isOtherUser ? " dm-bubble-avatar-other" : " dm-bubble-avatar-me");
|
|
563
|
-
avi.src = isDelegated
|
|
564
|
-
? (VENDOR_AVATARS[delegatedMeta.delegatedByVendor] || VENDOR_AVATARS.claude)
|
|
565
|
-
: isOtherUser
|
|
566
|
-
? userAvatarUrl(_targetUser || { id: fromUserId }, 36)
|
|
567
|
-
: (document.body.dataset.myAvatarUrl || userAvatarUrl(_targetUser || { id: myUserId }, 36));
|
|
568
|
-
div.appendChild(avi);
|
|
569
|
-
|
|
570
|
-
var contentWrap = document.createElement("div");
|
|
571
|
-
contentWrap.className = "dm-bubble-content";
|
|
572
|
-
|
|
573
|
-
var header = document.createElement("div");
|
|
574
|
-
header.className = "dm-bubble-header";
|
|
575
|
-
var nameSpan = document.createElement("span");
|
|
576
|
-
nameSpan.className = "dm-bubble-name";
|
|
577
|
-
nameSpan.textContent = _displayName;
|
|
578
|
-
header.appendChild(nameSpan);
|
|
579
|
-
var timeSpan = document.createElement("span");
|
|
580
|
-
timeSpan.className = "dm-bubble-time";
|
|
581
|
-
timeSpan.textContent = getMsgTime();
|
|
582
|
-
header.appendChild(timeSpan);
|
|
583
|
-
contentWrap.appendChild(header);
|
|
584
|
-
contentWrap.appendChild(bubble);
|
|
585
|
-
div.appendChild(contentWrap);
|
|
586
|
-
|
|
587
|
-
var actions = document.createElement("div");
|
|
588
|
-
actions.className = "msg-actions";
|
|
589
|
-
actions.innerHTML =
|
|
590
|
-
'<span class="msg-action-time">' + getMsgTime() + '</span>' +
|
|
591
|
-
'<button class="msg-action-btn msg-action-copy" type="button" title="Copy">' + iconHtml("copy") + '</button>' +
|
|
592
|
-
'<button class="msg-action-btn msg-action-fork" type="button" title="Fork">' + iconHtml("git-branch") + '</button>' +
|
|
593
|
-
(((store.get('vendorCapabilities') || {}).rewind !== false) ? '<button class="msg-action-btn msg-action-rewind msg-user-rewind-btn" type="button" title="Rewind">' + iconHtml("rotate-ccw") + '</button>' : '') +
|
|
594
|
-
'<button class="msg-action-btn msg-action-hidden msg-action-edit" type="button" title="Edit">' + iconHtml("pencil") + '</button>';
|
|
595
|
-
div.appendChild(actions);
|
|
596
|
-
|
|
597
|
-
actions.querySelector(".msg-action-copy").addEventListener("click", function () {
|
|
598
|
-
var self = this;
|
|
599
|
-
copyToClipboard(text || "");
|
|
600
|
-
self.innerHTML = iconHtml("check");
|
|
601
|
-
refreshIcons();
|
|
602
|
-
setTimeout(function () { self.innerHTML = iconHtml("copy"); refreshIcons(); }, 1200);
|
|
603
|
-
});
|
|
604
|
-
|
|
605
|
-
addToMessages(div);
|
|
606
|
-
refreshIcons();
|
|
607
|
-
forceScrollToBottom();
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
export function addSystemMessage(text, isError) {
|
|
611
|
-
var div = document.createElement("div");
|
|
612
|
-
div.className = "sys-msg" + (isError ? " error" : "");
|
|
613
|
-
div.innerHTML = '<span class="sys-text"></span>';
|
|
614
|
-
div.querySelector(".sys-text").textContent = text;
|
|
615
|
-
addToMessages(div);
|
|
616
|
-
scrollToBottom();
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
export function addConflictMessage(msg) {
|
|
620
|
-
var div = document.createElement("div");
|
|
621
|
-
div.className = "conflict-msg";
|
|
622
|
-
var header = document.createElement("div");
|
|
623
|
-
header.className = "conflict-header";
|
|
624
|
-
header.textContent = msg.text || "Another Claude Code process is already running.";
|
|
625
|
-
div.appendChild(header);
|
|
626
|
-
|
|
627
|
-
var hint = document.createElement("div");
|
|
628
|
-
hint.className = "conflict-hint";
|
|
629
|
-
hint.textContent = "Kill the conflicting process to continue, or use the existing Claude Code session.";
|
|
630
|
-
div.appendChild(hint);
|
|
631
|
-
|
|
632
|
-
for (var i = 0; i < msg.processes.length; i++) {
|
|
633
|
-
var p = msg.processes[i];
|
|
634
|
-
var row = document.createElement("div");
|
|
635
|
-
row.className = "conflict-process";
|
|
636
|
-
|
|
637
|
-
var info = document.createElement("span");
|
|
638
|
-
info.className = "conflict-pid";
|
|
639
|
-
info.textContent = "PID " + p.pid;
|
|
640
|
-
row.appendChild(info);
|
|
641
|
-
|
|
642
|
-
var cmd = document.createElement("code");
|
|
643
|
-
cmd.className = "conflict-cmd";
|
|
644
|
-
cmd.textContent = p.command.length > 80 ? p.command.substring(0, 80) + "..." : p.command;
|
|
645
|
-
cmd.title = p.command;
|
|
646
|
-
row.appendChild(cmd);
|
|
647
|
-
|
|
648
|
-
var killBtn = document.createElement("button");
|
|
649
|
-
killBtn.className = "conflict-kill-btn";
|
|
650
|
-
killBtn.textContent = "Kill Process";
|
|
651
|
-
killBtn.setAttribute("data-pid", p.pid);
|
|
652
|
-
killBtn.addEventListener("click", function() {
|
|
653
|
-
var pid = parseInt(this.getAttribute("data-pid"), 10);
|
|
654
|
-
getWs().send(JSON.stringify({ type: "kill_process", pid: pid }));
|
|
655
|
-
this.disabled = true;
|
|
656
|
-
this.textContent = "Killing...";
|
|
657
|
-
});
|
|
658
|
-
row.appendChild(killBtn);
|
|
659
|
-
div.appendChild(row);
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
addToMessages(div);
|
|
663
|
-
scrollToBottom();
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
export function addContextOverflowMessage(msg) {
|
|
667
|
-
var div = document.createElement("div");
|
|
668
|
-
div.className = "context-overflow-msg";
|
|
669
|
-
|
|
670
|
-
var header = document.createElement("div");
|
|
671
|
-
header.className = "context-overflow-header";
|
|
672
|
-
header.textContent = msg.text || "Conversation too long to continue.";
|
|
673
|
-
div.appendChild(header);
|
|
674
|
-
|
|
675
|
-
var hint = document.createElement("div");
|
|
676
|
-
hint.className = "context-overflow-hint";
|
|
677
|
-
hint.textContent = "The conversation has exceeded the model's context limit. Please start a new conversation to continue.";
|
|
678
|
-
div.appendChild(hint);
|
|
679
|
-
|
|
680
|
-
var btn = document.createElement("button");
|
|
681
|
-
btn.className = "context-overflow-btn";
|
|
682
|
-
btn.textContent = "New Conversation";
|
|
683
|
-
btn.addEventListener("click", function() {
|
|
684
|
-
getWs().send(JSON.stringify({ type: "new_session" }));
|
|
685
|
-
});
|
|
686
|
-
div.appendChild(btn);
|
|
687
|
-
|
|
688
|
-
addToMessages(div);
|
|
689
|
-
scrollToBottom();
|
|
690
|
-
}
|
|
691
|
-
|
|
692
110
|
// --- Pre-thinking (instant dots before server responds) ---
|
|
693
111
|
|
|
694
112
|
export function showClaudePreThinking() {
|
|
@@ -717,8 +135,9 @@ function doShowMatePreThinking(mateName, mateAvatar) {
|
|
|
717
135
|
'<div class="mate-thinking-dots"><span></span><span></span><span></span></div>' +
|
|
718
136
|
'</div>';
|
|
719
137
|
store.set({ matePreThinkingEl: _el });
|
|
720
|
-
|
|
721
|
-
|
|
138
|
+
var activity = getActivityEl();
|
|
139
|
+
if (activity && activity.parentNode) {
|
|
140
|
+
activity.parentNode.insertBefore(_el, activity);
|
|
722
141
|
} else {
|
|
723
142
|
addToMessages(_el);
|
|
724
143
|
}
|