@marimo-team/islands 0.19.3-dev42 → 0.19.3-dev45

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/dist/main.js CHANGED
@@ -71227,6 +71227,7 @@ Image URL: ${r.imageUrl}`)), contextToXml({
71227
71227
  }), z = (0, import_react.useRef)(null), { data: G } = useAsyncData(async () => (await e.get_chat_history({})).messages, []), q = e.value.length > 0 ? e.value : G, { messages: LY, sendMessage: RY, setMessages: zY, status: BY, stop: VY, error: HY, regenerate: UY, clearError: WY } = useChat({
71228
71228
  transport: new DefaultChatTransport({
71229
71229
  fetch: async (r2, c2) => {
71230
+ var _a3;
71230
71231
  if (c2 === void 0) return fetch(r2);
71231
71232
  let d2 = JSON.parse(c2.body), f2 = c2.signal, h2 = {
71232
71233
  max_tokens: M.current.max_tokens,
@@ -71238,98 +71239,49 @@ Image URL: ${r.imageUrl}`)), contextToXml({
71238
71239
  };
71239
71240
  try {
71240
71241
  let r3 = d2.messages.map((e2) => {
71241
- var _a3;
71242
+ var _a4;
71242
71243
  return {
71243
71244
  ...e2,
71244
- content: (_a3 = e2.parts) == null ? void 0 : _a3.map((e3) => "text" in e3 ? e3.text : "").join("\n")
71245
+ content: (_a4 = e2.parts) == null ? void 0 : _a4.map((e3) => "text" in e3 ? e3.text : "").join("\n")
71245
71246
  };
71246
- });
71247
- if (e.frontendManaged) {
71248
- let c4 = new ReadableStream({
71249
- start(e2) {
71250
- z.current = e2;
71251
- let r4 = () => {
71252
- try {
71253
- e2.close();
71254
- } catch (e3) {
71255
- Logger.debug("Controller may already be closed", {
71256
- error: e3
71257
- });
71258
- }
71259
- z.current = null;
71260
- };
71261
- return f2 == null ? void 0 : f2.addEventListener("abort", r4), () => {
71262
- f2 == null ? void 0 : f2.removeEventListener("abort", r4);
71263
- };
71264
- },
71265
- cancel() {
71266
- z.current = null;
71267
- }
71268
- });
71269
- return e.send_prompt({
71270
- messages: r3,
71271
- config: h2
71272
- }).catch((e2) => {
71273
- var _a3;
71274
- (_a3 = z.current) == null ? void 0 : _a3.error(e2), z.current = null;
71275
- }), createUIMessageStreamResponse({
71276
- stream: c4
71277
- });
71278
- }
71279
- if (f2 == null ? void 0 : f2.aborted) return new Response("Aborted", {
71280
- status: 499
71281
- });
71282
- let c3 = Date.now().toString();
71283
- zY((e2) => [
71284
- ...e2,
71285
- {
71286
- id: c3,
71287
- role: "assistant",
71288
- parts: [
71289
- {
71290
- type: "text",
71291
- text: ""
71247
+ }), c3 = new ReadableStream({
71248
+ start(e2) {
71249
+ z.current = e2;
71250
+ let r4 = () => {
71251
+ try {
71252
+ e2.close();
71253
+ } catch (e3) {
71254
+ Logger.debug("Controller may already be closed", {
71255
+ error: e3
71256
+ });
71292
71257
  }
71293
- ]
71258
+ z.current = null;
71259
+ };
71260
+ return f2 == null ? void 0 : f2.addEventListener("abort", r4), () => {
71261
+ f2 == null ? void 0 : f2.removeEventListener("abort", r4);
71262
+ };
71263
+ },
71264
+ cancel() {
71265
+ z.current = null;
71294
71266
  }
71295
- ]);
71296
- let _2 = e.send_prompt({
71267
+ });
71268
+ return e.send_prompt({
71297
71269
  messages: r3,
71298
71270
  config: h2
71299
- }), v2 = await new Promise((e2, r4) => {
71300
- let c4 = () => {
71301
- r4(new DOMException("Aborted", "AbortError"));
71302
- };
71303
- f2 == null ? void 0 : f2.addEventListener("abort", c4), _2.then(e2).catch(r4).finally(() => {
71304
- f2 == null ? void 0 : f2.removeEventListener("abort", c4);
71305
- });
71271
+ }).catch((e2) => {
71272
+ var _a4;
71273
+ (_a4 = z.current) == null ? void 0 : _a4.error(e2), z.current = null;
71274
+ }), createUIMessageStreamResponse({
71275
+ stream: c3
71306
71276
  });
71307
- return v2 === null ? (Logger.error("Non-frontend-managed response is null", {
71308
- response: v2
71309
- }), new Response("Internal server error", {
71310
- status: 500
71311
- })) : (I.current.backendMessageId === null && I.current.frontendMessageIndex === null && zY((e2) => {
71312
- let r4 = [
71313
- ...e2
71314
- ], d3 = r4.findIndex((e3) => e3.id === c3);
71315
- return d3 !== -1 && (r4[d3] = {
71316
- ...r4[d3],
71317
- parts: [
71318
- {
71319
- type: "text",
71320
- text: v2
71321
- }
71322
- ]
71323
- }), r4;
71324
- }), new Response(v2));
71325
71277
  } catch (e2) {
71326
71278
  if (I.current = {
71327
71279
  backendMessageId: null,
71328
71280
  frontendMessageIndex: null
71329
- }, e2.name === "AbortError") return new Response("Aborted", {
71281
+ }, e2 instanceof Error && e2.name === "AbortError") return new Response("Aborted", {
71330
71282
  status: 499
71331
71283
  });
71332
- let r3 = e2.message.split("failed with exception ").pop();
71284
+ let r3 = (_a3 = e2.message) == null ? void 0 : _a3.split("failed with exception ").pop();
71333
71285
  return new Response(r3, {
71334
71286
  status: 400
71335
71287
  });
@@ -71341,7 +71293,7 @@ Image URL: ${r.imageUrl}`)), contextToXml({
71341
71293
  y(void 0), S.current && (S.current.value = ""), Logger.debug("Finished streaming message:", r2), I.current = {
71342
71294
  backendMessageId: null,
71343
71295
  frontendMessageIndex: null
71344
- }, e.frontendManaged && e.setValue(r2.messages);
71296
+ }, e.setValue(r2.messages);
71345
71297
  },
71346
71298
  onError: (e2) => {
71347
71299
  Logger.error("An error occurred:", e2), I.current = {
@@ -71350,52 +71302,13 @@ Image URL: ${r.imageUrl}`)), contextToXml({
71350
71302
  };
71351
71303
  }
71352
71304
  });
