claude-simple 0.2.24 → 0.2.26
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/README.md +25 -2
- package/client.js +20 -3
- package/dist/app.0.2.26.min.js +1179 -0
- package/dist/index.html +2 -2
- package/dist/style.0.2.26.min.css +1851 -0
- package/package.json +1 -1
- package/public/api.js +10 -3
- package/public/app.js +1 -1
- package/public/chat.js +260 -12
- package/public/files.js +50 -0
- package/public/index.html +2 -2
- package/public/locks.js +139 -0
- package/public/privacy.html +111 -0
- package/public/router.js +5 -0
- package/public/settings.js +312 -63
- package/public/shell/boot.js +3 -0
- package/public/shell/notes.js +11 -3
- package/public/shell/session-list.js +50 -2
- package/public/shell/session-nav.js +49 -12
- package/public/shell/templates.js +5 -1
- package/public/shell.js +16 -4
- package/public/state.js +6 -2
- package/public/style.css +227 -3
- package/public/sw.js +3 -3
- package/public/ui.min.css +1 -1
- package/src/agent-env.js +211 -0
- package/src/agents/claude.js +10 -3
- package/src/agents/grok.js +24 -0
- package/src/agents/meta-agent.js +89 -17
- package/src/agents/models.js +30 -1
- package/src/agents/sessions.js +12 -2
- package/src/app.js +512 -25
- package/src/collab.js +267 -0
- package/src/runtime/external-session-sync.js +106 -0
- package/src/runtime/protocol.js +11 -2
- package/dist/app.0.2.24.min.js +0 -1104
- package/dist/style.0.2.24.min.css +0 -1627
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { batch, esc, $ } from '../lib.js';
|
|
3
3
|
import {
|
|
4
4
|
sessionsData, sessionFilter, filesPath, viewingFile, filteredSessions,
|
|
5
|
-
currentProject, currentTab, filesRoot, gitRoot, currentAgent, setAgent,
|
|
5
|
+
currentProject, currentTab, filesRoot, gitRoot, currentAgent, setAgent, isProcessing,
|
|
6
6
|
selectedMachineId, setSelectedMachine, AGENT_LABELS, ctx, hubMode,
|
|
7
7
|
} from '../state.js';
|
|
8
8
|
import { api } from '../api.js';
|
|
@@ -10,13 +10,32 @@ import { getLastSessionContext, setLastSessionContext } from './session-context.
|
|
|
10
10
|
import {
|
|
11
11
|
connectWS, clearMessages, appendMsg, appendSystemMsg, renderToolUse,
|
|
12
12
|
appendHistoryTokenBar, appendContextBar, flushToolBatch, coerceTs,
|
|
13
|
-
stashComposerDraft, restoreComposerDraft, discardComposerAttachments, parkQueuedMessages, detachProcessingForSessionSwitch,
|
|
13
|
+
stashComposerDraft, restoreComposerDraft, discardComposerAttachments, parkQueuedMessages, detachProcessingForSessionSwitch, setExternalSessionReadOnly,
|
|
14
14
|
} from '../chat.js';
|
|
15
|
+
import { acquireSessionLock, releaseSessionLock } from '../locks.js';
|
|
15
16
|
import { setHash, syncHash } from '../router.js';
|
|
16
17
|
import { refreshModelSelect } from './session-list.js';
|
|
17
18
|
import { renderSessionNotesBar } from './notes.js';
|
|
18
19
|
import { addRecentSession, updateDashboard, getDashboardSessions } from './dashboard.js';
|
|
19
20
|
|
|
21
|
+
// A terminal-owned agent writes its native transcript independently of this
|
|
22
|
+
// UI. Keep just the currently open session hot and coalesce file updates into
|
|
23
|
+
// one fresh history read, rather than attempting to replay every vendor JSONL
|
|
24
|
+
// event in the browser.
|
|
25
|
+
let activeExternalHistory = null;
|
|
26
|
+
let externalHistoryTimer = null;
|
|
27
|
+
|
|
28
|
+
document.addEventListener('external-session-updated', ({ detail }) => {
|
|
29
|
+
const active = activeExternalHistory;
|
|
30
|
+
if (!active || !detail || detail.sessionId !== active.sessionId) return;
|
|
31
|
+
if (detail.agent && active.agent && detail.agent !== active.agent) return;
|
|
32
|
+
if (externalHistoryTimer) clearTimeout(externalHistoryTimer);
|
|
33
|
+
externalHistoryTimer = setTimeout(() => {
|
|
34
|
+
externalHistoryTimer = null;
|
|
35
|
+
activeExternalHistory?.refresh();
|
|
36
|
+
}, 140);
|
|
37
|
+
});
|
|
38
|
+
|
|
20
39
|
/** Show which edge machine Files/Git/Shell paths belong to. */
|
|
21
40
|
function syncMachinePathHints(machineId) {
|
|
22
41
|
for (const id of ['files-machine-hint', 'git-machine-hint']) {
|
|
@@ -35,6 +54,9 @@ function syncMachinePathHints(machineId) {
|
|
|
35
54
|
}
|
|
36
55
|
|
|
37
56
|
export function openProject(project) {
|
|
57
|
+
activeExternalHistory = null;
|
|
58
|
+
releaseSessionLock();
|
|
59
|
+
setExternalSessionReadOnly(false);
|
|
38
60
|
stashComposerDraft();
|
|
39
61
|
parkQueuedMessages();
|
|
40
62
|
discardComposerAttachments();
|
|
@@ -67,6 +89,9 @@ export function openProject(project) {
|
|
|
67
89
|
}
|
|
68
90
|
|
|
69
91
|
export function goHome() {
|
|
92
|
+
activeExternalHistory = null;
|
|
93
|
+
releaseSessionLock();
|
|
94
|
+
setExternalSessionReadOnly(false);
|
|
70
95
|
stashComposerDraft();
|
|
71
96
|
parkQueuedMessages();
|
|
72
97
|
discardComposerAttachments();
|
|
@@ -144,6 +169,8 @@ export async function resumeSession(sid, cwd, configDir, agent, machineId, opts
|
|
|
144
169
|
}
|
|
145
170
|
|
|
146
171
|
ctx.sessionId = sid;
|
|
172
|
+
setExternalSessionReadOnly(false);
|
|
173
|
+
acquireSessionLock({ sessionId: sid, agent: resolvedAgent, machineId: resolvedMachine }).catch(() => {});
|
|
147
174
|
ctx.configDir = resolvedConfig;
|
|
148
175
|
ctx.agent = resolvedAgent;
|
|
149
176
|
setAgent(resolvedAgent);
|
|
@@ -153,7 +180,6 @@ export async function resumeSession(sid, cwd, configDir, agent, machineId, opts
|
|
|
153
180
|
// Default path = session cwd on the target machine (set immediately; no await)
|
|
154
181
|
let edgeCwd = resolvedCwd;
|
|
155
182
|
applyProjectPaths(edgeCwd, resolvedMachine, sid);
|
|
156
|
-
connectWS();
|
|
157
183
|
|
|
158
184
|
// Background: normalize path on the edge (does not block chat)
|
|
159
185
|
if (edgeCwd) {
|
|
@@ -169,6 +195,9 @@ export async function resumeSession(sid, cwd, configDir, agent, machineId, opts
|
|
|
169
195
|
|
|
170
196
|
// Switch to target tab BEFORE history (files/git/shell usable immediately)
|
|
171
197
|
clearMessages();
|
|
198
|
+
// Subscribe after clearing the prior transcript. A queued Grok snapshot can
|
|
199
|
+
// arrive immediately on socket-open and must belong to the new history.
|
|
200
|
+
connectWS();
|
|
172
201
|
switchTab(targetTab);
|
|
173
202
|
{
|
|
174
203
|
const cur = sessionsData.peek();
|
|
@@ -227,7 +256,7 @@ export async function resumeSession(sid, cwd, configDir, agent, machineId, opts
|
|
|
227
256
|
? m.content.map(p => p?.text || '').filter(Boolean).join('\n')
|
|
228
257
|
: String(m.content ?? ''));
|
|
229
258
|
if (content) {
|
|
230
|
-
appendMsg(m.role === 'user' ? 'user' : 'assistant', m.role === 'user' ? 'You' : label, content, { ts });
|
|
259
|
+
appendMsg(m.role === 'user' ? 'user' : 'assistant', m.role === 'user' ? 'You' : label, content, { ts, historyIndex: i });
|
|
231
260
|
}
|
|
232
261
|
} else if (typ === 'tool_use' || m.name) {
|
|
233
262
|
renderToolUse({ name: m.name || 'tool', input: m.input ?? m.arguments ?? {} }, { ts });
|
|
@@ -252,14 +281,13 @@ export async function resumeSession(sid, cwd, configDir, agent, machineId, opts
|
|
|
252
281
|
paintChunk();
|
|
253
282
|
};
|
|
254
283
|
|
|
255
|
-
const loadHistory = async () => {
|
|
284
|
+
const loadHistory = async ({ preferCache = true } = {}) => {
|
|
256
285
|
// Instant re-open from in-memory cache (never trust empty cache — race/stale)
|
|
257
286
|
const cached = historyCache.get(cacheKey);
|
|
258
|
-
if (cached && cached.msgs?.length && Date.now() - cached.at < HISTORY_CACHE_TTL) {
|
|
287
|
+
if (preferCache && cached && cached.msgs?.length && Date.now() - cached.at < HISTORY_CACHE_TTL) {
|
|
259
288
|
paintMessages(cached.msgs, cached.context, cached.meta);
|
|
260
|
-
return;
|
|
261
289
|
}
|
|
262
|
-
appendSystemMsg(`Loading ${label} history…`);
|
|
290
|
+
if (!cached?.msgs?.length) appendSystemMsg(`Loading ${label} history…`);
|
|
263
291
|
try {
|
|
264
292
|
// Hub mode must route to the edge that owns the session.
|
|
265
293
|
const mid = resolvedMachine || selectedMachineId.peek() || ctx.machineId || null;
|
|
@@ -273,10 +301,6 @@ export async function resumeSession(sid, cwd, configDir, agent, machineId, opts
|
|
|
273
301
|
const q = new URLSearchParams({
|
|
274
302
|
tail: '0',
|
|
275
303
|
compact: '1',
|
|
276
|
-
// Hub keeps a short-lived compact copy after completed turns. This
|
|
277
|
-
// makes a mobile re-open cache-first while the live WS remains the
|
|
278
|
-
// source for an in-progress answer.
|
|
279
|
-
cache: 'prefer',
|
|
280
304
|
});
|
|
281
305
|
if (agent) q.set('agent', agent);
|
|
282
306
|
// Also put machine in query for proxies that drop custom headers.
|
|
@@ -284,6 +308,9 @@ export async function resumeSession(sid, cwd, configDir, agent, machineId, opts
|
|
|
284
308
|
return api('GET', `/api/sessions/${sid}/messages?${q}`, undefined, undefined, {
|
|
285
309
|
machineId: mid || undefined,
|
|
286
310
|
dedupe: false,
|
|
311
|
+
// A transcript can be written shortly after the final stream frame.
|
|
312
|
+
// Never let an HTTP/browser cache hide a newer user prompt.
|
|
313
|
+
cache: 'no-store',
|
|
287
314
|
});
|
|
288
315
|
};
|
|
289
316
|
|
|
@@ -299,6 +326,10 @@ export async function resumeSession(sid, cwd, configDir, agent, machineId, opts
|
|
|
299
326
|
const meta = Array.isArray(data) ? {} : { truncated: data?.truncated, total: data?.total };
|
|
300
327
|
if (msgs.length) historyCache.set(cacheKey, { msgs, context, meta, at: Date.now() });
|
|
301
328
|
else historyCache.delete(cacheKey);
|
|
329
|
+
// A reconnect can receive new stream frames while this verification read
|
|
330
|
+
// is in flight. Keep those live frames authoritative; the fresh response
|
|
331
|
+
// is still retained for the next open.
|
|
332
|
+
if (isProcessing.peek() || $('messages')?.querySelector('.live-stream')) return;
|
|
302
333
|
paintMessages(msgs, context, meta);
|
|
303
334
|
} catch (e) {
|
|
304
335
|
if (ctx.sessionId !== sid) return;
|
|
@@ -308,6 +339,12 @@ export async function resumeSession(sid, cwd, configDir, agent, machineId, opts
|
|
|
308
339
|
}
|
|
309
340
|
};
|
|
310
341
|
|
|
342
|
+
activeExternalHistory = {
|
|
343
|
+
sessionId: sid,
|
|
344
|
+
agent: resolvedAgent,
|
|
345
|
+
refresh: () => loadHistory({ preferCache: false }).catch(() => {}),
|
|
346
|
+
};
|
|
347
|
+
|
|
311
348
|
// Never block tab UI on history; chat still loads ASAP in background
|
|
312
349
|
if (skipHistory) {
|
|
313
350
|
loadHistory().catch(() => {});
|
|
@@ -79,7 +79,6 @@ export const AppShell = (opts = {}) => {
|
|
|
79
79
|
<span id="topbar-project" class="text-sm text-base-content/50 flex-1 truncate min-w-0">Select a session</span>
|
|
80
80
|
<span id="conn-dot" class="w-2 h-2 rounded-full bg-base-content/30 flex-shrink-0" title="连接状态"></span>
|
|
81
81
|
<div class="flex gap-2 topbar-actions">
|
|
82
|
-
<button id="btn-share-session" class="btn btn-ghost btn-xs border border-base-300" title="复制当前会话分享链接">分享</button>
|
|
83
82
|
<button id="btn-pwa-install" class="btn btn-ghost btn-xs border border-base-300 hidden" title="安装到主屏幕">安装</button>
|
|
84
83
|
<button id="btn-meta-agent" class="btn btn-ghost btn-xs border border-primary/40 text-primary topbar-icon-btn" title="Meta Agent — 报告 / 问答 / VLM" aria-label="打开 Meta Agent">
|
|
85
84
|
<svg class="topbar-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><path d="m12 2 1.6 6.4L20 10l-6.4 1.6L12 18l-1.6-6.4L4 10l6.4-1.6L12 2Z"/><path d="m19 16 .7 2.3L22 19l-2.3.7L19 22l-.7-2.3L16 19l2.3-.7L19 16Z"/></svg><span class="topbar-icon-label">Agent</span>
|
|
@@ -92,6 +91,10 @@ export const AppShell = (opts = {}) => {
|
|
|
92
91
|
<button id="btn-logout" class="btn btn-ghost btn-xs border border-base-300">Sign out</button>
|
|
93
92
|
</div>
|
|
94
93
|
</div>
|
|
94
|
+
<button id="btn-expand-chat-chrome" class="chat-chrome-fab hidden" type="button"
|
|
95
|
+
title="显示会话工具栏" aria-label="显示会话工具栏">
|
|
96
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><path d="M7 14l5-5 5 5"/><path d="M5 19h14"/></svg>
|
|
97
|
+
</button>
|
|
95
98
|
<!-- Fixed panel + backdrop: siblings of topbar so flex layout never clips them -->
|
|
96
99
|
<div id="recent-menu-backdrop" class="hidden fixed inset-0 z-[55] bg-black/40" aria-hidden="true"></div>
|
|
97
100
|
<div id="recent-menu" class="hidden fixed z-[60] w-80 max-h-[min(24rem,70vh)] overflow-y-auto
|
|
@@ -255,6 +258,7 @@ export const AppShell = (opts = {}) => {
|
|
|
255
258
|
<div id="tab-chat" class="flex flex-col flex-1 overflow-hidden relative">
|
|
256
259
|
<div id="project-notes-bar" class="bg-base-200 border-b border-base-300 px-3 py-1.5 text-xs flex flex-col gap-1" style="display:none"></div>
|
|
257
260
|
<div id="session-notes-bar" class="bg-base-200/80 border-b border-base-300 px-3 py-1 text-xs flex justify-between items-center gap-2" style="display:none"></div>
|
|
261
|
+
<div id="session-lock-banner" class="session-lock-banner hidden px-3 py-1.5 text-[11px] text-center border-b"></div>
|
|
258
262
|
<div id="ws-banner" class="hidden px-3 py-1.5 text-[11px] text-center bg-warning/15 text-warning border-b border-warning/20 flex items-center justify-center gap-2">
|
|
259
263
|
<span id="ws-banner-text"></span>
|
|
260
264
|
<button type="button" id="ws-banner-retry" class="btn btn-ghost btn-xs h-6 min-h-0 px-2 text-warning border border-warning/30">重连</button>
|
package/public/shell.js
CHANGED
|
@@ -310,6 +310,12 @@ export function initShell(opts = {}) {
|
|
|
310
310
|
initSidebarResize();
|
|
311
311
|
initSidebarToggle();
|
|
312
312
|
initNotifications({ openSession: resumeSession });
|
|
313
|
+
const setChatChromeCollapsed = (collapsed) => {
|
|
314
|
+
$('app')?.classList.toggle('chat-chrome-collapsed', collapsed);
|
|
315
|
+
$('btn-expand-chat-chrome')?.classList.toggle('hidden', !collapsed);
|
|
316
|
+
localStorage.setItem('chatChromeCollapsed', collapsed ? '1' : '0');
|
|
317
|
+
};
|
|
318
|
+
setChatChromeCollapsed(localStorage.getItem('chatChromeCollapsed') === '1');
|
|
313
319
|
|
|
314
320
|
let sessionsChangedTimer = null;
|
|
315
321
|
document.addEventListener('sessions-changed', () => {
|
|
@@ -336,13 +342,17 @@ export function initShell(opts = {}) {
|
|
|
336
342
|
});
|
|
337
343
|
|
|
338
344
|
delegate.on('click', '#btn-home', goHome);
|
|
339
|
-
delegate.on('click', '#btn-share-session', async (_, el) => {
|
|
345
|
+
delegate.on('click', '#btn-share-session, #btn-share-session-current', async (_, el) => {
|
|
340
346
|
const url = currentSessionShareUrl();
|
|
341
347
|
if (!url) return;
|
|
342
348
|
try {
|
|
343
349
|
await navigator.clipboard.writeText(url);
|
|
344
|
-
el.
|
|
345
|
-
|
|
350
|
+
el.classList.add('is-copied');
|
|
351
|
+
el.title = '已复制分享链接';
|
|
352
|
+
setTimeout(() => {
|
|
353
|
+
el.classList.remove('is-copied');
|
|
354
|
+
el.title = '复制当前会话分享链接';
|
|
355
|
+
}, 1400);
|
|
346
356
|
} catch {
|
|
347
357
|
window.prompt('复制会话链接:', url);
|
|
348
358
|
}
|
|
@@ -432,6 +442,8 @@ export function initShell(opts = {}) {
|
|
|
432
442
|
document.getElementById('btn-pwa-install')?.classList.remove('hidden');
|
|
433
443
|
});
|
|
434
444
|
delegate.on('click', '#btn-settings', openSettings);
|
|
445
|
+
delegate.on('click', '#btn-collapse-chat-chrome', () => setChatChromeCollapsed(true));
|
|
446
|
+
delegate.on('click', '#btn-expand-chat-chrome', () => setChatChromeCollapsed(false));
|
|
435
447
|
delegate.on('click', '#btn-meta-agent', () => toggleMetaAgent());
|
|
436
448
|
delegate.on('click', '#btn-meta-agent-welcome', () => openMetaAgent('chat'));
|
|
437
449
|
delegate.on('click', '#btn-theme', toggleTheme);
|
|
@@ -610,7 +622,7 @@ export function initShell(opts = {}) {
|
|
|
610
622
|
});
|
|
611
623
|
document.addEventListener('agent-changed', refreshModelSelect);
|
|
612
624
|
|
|
613
|
-
delegate.on('click', '#btn-new-session, #btn-new-session-welcome', openNewSessionModal);
|
|
625
|
+
delegate.on('click', '#btn-new-session, #btn-new-session-current, #btn-new-session-welcome', openNewSessionModal);
|
|
614
626
|
delegate.on('click', '#new-session-cancel', () => $('modal-new-session').close());
|
|
615
627
|
delegate.on('click', '#new-session-start', startNewSession);
|
|
616
628
|
delegate.on('change', '#new-session-agent', updateNewSessionAgentUI);
|
package/public/state.js
CHANGED
|
@@ -77,7 +77,7 @@ export const agentFilters = signal((() => {
|
|
|
77
77
|
export const timeRange = signal(localStorage.getItem('timeRange') || '14'); // '0'|'1'|'7'|'14'|'30'
|
|
78
78
|
export const activityHits = signal(null); // null | { q, results, loading, error }
|
|
79
79
|
export const activityLoading = signal(false);
|
|
80
|
-
/** agent:sessionId → { favorite, notes, title, hidden, updatedAt } (title = rename map) */
|
|
80
|
+
/** agent:sessionId → { favorite, notes, title, hidden, branch, updatedAt } (title = rename map) */
|
|
81
81
|
export const sessionMetaMap = signal({});
|
|
82
82
|
/** Sidebar: show only favorited sessions */
|
|
83
83
|
export const favoritesOnly = signal(localStorage.getItem('favoritesOnly') === '1');
|
|
@@ -107,6 +107,10 @@ export const ctx = {
|
|
|
107
107
|
ws: null,
|
|
108
108
|
shellBubble: null,
|
|
109
109
|
configDir: null,
|
|
110
|
+
externalSessionReadOnly: false,
|
|
111
|
+
role: null,
|
|
112
|
+
username: null,
|
|
113
|
+
lock: { key: null, token: null, holder: null, held: false, foreign: false },
|
|
110
114
|
};
|
|
111
115
|
|
|
112
116
|
/** Select edge machine (hub). Keeps signal + ctx + localStorage in sync. */
|
|
@@ -129,7 +133,7 @@ export function metaKey(agent, sessionId, machineId) {
|
|
|
129
133
|
|
|
130
134
|
export function getSessionMeta(agent, sessionId, machineId) {
|
|
131
135
|
const m = sessionMetaMap.peek()?.[metaKey(agent, sessionId, machineId)];
|
|
132
|
-
return m || { favorite: false, notes: '', title: '', hidden: false };
|
|
136
|
+
return m || { favorite: false, notes: '', title: '', hidden: false, branch: null };
|
|
133
137
|
}
|
|
134
138
|
|
|
135
139
|
/** Display title: user rename map → original display → short id. */
|
package/public/style.css
CHANGED
|
@@ -23,13 +23,17 @@
|
|
|
23
23
|
#chat-input {
|
|
24
24
|
min-height: 32px !important;
|
|
25
25
|
max-height: 72px !important;
|
|
26
|
-
padding:
|
|
26
|
+
padding: 8px 12px 4px !important;
|
|
27
27
|
border-radius: 0.65rem 0.65rem 0 0;
|
|
28
28
|
/* 16px prevents iOS Safari auto-zoom on focus */
|
|
29
29
|
font-size: 16px !important;
|
|
30
30
|
line-height: 1.4 !important;
|
|
31
31
|
}
|
|
32
|
-
|
|
32
|
+
/* Two fixed icon slots: settings + attachment never wrap below the input. */
|
|
33
|
+
.composer-tools { flex: 0 0 64px; flex-wrap: nowrap; gap: 0; }
|
|
34
|
+
.composer-icon-btn { width: 32px; height: 32px; min-width: 32px; font-size: 17px; }
|
|
35
|
+
.composer-actions { flex-wrap: nowrap; gap: .35rem; }
|
|
36
|
+
.composer-submit { min-width: 0; flex-shrink: 1; }
|
|
33
37
|
.composer-send-btn, .composer-stop-btn { min-height: 40px; height: 40px; }
|
|
34
38
|
.composer-hint { display: none; }
|
|
35
39
|
#chat-opts {
|
|
@@ -541,6 +545,37 @@ html.pwa-standalone .ma-panel {
|
|
|
541
545
|
white-space: pre;
|
|
542
546
|
overflow-x: auto;
|
|
543
547
|
}
|
|
548
|
+
|
|
549
|
+
/* Markdown files open as a rendered document; source remains one tap away. */
|
|
550
|
+
.file-markdown-toolbar {
|
|
551
|
+
display: flex;
|
|
552
|
+
gap: 0.25rem;
|
|
553
|
+
padding: 0.4rem 1rem;
|
|
554
|
+
border-bottom: 1px solid color-mix(in oklch, var(--color-base-content) 10%, transparent);
|
|
555
|
+
background: color-mix(in oklch, var(--color-base-200) 75%, transparent);
|
|
556
|
+
}
|
|
557
|
+
.file-markdown-mode {
|
|
558
|
+
border: 1px solid transparent;
|
|
559
|
+
border-radius: 0.375rem;
|
|
560
|
+
padding: 0.18rem 0.55rem;
|
|
561
|
+
color: color-mix(in oklch, var(--color-base-content) 58%, transparent);
|
|
562
|
+
background: transparent;
|
|
563
|
+
font-size: 0.72rem;
|
|
564
|
+
font-weight: 600;
|
|
565
|
+
cursor: pointer;
|
|
566
|
+
}
|
|
567
|
+
.file-markdown-mode:hover {
|
|
568
|
+
color: var(--color-base-content);
|
|
569
|
+
background: color-mix(in oklch, var(--color-base-content) 7%, transparent);
|
|
570
|
+
}
|
|
571
|
+
.file-markdown-mode.active {
|
|
572
|
+
color: var(--color-primary);
|
|
573
|
+
border-color: color-mix(in oklch, var(--color-primary) 35%, transparent);
|
|
574
|
+
background: color-mix(in oklch, var(--color-primary) 12%, transparent);
|
|
575
|
+
}
|
|
576
|
+
.file-markdown-preview {
|
|
577
|
+
min-height: 0;
|
|
578
|
+
}
|
|
544
579
|
/* Reuse chat token palette outside .md so file preview is colored even if CDN theme lags */
|
|
545
580
|
.file-preview .hljs-comment,
|
|
546
581
|
.file-preview .hljs-quote {
|
|
@@ -733,6 +768,37 @@ html.pwa-standalone .ma-panel {
|
|
|
733
768
|
.session-row.session-thin {
|
|
734
769
|
opacity: 0.88;
|
|
735
770
|
}
|
|
771
|
+
.session-row.session-branch {
|
|
772
|
+
margin-left: calc(var(--branch-depth, 1) * 0.7rem);
|
|
773
|
+
border-left-style: dashed;
|
|
774
|
+
}
|
|
775
|
+
.session-branch-line {
|
|
776
|
+
margin-top: 1px;
|
|
777
|
+
font-size: 9px;
|
|
778
|
+
line-height: 1.25;
|
|
779
|
+
font-family: ui-monospace, Menlo, monospace;
|
|
780
|
+
color: color-mix(in oklch, var(--color-primary) 58%, var(--color-base-content));
|
|
781
|
+
overflow: hidden;
|
|
782
|
+
text-overflow: ellipsis;
|
|
783
|
+
white-space: nowrap;
|
|
784
|
+
}
|
|
785
|
+
.session-branch-badge {
|
|
786
|
+
font-size: 9px;
|
|
787
|
+
color: color-mix(in oklch, var(--color-primary) 82%, var(--color-base-content));
|
|
788
|
+
border: 1px solid color-mix(in oklch, var(--color-primary) 28%, transparent);
|
|
789
|
+
border-radius: 3px;
|
|
790
|
+
padding: 0 3px;
|
|
791
|
+
}
|
|
792
|
+
.session-branch-current {
|
|
793
|
+
margin-left: auto;
|
|
794
|
+
max-width: 10rem;
|
|
795
|
+
overflow: hidden;
|
|
796
|
+
text-overflow: ellipsis;
|
|
797
|
+
white-space: nowrap;
|
|
798
|
+
font-family: ui-monospace, Menlo, monospace;
|
|
799
|
+
font-size: 10px;
|
|
800
|
+
color: color-mix(in oklch, var(--color-primary) 72%, var(--color-base-content));
|
|
801
|
+
}
|
|
736
802
|
.session-thin-badge {
|
|
737
803
|
font-size: 9px;
|
|
738
804
|
font-family: ui-monospace, Menlo, monospace;
|
|
@@ -1095,7 +1161,7 @@ html.pwa-standalone .ma-panel {
|
|
|
1095
1161
|
width: 100%;
|
|
1096
1162
|
min-height: 32px;
|
|
1097
1163
|
max-height: 72px;
|
|
1098
|
-
padding:
|
|
1164
|
+
padding: 8px 12px 4px;
|
|
1099
1165
|
border: 0;
|
|
1100
1166
|
border-radius: 0.75rem 0.75rem 0 0;
|
|
1101
1167
|
background: transparent;
|
|
@@ -1153,6 +1219,51 @@ html.pwa-standalone .ma-panel {
|
|
|
1153
1219
|
flex: 0 0 auto;
|
|
1154
1220
|
}
|
|
1155
1221
|
.topbar-icon-label { display: inline; }
|
|
1222
|
+
.session-title-actions { margin-left: .1rem; }
|
|
1223
|
+
#session-notes-text { min-width: 0; flex: 1 1 4rem; }
|
|
1224
|
+
.session-bar-icon,
|
|
1225
|
+
#session-notes-bar .session-hide-btn {
|
|
1226
|
+
width: 26px;
|
|
1227
|
+
height: 26px;
|
|
1228
|
+
padding: 0;
|
|
1229
|
+
display: inline-flex;
|
|
1230
|
+
align-items: center;
|
|
1231
|
+
justify-content: center;
|
|
1232
|
+
border: 0;
|
|
1233
|
+
border-radius: .4rem;
|
|
1234
|
+
background: transparent;
|
|
1235
|
+
color: color-mix(in oklch, var(--color-base-content) 62%, transparent);
|
|
1236
|
+
cursor: pointer;
|
|
1237
|
+
}
|
|
1238
|
+
.session-bar-icon:hover,
|
|
1239
|
+
#session-notes-bar .session-hide-btn:hover { background: color-mix(in oklch, var(--color-base-content) 9%, transparent); color: var(--color-base-content); }
|
|
1240
|
+
.session-bar-icon.is-copied { color: var(--color-success); }
|
|
1241
|
+
.session-bar-icon svg,
|
|
1242
|
+
#session-notes-bar .session-hide-btn svg,
|
|
1243
|
+
.chat-chrome-fab svg { width: 15px; height: 15px; }
|
|
1244
|
+
.chat-chrome-fab {
|
|
1245
|
+
position: fixed;
|
|
1246
|
+
top: max(.6rem, env(safe-area-inset-top, 0px));
|
|
1247
|
+
left: max(.6rem, env(safe-area-inset-left, 0px));
|
|
1248
|
+
z-index: 40;
|
|
1249
|
+
width: 34px;
|
|
1250
|
+
height: 34px;
|
|
1251
|
+
display: inline-flex;
|
|
1252
|
+
align-items: center;
|
|
1253
|
+
justify-content: center;
|
|
1254
|
+
border: 1px solid color-mix(in oklch, var(--color-base-content) 16%, transparent);
|
|
1255
|
+
border-radius: 999px;
|
|
1256
|
+
color: var(--color-base-content);
|
|
1257
|
+
background: color-mix(in oklch, var(--color-base-100) 88%, transparent);
|
|
1258
|
+
box-shadow: 0 4px 14px color-mix(in oklch, black 24%, transparent);
|
|
1259
|
+
}
|
|
1260
|
+
.chat-chrome-fab.hidden { display: none; }
|
|
1261
|
+
#app.chat-chrome-collapsed #topbar,
|
|
1262
|
+
#app.chat-chrome-collapsed #project-view > .flex.bg-base-200,
|
|
1263
|
+
#app.chat-chrome-collapsed #project-notes-bar,
|
|
1264
|
+
#app.chat-chrome-collapsed #session-notes-bar,
|
|
1265
|
+
#app.chat-chrome-collapsed #session-lock-banner,
|
|
1266
|
+
#app.chat-chrome-collapsed #ws-banner { display: none !important; }
|
|
1156
1267
|
.mermaid-diagram {
|
|
1157
1268
|
overflow-x: auto;
|
|
1158
1269
|
padding: .75rem .25rem .25rem;
|
|
@@ -1304,6 +1415,41 @@ html.pwa-standalone .ma-panel {
|
|
|
1304
1415
|
user-select: none;
|
|
1305
1416
|
}
|
|
1306
1417
|
.msg-row.user .msg-ts { text-align: right; }
|
|
1418
|
+
.msg-row.queued-user-msg > div:not(.msg-ts) {
|
|
1419
|
+
opacity: .76;
|
|
1420
|
+
border-style: dashed;
|
|
1421
|
+
}
|
|
1422
|
+
.msg-row.cancelled-queued-user-msg > div:not(.msg-ts) {
|
|
1423
|
+
opacity: .48;
|
|
1424
|
+
text-decoration: line-through;
|
|
1425
|
+
}
|
|
1426
|
+
.msg-actions {
|
|
1427
|
+
display: flex;
|
|
1428
|
+
align-items: center;
|
|
1429
|
+
gap: 0.25rem;
|
|
1430
|
+
padding: 0 0.45rem;
|
|
1431
|
+
opacity: 0;
|
|
1432
|
+
transition: opacity .12s ease;
|
|
1433
|
+
}
|
|
1434
|
+
.msg-row.assistant:hover .msg-actions,
|
|
1435
|
+
.msg-row.assistant:focus-within .msg-actions { opacity: 1; }
|
|
1436
|
+
.msg-action-btn {
|
|
1437
|
+
appearance: none;
|
|
1438
|
+
border: 0;
|
|
1439
|
+
background: transparent;
|
|
1440
|
+
color: color-mix(in oklch, var(--color-base-content) 48%, transparent);
|
|
1441
|
+
font-size: 10px;
|
|
1442
|
+
line-height: 1.4;
|
|
1443
|
+
padding: 1px 4px;
|
|
1444
|
+
border-radius: 4px;
|
|
1445
|
+
cursor: pointer;
|
|
1446
|
+
}
|
|
1447
|
+
.msg-action-btn:hover:not(:disabled) {
|
|
1448
|
+
color: var(--color-primary);
|
|
1449
|
+
background: color-mix(in oklch, var(--color-primary) 12%, transparent);
|
|
1450
|
+
}
|
|
1451
|
+
.msg-action-btn:disabled { cursor: wait; opacity: .55; }
|
|
1452
|
+
@media (hover: none) { .msg-actions { opacity: 1; } }
|
|
1307
1453
|
|
|
1308
1454
|
.token-bar {
|
|
1309
1455
|
display: flex;
|
|
@@ -1623,3 +1769,81 @@ html.pwa-standalone .ma-panel {
|
|
|
1623
1769
|
padding: 0.05em 0 0.15em 0.5em !important;
|
|
1624
1770
|
}
|
|
1625
1771
|
}
|
|
1772
|
+
|
|
1773
|
+
/* ── Settings: full-page overlay using the same DaisyUI tokens as #app ── */
|
|
1774
|
+
|
|
1775
|
+
html.settings-open #app { visibility: hidden; }
|
|
1776
|
+
.set-page {
|
|
1777
|
+
position: fixed;
|
|
1778
|
+
inset: 0;
|
|
1779
|
+
z-index: 50;
|
|
1780
|
+
background: hsl(var(--b1));
|
|
1781
|
+
color: hsl(var(--bc));
|
|
1782
|
+
overflow: auto;
|
|
1783
|
+
min-height: 100dvh;
|
|
1784
|
+
}
|
|
1785
|
+
.set-island {
|
|
1786
|
+
position: sticky;
|
|
1787
|
+
top: 0;
|
|
1788
|
+
z-index: 2;
|
|
1789
|
+
display: flex;
|
|
1790
|
+
align-items: center;
|
|
1791
|
+
gap: 0.5rem;
|
|
1792
|
+
height: 44px;
|
|
1793
|
+
padding: 0 0.75rem;
|
|
1794
|
+
background: hsl(var(--b2));
|
|
1795
|
+
border-bottom: 1px solid hsl(var(--b3));
|
|
1796
|
+
}
|
|
1797
|
+
.set-split {
|
|
1798
|
+
display: grid;
|
|
1799
|
+
grid-template-columns: minmax(140px, 200px) minmax(0, 1fr);
|
|
1800
|
+
gap: 0.75rem;
|
|
1801
|
+
max-width: 56rem;
|
|
1802
|
+
margin: 1rem auto 2.5rem;
|
|
1803
|
+
padding: 0 1rem 2rem;
|
|
1804
|
+
}
|
|
1805
|
+
.set-nav { display: flex; flex-direction: column; gap: 0.25rem; padding-top: 0.25rem; }
|
|
1806
|
+
.set-nav-item.is-on {
|
|
1807
|
+
background: hsl(var(--b3));
|
|
1808
|
+
color: hsl(var(--bc));
|
|
1809
|
+
}
|
|
1810
|
+
.set-main { min-width: 0; }
|
|
1811
|
+
.set-panel { padding: 0.25rem 0 1.5rem; }
|
|
1812
|
+
.set-fields { display: grid; gap: 0.5rem; }
|
|
1813
|
+
.set-list { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 1rem; }
|
|
1814
|
+
.set-row {
|
|
1815
|
+
display: flex;
|
|
1816
|
+
align-items: center;
|
|
1817
|
+
gap: 0.75rem;
|
|
1818
|
+
padding: 0.65rem 0.75rem;
|
|
1819
|
+
border-radius: 0.5rem;
|
|
1820
|
+
background: hsl(var(--b2));
|
|
1821
|
+
border: 1px solid hsl(var(--b3));
|
|
1822
|
+
}
|
|
1823
|
+
.set-row-body { flex: 1; min-width: 0; }
|
|
1824
|
+
.set-add { display: grid; gap: 0.5rem; }
|
|
1825
|
+
.set-token { margin-top: 0.75rem; display: flex; flex-direction: column; gap: 0.5rem; }
|
|
1826
|
+
.set-token textarea { min-height: 7rem; resize: vertical; }
|
|
1827
|
+
@media (max-width: 767px) {
|
|
1828
|
+
.set-split { grid-template-columns: 1fr; }
|
|
1829
|
+
.set-nav { flex-direction: row; overflow-x: auto; gap: 0.25rem; }
|
|
1830
|
+
.set-nav-item { white-space: nowrap; }
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
/* Overlays must not eat keystrokes when hidden */
|
|
1834
|
+
#sidebar-overlay.hidden,
|
|
1835
|
+
.mermaid-zoom-overlay.hidden,
|
|
1836
|
+
#session-lock-banner.hidden {
|
|
1837
|
+
display: none !important;
|
|
1838
|
+
pointer-events: none !important;
|
|
1839
|
+
}
|
|
1840
|
+
#session-lock-banner {
|
|
1841
|
+
background: color-mix(in oklch, var(--color-warning) 16%, var(--color-base-200));
|
|
1842
|
+
color: color-mix(in oklch, var(--color-warning) 80%, var(--color-base-content));
|
|
1843
|
+
pointer-events: none;
|
|
1844
|
+
}
|
|
1845
|
+
#chat-input:disabled,
|
|
1846
|
+
#chat-input[readonly] {
|
|
1847
|
+
cursor: not-allowed;
|
|
1848
|
+
opacity: .78;
|
|
1849
|
+
}
|
package/public/sw.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/* Agent UI service worker — cache shell assets only.
|
|
2
2
|
* Never intercepts WebSocket or mutates API semantics beyond offline fallback.
|
|
3
3
|
* Bump CACHE when shipping UI that must invalidate old shells. */
|
|
4
|
-
const CACHE = 'agent-ui-shell-
|
|
4
|
+
const CACHE = 'agent-ui-shell-v27';
|
|
5
5
|
const PRECACHE = [
|
|
6
6
|
'/',
|
|
7
7
|
'/index.html',
|
|
8
8
|
'/manifest.webmanifest',
|
|
9
|
-
'/style.css?v=ui-
|
|
9
|
+
'/style.css?v=ui-22',
|
|
10
10
|
'/style.css',
|
|
11
11
|
'/ui.min.css',
|
|
12
12
|
'/vendor/mini-react.min.js',
|
|
@@ -16,7 +16,7 @@ const PRECACHE = [
|
|
|
16
16
|
'/vendor/mermaid.min.js',
|
|
17
17
|
'/vendor/katex.min.js',
|
|
18
18
|
'/vendor/katex.min.css',
|
|
19
|
-
'/app.js?v=ui-
|
|
19
|
+
'/app.js?v=ui-22',
|
|
20
20
|
'/icons/icon-192.png',
|
|
21
21
|
'/icons/icon-512.png',
|
|
22
22
|
'/icons/icon-maskable-512.png',
|