@polderlabs/bizar 4.4.13 → 4.5.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.
- package/bizar-dash/CHANGELOG.md +37 -276
- package/bizar-dash/dist/assets/__vite-browser-external-BIHI7g3E.js +2 -0
- package/bizar-dash/dist/assets/__vite-browser-external-BIHI7g3E.js.map +1 -0
- package/bizar-dash/dist/assets/main-eWZ4NlCL.css +1 -0
- package/bizar-dash/dist/assets/main-usWhlPWa.js +362 -0
- package/bizar-dash/dist/assets/main-usWhlPWa.js.map +1 -0
- package/bizar-dash/dist/assets/{mobile-DSb-t42Y.js → mobile-DQLFCjwJ.js} +2 -2
- package/bizar-dash/dist/assets/{mobile-DSb-t42Y.js.map → mobile-DQLFCjwJ.js.map} +1 -1
- package/bizar-dash/dist/assets/mobile-O6ANdD4W.js +352 -0
- package/bizar-dash/dist/assets/mobile-O6ANdD4W.js.map +1 -0
- package/bizar-dash/dist/index.html +3 -3
- package/bizar-dash/dist/mobile.html +2 -2
- package/bizar-dash/skills/agent-baseline/SKILL.md +80 -0
- package/bizar-dash/skills/bizar/SKILL.md +116 -0
- package/bizar-dash/skills/chat/SKILL.md +74 -0
- package/bizar-dash/skills/headroom/SKILL.md +94 -0
- package/bizar-dash/skills/lightrag/SKILL.md +86 -0
- package/bizar-dash/skills/minimax/SKILL.md +80 -0
- package/bizar-dash/skills/obsidian/SKILL.md +68 -0
- package/bizar-dash/skills/providers/SKILL.md +75 -0
- package/bizar-dash/skills/sdk/SKILL.md +138 -0
- package/bizar-dash/skills/self-improvement/SKILL.md +53 -0
- package/bizar-dash/skills/skills-cli/SKILL.md +94 -0
- package/bizar-dash/skills/usage/SKILL.md +62 -0
- package/bizar-dash/src/server/api.mjs +18 -0
- package/bizar-dash/src/server/headroom.mjs +645 -0
- package/bizar-dash/src/server/memory-lightrag.mjs +272 -2
- package/bizar-dash/src/server/memory-obsidian.mjs +230 -0
- package/bizar-dash/src/server/memory-store.mjs +189 -0
- package/bizar-dash/src/server/minimax-usage-store.mjs +372 -0
- package/bizar-dash/src/server/minimax.mjs +196 -5
- package/bizar-dash/src/server/providers-store.mjs +956 -0
- package/bizar-dash/src/server/routes/_shared.mjs +17 -0
- package/bizar-dash/src/server/routes/config.mjs +52 -1
- package/bizar-dash/src/server/routes/env-vars.mjs +165 -0
- package/bizar-dash/src/server/routes/headroom.mjs +126 -0
- package/bizar-dash/src/server/routes/lightrag.mjs +154 -0
- package/bizar-dash/src/server/routes/memory.mjs +668 -1
- package/bizar-dash/src/server/routes/opencode-session-detail.mjs +14 -29
- package/bizar-dash/src/server/routes/opencode-sessions.mjs +205 -3
- package/bizar-dash/src/server/routes/providers.mjs +266 -5
- package/bizar-dash/src/server/routes/skills.mjs +32 -43
- package/bizar-dash/src/server/routes/update.mjs +340 -0
- package/bizar-dash/src/server/routes/usage.mjs +136 -0
- package/bizar-dash/src/server/serve-info.mjs +135 -4
- package/bizar-dash/src/server/server.mjs +20 -0
- package/bizar-dash/src/server/skills-store.mjs +152 -262
- package/bizar-dash/src/web/App.tsx +120 -29
- package/bizar-dash/src/web/components/EnvVarManager.tsx +247 -0
- package/bizar-dash/src/web/components/HeadroomSettings.tsx +418 -0
- package/bizar-dash/src/web/components/HeadroomStatus.tsx +158 -0
- package/bizar-dash/src/web/components/SettingsSearch.tsx +213 -0
- package/bizar-dash/src/web/components/Topbar.tsx +2 -1
- package/bizar-dash/src/web/components/UsageChart.tsx +250 -0
- package/bizar-dash/src/web/components/UsageTable.tsx +90 -0
- package/bizar-dash/src/web/components/chat/ChatComposer.tsx +21 -25
- package/bizar-dash/src/web/components/chat/ChatInfoPanel.tsx +199 -37
- package/bizar-dash/src/web/components/chat/ChatThread.tsx +29 -17
- package/bizar-dash/src/web/components/chat/FloatingComposer.tsx +7 -1
- package/bizar-dash/src/web/components/chat/InfoPanel.tsx +71 -6
- package/bizar-dash/src/web/components/chat/useChat.ts +751 -257
- package/bizar-dash/src/web/lib/api.ts +43 -0
- package/bizar-dash/src/web/lib/types.ts +16 -0
- package/bizar-dash/src/web/main.tsx +2 -0
- package/bizar-dash/src/web/mobile/views/MobileChat.tsx +110 -35
- package/bizar-dash/src/web/styles/chat.css +135 -1
- package/bizar-dash/src/web/styles/main.css +46 -0
- package/bizar-dash/src/web/styles/memory.css +955 -0
- package/bizar-dash/src/web/styles/minimax-usage.css +335 -0
- package/bizar-dash/src/web/styles/settings.css +418 -0
- package/bizar-dash/src/web/styles/skills.css +302 -0
- package/bizar-dash/src/web/styles/tasks.css +288 -0
- package/bizar-dash/src/web/views/Chat.tsx +276 -48
- package/bizar-dash/src/web/views/Config.tsx +3 -2065
- package/bizar-dash/src/web/views/Memory.tsx +140 -0
- package/bizar-dash/src/web/views/MiniMaxUsage.tsx +476 -461
- package/bizar-dash/src/web/views/Overview.tsx +3 -0
- package/bizar-dash/src/web/views/Settings.tsx +36 -0
- package/bizar-dash/src/web/views/Skills.tsx +208 -260
- package/bizar-dash/src/web/views/Tasks.tsx +348 -1119
- package/bizar-dash/src/web/views/memory/ConfigPanel.tsx +289 -0
- package/bizar-dash/src/web/views/memory/GitSyncPanel.tsx +220 -0
- package/bizar-dash/src/web/views/memory/LightragPanel.tsx +307 -0
- package/bizar-dash/src/web/views/memory/MemoryOverview.tsx +354 -0
- package/bizar-dash/src/web/views/memory/MemoryStatusCard.tsx +160 -0
- package/bizar-dash/src/web/views/memory/ObsidianPanel.tsx +642 -0
- package/bizar-dash/src/web/views/memory/SemanticSearchPanel.tsx +194 -0
- package/bizar-dash/tests/chat-session-create.test.mjs +391 -0
- package/bizar-dash/tests/chat-session-stream.test.mjs +308 -0
- package/bizar-dash/tests/env-vars-store.test.mjs +216 -0
- package/bizar-dash/tests/headroom-install.test.mjs +173 -0
- package/bizar-dash/tests/headroom-settings.test.mjs +126 -0
- package/bizar-dash/tests/headroom-status.test.mjs +117 -0
- package/bizar-dash/tests/lightrag-defaults.node.test.mjs +118 -0
- package/bizar-dash/tests/memory-lightrag-extended.test.mjs +162 -0
- package/bizar-dash/tests/memory-obsidian.test.mjs +269 -0
- package/bizar-dash/tests/memory-tab.test.mjs +322 -0
- package/bizar-dash/tests/minimax-chat-usage.test.mjs +178 -0
- package/bizar-dash/tests/minimax-usage-store.node.test.mjs +293 -0
- package/bizar-dash/tests/mod-upgrade.node.test.mjs +1 -1
- package/bizar-dash/tests/opencode-sessions-detail.test.mjs +12 -9
- package/bizar-dash/tests/providers-store-backup-keys.node.test.mjs +479 -3
- package/bizar-dash/tests/providers-store-search.node.test.mjs +166 -0
- package/bizar-dash/tests/skills-list.test.mjs +232 -0
- package/bizar-dash/tests/skills-search.test.mjs +222 -0
- package/bizar-dash/tests/submit-feedback.test.mjs +6 -6
- package/bizar-dash/tests/tasks-create.test.mjs +187 -0
- package/bizar-dash/tests/update-check.test.mjs +127 -0
- package/bizar-dash/tests/update-run.test.mjs +266 -0
- package/cli/bin.mjs +247 -1
- package/cli/provision.mjs +118 -4
- package/config/agents/_shared/SKILLS.md +109 -0
- package/package.json +1 -1
- package/bizar-dash/dist/assets/main-BB5mJurD.js +0 -352
- package/bizar-dash/dist/assets/main-BB5mJurD.js.map +0 -1
- package/bizar-dash/dist/assets/main-BsnQLXdh.css +0 -1
- package/bizar-dash/dist/assets/mobile-Dl1q7Cyq.js +0 -354
- package/bizar-dash/dist/assets/mobile-Dl1q7Cyq.js.map +0 -1
|
@@ -1,16 +1,25 @@
|
|
|
1
|
-
// src/views/Chat.tsx —
|
|
1
|
+
// src/views/Chat.tsx — desktop chat view (v4.2.5 design).
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// v4.2.5 — chat overhaul:
|
|
4
|
+
// * 3-column grid preserved (rail / thread / info).
|
|
5
|
+
// * Composer goes through `chat.onSend`, which transparently routes
|
|
6
|
+
// to the right backend endpoint based on `activeSource`:
|
|
7
|
+
// - opencode session active → POST /api/opencode-sessions/:id/send
|
|
8
|
+
// - otherwise → POST /api/chat
|
|
9
|
+
// * "New session" button calls `chat.onCreateSession` which tries
|
|
10
|
+
// POST /api/opencode-sessions/new first (so the new session is a
|
|
11
|
+
// fully-fledged opencode session, scoped to the active worktree),
|
|
12
|
+
// falling back to the local jsonl store when the opencode plugin
|
|
13
|
+
// is offline.
|
|
14
|
+
// * Top-of-thread badge shows the active source ("opencode" or
|
|
15
|
+
// "bizar chat") so the user always knows where messages are going.
|
|
16
|
+
// * Info panel gets session metadata + rename/delete actions.
|
|
7
17
|
//
|
|
8
|
-
// The
|
|
9
|
-
//
|
|
10
|
-
// component (still wrapped in its own `.chat-thread` div with className
|
|
11
|
-
// `legacy` to opt into the original styles — see chat.css).
|
|
18
|
+
// The page-level grid and CSS classes are unchanged so the rail/info
|
|
19
|
+
// widths in the .chat-page grid continue to apply.
|
|
12
20
|
|
|
13
21
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
22
|
+
import { Trash2, Pencil } from 'lucide-react';
|
|
14
23
|
import { ChatTopBar } from '../components/chat/ChatTopBar';
|
|
15
24
|
import { ChatRail } from '../components/chat/ChatRail';
|
|
16
25
|
import { ChatThread } from '../components/chat/ChatThread';
|
|
@@ -33,18 +42,33 @@ interface Props {
|
|
|
33
42
|
onClearTaskId?: () => void;
|
|
34
43
|
}
|
|
35
44
|
|
|
36
|
-
export function Chat({
|
|
45
|
+
export function Chat({
|
|
46
|
+
snapshot,
|
|
47
|
+
settings,
|
|
48
|
+
setActiveTab,
|
|
49
|
+
initialTaskId,
|
|
50
|
+
onClearTaskId,
|
|
51
|
+
}: Props) {
|
|
37
52
|
const toast = useToast();
|
|
38
53
|
const modal = useModal();
|
|
39
54
|
|
|
40
55
|
const chat = useChat(snapshot, settings, initialTaskId ?? '');
|
|
56
|
+
// Wire toast + modal surfaces to useChat so async ops can surface
|
|
57
|
+
// feedback without each view passing handlers down.
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
chat.setToast({
|
|
60
|
+
error: (msg: string) => toast.error(msg),
|
|
61
|
+
success: (msg: string) => toast.success(msg),
|
|
62
|
+
info: (msg: string) => toast.info(msg),
|
|
63
|
+
warning: (msg: string) => toast.warning(msg),
|
|
64
|
+
});
|
|
65
|
+
}, [chat, toast]);
|
|
41
66
|
|
|
42
67
|
// ── Composer state (local) ───────────────────────────────────────────────
|
|
43
68
|
const [text, setText] = useState('');
|
|
44
69
|
const [agent, setAgent] = useState(settings.defaultAgent || 'odin');
|
|
45
70
|
const [model, setModel] = useState(settings.defaultModel || '');
|
|
46
71
|
const [attachments, setAttachments] = useState<string[]>([]);
|
|
47
|
-
const [creating, setCreating] = useState(false);
|
|
48
72
|
|
|
49
73
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
50
74
|
|
|
@@ -57,7 +81,6 @@ export function Chat({ snapshot, settings, setActiveTab, initialTaskId, onClearT
|
|
|
57
81
|
|
|
58
82
|
// ── File attach ──────────────────────────────────────────────────────────
|
|
59
83
|
const onAttach = () => fileInputRef.current?.click();
|
|
60
|
-
|
|
61
84
|
const onFiles = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
62
85
|
const files = e.target.files;
|
|
63
86
|
if (!files) return;
|
|
@@ -71,32 +94,19 @@ export function Chat({ snapshot, settings, setActiveTab, initialTaskId, onClearT
|
|
|
71
94
|
};
|
|
72
95
|
|
|
73
96
|
// ── Send ─────────────────────────────────────────────────────────────────
|
|
74
|
-
const handleSend = () => {
|
|
97
|
+
const handleSend = async () => {
|
|
75
98
|
const msg = text.trim();
|
|
76
99
|
if (!msg) return;
|
|
77
100
|
setText('');
|
|
78
101
|
setQuery('');
|
|
79
|
-
chat.onSend(msg, agent, model, attachments);
|
|
80
|
-
chat.jumpToLatest();
|
|
102
|
+
const result = await chat.onSend(msg, agent, model, attachments);
|
|
103
|
+
if (result.ok) chat.jumpToLatest();
|
|
81
104
|
};
|
|
82
105
|
|
|
83
106
|
// ── Create session ───────────────────────────────────────────────────────
|
|
84
107
|
const handleCreateSession = async () => {
|
|
85
|
-
if (
|
|
86
|
-
|
|
87
|
-
toast.warning('Pick a project in Overview to scope chat sessions.', 4000);
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
setCreating(true);
|
|
91
|
-
try {
|
|
92
|
-
const created = await fetch('/chat/sessions', { method: 'POST' }).then((r) => r.json());
|
|
93
|
-
chat.loadChat(created.id);
|
|
94
|
-
toast.success(`Session ${created.id} created.`);
|
|
95
|
-
} catch (err) {
|
|
96
|
-
toast.error(`Create failed: ${(err as Error).message}`);
|
|
97
|
-
} finally {
|
|
98
|
-
setCreating(false);
|
|
99
|
-
}
|
|
108
|
+
if (chat.busy.create) return;
|
|
109
|
+
await chat.onCreateSession();
|
|
100
110
|
};
|
|
101
111
|
|
|
102
112
|
// ── Delete message (via modal) ───────────────────────────────────────────
|
|
@@ -124,6 +134,121 @@ export function Chat({ snapshot, settings, setActiveTab, initialTaskId, onClearT
|
|
|
124
134
|
});
|
|
125
135
|
};
|
|
126
136
|
|
|
137
|
+
// ── Rename session (info panel action) ───────────────────────────────────
|
|
138
|
+
const handleRename = (id: string, currentTitle: string) => {
|
|
139
|
+
let next = '';
|
|
140
|
+
modal.open({
|
|
141
|
+
title: 'Rename session',
|
|
142
|
+
children: (
|
|
143
|
+
<input
|
|
144
|
+
autoFocus
|
|
145
|
+
defaultValue={currentTitle}
|
|
146
|
+
onChange={(e) => {
|
|
147
|
+
next = e.target.value;
|
|
148
|
+
}}
|
|
149
|
+
onKeyDown={(e) => {
|
|
150
|
+
if (e.key === 'Enter') {
|
|
151
|
+
e.preventDefault();
|
|
152
|
+
modal.close();
|
|
153
|
+
chat.renameSession(id, next).then((ok) => {
|
|
154
|
+
if (ok) {
|
|
155
|
+
toast.success('Renamed.');
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}}
|
|
160
|
+
style={{
|
|
161
|
+
width: '100%',
|
|
162
|
+
padding: '8px 10px',
|
|
163
|
+
background: 'var(--bg)',
|
|
164
|
+
border: '1px solid var(--border)',
|
|
165
|
+
borderRadius: 'var(--radius-sm)',
|
|
166
|
+
color: 'var(--text)',
|
|
167
|
+
font: '13px/1.4 var(--font-sans)',
|
|
168
|
+
marginTop: 6,
|
|
169
|
+
}}
|
|
170
|
+
/>
|
|
171
|
+
),
|
|
172
|
+
footer: (
|
|
173
|
+
<div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
|
|
174
|
+
<Button variant="secondary" size="sm" onClick={() => modal.close()}>
|
|
175
|
+
Cancel
|
|
176
|
+
</Button>
|
|
177
|
+
<Button
|
|
178
|
+
variant="primary"
|
|
179
|
+
size="sm"
|
|
180
|
+
onClick={() => {
|
|
181
|
+
modal.close();
|
|
182
|
+
chat.renameSession(id, next);
|
|
183
|
+
}}
|
|
184
|
+
>
|
|
185
|
+
Save
|
|
186
|
+
</Button>
|
|
187
|
+
</div>
|
|
188
|
+
),
|
|
189
|
+
});
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// ── Delete session (info panel action) ───────────────────────────────────
|
|
193
|
+
const handleDeleteSession = (id: string, title: string) => {
|
|
194
|
+
modal.open({
|
|
195
|
+
title: 'Delete session?',
|
|
196
|
+
children: (
|
|
197
|
+
<p style={{ margin: 0 }}>
|
|
198
|
+
Delete <strong>{title}</strong>? Messages on the opencode serve
|
|
199
|
+
will be removed. This cannot be undone.
|
|
200
|
+
</p>
|
|
201
|
+
),
|
|
202
|
+
footer: (
|
|
203
|
+
<div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
|
|
204
|
+
<Button variant="secondary" size="sm" onClick={() => modal.close()}>
|
|
205
|
+
Cancel
|
|
206
|
+
</Button>
|
|
207
|
+
<Button
|
|
208
|
+
variant="danger"
|
|
209
|
+
size="sm"
|
|
210
|
+
onClick={async () => {
|
|
211
|
+
modal.close();
|
|
212
|
+
await chat.deleteSession(id);
|
|
213
|
+
}}
|
|
214
|
+
>
|
|
215
|
+
Delete
|
|
216
|
+
</Button>
|
|
217
|
+
</div>
|
|
218
|
+
),
|
|
219
|
+
});
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// ── Export transcript ────────────────────────────────────────────────────
|
|
223
|
+
const handleExport = () => {
|
|
224
|
+
const sessionId =
|
|
225
|
+
chat.activeSource === 'opencode'
|
|
226
|
+
? chat.activeOpencodeSessionId
|
|
227
|
+
: chat.sessionId;
|
|
228
|
+
if (!sessionId) return;
|
|
229
|
+
const msgs =
|
|
230
|
+
chat.activeSource === 'opencode'
|
|
231
|
+
? chat.opencodeMessages
|
|
232
|
+
: chat.bizarMessages;
|
|
233
|
+
const text = msgs
|
|
234
|
+
.map((m) => {
|
|
235
|
+
const ts = m.ts ?? '';
|
|
236
|
+
const who = (m.role || 'unknown').toUpperCase();
|
|
237
|
+
const c = m.content || m.message || '';
|
|
238
|
+
return `[${ts}] ${who}: ${c}`;
|
|
239
|
+
})
|
|
240
|
+
.join('\n\n');
|
|
241
|
+
const blob = new Blob([text], { type: 'text/plain;charset=utf-8' });
|
|
242
|
+
const url = URL.createObjectURL(blob);
|
|
243
|
+
const a = document.createElement('a');
|
|
244
|
+
a.href = url;
|
|
245
|
+
a.download = `${sessionId}.txt`;
|
|
246
|
+
document.body.appendChild(a);
|
|
247
|
+
a.click();
|
|
248
|
+
document.body.removeChild(a);
|
|
249
|
+
URL.revokeObjectURL(url);
|
|
250
|
+
};
|
|
251
|
+
|
|
127
252
|
// ── Derived: sessions with display state overlaid ────────────────────────
|
|
128
253
|
const displaySessions = useMemo(
|
|
129
254
|
() => chat.sessions.map((s) => chat.getSessionDisplay(s)),
|
|
@@ -136,9 +261,10 @@ export function Chat({ snapshot, settings, setActiveTab, initialTaskId, onClearT
|
|
|
136
261
|
|
|
137
262
|
// ── Current session for the thread head subtitle ────────────────────────
|
|
138
263
|
const activeSessionDisplay = useMemo(() => {
|
|
139
|
-
const id =
|
|
140
|
-
|
|
141
|
-
|
|
264
|
+
const id =
|
|
265
|
+
chat.activeSource === 'opencode'
|
|
266
|
+
? chat.activeOpencodeSessionId ?? ''
|
|
267
|
+
: chat.sessionId;
|
|
142
268
|
if (!id) return null;
|
|
143
269
|
return (
|
|
144
270
|
displaySessions.find((s) => s.id === id) ??
|
|
@@ -161,6 +287,10 @@ export function Chat({ snapshot, settings, setActiveTab, initialTaskId, onClearT
|
|
|
161
287
|
return `${base} · idle`;
|
|
162
288
|
})();
|
|
163
289
|
|
|
290
|
+
// The badge in the thread head: "opencode" or "bizar chat".
|
|
291
|
+
const sourceLabel =
|
|
292
|
+
chat.activeSource === 'opencode' ? 'opencode' : 'bizar chat';
|
|
293
|
+
|
|
164
294
|
return (
|
|
165
295
|
<div className="chat-shell">
|
|
166
296
|
<ChatTopBar
|
|
@@ -180,48 +310,122 @@ export function Chat({ snapshot, settings, setActiveTab, initialTaskId, onClearT
|
|
|
180
310
|
activeSessionId={chat.sessionId}
|
|
181
311
|
activeOpencodeSessionId={chat.activeOpencodeSessionId}
|
|
182
312
|
activeProject={snapshot.activeProject}
|
|
183
|
-
creating={
|
|
313
|
+
creating={chat.busy.create}
|
|
184
314
|
onCreateSession={handleCreateSession}
|
|
185
315
|
onSelectSession={chat.selectBizarSession}
|
|
186
316
|
onSelectOpencodeSession={(s) => chat.loadOpencodeSession(s.id)}
|
|
187
|
-
onRenameSession={
|
|
188
|
-
onDeleteSession={(id) =>
|
|
317
|
+
onRenameSession={handleRename}
|
|
318
|
+
onDeleteSession={(id) => {
|
|
319
|
+
const s = chat.sessions.find((s) => s.id === id) ?? chat.opencodeSessions.find((s) => s.id === id);
|
|
320
|
+
handleDeleteSession(id, s?.title ?? '');
|
|
321
|
+
}}
|
|
189
322
|
/>
|
|
190
323
|
|
|
191
324
|
{/* ── thread ───────────────────────────────────────────────────── */}
|
|
192
325
|
<section className="chat-thread-section">
|
|
193
326
|
<div className="chat-thread-head">
|
|
194
327
|
<div>
|
|
195
|
-
<div className="chat-thread-title">
|
|
196
|
-
|
|
328
|
+
<div className="chat-thread-title-row">
|
|
329
|
+
<div className="chat-thread-title">
|
|
330
|
+
{activeSessionDisplay?.title ?? chat.sessionId ?? 'New chat'}
|
|
331
|
+
</div>
|
|
332
|
+
<span
|
|
333
|
+
className={`chat-source-badge chat-source-${
|
|
334
|
+
chat.activeSource ?? 'none'
|
|
335
|
+
}`}
|
|
336
|
+
title={
|
|
337
|
+
chat.activeSource === 'opencode'
|
|
338
|
+
? 'Messages go to the opencode serve child'
|
|
339
|
+
: 'Messages go to the local chat store'
|
|
340
|
+
}
|
|
341
|
+
>
|
|
342
|
+
{sourceLabel}
|
|
343
|
+
</span>
|
|
197
344
|
</div>
|
|
198
345
|
<div
|
|
199
|
-
className={`chat-thread-sub chat-muted state-${
|
|
346
|
+
className={`chat-thread-sub chat-muted state-${
|
|
347
|
+
activeSessionDisplay?.state ?? 'idle'
|
|
348
|
+
}`}
|
|
200
349
|
>
|
|
201
350
|
<span className="chat-thread-dot" />
|
|
202
351
|
{threadSubtitle}
|
|
203
352
|
</div>
|
|
353
|
+
{chat.opencodeError && (
|
|
354
|
+
<div className="chat-thread-error" role="alert">
|
|
355
|
+
{chat.opencodeError}
|
|
356
|
+
</div>
|
|
357
|
+
)}
|
|
204
358
|
</div>
|
|
205
359
|
<div className="chat-thread-actions">
|
|
206
|
-
|
|
207
|
-
<
|
|
360
|
+
{activeSessionDisplay && (
|
|
361
|
+
<button
|
|
362
|
+
className="btn btn-ghost"
|
|
363
|
+
title="Rename session"
|
|
364
|
+
type="button"
|
|
365
|
+
disabled={chat.busy.rename}
|
|
366
|
+
onClick={() =>
|
|
367
|
+
handleRename(
|
|
368
|
+
activeSessionDisplay.id,
|
|
369
|
+
activeSessionDisplay.title ?? '',
|
|
370
|
+
)
|
|
371
|
+
}
|
|
372
|
+
>
|
|
373
|
+
<Pencil size={12} aria-hidden />{' '}
|
|
374
|
+
<span className="mono">rename</span>
|
|
375
|
+
</button>
|
|
376
|
+
)}
|
|
377
|
+
{activeSessionDisplay && (
|
|
378
|
+
<button
|
|
379
|
+
className="btn btn-ghost btn-danger"
|
|
380
|
+
title="Delete session"
|
|
381
|
+
type="button"
|
|
382
|
+
disabled={chat.busy.delete}
|
|
383
|
+
onClick={() =>
|
|
384
|
+
handleDeleteSession(
|
|
385
|
+
activeSessionDisplay.id,
|
|
386
|
+
activeSessionDisplay.title ?? activeSessionDisplay.id,
|
|
387
|
+
)
|
|
388
|
+
}
|
|
389
|
+
>
|
|
390
|
+
<Trash2 size={12} aria-hidden />{' '}
|
|
391
|
+
<span className="mono">delete</span>
|
|
392
|
+
</button>
|
|
393
|
+
)}
|
|
394
|
+
<button
|
|
395
|
+
className="btn btn-ghost"
|
|
396
|
+
title="Export transcript"
|
|
397
|
+
type="button"
|
|
398
|
+
onClick={handleExport}
|
|
399
|
+
>
|
|
400
|
+
<span className="mono">export</span>
|
|
208
401
|
</button>
|
|
209
402
|
</div>
|
|
210
403
|
</div>
|
|
211
404
|
|
|
212
|
-
<div
|
|
405
|
+
<div
|
|
406
|
+
className="chat-thread-scroll"
|
|
407
|
+
ref={chat.listRef}
|
|
408
|
+
onScroll={chat.handleScroll}
|
|
409
|
+
>
|
|
213
410
|
<ChatThread
|
|
214
|
-
messages={
|
|
411
|
+
messages={
|
|
412
|
+
chat.activeSource === 'opencode'
|
|
413
|
+
? chat.opencodeMessages
|
|
414
|
+
: chat.bizarMessages
|
|
415
|
+
}
|
|
215
416
|
loading={chat.loading}
|
|
216
417
|
activeProject={snapshot.activeProject}
|
|
217
418
|
sessionId={
|
|
218
419
|
chat.activeSource === 'opencode'
|
|
219
|
-
?
|
|
420
|
+
? chat.activeOpencodeSessionId ?? chat.sessionId
|
|
220
421
|
: chat.sessionId
|
|
221
422
|
}
|
|
222
423
|
pinned={chat.pinned}
|
|
424
|
+
activeSource={chat.activeSource}
|
|
223
425
|
onPickSuggestion={(t) => setText(t)}
|
|
224
|
-
onCopy={(m) =>
|
|
426
|
+
onCopy={(m) =>
|
|
427
|
+
chat.copyMessage(m as Parameters<typeof chat.copyMessage>[0])
|
|
428
|
+
}
|
|
225
429
|
onDelete={handleDelete}
|
|
226
430
|
onTogglePin={chat.togglePin}
|
|
227
431
|
onRegenerate={chat.onRegenerate}
|
|
@@ -244,6 +448,7 @@ export function Chat({ snapshot, settings, setActiveTab, initialTaskId, onClearT
|
|
|
244
448
|
text={text}
|
|
245
449
|
setText={setText}
|
|
246
450
|
sending={chat.sending}
|
|
451
|
+
activeSource={chat.activeSource}
|
|
247
452
|
onSend={handleSend}
|
|
248
453
|
attachments={attachments}
|
|
249
454
|
setAttachments={setAttachments}
|
|
@@ -265,18 +470,41 @@ export function Chat({ snapshot, settings, setActiveTab, initialTaskId, onClearT
|
|
|
265
470
|
<ChatInfoPanel
|
|
266
471
|
sessionId={
|
|
267
472
|
chat.activeSource === 'opencode'
|
|
268
|
-
?
|
|
473
|
+
? chat.activeOpencodeSessionId ?? chat.sessionId
|
|
269
474
|
: chat.sessionId
|
|
270
475
|
}
|
|
271
|
-
messages={
|
|
476
|
+
messages={
|
|
477
|
+
chat.activeSource === 'opencode'
|
|
478
|
+
? chat.opencodeMessages
|
|
479
|
+
: chat.bizarMessages
|
|
480
|
+
}
|
|
272
481
|
pinned={chat.pinned}
|
|
273
482
|
agent={agent}
|
|
274
483
|
model={model}
|
|
275
484
|
agents={snapshot.agents || []}
|
|
276
485
|
mcps={snapshot.mcps || []}
|
|
277
486
|
allCommands={allCommands}
|
|
487
|
+
activeSource={chat.activeSource}
|
|
488
|
+
onRename={() => {
|
|
489
|
+
if (activeSessionDisplay) {
|
|
490
|
+
handleRename(
|
|
491
|
+
activeSessionDisplay.id,
|
|
492
|
+
activeSessionDisplay.title ?? '',
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
}}
|
|
496
|
+
onDelete={() => {
|
|
497
|
+
if (activeSessionDisplay) {
|
|
498
|
+
handleDeleteSession(
|
|
499
|
+
activeSessionDisplay.id,
|
|
500
|
+
activeSessionDisplay.title ?? activeSessionDisplay.id,
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
}}
|
|
504
|
+
onExport={handleExport}
|
|
505
|
+
busy={chat.busy}
|
|
278
506
|
/>
|
|
279
507
|
</div>
|
|
280
508
|
</div>
|
|
281
509
|
);
|
|
282
|
-
}
|
|
510
|
+
}
|