71353
- useEventListener(e.host, MarimoIncomingMessageEvent.TYPE, (r2) => {
71354
- let c2 = r2.detail.message;
71355
- if (typeof c2 != "object" || !c2 || !("type" in c2) || c2.type !== "stream_chunk") return;
71356
- if (e.frontendManaged) {
71357
- let e2 = z.current;
71358
- if (!e2) return;
71359
- let r3 = c2;
71360
- r3.content && e2.enqueue(r3.content), r3.is_final && (e2.close(), z.current = null);
71361
- return;
71362
- }
71363
- let d2 = c2;
71364
- I.current.backendMessageId === null && zY((e2) => {
71365
- let r3 = [
71366
- ...e2
71367
- ];
71368
- for (let e3 = r3.length - 1; e3 >= 0; e3--) if (r3[e3].role === "assistant") {
71369
- I.current = {
71370
- backendMessageId: d2.message_id,
71371
- frontendMessageIndex: e3
71372
- };
71373
- break;
71374
- }
71375
- return r3;
71376
- });
71377
- let f2 = I.current.frontendMessageIndex;
71378
- I.current.backendMessageId === d2.message_id && f2 !== null && (zY((e2) => {
71379
- let r3 = [
71380
- ...e2
71381
- ], c3 = f2;
71382
- if (c3 < r3.length) {
71383
- let e3 = r3[c3];
71384
- e3.role === "assistant" && (r3[c3] = {
71385
- ...e3,
71386
- parts: [
71387
- {
71388
- type: "text",
71389
- text: d2.content
71390
- }
71391
- ]
71392
- });
71393
- }
71394
- return r3;
71395
- }), d2.is_final && (I.current = {
71396
- backendMessageId: null,
71397
- frontendMessageIndex: null
71398
- }));
71305
+ useEventListener(e.host, MarimoIncomingMessageEvent.TYPE, (e2) => {
71306
+ let r2 = e2.detail.message;
71307
+ if (typeof r2 != "object" || !r2 || !("type" in r2) || r2.type !== "stream_chunk") return;
71308
+ let c2 = z.current;
71309
+ if (!c2) return;
71310
+ let d2 = r2;
71311
+ d2.content && c2.enqueue(d2.content), d2.is_final && (c2.close(), z.current = null);
71399
71312
  });
71400
71313
  let GY = BY === "submitted" || BY === "streaming", KY = (r2) => {
71401
71314
  let c2 = LY.findIndex((e2) => e2.id === r2);
@@ -71403,7 +71316,7 @@ Image URL: ${r.imageUrl}`)), contextToXml({
71403
71316
  let d2 = LY.filter((e2) => e2.id !== r2);
71404
71317
  e.delete_chat_message({
71405
71318
  index: c2
71406
- }), zY(d2), e.frontendManaged && e.setValue(d2);
71319
+ }), zY(d2), e.setValue(d2);
71407
71320
  }
71408
71321
  }, qY = Array.isArray(e.allowAttachments) && e.allowAttachments.length > 0 || e.allowAttachments === true, JY = {
71409
71322
  triggerCompletionRegex: /^\/(\w+)?/,
@@ -71509,7 +71422,7 @@ Image URL: ${r.imageUrl}`)), contextToXml({
71509
71422
  ]
71510
71423
  })
