bloby-bot 0.21.4 → 0.21.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bloby-bot",
3
- "version": "0.21.4",
3
+ "version": "0.21.5",
4
4
  "releaseNotes": [
5
5
  "1. react router implemented",
6
6
  "2. new workspace design",
@@ -39,6 +39,10 @@ export function useChat(ws: WsClient | null) {
39
39
  const loaded = useRef(false);
40
40
  /** Count of user messages awaiting a bot response — keeps typing indicator alive across turns */
41
41
  const pendingMessages = useRef(0);
42
+ /** ID of a partial assistant message committed early (when user sends while bot is streaming) */
43
+ const partialMsgId = useRef<string | null>(null);
44
+ /** Ref to current streamBuffer value (accessible in callbacks without stale closures) */
45
+ const streamBufferRef = useRef('');
42
46
 
43
47
  // Load current conversation from DB on mount
44
48
  useEffect(() => {
@@ -132,7 +136,11 @@ export function useChat(ws: WsClient | null) {
132
136
  ws.on('bot:token', (data: { token: string }) => {
133
137
  // Auto-detect new turn — if tokens arrive after a response cleared streaming
134
138
  setStreaming(true);
135
- setStreamBuffer((buf) => buf + data.token);
139
+ setStreamBuffer((buf) => {
140
+ const next = buf + data.token;
141
+ streamBufferRef.current = next;
142
+ return next;
143
+ });
136
144
  }),
137
145
  ws.on('bot:tool', (data: { name: string; input?: any; status?: string }) => {
138
146
  setTools((prev) => {
@@ -143,16 +151,29 @@ export function useChat(ws: WsClient | null) {
143
151
  }),
144
152
  ws.on('bot:response', (data: { conversationId: string; messageId?: string; content: string }) => {
145
153
  setConversationId(data.conversationId);
146
- setMessages((msgs) => [
147
- ...msgs,
148
- {
149
- id: data.messageId || Date.now().toString(),
150
- role: 'assistant',
151
- content: data.content,
152
- timestamp: new Date().toISOString(),
153
- },
154
- ]);
154
+
155
+ if (partialMsgId.current) {
156
+ // A partial message was committed — update it with the full response
157
+ const pid = partialMsgId.current;
158
+ partialMsgId.current = null;
159
+ setMessages((msgs) => msgs.map((m) =>
160
+ m.id === pid ? { ...m, content: data.content } : m,
161
+ ));
162
+ } else {
163
+ // Normal: add new message bubble
164
+ setMessages((msgs) => [
165
+ ...msgs,
166
+ {
167
+ id: data.messageId || Date.now().toString(),
168
+ role: 'assistant',
169
+ content: data.content,
170
+ timestamp: new Date().toISOString(),
171
+ },
172
+ ]);
173
+ }
174
+
155
175
  setStreamBuffer('');
176
+ streamBufferRef.current = '';
156
177
  setTools([]);
157
178
  // Only stop streaming when all pending messages have been answered
158
179
  pendingMessages.current = Math.max(0, pendingMessages.current - 1);
@@ -201,6 +222,25 @@ export function useChat(ws: WsClient | null) {
201
222
  audioData: audioData ? (audioData.startsWith('data:') ? audioData : `data:audio/webm;base64,${audioData}`) : undefined,
202
223
  attachments: optimisticAttachments,
203
224
  };
225
+ // If bot is currently streaming, commit the partial response as a message
226
+ // so the user's new message appears BELOW the bot's in-progress text
227
+ if (streamBufferRef.current) {
228
+ const pid = 'partial-' + Date.now();
229
+ partialMsgId.current = pid;
230
+ const partialContent = streamBufferRef.current;
231
+ setMessages((msgs) => [
232
+ ...msgs,
233
+ {
234
+ id: pid,
235
+ role: 'assistant' as const,
236
+ content: partialContent,
237
+ timestamp: new Date().toISOString(),
238
+ },
239
+ ]);
240
+ setStreamBuffer('');
241
+ streamBufferRef.current = '';
242
+ }
243
+
204
244
  setMessages((msgs) => [...msgs, userMsg]);
205
245
  pendingMessages.current += 1;
206
246
 
@@ -231,17 +271,21 @@ export function useChat(ws: WsClient | null) {
231
271
  ws.send('user:stop', { conversationId });
232
272
  setStreaming(false);
233
273
  setStreamBuffer('');
274
+ streamBufferRef.current = '';
234
275
  setTools([]);
235
276
  pendingMessages.current = 0;
277
+ partialMsgId.current = null;
236
278
  }, [ws, conversationId]);
237
279
 
238
280
  const clearContext = useCallback(() => {
239
281
  setMessages([]);
240
282
  setConversationId(null);
241
283
  setStreamBuffer('');
284
+ streamBufferRef.current = '';
242
285
  setStreaming(false);
243
286
  setTools([]);
244
287
  pendingMessages.current = 0;
288
+ partialMsgId.current = null;
245
289
  prevConvId.current = null;
246
290
  loaded.current = false;
247
291
  fetch('/api/context/clear', { method: 'POST' }).catch(() => {});
@@ -80,7 +80,7 @@ const SW_JS = `// Service worker — app-shell caching + push notifications
80
80
  // JS/CSS modules → stale-while-revalidate
81
81
  // API, WebSocket, Vite internals → network-only (no cache)
82
82
 
83
- var CACHE = 'bloby-v5';
83
+ var CACHE = 'bloby-v6';
84
84
  var HASHED_RE = new RegExp('/assets/.+-[a-zA-Z0-9]{6,}[.](js|css)$');
85
85
 
86
86
  // Precache the HTML shell on install so the cache is never empty.