clay-server 2.36.1-beta.5 → 2.36.2-beta.1
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.
|
@@ -1346,6 +1346,38 @@ pre.mermaid-error {
|
|
|
1346
1346
|
padding: 0 20px;
|
|
1347
1347
|
}
|
|
1348
1348
|
|
|
1349
|
+
/* Dead-session compaction: when a session is resumed without a live SDK
|
|
1350
|
+
process and the todo widget still has pending/in_progress items, the
|
|
1351
|
+
item list is hidden so the widget doesn't anchor visual position
|
|
1352
|
+
mid-page. Click the header to toggle the .todo-widget-dead-expanded
|
|
1353
|
+
override class which restores the full view. */
|
|
1354
|
+
.todo-widget-dead-compact .todo-header {
|
|
1355
|
+
cursor: pointer;
|
|
1356
|
+
border-radius: 12px;
|
|
1357
|
+
}
|
|
1358
|
+
.todo-widget-dead-compact .todo-header::after {
|
|
1359
|
+
content: "paused";
|
|
1360
|
+
font-size: 10px;
|
|
1361
|
+
color: var(--text-muted);
|
|
1362
|
+
background: rgba(var(--overlay-rgb), 0.06);
|
|
1363
|
+
padding: 2px 6px;
|
|
1364
|
+
border-radius: 4px;
|
|
1365
|
+
margin-left: 6px;
|
|
1366
|
+
letter-spacing: 0.04em;
|
|
1367
|
+
text-transform: uppercase;
|
|
1368
|
+
}
|
|
1369
|
+
.todo-widget-dead-compact .todo-progress,
|
|
1370
|
+
.todo-widget-dead-compact .todo-items {
|
|
1371
|
+
display: none;
|
|
1372
|
+
}
|
|
1373
|
+
.todo-widget-dead-compact.todo-widget-dead-expanded .todo-header {
|
|
1374
|
+
border-radius: 12px 12px 0 0;
|
|
1375
|
+
}
|
|
1376
|
+
.todo-widget-dead-compact.todo-widget-dead-expanded .todo-progress,
|
|
1377
|
+
.todo-widget-dead-compact.todo-widget-dead-expanded .todo-items {
|
|
1378
|
+
display: block;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1349
1381
|
.todo-header {
|
|
1350
1382
|
display: flex;
|
|
1351
1383
|
align-items: center;
|
|
@@ -8,7 +8,7 @@ import { getWs } from './ws-ref.js';
|
|
|
8
8
|
// --- Leaf module imports ---
|
|
9
9
|
import { showToast } from './utils.js';
|
|
10
10
|
import { refreshIcons, iconHtml } from './icons.js';
|
|
11
|
-
import { renderMarkdown } from './markdown.js';
|
|
11
|
+
import { renderMarkdown, highlightCodeBlocks, renderMermaidBlocks } from './markdown.js';
|
|
12
12
|
import { updatePageTitle } from './sidebar.js';
|
|
13
13
|
import { renderSessionList, updateSessionPresence, populateCliSessionList, handleSearchResults, updateSessionBadge } from './sidebar-sessions.js';
|
|
14
14
|
import { updateDmBadge, renderSidebarPresence, setMentionActive, renderUserStrip } from './sidebar-mates.js';
|
|
@@ -20,7 +20,7 @@ import { renderMemoryList } from './mate-memory.js';
|
|
|
20
20
|
import { handlePaletteSessionSwitch, setPaletteVersion } from './command-palette.js';
|
|
21
21
|
import { handleFindInSessionResults } from './session-search.js';
|
|
22
22
|
import { handleInputSync, autoResize, builtinCommands, setScheduleBtnDisabled } from './input.js';
|
|
23
|
-
import { startThinking, appendThinking, stopThinking, resetThinkingGroup, createToolItem, updateToolExecuting, updateToolResult, markAllToolsDone, closeToolGroup, removeToolFromGroup, resetToolState, getTools, getPlanContent, setPlanContent, renderPlanBanner, renderPlanCard, getTodoTools, handleTodoWrite, handleTaskCreate, handleTaskUpdate, isPlanFilePath, enableMainInput, addTurnMeta, updateSubagentActivity, addSubagentToolEntry, markSubagentDone, initSubagentStop, updateSubagentProgress, updateSubagentTaskStatus, renderAskUserQuestion, markAskUserAnswered, renderPermissionRequest, markPermissionCancelled, markPermissionResolved, renderElicitationRequest, markElicitationResolved } from './tools.js';
|
|
23
|
+
import { startThinking, appendThinking, stopThinking, resetThinkingGroup, createToolItem, updateToolExecuting, updateToolResult, markAllToolsDone, closeToolGroup, removeToolFromGroup, resetToolState, getTools, getPlanContent, setPlanContent, renderPlanBanner, renderPlanCard, getTodoTools, handleTodoWrite, handleTaskCreate, handleTaskUpdate, applyDeadSessionTodoCompaction, isPlanFilePath, enableMainInput, addTurnMeta, updateSubagentActivity, addSubagentToolEntry, markSubagentDone, initSubagentStop, updateSubagentProgress, updateSubagentTaskStatus, renderAskUserQuestion, markAskUserAnswered, renderPermissionRequest, markPermissionCancelled, markPermissionResolved, renderElicitationRequest, markElicitationResolved } from './tools.js';
|
|
24
24
|
import { showDoneNotification, playDoneSound, isNotifAlertEnabled, isNotifSoundEnabled } from './notifications.js';
|
|
25
25
|
import { handleFsList, handleFsRead, handleFileChanged, handleDirChanged, handleFileHistory, handleGitDiff, handleFileAt, refreshIfOpen, getPendingNavigate, handleFsSearch } from './filebrowser.js';
|
|
26
26
|
import { isProjectSettingsOpen, handleInstructionsRead, handleInstructionsWrite, handleProjectEnv, handleProjectEnvSaved, handleProjectSharedEnv, handleProjectSharedEnvSaved, handleProjectOwnerChanged } from './project-settings.js';
|
|
@@ -149,6 +149,20 @@ export function processMessage(msg) {
|
|
|
149
149
|
|
|
150
150
|
case "history_done":
|
|
151
151
|
store.set({ replayingHistory: false });
|
|
152
|
+
// Batched syntax highlight + mermaid pass for the entire replayed
|
|
153
|
+
// transcript. Per-message highlights are skipped during replay
|
|
154
|
+
// (see markdown.js) to avoid cascading reflows that the sticky-
|
|
155
|
+
// bottom observer chases for several seconds on long sessions.
|
|
156
|
+
if (messagesEl) {
|
|
157
|
+
highlightCodeBlocks(messagesEl);
|
|
158
|
+
renderMermaidBlocks(messagesEl);
|
|
159
|
+
}
|
|
160
|
+
// Compact dead-session todo widgets (unfinished items will never
|
|
161
|
+
// resolve — the agent isn't coming back) so they don't anchor
|
|
162
|
+
// visual position mid-page on resume.
|
|
163
|
+
if (!store.get('sessionIsProcessing')) {
|
|
164
|
+
applyDeadSessionTodoCompaction();
|
|
165
|
+
}
|
|
152
166
|
// Hide vendor toggle if session has history (vendor already locked)
|
|
153
167
|
var _hTotal = store.get('historyTotal') || 0;
|
|
154
168
|
var _vtw2 = document.getElementById("vendor-toggle-wrap");
|
|
@@ -558,7 +572,7 @@ export function processMessage(msg) {
|
|
|
558
572
|
} else if (_prevSid) {
|
|
559
573
|
delete store.get('sessionDrafts')[_prevSid];
|
|
560
574
|
}
|
|
561
|
-
store.set({ activeSessionId: msg.id, cliSessionId: msg.cliSessionId || null, vendorCapabilities: msg.capabilities || {} });
|
|
575
|
+
store.set({ activeSessionId: msg.id, cliSessionId: msg.cliSessionId || null, vendorCapabilities: msg.capabilities || {}, sessionIsProcessing: !!msg.isProcessing });
|
|
562
576
|
if (msg.vendor) {
|
|
563
577
|
if (!store.get('vendorSelectionLocked') || msg.hasHistory) {
|
|
564
578
|
store.set({ currentVendor: msg.vendor });
|
|
@@ -715,6 +729,10 @@ export function processMessage(msg) {
|
|
|
715
729
|
case "status":
|
|
716
730
|
if (msg.status === "processing") {
|
|
717
731
|
setStatus("processing");
|
|
732
|
+
// Session became live — undo any dead-session todo compaction
|
|
733
|
+
// applied at history_done time.
|
|
734
|
+
store.set({ sessionIsProcessing: true });
|
|
735
|
+
applyDeadSessionTodoCompaction();
|
|
718
736
|
if (!(store.get('dmMode') && store.get('dmTargetUser') && store.get('dmTargetUser').isMate) && !store.get('matePreThinkingEl')) {
|
|
719
737
|
setActivity("thinking");
|
|
720
738
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { copyToClipboard, escapeHtml } from './utils.js';
|
|
2
2
|
import { refreshIcons } from './icons.js';
|
|
3
3
|
import { getMermaidThemeVars } from './theme.js';
|
|
4
|
+
import { store } from './store.js';
|
|
4
5
|
|
|
5
6
|
// Initialize markdown parser
|
|
6
7
|
marked.use({ gfm: true, breaks: false });
|
|
@@ -57,6 +58,13 @@ export function parseEmojis(el) {
|
|
|
57
58
|
var langAliases = { jsonl: "json", dotenv: "bash" };
|
|
58
59
|
|
|
59
60
|
export function highlightCodeBlocks(el) {
|
|
61
|
+
// Defer all syntax highlighting + copy-button injection while replaying
|
|
62
|
+
// history. Doing this per-message during replay triggers a long tail of
|
|
63
|
+
// sequential reflows that the sticky-bottom scroll observer chases for
|
|
64
|
+
// 4-8s. After history_done, app-messages.js calls this once on the full
|
|
65
|
+
// messages container, so unhighlighted blocks (selector :not(.hljs))
|
|
66
|
+
// are picked up in a single pass.
|
|
67
|
+
if (store.get('replayingHistory')) return;
|
|
60
68
|
el.querySelectorAll("pre code:not(.hljs):not(.language-mermaid)").forEach(function (block) {
|
|
61
69
|
var cls = Array.from(block.classList).find(function (c) { return c.startsWith("language-"); });
|
|
62
70
|
if (cls) {
|
|
@@ -93,6 +101,9 @@ export function highlightCodeBlocks(el) {
|
|
|
93
101
|
}
|
|
94
102
|
|
|
95
103
|
export function renderMermaidBlocks(el) {
|
|
104
|
+
// Same rationale as highlightCodeBlocks: defer mermaid renders until
|
|
105
|
+
// history_done so they don't cause cascading layout shifts during replay.
|
|
106
|
+
if (store.get('replayingHistory')) return;
|
|
96
107
|
var blocks = el.querySelectorAll("pre code.language-mermaid");
|
|
97
108
|
blocks.forEach(function (codeEl) {
|
|
98
109
|
var pre = codeEl.parentElement;
|
|
@@ -30,6 +30,11 @@ var todoItems = [];
|
|
|
30
30
|
var todoWidgetEl = null;
|
|
31
31
|
var todoWidgetVisible = true; // whether in-chat widget is in viewport
|
|
32
32
|
var todoObserver = null;
|
|
33
|
+
// When a session is resumed without a live SDK process and still has
|
|
34
|
+
// pending/in_progress items, the widget is rendered in compact mode
|
|
35
|
+
// (header + count only) so it doesn't anchor visual position mid-page
|
|
36
|
+
// or extend the sticky-bottom settle window with a tall element.
|
|
37
|
+
var todoDeadCompact = false;
|
|
33
38
|
var todoMeta = {
|
|
34
39
|
variant: "tasks",
|
|
35
40
|
title: "Tasks",
|
|
@@ -1289,7 +1294,12 @@ export function handleTodoWrite(input) {
|
|
|
1289
1294
|
activeForm: t.activeForm || "",
|
|
1290
1295
|
};
|
|
1291
1296
|
});
|
|
1297
|
+
// A fresh TodoWrite during replay is just historical state. After replay
|
|
1298
|
+
// ends, applyDeadSessionTodoCompaction (called from history_done) decides
|
|
1299
|
+
// whether to compact. During live operation, sessionIsProcessing is true
|
|
1300
|
+
// and applyDeadSessionTodoCompaction is a no-op for compaction.
|
|
1292
1301
|
renderTodoWidget();
|
|
1302
|
+
applyDeadSessionTodoCompaction();
|
|
1293
1303
|
}
|
|
1294
1304
|
|
|
1295
1305
|
export function handleTaskCreate(input) {
|
|
@@ -1359,7 +1369,9 @@ function renderTodoWidget() {
|
|
|
1359
1369
|
todoWidgetEl = document.createElement("div");
|
|
1360
1370
|
todoWidgetEl.className = "todo-widget";
|
|
1361
1371
|
}
|
|
1362
|
-
todoWidgetEl.className = "todo-widget"
|
|
1372
|
+
todoWidgetEl.className = "todo-widget"
|
|
1373
|
+
+ (todoMeta.variant === "plan" ? " todo-widget-plan" : "")
|
|
1374
|
+
+ (todoDeadCompact ? " todo-widget-dead-compact" : "");
|
|
1363
1375
|
|
|
1364
1376
|
var completed = 0;
|
|
1365
1377
|
for (var i = 0; i < todoItems.length; i++) {
|
|
@@ -1395,12 +1407,38 @@ function renderTodoWidget() {
|
|
|
1395
1407
|
if (isNew) {
|
|
1396
1408
|
ctx.addToMessages(todoWidgetEl);
|
|
1397
1409
|
setupTodoObserver();
|
|
1410
|
+
// Click-to-expand for compact mode. Toggling the override class lets
|
|
1411
|
+
// the user inspect items without permanently un-compacting the widget.
|
|
1412
|
+
todoWidgetEl.addEventListener("click", function (e) {
|
|
1413
|
+
if (!todoWidgetEl.classList.contains("todo-widget-dead-compact")) return;
|
|
1414
|
+
// Only expand on header click, not on items inside an already-expanded view
|
|
1415
|
+
var header = e.target.closest(".todo-header");
|
|
1416
|
+
if (!header) return;
|
|
1417
|
+
todoWidgetEl.classList.toggle("todo-widget-dead-expanded");
|
|
1418
|
+
});
|
|
1398
1419
|
}
|
|
1399
1420
|
updateTodoSticky();
|
|
1400
1421
|
refreshIcons();
|
|
1401
1422
|
maybeScrollToBottom();
|
|
1402
1423
|
}
|
|
1403
1424
|
|
|
1425
|
+
export function applyDeadSessionTodoCompaction() {
|
|
1426
|
+
// Called after history_done and on status changes. Decides whether the
|
|
1427
|
+
// current session is "dead" (resumed, no live SDK process) and whether
|
|
1428
|
+
// the todo widget has unfinished items that will never resolve.
|
|
1429
|
+
var isLive = !!store.get('sessionIsProcessing');
|
|
1430
|
+
var hasUnfinished = false;
|
|
1431
|
+
for (var i = 0; i < todoItems.length; i++) {
|
|
1432
|
+
var s = todoItems[i].status;
|
|
1433
|
+
if (s === "in_progress" || s === "pending") { hasUnfinished = true; break; }
|
|
1434
|
+
}
|
|
1435
|
+
todoDeadCompact = !isLive && hasUnfinished;
|
|
1436
|
+
if (todoWidgetEl) {
|
|
1437
|
+
todoWidgetEl.classList.toggle("todo-widget-dead-compact", todoDeadCompact);
|
|
1438
|
+
if (!todoDeadCompact) todoWidgetEl.classList.remove("todo-widget-dead-expanded");
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1404
1442
|
function setupTodoObserver() {
|
|
1405
1443
|
if (todoObserver) { todoObserver.disconnect(); todoObserver = null; }
|
|
1406
1444
|
if (!todoWidgetEl) return;
|
package/lib/sessions.js
CHANGED
|
@@ -345,7 +345,10 @@ function createSessionManager(opts) {
|
|
|
345
345
|
return session;
|
|
346
346
|
}
|
|
347
347
|
|
|
348
|
-
|
|
348
|
+
// Initial replay payload size. Lowered from 200 to reduce client-side
|
|
349
|
+
// layout work on resume — older items are loaded progressively on
|
|
350
|
+
// scroll-up via the existing pagination path.
|
|
351
|
+
var HISTORY_PAGE_SIZE = 100;
|
|
349
352
|
|
|
350
353
|
function findTurnBoundary(history, targetIndex) {
|
|
351
354
|
for (var i = targetIndex; i >= 0; i--) {
|
|
@@ -420,7 +423,7 @@ function createSessionManager(opts) {
|
|
|
420
423
|
var _capsByVendor = capabilitiesByVendor || {};
|
|
421
424
|
var _sessionVendor = session.vendor || defaultVendor || "claude";
|
|
422
425
|
var _vendorCaps = _capsByVendor[_sessionVendor] || {};
|
|
423
|
-
_send({ type: "session_switched", id: localId, cliSessionId: session.cliSessionId || null, loop: session.loop || null, vendor: session.vendor || null, hasHistory: (session.history && session.history.length > 0), capabilities: _vendorCaps });
|
|
426
|
+
_send({ type: "session_switched", id: localId, cliSessionId: session.cliSessionId || null, loop: session.loop || null, vendor: session.vendor || null, hasHistory: (session.history && session.history.length > 0), capabilities: _vendorCaps, isProcessing: !!session.isProcessing });
|
|
424
427
|
// Send vendor-specific slash commands
|
|
425
428
|
var _vendorCmds = slashCommandsByVendor[_sessionVendor] || slashCommands || [];
|
|
426
429
|
_send({ type: "slash_commands", commands: _vendorCmds, vendor: _sessionVendor });
|
package/package.json
CHANGED