71511
71424
  ]
71512
- }, e2.id);
71425
+ }, `${e2.id}-${r2}`);
71513
71426
  }),
71514
71427
  GY && (0, import_jsx_runtime.jsxs)("div", {
71515
71428
  className: "flex items-center justify-center space-x-2 mb-4",
@@ -71975,8 +71888,7 @@ Image URL: ${r.imageUrl}`)), contextToXml({
71975
71888
  allowAttachments: union([
71976
71889
  boolean$2(),
71977
71890
  string$2().array()
71978
- ]),
71979
- frontendManaged: boolean$2()
71891
+ ])
71980
71892
  })).withFunctions({
71981
71893
  get_chat_history: rpc.input(object$1({})).output(object$1({
71982
71894
  messages: messageSchema
@@ -71988,10 +71900,7 @@ Image URL: ${r.imageUrl}`)), contextToXml({
71988
71900
  send_prompt: rpc.input(object$1({
71989
71901
  messages: messageSchema,
71990
71902
  config: configSchema
71991
- })).output(union([
71992
- string$2(),
71993
- _null()
71994
- ]))
71903
+ })).output(unknown())
71995
71904
  }).renderer((e) => {
71996
71905
  var _a2;
71997
71906
  return (0, import_jsx_runtime.jsx)(TooltipProvider, {
@@ -72001,7 +71910,6 @@ Image URL: ${r.imageUrl}`)), contextToXml({
72001
71910
  showConfigurationControls: e.data.showConfigurationControls,
72002
71911
  maxHeight: e.data.maxHeight,
72003
71912
  allowAttachments: e.data.allowAttachments,
72004
- frontendManaged: e.data.frontendManaged,
72005
71913
  config: e.data.config,
72006
71914
  get_chat_history: e.functions.get_chat_history,
72007
71915
  delete_chat_history: e.functions.delete_chat_history,
@@ -101153,7 +101061,7 @@ Defaulting to \`null\`.`;
101153
101061
  return Logger.warn("Failed to get version from mount config"), null;
101154
101062
  }
101155
101063
  }
101156
- const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.19.3-dev42"), showCodeInRunModeAtom = atom(true);
101064
+ const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.19.3-dev45"), showCodeInRunModeAtom = atom(true);
101157
101065
  atom(null);
101158
101066
  var VIRTUAL_FILE_REGEX = /\/@file\/([^\s"&'/]+)\.([\dA-Za-z]+)/g, VirtualFileTracker = class e {
101159
101067
  constructor() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/islands",
3
- "version": "0.19.3-dev42",
3
+ "version": "0.19.3-dev45",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -15,7 +15,7 @@ export type PluginFunctions = {
15
15
  get_chat_history: (req: {}) => Promise<{ messages: UIMessage[] }>;
16
16
  delete_chat_history: (req: {}) => Promise<null>;
17
17
  delete_chat_message: (req: { index: number }) => Promise<null>;
18
- send_prompt: (req: SendMessageRequest) => Promise<string | null>;
18
+ send_prompt: (req: SendMessageRequest) => Promise<unknown>;
19
19
  };
20
20
 
21
21
  const messageSchema = z.array(
@@ -47,7 +47,6 @@ export const ChatPlugin = createPlugin<{ messages: UIMessage[] }>(
47
47
  maxHeight: z.number().optional(),
48
48
  config: configSchema,
49
49
  allowAttachments: z.union([z.boolean(), z.string().array()]),
50
- frontendManaged: z.boolean(),
51
50
  }),
52
51
  )
53
52
  .withFunctions<PluginFunctions>({
@@ -67,7 +66,7 @@ export const ChatPlugin = createPlugin<{ messages: UIMessage[] }>(
67
66
  config: configSchema,
68
67
  }),
69
68
  )
70
- .output(z.union([z.string(), z.null()])),
69
+ .output(z.unknown()),
71
70
  })
72
71
  .renderer((props) => (
73
72
  <TooltipProvider>
@@ -77,7 +76,6 @@ export const ChatPlugin = createPlugin<{ messages: UIMessage[] }>(
77
76
  showConfigurationControls={props.data.showConfigurationControls}
78
77
  maxHeight={props.data.maxHeight}
79
78
  allowAttachments={props.data.allowAttachments}
80
- frontendManaged={props.data.frontendManaged}
81
79
  config={props.data.config}
82
80
  get_chat_history={props.functions.get_chat_history}
83
81
  delete_chat_history={props.functions.delete_chat_history}
@@ -66,7 +66,6 @@ interface Props extends PluginFunctions {
66
66
  showConfigurationControls: boolean;
67
67
  maxHeight: number | undefined;
68
68
  allowAttachments: boolean | string[];
69
- frontendManaged: boolean;
70
69
  value: UIMessage[];
71
70
  setValue: (messages: UIMessage[]) => void;
72
71
  host: HTMLElement;
@@ -166,113 +165,42 @@ export const Chatbot: React.FC<Props> = (props) => {
166
165
  };
167
166
  });
168
167
 
169
- if (props.frontendManaged) {
170
- const stream = new ReadableStream<UIMessageChunk>({
171
- start(controller) {
172
- frontendStreamControllerRef.current = controller;
168
+ const stream = new ReadableStream<UIMessageChunk>({
169
+ start(controller) {
170
+ frontendStreamControllerRef.current = controller;
173
171
 
174
- const abortHandler = () => {
175
- try {
176
- controller.close();
177
- } catch (error) {
178
- Logger.debug("Controller may already be closed", { error });
179
- }
180
- frontendStreamControllerRef.current = null;
181
- };
182
- signal?.addEventListener("abort", abortHandler);
183
-
184
- return () => {
185
- signal?.removeEventListener("abort", abortHandler);
186
- };
187
- },
188
- cancel() {
189
- frontendStreamControllerRef.current = null;
190
- },
191
- });
192
-
193
- // Start the prompt, chunks will be sent via events
194
- props
195
- .send_prompt({
196
- messages: messages,
197
- config: chatConfig,
198
- })
199
- .catch((error: Error) => {
200
- frontendStreamControllerRef.current?.error(error);
201
- frontendStreamControllerRef.current = null;
202
- });
203
-
204
- return createUIMessageStreamResponse({ stream });
205
- }
206
-
207
- if (signal?.aborted) {
208
- return new Response("Aborted", { status: 499 });
209
- }
210
-
211
- // Create a placeholder message for streaming (backend-managed)
212
- const messageId = Date.now().toString();
213
-
214
- setMessages((prev) => [
215
- ...prev,
216
- {
217
- id: messageId,
218
- role: "assistant",
219
- parts: [{ type: "text", text: "" }],
220
- },
221
- ]);
222
-
223
- // Create an abort-aware promise for the send_prompt call
224
- const sendPromptPromise = props.send_prompt({
225
- messages: messages,
226
- config: chatConfig,
227
- });
228
-
229
- // Race the send_prompt with an abort signal
230
- const response = await new Promise<string | null>(
231
- (resolve, reject) => {
232
- // Listen for abort
233
172
  const abortHandler = () => {
234
- reject(new DOMException("Aborted", "AbortError"));
173
+ try {
174
+ controller.close();
175
+ } catch (error) {
176
+ Logger.debug("Controller may already be closed", { error });
177
+ }
178
+ frontendStreamControllerRef.current = null;
235
179
  };
236
180
  signal?.addEventListener("abort", abortHandler);
237
181
 
238
- sendPromptPromise
239
- .then(resolve)
240
- .catch(reject)
241
- .finally(() => {
242
- signal?.removeEventListener("abort", abortHandler);
243
- });
182
+ return () => {
183
+ signal?.removeEventListener("abort", abortHandler);
184
+ };
244
185
  },
245
- );
186
+ cancel() {
187
+ frontendStreamControllerRef.current = null;
188
+ },
189
+ });
246
190
 
247
- if (response === null) {
248
- Logger.error("Non-frontend-managed response is null", {
249
- response,
191
+ // Start the prompt, chunks will be sent via events
192
+ void props
193
+ .send_prompt({
194
+ messages: messages,
195
+ config: chatConfig,
196
+ })
197
+ .catch((error: Error) => {
198
+ frontendStreamControllerRef.current?.error(error);
199
+ frontendStreamControllerRef.current = null;
250
200
  });
251
- return new Response("Internal server error", { status: 500 });
252
- }
253
201
 
254
- // If streaming didn't happen (non-generator response), update the message
255
- // Check if streaming state is still set (meaning no chunks were received)
256
- if (
257
- streamingStateRef.current.backendMessageId === null &&
258
- streamingStateRef.current.frontendMessageIndex === null
259
- ) {
260
- setMessages((prev) => {
261
- const updated = [...prev];
262
- const index = updated.findIndex((m) => m.id === messageId);
263
- if (index !== -1) {
264
- updated[index] = {
265
- ...updated[index],
266
- parts: [{ type: "text", text: response }],
267
- };
268
- }
269
- return updated;
270
- });
271
- }
272
-
273
- return new Response(response);
274
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
275
- } catch (error: any) {
202
+ return createUIMessageStreamResponse({ stream });
203
+ } catch (error: unknown) {
276
204
  // Clear streaming state on error
277
205
  streamingStateRef.current = {
278
206
  backendMessageId: null,
@@ -280,13 +208,13 @@ export const Chatbot: React.FC<Props> = (props) => {
280
208
  };
281
209
 
282
210
  // Handle abort gracefully without showing an error
283
- if (error.name === "AbortError") {
211
+ if (error instanceof Error && error.name === "AbortError") {
284
212
  return new Response("Aborted", { status: 499 });
285
213
  }
286
214
 
287
215
  // HACK: strip the error message to clean up the response
288
- const strippedError = error.message
289
- .split("failed with exception ")
216
+ const strippedError = (error as Error).message
217
+ ?.split("failed with exception ")
290
218
  .pop();
291
219
  return new Response(strippedError, { status: 400 });
292
220
  }
@@ -307,11 +235,7 @@ export const Chatbot: React.FC<Props> = (props) => {
307
235
  frontendMessageIndex: null,
308
236
  };
309
237
 
310
- // For frontend-managed streaming, we set the value directly from the frontend.
311
- // Because useChat creates the proper message structure for us.
312
- if (props.frontendManaged) {
313
- props.setValue(message.messages);
314
- }
238
+ props.setValue(message.messages);
315
239
  },
316
240
  onError: (error) => {
317
241
  Logger.error("An error occurred:", error);
@@ -338,90 +262,28 @@ export const Chatbot: React.FC<Props> = (props) => {
338
262
  return;
339
263
  }
340
264
 
341
- if (props.frontendManaged) {
342
- // Push to the stream for useChat to process
343
- const controller = frontendStreamControllerRef.current;
344
- if (!controller) {
345
- return;
346
- }
347
-
348
- const frontendMessage = message as {
349
- type: string;
350
- message_id: string;
351
- content?: UIMessageChunk;
352
- is_final?: boolean;
353
- };
354
-
355
- if (frontendMessage.content) {
356
- controller.enqueue(frontendMessage.content);
357
- }
358
- if (frontendMessage.is_final) {
359
- controller.close();
360
- frontendStreamControllerRef.current = null;
361
- }
265
+ // Push to the stream for useChat to process
266
+ const controller = frontendStreamControllerRef.current;
267
+ if (!controller) {
362
268
  return;
363
269
  }
364
270
 
365
- // Handle regular text streaming chunks
366
- const chunkMessage = message as {
271
+ const frontendMessage = message as {
367
272
  type: string;
368
273
  message_id: string;
369
- content: string;
370
- is_final: boolean;
274
+ content?: UIMessageChunk;
275
+ is_final?: boolean;
371
276
  };
372
277
 
373
- // Initialize streaming state on first chunk if not already set
374
- if (streamingStateRef.current.backendMessageId === null) {
375
- // Find the last assistant message (which should be the placeholder we created)
376
- setMessages((prev) => {
377
- const updated = [...prev];
378
- // Find the last assistant message
379
- for (let i = updated.length - 1; i >= 0; i--) {
380
- if (updated[i].role === "assistant") {
381
- streamingStateRef.current = {
382
- backendMessageId: chunkMessage.message_id,
383
- frontendMessageIndex: i,
384
- };
385
- break;
386
- }
387
- }
388
- return updated;
389
- });
278
+ if (frontendMessage.content) {
279
+ controller.enqueue(frontendMessage.content);
390
280
  }
391
-
392
- // Only process chunks for the current streaming message
393
- const frontendIndex = streamingStateRef.current.frontendMessageIndex;
394
- if (
395
- streamingStateRef.current.backendMessageId ===
396
- chunkMessage.message_id &&
397
- frontendIndex !== null
398
- ) {
399
- setMessages((prev) => {
400
- const updated = [...prev];
401
- const index = frontendIndex;
402
-
403
- // Update the message at the tracked index
404
- if (index < updated.length) {
405
- const messageToUpdate = updated[index];
406
- if (messageToUpdate.role === "assistant") {
407
- updated[index] = {
408
- ...messageToUpdate,
409
- parts: [{ type: "text", text: chunkMessage.content }],
410
- };
411
- }
412
- }
413
-
414
- return updated;
415
- });
416
-
417
- // Clear streaming state when final chunk arrives
418
- if (chunkMessage.is_final) {
419
- streamingStateRef.current = {
420
- backendMessageId: null,
421
- frontendMessageIndex: null,
422
- };
423
- }
281
+ if (frontendMessage.is_final) {
282
+ controller.close();
283
+ frontendStreamControllerRef.current = null;
424
284
  }
285
+
286
+ return;
425
287
  },
426
288
  );
427
289
 
@@ -434,10 +296,7 @@ export const Chatbot: React.FC<Props> = (props) => {
434
296
  props.delete_chat_message({ index });
435
297
  setMessages(newMessages);
436
298
 
437
- // Since we manage the state in the frontend, we need to update the value.
438
- if (props.frontendManaged) {
439
- props.setValue(newMessages);
440
- }
299
+ props.setValue(newMessages);
441
300
  }
442
301
  };
443
302
 
@@ -526,7 +385,7 @@ export const Chatbot: React.FC<Props> = (props) => {
526
385
 
527
386
  return (
528
387
  <div
529
- key={message.id}
388
+ key={`${message.id}-${index}`}
530
389
  className={cn(
531
390
  "flex flex-col group gap-2",
532
391
  message.role === "user" ? "items-end" : "items-start",