claude-code-kanban 4.5.0 → 4.7.0
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/parsers.js +109 -7
- package/package.json +1 -1
- package/public/app.js +113 -17
- package/public/style.css +125 -93
- package/server.js +9 -0
package/lib/parsers.js
CHANGED
|
@@ -107,6 +107,15 @@ function parseJsonlLine(line) {
|
|
|
107
107
|
};
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
if (obj.type === 'queue-operation' && obj.operation === 'enqueue') {
|
|
111
|
+
return {
|
|
112
|
+
...base,
|
|
113
|
+
role: 'user',
|
|
114
|
+
queued: true,
|
|
115
|
+
content: typeof obj.content === 'string' ? obj.content : null
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
110
119
|
if (obj.type === 'progress') {
|
|
111
120
|
return { ...base, cwd: obj.cwd || null, version: obj.version || null, slug: obj.slug || null };
|
|
112
121
|
}
|
|
@@ -118,6 +127,25 @@ const TOOL_RESULT_MAX = 1500;
|
|
|
118
127
|
const USER_TEXT_MAX = 500;
|
|
119
128
|
const INTERRUPT_MARKER = '[Request interrupted by user]';
|
|
120
129
|
|
|
130
|
+
// Newer Claude Code no longer inlines pasted images as base64 blocks. It writes
|
|
131
|
+
// them to ~/.claude/image-cache/<sessionId>/<N>.<ext> and leaves only a text marker
|
|
132
|
+
// in the message — an inline "[Image #N]" placeholder and/or a standalone
|
|
133
|
+
// "[Image: source: ...\N.ext]" line. Pull out the N references (so the UI can serve
|
|
134
|
+
// the cached file) and strip the verbose source lines from the displayed text — the
|
|
135
|
+
// rendered preview replaces them; the short "[Image #N]" placeholders stay.
|
|
136
|
+
// Runs on the per-message scan hot path, so it skips the regexes entirely unless a
|
|
137
|
+
// marker is present. Returns { refs: [{ kind: 'cache', n }], text }.
|
|
138
|
+
function parseImageMarkers(text) {
|
|
139
|
+
const raw = text || '';
|
|
140
|
+
if (!raw.includes('[Image')) return { refs: [], text: raw.trim() };
|
|
141
|
+
const ns = new Set();
|
|
142
|
+
for (const m of raw.matchAll(/\[Image #(\d+)\]/g)) ns.add(Number(m[1]));
|
|
143
|
+
for (const m of raw.matchAll(/\[Image: source:[^\]]*?(\d+)\.[a-z0-9]+\]/gi)) ns.add(Number(m[1]));
|
|
144
|
+
const refs = [...ns].sort((a, b) => a - b).map((n) => ({ kind: 'cache', n }));
|
|
145
|
+
const cleaned = raw.replace(/\[Image: source:[^\]]*\]/gi, '').trim();
|
|
146
|
+
return { refs, text: cleaned };
|
|
147
|
+
}
|
|
148
|
+
|
|
121
149
|
function pushUserMessage(messages, text, timestamp, sysLabel, extras) {
|
|
122
150
|
if (sysLabel === '__skip__') return;
|
|
123
151
|
const safeText = text || '';
|
|
@@ -133,6 +161,7 @@ function pushUserMessage(messages, text, timestamp, sysLabel, extras) {
|
|
|
133
161
|
if (extras.uuid) msg.uuid = extras.uuid;
|
|
134
162
|
if (extras.images && extras.images.length) msg.images = extras.images;
|
|
135
163
|
if (extras.toolResultRefs && extras.toolResultRefs.length) msg.toolResultRefs = extras.toolResultRefs;
|
|
164
|
+
if (extras.queued) msg.queued = true;
|
|
136
165
|
}
|
|
137
166
|
messages.push(msg);
|
|
138
167
|
}
|
|
@@ -413,13 +442,20 @@ function getSystemMessageLabel(text) {
|
|
|
413
442
|
const statusMatch = text.match(/<status>([^<]+)<\/status>/);
|
|
414
443
|
return statusMatch ? `Background task ${statusMatch[1]}` : 'Background task notification';
|
|
415
444
|
}
|
|
416
|
-
|
|
445
|
+
// The "Compacted (ctrl+o…)" stdout echo is redundant with the isCompactSummary
|
|
446
|
+
// chip (modern Claude Code stores the summary inline). Skipping it lets the one
|
|
447
|
+
// summary-bearing chip stand alone instead of trailing a bare marker that the
|
|
448
|
+
// resume prompt sorts away from, preventing collapse.
|
|
449
|
+
if (text.includes('<local-command-stdout>') && text.includes('Compacted')) return '__skip__';
|
|
417
450
|
if (text.includes('<local-command-stdout>')) return 'Command output';
|
|
418
451
|
if (text.includes('<local-command-caveat>')) return 'System notification';
|
|
419
452
|
if (text.includes('.output completed') && text.includes('Background command')) return 'Background task completed';
|
|
420
453
|
if (text.startsWith('This session is being continued from a previous conversation')) return '__skip__';
|
|
421
454
|
if (text.includes('<command-name>/clear</command-name>')) return '__skip__';
|
|
422
|
-
|
|
455
|
+
// The /compact trigger record is redundant: the boundary stdout + isCompactSummary
|
|
456
|
+
// record already render as one expandable "Compacted" chip. Skip it so the chip
|
|
457
|
+
// collapses cleanly instead of leaving a separate marker where the user typed it.
|
|
458
|
+
if (text.includes('<command-name>/compact</command-name>')) return '__skip__';
|
|
423
459
|
return null;
|
|
424
460
|
}
|
|
425
461
|
|
|
@@ -612,6 +648,26 @@ function readRecentMessages(jsonlPath, limit = 10) {
|
|
|
612
648
|
}
|
|
613
649
|
}
|
|
614
650
|
} else if (obj.type === 'user' && obj.message?.role === 'user' && !obj.isMeta) {
|
|
651
|
+
if (obj.isCompactSummary) {
|
|
652
|
+
// Surface the compaction summary as a single expandable "Compacted"
|
|
653
|
+
// chip (compactSummary), never a raw bubble. The boundary marker plus
|
|
654
|
+
// the /compact command/stdout records collapse into this one entry below.
|
|
655
|
+
const raw = typeof obj.message.content === 'string'
|
|
656
|
+
? obj.message.content
|
|
657
|
+
: Array.isArray(obj.message.content)
|
|
658
|
+
? obj.message.content.filter((b) => b.type === 'text').map((b) => b.text).join('\n')
|
|
659
|
+
: '';
|
|
660
|
+
const summary = raw
|
|
661
|
+
.replace(/^This session is being continued[^\n]*\n+(The summary below[^\n]*\n+)?/i, '')
|
|
662
|
+
.trim();
|
|
663
|
+
messages.push({
|
|
664
|
+
type: 'user',
|
|
665
|
+
systemLabel: 'Compacted',
|
|
666
|
+
compactSummary: summary || null,
|
|
667
|
+
timestamp: obj.timestamp
|
|
668
|
+
});
|
|
669
|
+
continue;
|
|
670
|
+
}
|
|
615
671
|
if (typeof obj.message.content === 'string') {
|
|
616
672
|
const t = obj.message.content;
|
|
617
673
|
const tmMatch = t.match(/<teammate-message\s+([^>]*)>([\s\S]*?)<\/teammate-message>/);
|
|
@@ -669,6 +725,7 @@ function readRecentMessages(jsonlPath, limit = 10) {
|
|
|
669
725
|
texts.push(block.text);
|
|
670
726
|
} else if (block.type === 'image' && block.source && block.source.type === 'base64') {
|
|
671
727
|
images.push({
|
|
728
|
+
kind: 'base64',
|
|
672
729
|
blockIndex: idx,
|
|
673
730
|
mediaType: block.source.media_type || 'image/png',
|
|
674
731
|
dataLen: typeof block.source.data === 'string' ? block.source.data.length : 0
|
|
@@ -709,18 +766,31 @@ function readRecentMessages(jsonlPath, limit = 10) {
|
|
|
709
766
|
}
|
|
710
767
|
});
|
|
711
768
|
const joined = texts.join('\n').trim();
|
|
712
|
-
|
|
713
|
-
|
|
769
|
+
// Prefer inline base64 blocks when present; otherwise fall back to
|
|
770
|
+
// file-cache references parsed from the text markers.
|
|
771
|
+
const { refs, text: displayText } = parseImageMarkers(joined);
|
|
772
|
+
const allImages = images.length ? images : refs;
|
|
773
|
+
const hasText = displayText && displayText !== INTERRUPT_MARKER;
|
|
774
|
+
const hasImages = allImages.length > 0;
|
|
714
775
|
if (hasText || hasImages) {
|
|
715
776
|
pushUserMessage(
|
|
716
777
|
messages,
|
|
717
|
-
|
|
778
|
+
displayText,
|
|
718
779
|
obj.timestamp,
|
|
719
|
-
getSystemMessageLabel(
|
|
720
|
-
{ uuid: obj.uuid, images, toolResultRefs: hasText ? toolResultRefs : [] }
|
|
780
|
+
getSystemMessageLabel(displayText),
|
|
781
|
+
{ uuid: obj.uuid, images: allImages, toolResultRefs: hasText ? toolResultRefs : [] }
|
|
721
782
|
);
|
|
722
783
|
}
|
|
723
784
|
}
|
|
785
|
+
} else if (obj.type === 'queue-operation' && obj.operation === 'enqueue') {
|
|
786
|
+
// Queued messages are stored as top-level `content`, not under
|
|
787
|
+
// message.content, and never re-emitted as a type:'user' line.
|
|
788
|
+
// Surface them so the Session Log reflects what the user sent.
|
|
789
|
+
const qt = typeof obj.content === 'string' ? obj.content : '';
|
|
790
|
+
if (qt) {
|
|
791
|
+
const { refs, text } = parseImageMarkers(qt);
|
|
792
|
+
pushUserMessage(messages, text, obj.timestamp, null, { queued: true, images: refs });
|
|
793
|
+
}
|
|
724
794
|
}
|
|
725
795
|
} catch (e) { /* partial line */ }
|
|
726
796
|
}
|
|
@@ -758,6 +828,12 @@ function readRecentMessages(jsonlPath, limit = 10) {
|
|
|
758
828
|
messages.sort((a, b) => (a.timestamp || '').localeCompare(b.timestamp || ''));
|
|
759
829
|
for (let i = messages.length - 1; i > 0; i--) {
|
|
760
830
|
if (messages[i].systemLabel === 'Compacted' && messages[i - 1].systemLabel === 'Compacted') {
|
|
831
|
+
// Carry the summary body onto the surviving chip so collapsing the
|
|
832
|
+
// boundary + /compact command + stdout + isCompactSummary records into
|
|
833
|
+
// one entry never drops the expandable summary.
|
|
834
|
+
if (messages[i].compactSummary && !messages[i - 1].compactSummary) {
|
|
835
|
+
messages[i - 1].compactSummary = messages[i].compactSummary;
|
|
836
|
+
}
|
|
761
837
|
messages.splice(i, 1);
|
|
762
838
|
}
|
|
763
839
|
}
|
|
@@ -821,6 +897,31 @@ function readUserImage(jsonlPath, msgUuid, blockIndex) {
|
|
|
821
897
|
return null;
|
|
822
898
|
}
|
|
823
899
|
|
|
900
|
+
const CACHED_IMAGE_MIME = {
|
|
901
|
+
png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif', webp: 'image/webp',
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
// Serve pasted image N from ~/.claude/image-cache/<sessionId>/. The cached file may
|
|
905
|
+
// be any image format, so resolve it by index rather than assuming .png. sessionId
|
|
906
|
+
// is validated to a bare id to prevent path traversal.
|
|
907
|
+
function readCachedImage(sessionId, n) {
|
|
908
|
+
const idx = Number(n);
|
|
909
|
+
if (!sessionId || !/^[A-Za-z0-9._-]+$/.test(sessionId)) return null;
|
|
910
|
+
if (!Number.isInteger(idx) || idx < 0) return null;
|
|
911
|
+
const dir = path.join(require('os').homedir(), '.claude', 'image-cache', sessionId);
|
|
912
|
+
try {
|
|
913
|
+
const match = readdirSync(dir).find((f) => {
|
|
914
|
+
const dot = f.lastIndexOf('.');
|
|
915
|
+
return dot > 0 && f.slice(0, dot) === String(idx) && CACHED_IMAGE_MIME[f.slice(dot + 1).toLowerCase()];
|
|
916
|
+
});
|
|
917
|
+
if (!match) return null;
|
|
918
|
+
const ext = match.slice(match.lastIndexOf('.') + 1).toLowerCase();
|
|
919
|
+
return { mediaType: CACHED_IMAGE_MIME[ext], buffer: readFileSync(path.join(dir, match)) };
|
|
920
|
+
} catch (_) {
|
|
921
|
+
return null;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
824
925
|
function readMessagesPage(jsonlPath, limit = 10, beforeTimestamp = null) {
|
|
825
926
|
const fetchLimit = limit + 1;
|
|
826
927
|
const applyFilter = beforeTimestamp
|
|
@@ -1237,6 +1338,7 @@ module.exports = {
|
|
|
1237
1338
|
readMessagesPage,
|
|
1238
1339
|
readFullToolResult,
|
|
1239
1340
|
readUserImage,
|
|
1341
|
+
readCachedImage,
|
|
1240
1342
|
updateLoopInfo,
|
|
1241
1343
|
buildLoopInfoFromState,
|
|
1242
1344
|
buildAgentProgressMap,
|
package/package.json
CHANGED
package/public/app.js
CHANGED
|
@@ -27,6 +27,8 @@ let agentDurationInterval = null;
|
|
|
27
27
|
let agentPollInterval = null;
|
|
28
28
|
let selectedTaskId = null;
|
|
29
29
|
let selectedSessionId = null;
|
|
30
|
+
// Task stays selected (keyboard nav) but its white highlight is dimmed once the detail panel closes.
|
|
31
|
+
let taskHighlightDimmed = false;
|
|
30
32
|
let focusZone = 'board'; // 'board' | 'sidebar'
|
|
31
33
|
let appConfig = { marketplaceUrl: null, costUrl: null, memoryUrl: null };
|
|
32
34
|
let selectedSessionIdx = -1;
|
|
@@ -509,6 +511,8 @@ function setActivityFilter(kind) {
|
|
|
509
511
|
// waiting is a sub-state of active — couple them so one click covers all running sessions
|
|
510
512
|
toggleActivityKind('active');
|
|
511
513
|
toggleActivityKind('waiting');
|
|
514
|
+
// Expand sections that have active sessions so they're visible on click
|
|
515
|
+
if (activityFilter.has('active')) expandActiveGroups();
|
|
512
516
|
} else {
|
|
513
517
|
toggleActivityKind(kind);
|
|
514
518
|
}
|
|
@@ -1029,7 +1033,7 @@ function renderToolItem(m, i, compact) {
|
|
|
1029
1033
|
? `onclick="showAgentModal('${escapeHtml(m.agentId)}')" ${combinedStyle}`
|
|
1030
1034
|
: `onclick="msgDetailFollowLatest=false;showMsgDetail(${i})" ${combinedStyle}`;
|
|
1031
1035
|
const pinBtn = renderMsgPinBtn(m, i);
|
|
1032
|
-
return `<div class="msg-item msg-tool${compactClass}" ${itemClickAttr}>
|
|
1036
|
+
return `<div class="msg-item msg-tool${compactClass}" data-msg-idx="${i}" ${itemClickAttr}>
|
|
1033
1037
|
${getToolIcon(m.tool)}
|
|
1034
1038
|
<div class="msg-body"><div class="msg-text">${escapeHtml(m.tool)}${toolDetail}${agentLink}</div><div class="msg-time">${formatDate(m.timestamp)}</div></div>${agentLogBtn}${pinBtn}
|
|
1035
1039
|
</div>`;
|
|
@@ -1071,7 +1075,7 @@ function renderMessageList(messages) {
|
|
|
1071
1075
|
continue;
|
|
1072
1076
|
}
|
|
1073
1077
|
|
|
1074
|
-
const clickable = `onclick="msgDetailFollowLatest=false;showMsgDetail(${i})" style="cursor:pointer"`;
|
|
1078
|
+
const clickable = `data-msg-idx="${i}" onclick="msgDetailFollowLatest=false;showMsgDetail(${i})" style="cursor:pointer"`;
|
|
1075
1079
|
const pinBtn = renderMsgPinBtn(m, i);
|
|
1076
1080
|
if (m.type === 'user') {
|
|
1077
1081
|
if (m.systemLabel) {
|
|
@@ -1097,9 +1101,10 @@ function renderMessageList(messages) {
|
|
|
1097
1101
|
if (displayText) textHtml = isCmd ? `<code>${escapeHtml(displayText)}</code>` : displayText;
|
|
1098
1102
|
else if (chips.length) textHtml = '<em class="msg-text-muted">(attachment)</em>';
|
|
1099
1103
|
else textHtml = '';
|
|
1100
|
-
|
|
1104
|
+
const queuedTag = m.queued ? '<span class="msg-queued-tag">queued</span>' : '';
|
|
1105
|
+
parts.push(`<div class="msg-item msg-user${isCmd ? ' msg-cmd' : ''}${m.queued ? ' msg-queued' : ''}" ${clickable}>
|
|
1101
1106
|
${MSG_ICON_USER}
|
|
1102
|
-
<div class="msg-body"><div class="msg-text">${textHtml}${cmdArgsHtml}</div>${chipsHtml}<div class="msg-time">${formatDate(m.timestamp)}</div></div>${pinBtn}
|
|
1107
|
+
<div class="msg-body"><div class="msg-text">${textHtml}${cmdArgsHtml}</div>${chipsHtml}<div class="msg-time">${formatDate(m.timestamp)}${queuedTag}</div></div>${pinBtn}
|
|
1103
1108
|
</div>`);
|
|
1104
1109
|
}
|
|
1105
1110
|
} else if (m.type === 'assistant') {
|
|
@@ -1255,6 +1260,7 @@ function renderMessages(messages) {
|
|
|
1255
1260
|
? `<div class="msg-limit-banner">Showing last ${MSG_MAX_LOADED} messages</div>`
|
|
1256
1261
|
: '';
|
|
1257
1262
|
container.innerHTML = limitBanner + msgsHtml + renderWaitingEntry();
|
|
1263
|
+
highlightSelectedMsg();
|
|
1258
1264
|
if (!msgUserScrolledUp) container.scrollTop = container.scrollHeight;
|
|
1259
1265
|
// Auto-load more if content doesn't overflow yet
|
|
1260
1266
|
if (
|
|
@@ -1269,6 +1275,8 @@ function renderMessages(messages) {
|
|
|
1269
1275
|
|
|
1270
1276
|
let currentMsgDetailIdx = null;
|
|
1271
1277
|
let msgDetailFollowLatest = false;
|
|
1278
|
+
// Message stays tracked but its highlight is dimmed once the detail modal closes.
|
|
1279
|
+
let msgHighlightDimmed = false;
|
|
1272
1280
|
const MSG_DETAIL_WAITING_IDX = -2;
|
|
1273
1281
|
let currentPins = [];
|
|
1274
1282
|
let pinnedCollapsed = false;
|
|
@@ -1287,6 +1295,8 @@ const MSG_ICON_TEAMMATE =
|
|
|
1287
1295
|
'<svg class="msg-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>';
|
|
1288
1296
|
const MSG_ICON_IDLE =
|
|
1289
1297
|
'<svg class="msg-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 6v6"/></svg>';
|
|
1298
|
+
const ICON_AGENT =
|
|
1299
|
+
'<svg class="msg-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2"/><rect x="9" y="9" width="6" height="6"/><path d="M9 2v2M15 2v2M9 20v2M15 20v2M20 9h2M20 14h2M2 9h2M2 14h2"/></svg>';
|
|
1290
1300
|
const ICON_TASK =
|
|
1291
1301
|
'<svg class="msg-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>';
|
|
1292
1302
|
const ICON_WEB =
|
|
@@ -1309,7 +1319,7 @@ const TOOL_ICONS = {
|
|
|
1309
1319
|
Edit: '<svg class="msg-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>',
|
|
1310
1320
|
Glob: '<svg class="msg-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/><circle cx="14" cy="14" r="3"/><line x1="16.5" y1="16.5" x2="19" y2="19"/></svg>',
|
|
1311
1321
|
Grep: '<svg class="msg-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>',
|
|
1312
|
-
Agent:
|
|
1322
|
+
Agent: ICON_AGENT,
|
|
1313
1323
|
SendMessage:
|
|
1314
1324
|
'<svg class="msg-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>',
|
|
1315
1325
|
TaskCreate: ICON_TASK,
|
|
@@ -1646,10 +1656,20 @@ const linkSvg = (size) =>
|
|
|
1646
1656
|
//#region MODALS
|
|
1647
1657
|
function renderUserAttachments(m) {
|
|
1648
1658
|
const parts = [];
|
|
1649
|
-
if (m.images?.length &&
|
|
1659
|
+
if (m.images?.length && currentSessionId) {
|
|
1660
|
+
const sid = encodeURIComponent(currentSessionId);
|
|
1650
1661
|
const imgs = m.images
|
|
1651
1662
|
.map((img) => {
|
|
1652
|
-
|
|
1663
|
+
// Cache-kind images live on disk (image-cache/<sessionId>/<n>.png); base64
|
|
1664
|
+
// images are read from the JSONL block and need the message uuid.
|
|
1665
|
+
let url;
|
|
1666
|
+
if (img.kind === 'cache') {
|
|
1667
|
+
url = `/api/sessions/${sid}/cached-image/${img.n}`;
|
|
1668
|
+
} else if (m.uuid) {
|
|
1669
|
+
url = `/api/sessions/${sid}/user-image/${encodeURIComponent(m.uuid)}/${img.blockIndex}`;
|
|
1670
|
+
} else {
|
|
1671
|
+
return '';
|
|
1672
|
+
}
|
|
1653
1673
|
return `<img src="${url}" loading="lazy" alt="user image" class="user-attach-image" />`;
|
|
1654
1674
|
})
|
|
1655
1675
|
.join('');
|
|
@@ -1678,10 +1698,22 @@ function renderUserAttachments(m) {
|
|
|
1678
1698
|
return parts.join('');
|
|
1679
1699
|
}
|
|
1680
1700
|
|
|
1701
|
+
// Highlight the message whose detail is open, mirroring task-card selection.
|
|
1702
|
+
function highlightSelectedMsg() {
|
|
1703
|
+
const container = document.getElementById('message-panel-content');
|
|
1704
|
+
if (!container) return;
|
|
1705
|
+
for (const el of container.querySelectorAll('.msg-item.selected')) el.classList.remove('selected');
|
|
1706
|
+
if (msgHighlightDimmed || currentMsgDetailIdx == null || currentMsgDetailIdx < 0) return;
|
|
1707
|
+
const el = container.querySelector(`.msg-item[data-msg-idx="${currentMsgDetailIdx}"]`);
|
|
1708
|
+
if (el) el.classList.add('selected');
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1681
1711
|
function showMsgDetail(idx) {
|
|
1682
1712
|
currentMsgDetailIdx = idx;
|
|
1713
|
+
msgHighlightDimmed = false;
|
|
1683
1714
|
const m = currentMessages[idx];
|
|
1684
1715
|
if (!m) return;
|
|
1716
|
+
highlightSelectedMsg();
|
|
1685
1717
|
const body = document.getElementById('msg-detail-body');
|
|
1686
1718
|
if (m.type === 'tool_use') {
|
|
1687
1719
|
document.getElementById('msg-detail-title').textContent = m.tool;
|
|
@@ -1785,6 +1817,12 @@ function showMsgDetail(idx) {
|
|
|
1785
1817
|
function closeMsgDetailModal() {
|
|
1786
1818
|
resetModalFullscreen('msg-detail-modal');
|
|
1787
1819
|
msgDetailFollowLatest = false;
|
|
1820
|
+
// Drop the message highlight on close, mirroring task-card behavior.
|
|
1821
|
+
msgHighlightDimmed = true;
|
|
1822
|
+
const container = document.getElementById('message-panel-content');
|
|
1823
|
+
if (container) {
|
|
1824
|
+
for (const el of container.querySelectorAll('.msg-item.selected')) el.classList.remove('selected');
|
|
1825
|
+
}
|
|
1788
1826
|
}
|
|
1789
1827
|
|
|
1790
1828
|
function _setModalWidth(modal, slot, on, maxWidth, width) {
|
|
@@ -2365,7 +2403,8 @@ function renderAgentFooter() {
|
|
|
2365
2403
|
: '';
|
|
2366
2404
|
const agentColor = resolveNamedColor(a.color);
|
|
2367
2405
|
const colorStyle = agentColor ? ` style="border-left:3px solid ${agentColor.color}"` : '';
|
|
2368
|
-
|
|
2406
|
+
const selectedClass = a.agentId === currentAgentModalId ? ' selected' : '';
|
|
2407
|
+
return `<div class="agent-card${selectedClass}" data-agent-id="${escapeHtml(a.agentId)}"${colorStyle} onclick="showAgentModal('${a.agentId}')">
|
|
2369
2408
|
<div class="agent-type-row">${typeNs ? `<span class="agent-type-ns">${escapeHtml(typeNs)}</span>` : ''}<span class="agent-type-name">${escapeHtml(typeName)}</span>${nameBadgeHtml}</div>
|
|
2370
2409
|
<div class="agent-status-row"><span class="agent-dot ${a.status}"></span><span class="agent-status">${statusText}</span></div>
|
|
2371
2410
|
${msgHtml}
|
|
@@ -2457,6 +2496,7 @@ function showAgentModal(agentId) {
|
|
|
2457
2496
|
const agent = findAgentById(agentId);
|
|
2458
2497
|
if (!agent) return;
|
|
2459
2498
|
currentAgentModalId = agentId;
|
|
2499
|
+
highlightSelectedAgent();
|
|
2460
2500
|
const modal = document.getElementById('agent-modal');
|
|
2461
2501
|
const title = document.getElementById('agent-modal-title');
|
|
2462
2502
|
const body = document.getElementById('agent-modal-body');
|
|
@@ -2526,6 +2566,16 @@ function showAgentModal(agentId) {
|
|
|
2526
2566
|
function closeAgentModal() {
|
|
2527
2567
|
resetModalFullscreen('agent-modal');
|
|
2528
2568
|
currentAgentModalId = null;
|
|
2569
|
+
highlightSelectedAgent();
|
|
2570
|
+
}
|
|
2571
|
+
|
|
2572
|
+
function highlightSelectedAgent() {
|
|
2573
|
+
const content = document.getElementById('agent-footer-content');
|
|
2574
|
+
if (!content) return;
|
|
2575
|
+
for (const el of content.querySelectorAll('.agent-card.selected')) el.classList.remove('selected');
|
|
2576
|
+
if (currentAgentModalId == null) return;
|
|
2577
|
+
const el = content.querySelector(`.agent-card[data-agent-id="${currentAgentModalId}"]`);
|
|
2578
|
+
if (el) el.classList.add('selected');
|
|
2529
2579
|
}
|
|
2530
2580
|
|
|
2531
2581
|
//#endregion
|
|
@@ -2760,7 +2810,7 @@ function renderSessions() {
|
|
|
2760
2810
|
<span class="session-indicators">
|
|
2761
2811
|
${isTeam ? `<span class="team-badge" title="${memberCount} team members"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>${memberCount}</span>` : ''}
|
|
2762
2812
|
${session.sharedTaskList ? `<span class="shared-tasklist-badge" title="Shared task list: ${escapeHtml(session.sharedTaskList)}">${linkSvg(12)}</span>` : ''}
|
|
2763
|
-
${isTeam || session.project || showCtx ? `<span class="team-info-btn" onclick="event.stopPropagation(); showSessionInfoModal('${session.id}')" title="View session info"
|
|
2813
|
+
${isTeam || session.project || showCtx ? `<span class="team-info-btn" onclick="event.stopPropagation(); showSessionInfoModal('${session.id}')" title="View session info"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg></span>` : ''}
|
|
2764
2814
|
${session.hasPlan && !session.planSourceSessionId ? `<span class="plan-indicator" onclick="event.stopPropagation(); openPlanForSession('${session.id}')" title="View plan"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg></span>` : ''}
|
|
2765
2815
|
${renderLoopBadge(session)}
|
|
2766
2816
|
${linkedDocsCount > 0 ? `<span class="linked-docs-badge" onclick="event.stopPropagation(); showSessionInfoModal('${session.id}')" title="${linkedDocsCount} linked document${linkedDocsCount > 1 ? 's' : ''}">${linkSvg(10)}${linkedDocsCount}</span>` : ''}
|
|
@@ -2781,6 +2831,8 @@ function renderSessions() {
|
|
|
2781
2831
|
|
|
2782
2832
|
// Group active sessions by project
|
|
2783
2833
|
if (sessionFilter === 'active') {
|
|
2834
|
+
// Auto-expand a collapsed section when newly-active work lands in it (live refresh).
|
|
2835
|
+
expandActiveGroups({ onlyNew: true });
|
|
2784
2836
|
const groups = new Map();
|
|
2785
2837
|
const ungrouped = [];
|
|
2786
2838
|
for (const session of filteredSessions) {
|
|
@@ -3080,7 +3132,7 @@ function renderKanban() {
|
|
|
3080
3132
|
document.querySelector(`.task-card[data-task-id="${selectedTaskId}"][data-session-id="${selectedSessionId}"]`) ||
|
|
3081
3133
|
document.querySelector(`.task-card[data-task-id="${selectedTaskId}"]`);
|
|
3082
3134
|
if (card) {
|
|
3083
|
-
if (focusZone === 'board') card.classList.add('selected');
|
|
3135
|
+
if (focusZone === 'board' && !taskHighlightDimmed) card.classList.add('selected');
|
|
3084
3136
|
} else {
|
|
3085
3137
|
selectedTaskId = null;
|
|
3086
3138
|
selectedSessionId = null;
|
|
@@ -3164,10 +3216,10 @@ async function onColumnDrop(e) {
|
|
|
3164
3216
|
|
|
3165
3217
|
//#region KEYBOARD_NAV
|
|
3166
3218
|
function selectTask(taskId, sessionId) {
|
|
3167
|
-
|
|
3168
|
-
if (prev) prev.classList.remove('selected');
|
|
3219
|
+
clearTaskSelection();
|
|
3169
3220
|
selectedTaskId = taskId;
|
|
3170
3221
|
selectedSessionId = sessionId;
|
|
3222
|
+
taskHighlightDimmed = false;
|
|
3171
3223
|
if (!taskId) return;
|
|
3172
3224
|
const card =
|
|
3173
3225
|
document.querySelector(`.task-card[data-task-id="${taskId}"][data-session-id="${sessionId}"]`) ||
|
|
@@ -3281,6 +3333,11 @@ function clearKbSelection() {
|
|
|
3281
3333
|
if (prev) prev.classList.remove('kb-selected');
|
|
3282
3334
|
}
|
|
3283
3335
|
|
|
3336
|
+
function clearTaskSelection() {
|
|
3337
|
+
const prev = document.querySelector('.task-card.selected');
|
|
3338
|
+
if (prev) prev.classList.remove('selected');
|
|
3339
|
+
}
|
|
3340
|
+
|
|
3284
3341
|
function selectSessionByIndex(idx, items) {
|
|
3285
3342
|
items = items || getNavigableItems();
|
|
3286
3343
|
if (items.length === 0) return;
|
|
@@ -3319,11 +3376,37 @@ function setGroupCollapsed(header, collapsed) {
|
|
|
3319
3376
|
header.classList.toggle('collapsed', collapsed);
|
|
3320
3377
|
const container = getGroupSessionsContainer(header);
|
|
3321
3378
|
if (container) container.classList.toggle('collapsed', collapsed);
|
|
3379
|
+
persistCollapsedGroups();
|
|
3380
|
+
}
|
|
3381
|
+
|
|
3382
|
+
function persistCollapsedGroups() {
|
|
3322
3383
|
try {
|
|
3323
3384
|
localStorage.setItem('collapsedGroups', JSON.stringify([...collapsedProjectGroups]));
|
|
3324
3385
|
} catch (_) {}
|
|
3325
3386
|
}
|
|
3326
3387
|
|
|
3388
|
+
let prevActiveSessionIds = null; // null until primed by the first onlyNew pass
|
|
3389
|
+
|
|
3390
|
+
// Expand collapsed project sections that contain active sessions; leave the rest collapsed.
|
|
3391
|
+
// onlyNew: expand only for sessions that became active since the last call, so a section the
|
|
3392
|
+
// user manually collapsed stays collapsed until NEW active work lands in it (live refresh path).
|
|
3393
|
+
// The first onlyNew pass only primes the tracking set — it respects saved collapse state on load.
|
|
3394
|
+
// onlyNew=false force-expands every section with active work (explicit chip click).
|
|
3395
|
+
function expandActiveGroups({ onlyNew = false } = {}) {
|
|
3396
|
+
const primed = prevActiveSessionIds !== null;
|
|
3397
|
+
const activeIds = new Set();
|
|
3398
|
+
let changed = false;
|
|
3399
|
+
for (const s of sessions) {
|
|
3400
|
+
if (!isSessionActive(s)) continue;
|
|
3401
|
+
activeIds.add(s.id);
|
|
3402
|
+
if (onlyNew && (!primed || prevActiveSessionIds.has(s.id))) continue;
|
|
3403
|
+
if (collapsedProjectGroups.delete(s.project || '__ungrouped__')) changed = true;
|
|
3404
|
+
}
|
|
3405
|
+
prevActiveSessionIds = activeIds;
|
|
3406
|
+
if (changed) persistCollapsedGroups();
|
|
3407
|
+
// renderSessions() re-renders headers/containers from the updated set
|
|
3408
|
+
}
|
|
3409
|
+
|
|
3327
3410
|
function isGroupHeader(el) {
|
|
3328
3411
|
return el.classList.contains('project-group-header') || el.classList.contains('pinned-sub-header');
|
|
3329
3412
|
}
|
|
@@ -3385,8 +3468,7 @@ function setFocusZone(zone) {
|
|
|
3385
3468
|
// Clear all zone visuals
|
|
3386
3469
|
sidebar.classList.remove('sidebar-focused');
|
|
3387
3470
|
clearKbSelection();
|
|
3388
|
-
|
|
3389
|
-
if (selCard) selCard.classList.remove('selected');
|
|
3471
|
+
clearTaskSelection();
|
|
3390
3472
|
|
|
3391
3473
|
focusZone = zone;
|
|
3392
3474
|
if (zone === 'sidebar') {
|
|
@@ -3408,6 +3490,8 @@ function setFocusZone(zone) {
|
|
|
3408
3490
|
}
|
|
3409
3491
|
}
|
|
3410
3492
|
} else {
|
|
3493
|
+
// Focusing the board is an explicit nav gesture — restore the highlight.
|
|
3494
|
+
taskHighlightDimmed = false;
|
|
3411
3495
|
// Session changed while in sidebar — reset stale selection
|
|
3412
3496
|
if (selectedSessionId && selectedSessionId !== currentSessionId) {
|
|
3413
3497
|
selectedTaskId = null;
|
|
@@ -3710,6 +3794,8 @@ async function addNote(event, taskId, sessionId) {
|
|
|
3710
3794
|
function closeDetailPanel() {
|
|
3711
3795
|
detailPanel.classList.remove('visible');
|
|
3712
3796
|
document.getElementById('delete-task-btn').style.display = 'none';
|
|
3797
|
+
// Keep the task selected AND highlighted after closing (no dim-off).
|
|
3798
|
+
taskHighlightDimmed = false;
|
|
3713
3799
|
}
|
|
3714
3800
|
|
|
3715
3801
|
let deleteTaskId = null;
|
|
@@ -4563,7 +4649,10 @@ document.addEventListener('keydown', (e) => {
|
|
|
4563
4649
|
return;
|
|
4564
4650
|
}
|
|
4565
4651
|
if (e.key === 'Escape') {
|
|
4566
|
-
|
|
4652
|
+
// Plain unfocus — drop the keyboard cursor without jumping into the board
|
|
4653
|
+
document.querySelector('.sidebar').classList.remove('sidebar-focused');
|
|
4654
|
+
clearKbSelection();
|
|
4655
|
+
focusZone = 'board';
|
|
4567
4656
|
return;
|
|
4568
4657
|
}
|
|
4569
4658
|
}
|
|
@@ -4614,6 +4703,11 @@ document.addEventListener('keydown', (e) => {
|
|
|
4614
4703
|
if (detailPanel.classList.contains('visible')) closeDetailPanel();
|
|
4615
4704
|
else if (agentLogMode) exitAgentLogMode();
|
|
4616
4705
|
else if (messagePanelOpen) toggleMessagePanel();
|
|
4706
|
+
else {
|
|
4707
|
+
// Nothing open — plain unfocus: drop the task-card selection highlight
|
|
4708
|
+
clearTaskSelection();
|
|
4709
|
+
selectedTaskId = null;
|
|
4710
|
+
}
|
|
4617
4711
|
return;
|
|
4618
4712
|
}
|
|
4619
4713
|
|
|
@@ -5284,6 +5378,8 @@ function isWaitingFresh() {
|
|
|
5284
5378
|
function showWaitingDetail() {
|
|
5285
5379
|
if (!isWaitingFresh()) return;
|
|
5286
5380
|
currentMsgDetailIdx = MSG_DETAIL_WAITING_IDX;
|
|
5381
|
+
msgHighlightDimmed = false;
|
|
5382
|
+
highlightSelectedMsg();
|
|
5287
5383
|
const tool = currentWaiting.toolName || 'unknown';
|
|
5288
5384
|
const label = getWaitingLabel(currentWaiting.kind, tool);
|
|
5289
5385
|
const body = document.getElementById('msg-detail-body');
|
|
@@ -5923,9 +6019,9 @@ function showInfoModal(session, teamConfig, tasks, planContent, parentInfo) {
|
|
|
5923
6019
|
infoRows.push(['Team Config', teamConfig.configPath, { openPath: configDir, openFile: teamConfig.configPath }]);
|
|
5924
6020
|
}
|
|
5925
6021
|
const clickableStyle =
|
|
5926
|
-
|
|
6022
|
+
'font-family: var(--mono); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; color: var(--accent-text); text-decoration: underline; text-decoration-style: dotted; text-underline-offset: 3px;';
|
|
5927
6023
|
const plainStyle =
|
|
5928
|
-
|
|
6024
|
+
'font-family: var(--mono); font-size: 12px; color: var(--text-primary); user-select: all; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;';
|
|
5929
6025
|
html += `<div class="team-modal-meta info-grid">`;
|
|
5930
6026
|
infoRows.forEach(([label, value, opts]) => {
|
|
5931
6027
|
const copyVal = escapeHtml(value).replace(/"/g, '"');
|
package/public/style.css
CHANGED
|
@@ -18,16 +18,22 @@
|
|
|
18
18
|
--accent-text: #f0a070;
|
|
19
19
|
--accent-dim: rgba(232, 111, 51, 0.22);
|
|
20
20
|
--accent-glow: rgba(232, 111, 51, 0.55);
|
|
21
|
+
--gold: #d9b667;
|
|
21
22
|
--success: #3ecf8e;
|
|
22
23
|
--success-dim: rgba(62, 207, 142, 0.18);
|
|
23
24
|
--warning: #f0b429;
|
|
24
25
|
--warning-dim: rgba(240, 180, 41, 0.18);
|
|
26
|
+
--danger: #ef5350;
|
|
27
|
+
--danger-dim: rgba(239, 83, 80, 0.18);
|
|
25
28
|
--team: #60a5fa;
|
|
26
29
|
--team-dim: rgba(96, 165, 250, 0.18);
|
|
27
30
|
--plan: #86a886;
|
|
28
31
|
--plan-dim: rgba(134, 168, 134, 0.18);
|
|
29
32
|
--mono: 'IBM Plex Mono', monospace;
|
|
30
33
|
--serif: 'Playfair Display', serif;
|
|
34
|
+
/* aliases: many rules reference --font-mono/--font-serif; keep them on-brand */
|
|
35
|
+
--font-mono: var(--mono);
|
|
36
|
+
--font-serif: var(--serif);
|
|
31
37
|
}
|
|
32
38
|
|
|
33
39
|
* {
|
|
@@ -85,7 +91,7 @@ body::before {
|
|
|
85
91
|
.sidebar {
|
|
86
92
|
width: var(--sidebar-width, 300px);
|
|
87
93
|
background: var(--bg-surface);
|
|
88
|
-
border-right: 1px solid var(--border);
|
|
94
|
+
border-right: 1px solid color-mix(in srgb, var(--border) 35%, transparent);
|
|
89
95
|
box-shadow: 1px 0 12px rgba(0, 0, 0, 0.04);
|
|
90
96
|
display: flex;
|
|
91
97
|
flex-direction: column;
|
|
@@ -448,22 +454,15 @@ body::before {
|
|
|
448
454
|
padding: 12px 14px;
|
|
449
455
|
margin-bottom: 4px;
|
|
450
456
|
background: transparent;
|
|
451
|
-
border: 1px solid
|
|
457
|
+
border: 1px solid var(--border);
|
|
452
458
|
border-radius: 8px;
|
|
453
459
|
text-align: left;
|
|
454
460
|
cursor: pointer;
|
|
455
461
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
456
|
-
box-shadow:
|
|
457
|
-
0 1px 3px rgba(0, 0, 0, 0.12),
|
|
458
|
-
0 1px 2px rgba(0, 0, 0, 0.08);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
.session-item:hover {
|
|
462
|
-
background: var(--bg-hover);
|
|
463
462
|
}
|
|
464
463
|
|
|
465
464
|
.session-item.active {
|
|
466
|
-
background: var(--bg-
|
|
465
|
+
background: var(--bg-deep);
|
|
467
466
|
border-color: var(--accent);
|
|
468
467
|
}
|
|
469
468
|
|
|
@@ -510,7 +509,7 @@ body::before {
|
|
|
510
509
|
color: var(--text-secondary);
|
|
511
510
|
margin-top: 2px;
|
|
512
511
|
display: block;
|
|
513
|
-
font-family:
|
|
512
|
+
font-family: var(--mono);
|
|
514
513
|
white-space: nowrap;
|
|
515
514
|
overflow: hidden;
|
|
516
515
|
text-overflow: ellipsis;
|
|
@@ -656,10 +655,10 @@ body::before {
|
|
|
656
655
|
gap: 8px;
|
|
657
656
|
}
|
|
658
657
|
.detail-context-stats .stat-label {
|
|
659
|
-
color: var(--text-
|
|
658
|
+
color: var(--text-secondary);
|
|
660
659
|
}
|
|
661
660
|
.detail-context-stats .stat-value {
|
|
662
|
-
color: var(--text-
|
|
661
|
+
color: var(--text-primary);
|
|
663
662
|
}
|
|
664
663
|
.detail-context-stats .stat-divider {
|
|
665
664
|
grid-column: 1 / -1;
|
|
@@ -811,7 +810,7 @@ body::before {
|
|
|
811
810
|
|
|
812
811
|
/* #region HEADER */
|
|
813
812
|
.view-header {
|
|
814
|
-
padding:
|
|
813
|
+
padding: 12px 24px;
|
|
815
814
|
border-bottom: 1px solid var(--border);
|
|
816
815
|
background: var(--bg-surface);
|
|
817
816
|
display: flex;
|
|
@@ -821,9 +820,11 @@ body::before {
|
|
|
821
820
|
|
|
822
821
|
.view-title {
|
|
823
822
|
font-family: var(--serif);
|
|
824
|
-
font-size:
|
|
823
|
+
font-size: 20px;
|
|
825
824
|
font-weight: 400;
|
|
826
825
|
letter-spacing: -0.03em;
|
|
826
|
+
line-height: 1.15;
|
|
827
|
+
text-wrap: balance;
|
|
827
828
|
display: flex;
|
|
828
829
|
align-items: center;
|
|
829
830
|
gap: 12px;
|
|
@@ -1018,12 +1019,12 @@ body::before {
|
|
|
1018
1019
|
|
|
1019
1020
|
/* #region TASK_CARD */
|
|
1020
1021
|
.task-card {
|
|
1021
|
-
padding:
|
|
1022
|
-
padding-left:
|
|
1022
|
+
padding: 10px 12px;
|
|
1023
|
+
padding-left: 14px;
|
|
1023
1024
|
background: var(--bg-surface);
|
|
1024
1025
|
border: 1px solid var(--border);
|
|
1025
1026
|
border-left: 2px solid var(--text-muted);
|
|
1026
|
-
border-radius:
|
|
1027
|
+
border-radius: 6px;
|
|
1027
1028
|
cursor: pointer;
|
|
1028
1029
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
1029
1030
|
}
|
|
@@ -1067,18 +1068,18 @@ body::before {
|
|
|
1067
1068
|
.task-card.selected,
|
|
1068
1069
|
.task-card.selected:hover {
|
|
1069
1070
|
background: var(--bg-elevated);
|
|
1070
|
-
border-color: var(--
|
|
1071
|
-
border-left: 2px solid var(--
|
|
1071
|
+
border-color: var(--accent);
|
|
1072
|
+
border-left: 2px solid var(--accent);
|
|
1072
1073
|
box-shadow:
|
|
1073
|
-
0 0 0 1px var(--
|
|
1074
|
-
0 0 12px var(--
|
|
1074
|
+
0 0 0 1px var(--accent-dim),
|
|
1075
|
+
0 0 12px var(--accent-dim);
|
|
1075
1076
|
opacity: 1;
|
|
1076
1077
|
}
|
|
1077
1078
|
|
|
1078
1079
|
.task-id {
|
|
1079
1080
|
font-size: 11px;
|
|
1080
1081
|
color: var(--text-muted);
|
|
1081
|
-
margin-bottom:
|
|
1082
|
+
margin-bottom: 4px;
|
|
1082
1083
|
display: flex;
|
|
1083
1084
|
align-items: center;
|
|
1084
1085
|
gap: 8px;
|
|
@@ -1099,9 +1100,9 @@ body::before {
|
|
|
1099
1100
|
}
|
|
1100
1101
|
|
|
1101
1102
|
.task-title {
|
|
1102
|
-
font-size:
|
|
1103
|
+
font-size: 13px;
|
|
1103
1104
|
color: var(--text-primary);
|
|
1104
|
-
line-height: 1.
|
|
1105
|
+
line-height: 1.35;
|
|
1105
1106
|
}
|
|
1106
1107
|
|
|
1107
1108
|
.task-card.completed .task-title {
|
|
@@ -1112,15 +1113,15 @@ body::before {
|
|
|
1112
1113
|
.task-session {
|
|
1113
1114
|
font-size: 12px;
|
|
1114
1115
|
color: var(--accent);
|
|
1115
|
-
margin-top:
|
|
1116
|
+
margin-top: 4px;
|
|
1116
1117
|
}
|
|
1117
1118
|
|
|
1118
1119
|
.task-active {
|
|
1119
1120
|
display: flex;
|
|
1120
1121
|
align-items: center;
|
|
1121
1122
|
gap: 6px;
|
|
1122
|
-
margin-top:
|
|
1123
|
-
padding-top:
|
|
1123
|
+
margin-top: 8px;
|
|
1124
|
+
padding-top: 8px;
|
|
1124
1125
|
border-top: 1px solid var(--border);
|
|
1125
1126
|
font-size: 11px;
|
|
1126
1127
|
font-weight: 500;
|
|
@@ -1147,7 +1148,7 @@ body::before {
|
|
|
1147
1148
|
font-size: 12px;
|
|
1148
1149
|
font-weight: 450;
|
|
1149
1150
|
color: var(--text-tertiary);
|
|
1150
|
-
margin-top:
|
|
1151
|
+
margin-top: 6px;
|
|
1151
1152
|
display: -webkit-box;
|
|
1152
1153
|
-webkit-line-clamp: 2;
|
|
1153
1154
|
-webkit-box-orient: vertical;
|
|
@@ -1164,8 +1165,8 @@ body::before {
|
|
|
1164
1165
|
width: var(--detail-panel-width, 540px);
|
|
1165
1166
|
height: 100vh;
|
|
1166
1167
|
background: var(--bg-surface);
|
|
1167
|
-
border-left: 1px solid var(--border);
|
|
1168
|
-
box-shadow: -
|
|
1168
|
+
border-left: 1px solid color-mix(in srgb, var(--border) 35%, transparent);
|
|
1169
|
+
box-shadow: -1px 0 12px rgba(0, 0, 0, 0.04);
|
|
1169
1170
|
display: none;
|
|
1170
1171
|
flex-direction: column;
|
|
1171
1172
|
z-index: 100;
|
|
@@ -1441,13 +1442,10 @@ body::before {
|
|
|
1441
1442
|
font-size: 12px;
|
|
1442
1443
|
}
|
|
1443
1444
|
|
|
1444
|
-
.detail-desc pre code.hljs
|
|
1445
|
-
padding: 12px;
|
|
1446
|
-
border-radius: 6px;
|
|
1447
|
-
}
|
|
1448
|
-
|
|
1445
|
+
.detail-desc pre code.hljs,
|
|
1449
1446
|
.detail-desc pre code {
|
|
1450
|
-
background: var(--
|
|
1447
|
+
background: color-mix(in srgb, var(--text-muted) 8%, transparent);
|
|
1448
|
+
border: 1px solid color-mix(in srgb, var(--border) 55%, transparent);
|
|
1451
1449
|
padding: 12px;
|
|
1452
1450
|
border-radius: 6px;
|
|
1453
1451
|
display: block;
|
|
@@ -1455,9 +1453,10 @@ body::before {
|
|
|
1455
1453
|
}
|
|
1456
1454
|
|
|
1457
1455
|
.detail-desc code {
|
|
1458
|
-
background: var(--
|
|
1459
|
-
|
|
1460
|
-
|
|
1456
|
+
background: color-mix(in srgb, var(--text-muted) 12%, transparent);
|
|
1457
|
+
border: 1px solid color-mix(in srgb, var(--border) 45%, transparent);
|
|
1458
|
+
padding: 1px 5px;
|
|
1459
|
+
border-radius: 4px;
|
|
1461
1460
|
font-size: 0.9em;
|
|
1462
1461
|
}
|
|
1463
1462
|
|
|
@@ -1588,18 +1587,19 @@ body::before {
|
|
|
1588
1587
|
align-items: center;
|
|
1589
1588
|
justify-content: center;
|
|
1590
1589
|
font-size: 11px;
|
|
1591
|
-
background:
|
|
1590
|
+
background: transparent;
|
|
1592
1591
|
border: 1px solid transparent;
|
|
1593
1592
|
border-radius: 4px;
|
|
1594
|
-
color: var(--
|
|
1593
|
+
color: var(--text-muted);
|
|
1595
1594
|
cursor: pointer;
|
|
1596
1595
|
flex-shrink: 0;
|
|
1597
1596
|
transition: all 0.15s ease;
|
|
1598
1597
|
}
|
|
1599
1598
|
|
|
1600
1599
|
.team-info-btn:hover {
|
|
1601
|
-
background: var(--
|
|
1602
|
-
border-color: var(--
|
|
1600
|
+
background: var(--bg-hover);
|
|
1601
|
+
border-color: var(--border);
|
|
1602
|
+
color: var(--text-secondary);
|
|
1603
1603
|
}
|
|
1604
1604
|
|
|
1605
1605
|
.plan-indicator {
|
|
@@ -1737,10 +1737,10 @@ body::before {
|
|
|
1737
1737
|
}
|
|
1738
1738
|
|
|
1739
1739
|
.loop-field-val code {
|
|
1740
|
-
background: var(--bg);
|
|
1740
|
+
background: var(--bg-deep);
|
|
1741
1741
|
padding: 2px 6px;
|
|
1742
1742
|
border-radius: 4px;
|
|
1743
|
-
font-family:
|
|
1743
|
+
font-family: var(--mono);
|
|
1744
1744
|
font-size: 12px;
|
|
1745
1745
|
}
|
|
1746
1746
|
|
|
@@ -1830,8 +1830,8 @@ body::before {
|
|
|
1830
1830
|
font-size: 11px;
|
|
1831
1831
|
font-weight: 500;
|
|
1832
1832
|
border-radius: 999px;
|
|
1833
|
-
border: 1px solid var(--border
|
|
1834
|
-
background: var(--bg-
|
|
1833
|
+
border: 1px solid var(--border);
|
|
1834
|
+
background: var(--bg-surface);
|
|
1835
1835
|
color: var(--text-secondary);
|
|
1836
1836
|
white-space: nowrap;
|
|
1837
1837
|
line-height: 1.4;
|
|
@@ -2002,9 +2002,9 @@ body::before {
|
|
|
2002
2002
|
right: 0;
|
|
2003
2003
|
width: var(--message-panel-width, 540px);
|
|
2004
2004
|
height: 100vh;
|
|
2005
|
-
background: var(--bg-
|
|
2006
|
-
border-left: 1px solid var(--border);
|
|
2007
|
-
box-shadow: -
|
|
2005
|
+
background: var(--bg-deep);
|
|
2006
|
+
border-left: 1px solid color-mix(in srgb, var(--border) 35%, transparent);
|
|
2007
|
+
box-shadow: -1px 0 12px rgba(0, 0, 0, 0.04);
|
|
2008
2008
|
display: none;
|
|
2009
2009
|
flex-direction: column;
|
|
2010
2010
|
z-index: 99;
|
|
@@ -2047,22 +2047,39 @@ body::before {
|
|
|
2047
2047
|
padding: 8px 10px;
|
|
2048
2048
|
border-radius: 6px;
|
|
2049
2049
|
font-size: 12px;
|
|
2050
|
-
line-height: 1.
|
|
2051
|
-
background: var(--bg-
|
|
2052
|
-
border: 1px solid var(--border);
|
|
2050
|
+
line-height: 1.4;
|
|
2051
|
+
background: var(--bg-surface);
|
|
2052
|
+
border: 1px solid color-mix(in srgb, var(--border) 65%, transparent);
|
|
2053
2053
|
}
|
|
2054
2054
|
.msg-item.msg-user {
|
|
2055
|
-
border-left: 3px solid var(--
|
|
2055
|
+
border-left: 3px solid var(--gold);
|
|
2056
|
+
}
|
|
2057
|
+
.msg-queued-tag {
|
|
2058
|
+
margin-left: 8px;
|
|
2059
|
+
padding: 0 5px;
|
|
2060
|
+
border-radius: 3px;
|
|
2061
|
+
font-size: 9px;
|
|
2062
|
+
letter-spacing: 0.06em;
|
|
2063
|
+
text-transform: uppercase;
|
|
2064
|
+
color: var(--gold);
|
|
2065
|
+
background: color-mix(in srgb, var(--gold) 14%, transparent);
|
|
2056
2066
|
}
|
|
2057
2067
|
.msg-item.msg-assistant {
|
|
2058
2068
|
border-left: 3px solid var(--accent);
|
|
2059
2069
|
}
|
|
2060
2070
|
.msg-item.msg-tool {
|
|
2061
|
-
border-left:
|
|
2071
|
+
border-left: 2px solid color-mix(in srgb, var(--text-muted) 45%, transparent);
|
|
2062
2072
|
font-size: 11px;
|
|
2063
2073
|
padding: 5px 10px;
|
|
2064
2074
|
color: var(--text-secondary);
|
|
2065
2075
|
}
|
|
2076
|
+
.msg-item.selected,
|
|
2077
|
+
.msg-item.selected:hover {
|
|
2078
|
+
background: var(--bg-elevated);
|
|
2079
|
+
box-shadow:
|
|
2080
|
+
0 0 0 1px var(--accent),
|
|
2081
|
+
0 0 12px var(--accent-dim);
|
|
2082
|
+
}
|
|
2066
2083
|
.msg-icon {
|
|
2067
2084
|
flex-shrink: 0;
|
|
2068
2085
|
width: 16px;
|
|
@@ -2093,7 +2110,7 @@ body::before {
|
|
|
2093
2110
|
display: inline-flex;
|
|
2094
2111
|
align-items: center;
|
|
2095
2112
|
justify-content: center;
|
|
2096
|
-
background: var(--hover);
|
|
2113
|
+
background: var(--bg-hover);
|
|
2097
2114
|
color: var(--text-secondary);
|
|
2098
2115
|
font-size: 10px;
|
|
2099
2116
|
font-weight: 600;
|
|
@@ -2213,7 +2230,7 @@ body::before {
|
|
|
2213
2230
|
}
|
|
2214
2231
|
.pinned-item-unpin:hover {
|
|
2215
2232
|
opacity: 1;
|
|
2216
|
-
color: var(--danger
|
|
2233
|
+
color: var(--danger);
|
|
2217
2234
|
}
|
|
2218
2235
|
.msg-pin-btn.pinned svg,
|
|
2219
2236
|
#msg-detail-pin-btn.active svg,
|
|
@@ -2313,7 +2330,7 @@ body::before {
|
|
|
2313
2330
|
}
|
|
2314
2331
|
.agent-tab-copy {
|
|
2315
2332
|
margin-left: auto;
|
|
2316
|
-
background: var(--
|
|
2333
|
+
background: var(--bg-hover);
|
|
2317
2334
|
border: 1px solid var(--border);
|
|
2318
2335
|
border-radius: 6px;
|
|
2319
2336
|
padding: 4px 6px;
|
|
@@ -2431,8 +2448,9 @@ body::before {
|
|
|
2431
2448
|
font-size: 0.85rem;
|
|
2432
2449
|
}
|
|
2433
2450
|
.msg-detail-pre-tinted {
|
|
2434
|
-
background:
|
|
2435
|
-
border
|
|
2451
|
+
background: color-mix(in srgb, var(--text-muted) 8%, transparent);
|
|
2452
|
+
border: 1px solid color-mix(in srgb, var(--border) 55%, transparent);
|
|
2453
|
+
border-radius: 6px;
|
|
2436
2454
|
padding: 8px 10px;
|
|
2437
2455
|
}
|
|
2438
2456
|
.expand-toggle-btn {
|
|
@@ -2546,7 +2564,7 @@ body::before {
|
|
|
2546
2564
|
.msg-waiting-discard:hover {
|
|
2547
2565
|
/* biome-ignore lint/complexity/noImportantStyles: override parent hover opacity */
|
|
2548
2566
|
opacity: 1 !important;
|
|
2549
|
-
color: var(--danger
|
|
2567
|
+
color: var(--danger);
|
|
2550
2568
|
}
|
|
2551
2569
|
.msg-waiting .msg-text {
|
|
2552
2570
|
font-weight: 500;
|
|
@@ -2666,7 +2684,7 @@ body::before {
|
|
|
2666
2684
|
display: inline-block;
|
|
2667
2685
|
padding: 2px 10px;
|
|
2668
2686
|
border-radius: 4px;
|
|
2669
|
-
background: var(--bg-
|
|
2687
|
+
background: var(--bg-surface);
|
|
2670
2688
|
color: var(--text-secondary);
|
|
2671
2689
|
font-size: 0.8rem;
|
|
2672
2690
|
font-weight: 600;
|
|
@@ -2706,7 +2724,7 @@ body::before {
|
|
|
2706
2724
|
left: 50%;
|
|
2707
2725
|
transform: translateX(-50%);
|
|
2708
2726
|
background: var(--accent);
|
|
2709
|
-
color: var(--bg);
|
|
2727
|
+
color: var(--bg-deep);
|
|
2710
2728
|
border: none;
|
|
2711
2729
|
border-radius: 16px;
|
|
2712
2730
|
padding: 6px 16px;
|
|
@@ -2722,7 +2740,7 @@ body::before {
|
|
|
2722
2740
|
.msg-loading-more {
|
|
2723
2741
|
text-align: center;
|
|
2724
2742
|
padding: 8px;
|
|
2725
|
-
color: var(--text-
|
|
2743
|
+
color: var(--text-muted);
|
|
2726
2744
|
font-size: 12px;
|
|
2727
2745
|
}
|
|
2728
2746
|
.msg-limit-banner {
|
|
@@ -2783,18 +2801,28 @@ body::before {
|
|
|
2783
2801
|
flex-direction: column;
|
|
2784
2802
|
gap: 3px;
|
|
2785
2803
|
padding: 8px 12px;
|
|
2786
|
-
background: var(--bg-
|
|
2787
|
-
border: 1px solid var(--border);
|
|
2804
|
+
background: var(--bg-deep);
|
|
2805
|
+
border: 1px solid color-mix(in srgb, var(--border) 55%, transparent);
|
|
2788
2806
|
border-radius: 8px;
|
|
2789
2807
|
white-space: nowrap;
|
|
2790
2808
|
min-width: 0;
|
|
2791
2809
|
overflow: hidden;
|
|
2792
|
-
transition:
|
|
2810
|
+
transition:
|
|
2811
|
+
opacity 0.3s,
|
|
2812
|
+
border-color 0.15s ease;
|
|
2793
2813
|
cursor: pointer;
|
|
2794
2814
|
position: relative;
|
|
2795
2815
|
}
|
|
2796
2816
|
.agent-card:hover {
|
|
2817
|
+
border-color: color-mix(in srgb, var(--accent) 60%, var(--border));
|
|
2818
|
+
}
|
|
2819
|
+
.agent-card.selected,
|
|
2820
|
+
.agent-card.selected:hover {
|
|
2821
|
+
background: var(--bg-elevated);
|
|
2797
2822
|
border-color: var(--accent);
|
|
2823
|
+
box-shadow:
|
|
2824
|
+
0 0 0 1px var(--accent-dim),
|
|
2825
|
+
0 0 12px var(--accent-dim);
|
|
2798
2826
|
}
|
|
2799
2827
|
.agent-card.fading {
|
|
2800
2828
|
opacity: 0.4;
|
|
@@ -2867,7 +2895,7 @@ body::before {
|
|
|
2867
2895
|
.agent-dismiss-btn {
|
|
2868
2896
|
font-size: 12px;
|
|
2869
2897
|
padding: 4px 10px;
|
|
2870
|
-
background: var(--bg-
|
|
2898
|
+
background: var(--bg-elevated);
|
|
2871
2899
|
color: var(--text-secondary);
|
|
2872
2900
|
border: 1px solid var(--border);
|
|
2873
2901
|
}
|
|
@@ -2983,10 +3011,10 @@ body::before {
|
|
|
2983
3011
|
/* #region LIGHT_THEME */
|
|
2984
3012
|
body.light {
|
|
2985
3013
|
--bg-deep: #e8e6e3;
|
|
2986
|
-
--bg-surface: #
|
|
2987
|
-
--bg-elevated: #
|
|
3014
|
+
--bg-surface: #efede9;
|
|
3015
|
+
--bg-elevated: #fbfaf9;
|
|
2988
3016
|
--bg-hover: #d2d0cc;
|
|
2989
|
-
--border: #
|
|
3017
|
+
--border: #cfcbc4;
|
|
2990
3018
|
--text-primary: #0a0a0a;
|
|
2991
3019
|
--text-secondary: #444444;
|
|
2992
3020
|
--text-tertiary: #666666;
|
|
@@ -2994,10 +3022,13 @@ body.light {
|
|
|
2994
3022
|
--accent-text: #b85a20;
|
|
2995
3023
|
--accent-dim: rgba(232, 111, 51, 0.18);
|
|
2996
3024
|
--accent-glow: rgba(232, 111, 51, 0.5);
|
|
3025
|
+
--gold: #a8842f;
|
|
2997
3026
|
--success: #1a8a5a;
|
|
2998
3027
|
--success-dim: rgba(26, 138, 90, 0.15);
|
|
2999
3028
|
--warning: #b07d0a;
|
|
3000
3029
|
--warning-dim: rgba(176, 125, 10, 0.15);
|
|
3030
|
+
--danger: #c0392b;
|
|
3031
|
+
--danger-dim: rgba(192, 57, 43, 0.15);
|
|
3001
3032
|
--plan: #5a7a5a;
|
|
3002
3033
|
--plan-dim: rgba(90, 122, 90, 0.15);
|
|
3003
3034
|
}
|
|
@@ -3126,7 +3157,9 @@ body.light .msg-assistant .msg-text {
|
|
|
3126
3157
|
max-width: 860px;
|
|
3127
3158
|
max-height: 90vh;
|
|
3128
3159
|
padding: 24px;
|
|
3129
|
-
box-shadow:
|
|
3160
|
+
box-shadow:
|
|
3161
|
+
0 16px 44px rgba(0, 0, 0, 0.38),
|
|
3162
|
+
0 4px 12px rgba(0, 0, 0, 0.2);
|
|
3130
3163
|
overflow-y: auto;
|
|
3131
3164
|
}
|
|
3132
3165
|
|
|
@@ -3209,7 +3242,7 @@ body.light .msg-assistant .msg-text {
|
|
|
3209
3242
|
}
|
|
3210
3243
|
|
|
3211
3244
|
.preview-fm .fm-v {
|
|
3212
|
-
color: var(--text
|
|
3245
|
+
color: var(--text-primary);
|
|
3213
3246
|
word-break: break-word;
|
|
3214
3247
|
white-space: pre-wrap;
|
|
3215
3248
|
}
|
|
@@ -3397,13 +3430,14 @@ select.form-input option:checked {
|
|
|
3397
3430
|
}
|
|
3398
3431
|
|
|
3399
3432
|
.btn-secondary {
|
|
3400
|
-
background:
|
|
3401
|
-
color: var(--text-
|
|
3433
|
+
background: transparent;
|
|
3434
|
+
color: var(--text-secondary);
|
|
3402
3435
|
border: 1px solid var(--border);
|
|
3403
3436
|
}
|
|
3404
3437
|
|
|
3405
3438
|
.btn-secondary:hover {
|
|
3406
3439
|
background: var(--bg-hover);
|
|
3440
|
+
color: var(--text-primary);
|
|
3407
3441
|
border-color: var(--text-muted);
|
|
3408
3442
|
}
|
|
3409
3443
|
|
|
@@ -3434,7 +3468,7 @@ select.form-input option:checked {
|
|
|
3434
3468
|
display: flex;
|
|
3435
3469
|
flex-direction: column;
|
|
3436
3470
|
align-items: center;
|
|
3437
|
-
background: var(--bg-
|
|
3471
|
+
background: var(--bg-surface);
|
|
3438
3472
|
border: 1px solid var(--border);
|
|
3439
3473
|
border-radius: 8px;
|
|
3440
3474
|
padding: 10px 16px;
|
|
@@ -3449,7 +3483,7 @@ select.form-input option:checked {
|
|
|
3449
3483
|
}
|
|
3450
3484
|
|
|
3451
3485
|
.tool-stats-chip-val.failed {
|
|
3452
|
-
color: var(--
|
|
3486
|
+
color: var(--danger);
|
|
3453
3487
|
}
|
|
3454
3488
|
|
|
3455
3489
|
.tool-stats-chip-lbl {
|
|
@@ -3490,14 +3524,14 @@ select.form-input option:checked {
|
|
|
3490
3524
|
}
|
|
3491
3525
|
|
|
3492
3526
|
.tool-stats-table tbody tr:nth-child(even) {
|
|
3493
|
-
background: var(--bg-
|
|
3527
|
+
background: var(--bg-surface);
|
|
3494
3528
|
}
|
|
3495
3529
|
|
|
3496
3530
|
.tool-stats-table tbody td {
|
|
3497
3531
|
padding: 6px 10px;
|
|
3498
3532
|
text-align: right;
|
|
3499
3533
|
color: var(--text-primary);
|
|
3500
|
-
border-bottom: 1px solid var(--border
|
|
3534
|
+
border-bottom: 1px solid var(--border);
|
|
3501
3535
|
}
|
|
3502
3536
|
|
|
3503
3537
|
.tool-stats-table tbody td.tool-name {
|
|
@@ -3551,7 +3585,7 @@ select.form-input option:checked {
|
|
|
3551
3585
|
width: 48px;
|
|
3552
3586
|
height: 6px;
|
|
3553
3587
|
border-radius: 3px;
|
|
3554
|
-
background: var(--bg-hover
|
|
3588
|
+
background: var(--bg-hover);
|
|
3555
3589
|
flex-shrink: 0;
|
|
3556
3590
|
overflow: hidden;
|
|
3557
3591
|
}
|
|
@@ -3584,11 +3618,11 @@ select.form-input option:checked {
|
|
|
3584
3618
|
max-height: 50vh;
|
|
3585
3619
|
resize: vertical;
|
|
3586
3620
|
padding: 12px;
|
|
3587
|
-
font-family:
|
|
3621
|
+
font-family: var(--mono);
|
|
3588
3622
|
font-size: 13px;
|
|
3589
3623
|
line-height: 1.6;
|
|
3590
3624
|
color: var(--text-primary);
|
|
3591
|
-
background: var(--bg-
|
|
3625
|
+
background: var(--bg-deep);
|
|
3592
3626
|
border: 1px solid var(--border);
|
|
3593
3627
|
border-radius: 8px;
|
|
3594
3628
|
outline: none;
|
|
@@ -3842,20 +3876,23 @@ select.form-input option:checked {
|
|
|
3842
3876
|
@media (prefers-color-scheme: light) {
|
|
3843
3877
|
body:not(.dark-forced) {
|
|
3844
3878
|
--bg-deep: #e8e6e3;
|
|
3845
|
-
--bg-surface: #
|
|
3846
|
-
--bg-elevated: #
|
|
3879
|
+
--bg-surface: #efede9;
|
|
3880
|
+
--bg-elevated: #fbfaf9;
|
|
3847
3881
|
--bg-hover: #d2d0cc;
|
|
3848
|
-
--border: #
|
|
3882
|
+
--border: #cfcbc4;
|
|
3849
3883
|
--text-primary: #0a0a0a;
|
|
3850
3884
|
--text-secondary: #444444;
|
|
3851
3885
|
--text-tertiary: #666666;
|
|
3852
3886
|
--text-muted: #888888;
|
|
3853
3887
|
--accent-dim: rgba(232, 111, 51, 0.18);
|
|
3854
3888
|
--accent-glow: rgba(232, 111, 51, 0.5);
|
|
3889
|
+
--gold: #a8842f;
|
|
3855
3890
|
--success: #1a8a5a;
|
|
3856
3891
|
--success-dim: rgba(26, 138, 90, 0.15);
|
|
3857
3892
|
--warning: #b07d0a;
|
|
3858
3893
|
--warning-dim: rgba(176, 125, 10, 0.15);
|
|
3894
|
+
--danger: #c0392b;
|
|
3895
|
+
--danger-dim: rgba(192, 57, 43, 0.15);
|
|
3859
3896
|
--plan: #5a7a5a;
|
|
3860
3897
|
--plan-dim: rgba(90, 122, 90, 0.15);
|
|
3861
3898
|
}
|
|
@@ -4184,11 +4221,6 @@ pre.mermaid svg {
|
|
|
4184
4221
|
width: 0;
|
|
4185
4222
|
}
|
|
4186
4223
|
|
|
4187
|
-
.sidebar-focused .session-item.active {
|
|
4188
|
-
background: transparent;
|
|
4189
|
-
border-color: var(--accent);
|
|
4190
|
-
}
|
|
4191
|
-
|
|
4192
4224
|
.sidebar-focused .session-item.active::before {
|
|
4193
4225
|
width: 0;
|
|
4194
4226
|
}
|
|
@@ -4197,8 +4229,8 @@ pre.mermaid svg {
|
|
|
4197
4229
|
.session-item.kb-selected:hover,
|
|
4198
4230
|
.session-item.active.kb-selected {
|
|
4199
4231
|
background: var(--bg-elevated);
|
|
4200
|
-
border-color: var(--
|
|
4201
|
-
box-shadow: 0 0 0 1px var(--
|
|
4232
|
+
border-color: var(--accent);
|
|
4233
|
+
box-shadow: 0 0 0 1px var(--accent-dim);
|
|
4202
4234
|
}
|
|
4203
4235
|
|
|
4204
4236
|
.session-item.kb-selected::before {
|
package/server.js
CHANGED
|
@@ -22,6 +22,7 @@ const {
|
|
|
22
22
|
extractModelFromTranscript,
|
|
23
23
|
readFullToolResult,
|
|
24
24
|
readUserImage,
|
|
25
|
+
readCachedImage,
|
|
25
26
|
updateLoopInfo,
|
|
26
27
|
buildLoopInfoFromState
|
|
27
28
|
} = require('./lib/parsers');
|
|
@@ -1912,6 +1913,14 @@ app.get('/api/sessions/:sessionId/user-image/:msgUuid/:blockIndex', (req, res) =
|
|
|
1912
1913
|
res.end(buf);
|
|
1913
1914
|
});
|
|
1914
1915
|
|
|
1916
|
+
app.get('/api/sessions/:sessionId/cached-image/:n', (req, res) => {
|
|
1917
|
+
const img = readCachedImage(req.params.sessionId, req.params.n);
|
|
1918
|
+
if (!img) return res.status(404).end();
|
|
1919
|
+
res.setHeader('Content-Type', img.mediaType);
|
|
1920
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
1921
|
+
res.end(img.buffer);
|
|
1922
|
+
});
|
|
1923
|
+
|
|
1915
1924
|
app.get('/api/version', (req, res) => {
|
|
1916
1925
|
const pkg = require('./package.json');
|
|
1917
1926
|
res.json({ version: pkg.version });
|