@opentrace/opentrace 0.4.0-rc.528 → 0.4.0-rc.534

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.
@@ -70323,6 +70323,8 @@ function ChatPanel({
70323
70323
  const [showSettings, setShowSettings] = useState(false);
70324
70324
  const [input, setInput] = useState("");
70325
70325
  const [streaming, setStreaming] = useState(false);
70326
+ const [editingIndex, setEditingIndex] = useState(null);
70327
+ const [editText, setEditText] = useState("");
70326
70328
  const [highlightEnabled, setHighlightEnabled] = useState(true);
70327
70329
  const [hasFoundNodes, setHasFoundNodes] = useState(false);
70328
70330
  const [activeTab, setActiveTab] = useState("chat");
@@ -70331,6 +70333,7 @@ function ChatPanel({
70331
70333
  const messagesRef = useRef(messages);
70332
70334
  const textareaRef = useRef(null);
70333
70335
  const abortRef = useRef(null);
70336
+ const streamingRef = useRef(false);
70334
70337
  const chatFoundNodesRef = useRef(/* @__PURE__ */ new Set());
70335
70338
  const keyInputRef = useRef(null);
70336
70339
  const localUrlInputRef = useRef(null);
@@ -70485,16 +70488,18 @@ function ChatPanel({
70485
70488
  };
70486
70489
  const sendMessage = async (text, images, attachments) => {
70487
70490
  const trimmed = text.trim();
70491
+ const hasImages = images && images.length > 0;
70488
70492
  const hasAtts = attachments && attachments.length > 0;
70489
- if (!trimmed && true && !hasAtts || providerId !== "local" && !apiKey || streaming)
70493
+ if (!trimmed && !hasImages && !hasAtts || providerId !== "local" && !apiKey || streamingRef.current)
70490
70494
  return;
70491
70495
  abortRef.current?.abort();
70492
70496
  const controller = new AbortController();
70493
70497
  abortRef.current = controller;
70498
+ streamingRef.current = true;
70494
70499
  const userMsg = {
70495
70500
  role: "user",
70496
70501
  content: trimmed,
70497
- ...{},
70502
+ ...hasImages ? { images } : {},
70498
70503
  ...hasAtts ? { attachments } : {}
70499
70504
  };
70500
70505
  const assistantMsg = {
@@ -70502,7 +70507,7 @@ function ChatPanel({
70502
70507
  content: "",
70503
70508
  parts: []
70504
70509
  };
70505
- const newMessages = [...messages, userMsg];
70510
+ const newMessages = [...messagesRef.current, userMsg];
70506
70511
  setMessages([...newMessages, assistantMsg]);
70507
70512
  setInput("");
70508
70513
  if (textareaRef.current) textareaRef.current.style.height = "auto";
@@ -70732,6 +70737,7 @@ ${att.textContent}
70732
70737
  return parts;
70733
70738
  });
70734
70739
  } finally {
70740
+ streamingRef.current = false;
70735
70741
  setStreaming(false);
70736
70742
  progress.setListener(null);
70737
70743
  const hasUsage = usageAccum.inputTokens > 0 || usageAccum.outputTokens > 0;
@@ -70767,6 +70773,40 @@ ${att.textContent}
70767
70773
  }
70768
70774
  sendMessage(input, void 0, atts);
70769
70775
  };
70776
+ const handleStop = () => {
70777
+ abortRef.current?.abort();
70778
+ streamingRef.current = false;
70779
+ setStreaming(false);
70780
+ };
70781
+ const startEditMessage = (index2) => {
70782
+ const m = messages[index2];
70783
+ if (!m || m.role !== "user") return;
70784
+ setEditingIndex(index2);
70785
+ setEditText(m.content);
70786
+ };
70787
+ const cancelEditMessage = () => {
70788
+ setEditingIndex(null);
70789
+ setEditText("");
70790
+ };
70791
+ const submitEditMessage = async (index2) => {
70792
+ const original = messages[index2];
70793
+ if (!original || original.role !== "user") return;
70794
+ const trimmed = editText.trim();
70795
+ const hasAtts = !!(original.attachments && original.attachments.length);
70796
+ const hasImgs = !!(original.images && original.images.length);
70797
+ if (!trimmed && !hasAtts && !hasImgs) return;
70798
+ if (streaming) {
70799
+ abortRef.current?.abort();
70800
+ streamingRef.current = false;
70801
+ setStreaming(false);
70802
+ }
70803
+ const truncated = messages.slice(0, index2);
70804
+ setMessages(truncated);
70805
+ messagesRef.current = truncated;
70806
+ setEditingIndex(null);
70807
+ setEditText("");
70808
+ await sendMessage(trimmed, original.images, original.attachments);
70809
+ };
70770
70810
  const handleTemplate = (prompt) => sendMessage(prompt);
70771
70811
  const addAttachments = useCallback(
70772
70812
  async (files) => {
@@ -70820,6 +70860,7 @@ ${att.textContent}
70820
70860
  const handleClearChat = () => {
70821
70861
  abortRef.current?.abort();
70822
70862
  startNewConversation();
70863
+ streamingRef.current = false;
70823
70864
  setStreaming(false);
70824
70865
  chatFoundNodesRef.current.clear();
70825
70866
  setHasFoundNodes(false);
@@ -71285,61 +71326,166 @@ ${att.textContent}
71285
71326
  ),
71286
71327
  /* @__PURE__ */ jsx(ChatTemplates, { onSelect: handleTemplate })
71287
71328
  ] }),
71288
- messages.map((m, i) => /* @__PURE__ */ jsxs(
71289
- "div",
71290
- {
71291
- className: `message ${m.role === "user" ? "user" : "ai"}`,
71292
- ...m.role === "assistant" && i === messages.length - 1 && !streaming ? { "data-testid": "chat-response-done" } : {},
71293
- children: [
71294
- /* @__PURE__ */ jsx("div", { className: "message-content", children: m.role === "assistant" ? /* @__PURE__ */ jsx(
71295
- ChatParts,
71296
- {
71297
- parts: m.parts,
71298
- streaming: streaming && i === messages.length - 1,
71299
- onNodeSelect,
71300
- onPostComment: prClient ? handlePostComment : void 0
71301
- }
71302
- ) : /* @__PURE__ */ jsxs(Fragment, { children: [
71303
- m.content,
71304
- m.attachments && m.attachments.length > 0 && /* @__PURE__ */ jsx("div", { className: "user-message-attachments", children: m.attachments.map(
71305
- (att) => att.kind === "image" ? /* @__PURE__ */ jsx(
71329
+ (() => {
71330
+ let lastUserIdx = -1;
71331
+ for (let k = messages.length - 1; k >= 0; k--) {
71332
+ if (messages[k].role === "user") {
71333
+ lastUserIdx = k;
71334
+ break;
71335
+ }
71336
+ }
71337
+ return messages.map((m, i) => /* @__PURE__ */ jsxs(
71338
+ "div",
71339
+ {
71340
+ className: `message ${m.role === "user" ? "user" : "ai"}`,
71341
+ ...m.role === "assistant" && i === messages.length - 1 && !streaming ? { "data-testid": "chat-response-done" } : {},
71342
+ children: [
71343
+ /* @__PURE__ */ jsx("div", { className: "message-content", children: m.role === "assistant" ? /* @__PURE__ */ jsx(
71344
+ ChatParts,
71345
+ {
71346
+ parts: m.parts,
71347
+ streaming: streaming && i === messages.length - 1,
71348
+ onNodeSelect,
71349
+ onPostComment: prClient ? handlePostComment : void 0
71350
+ }
71351
+ ) : editingIndex === i ? /* @__PURE__ */ jsxs("div", { className: "message-edit-form", children: [
71352
+ /* @__PURE__ */ jsx(
71353
+ "textarea",
71354
+ {
71355
+ className: "message-edit-textarea",
71356
+ value: editText,
71357
+ autoFocus: true,
71358
+ rows: Math.min(
71359
+ 8,
71360
+ Math.max(2, editText.split("\n").length)
71361
+ ),
71362
+ onChange: (e) => setEditText(e.target.value),
71363
+ onKeyDown: (e) => {
71364
+ if (e.key === "Enter" && !e.shiftKey) {
71365
+ e.preventDefault();
71366
+ submitEditMessage(i);
71367
+ } else if (e.key === "Escape") {
71368
+ e.preventDefault();
71369
+ cancelEditMessage();
71370
+ }
71371
+ }
71372
+ }
71373
+ ),
71374
+ /* @__PURE__ */ jsxs("div", { className: "message-edit-actions", children: [
71375
+ /* @__PURE__ */ jsx(
71376
+ "button",
71377
+ {
71378
+ className: "message-edit-cancel",
71379
+ onClick: cancelEditMessage,
71380
+ children: "Cancel"
71381
+ }
71382
+ ),
71383
+ /* @__PURE__ */ jsxs(
71384
+ "button",
71385
+ {
71386
+ className: "message-edit-submit",
71387
+ onClick: () => submitEditMessage(i),
71388
+ disabled: !editText.trim() && !(m.attachments && m.attachments.length) && !(m.images && m.images.length),
71389
+ title: "Resend edited message",
71390
+ children: [
71391
+ /* @__PURE__ */ jsxs(
71392
+ "svg",
71393
+ {
71394
+ width: "14",
71395
+ height: "14",
71396
+ viewBox: "0 0 24 24",
71397
+ fill: "none",
71398
+ stroke: "currentColor",
71399
+ strokeWidth: "2",
71400
+ strokeLinecap: "round",
71401
+ strokeLinejoin: "round",
71402
+ children: [
71403
+ /* @__PURE__ */ jsx("path", { d: "M5 12h14" }),
71404
+ /* @__PURE__ */ jsx("path", { d: "m12 5 7 7-7 7" })
71405
+ ]
71406
+ }
71407
+ ),
71408
+ /* @__PURE__ */ jsx("span", { children: "Resend" })
71409
+ ]
71410
+ }
71411
+ )
71412
+ ] })
71413
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
71414
+ m.content,
71415
+ m.attachments && m.attachments.length > 0 && /* @__PURE__ */ jsx("div", { className: "user-message-attachments", children: m.attachments.map(
71416
+ (att) => att.kind === "image" ? /* @__PURE__ */ jsx(
71417
+ "img",
71418
+ {
71419
+ src: att.dataUrl,
71420
+ alt: att.name || "Attached image",
71421
+ className: "user-message-image",
71422
+ onClick: () => setLightboxImage(att)
71423
+ },
71424
+ att.id
71425
+ ) : /* @__PURE__ */ jsxs(
71426
+ "div",
71427
+ {
71428
+ className: "user-message-file",
71429
+ title: att.name,
71430
+ children: [
71431
+ /* @__PURE__ */ jsx("span", { className: "file-icon", children: "📄" }),
71432
+ /* @__PURE__ */ jsx("span", { className: "file-name", children: att.name })
71433
+ ]
71434
+ },
71435
+ att.id
71436
+ )
71437
+ ) }),
71438
+ m.images && m.images.length > 0 && /* @__PURE__ */ jsx("div", { className: "user-message-images", children: m.images.map((img) => /* @__PURE__ */ jsx(
71306
71439
  "img",
71307
71440
  {
71308
- src: att.dataUrl,
71309
- alt: att.name || "Attached image",
71441
+ src: img.dataUrl,
71442
+ alt: img.name || "Attached image",
71310
71443
  className: "user-message-image",
71311
- onClick: () => setLightboxImage(att)
71312
- },
71313
- att.id
71314
- ) : /* @__PURE__ */ jsxs(
71315
- "div",
71316
- {
71317
- className: "user-message-file",
71318
- title: att.name,
71319
- children: [
71320
- /* @__PURE__ */ jsx("span", { className: "file-icon", children: "📄" }),
71321
- /* @__PURE__ */ jsx("span", { className: "file-name", children: att.name })
71322
- ]
71444
+ onClick: () => setLightboxImage(img)
71323
71445
  },
71324
- att.id
71325
- )
71326
- ) }),
71327
- m.images && m.images.length > 0 && /* @__PURE__ */ jsx("div", { className: "user-message-images", children: m.images.map((img) => /* @__PURE__ */ jsx(
71328
- "img",
71446
+ img.id
71447
+ )) })
71448
+ ] }) }),
71449
+ m.role === "assistant" && m.usage && !(streaming && i === messages.length - 1) && /* @__PURE__ */ jsx(
71450
+ TokenUsageFooter,
71329
71451
  {
71330
- src: img.dataUrl,
71331
- alt: img.name || "Attached image",
71332
- className: "user-message-image",
71333
- onClick: () => setLightboxImage(img)
71334
- },
71335
- img.id
71336
- )) })
71337
- ] }) }),
71338
- m.role === "assistant" && m.usage && !(streaming && i === messages.length - 1) && /* @__PURE__ */ jsx(TokenUsageFooter, { usage: m.usage })
71339
- ]
71340
- },
71341
- i
71342
- ))
71452
+ usage: m.usage
71453
+ }
71454
+ ),
71455
+ m.role === "user" && i === lastUserIdx && editingIndex !== i && /* @__PURE__ */ jsxs(
71456
+ "button",
71457
+ {
71458
+ className: "edit-message-btn",
71459
+ onClick: () => startEditMessage(i),
71460
+ title: "Edit & resend",
71461
+ "aria-label": "Edit message",
71462
+ children: [
71463
+ /* @__PURE__ */ jsxs(
71464
+ "svg",
71465
+ {
71466
+ width: "12",
71467
+ height: "12",
71468
+ viewBox: "0 0 24 24",
71469
+ fill: "none",
71470
+ stroke: "currentColor",
71471
+ strokeWidth: "2",
71472
+ strokeLinecap: "round",
71473
+ strokeLinejoin: "round",
71474
+ children: [
71475
+ /* @__PURE__ */ jsx("path", { d: "M12 20h9" }),
71476
+ /* @__PURE__ */ jsx("path", { d: "M16.5 3.5a2.121 2.121 0 1 1 3 3L7 19l-4 1 1-4Z" })
71477
+ ]
71478
+ }
71479
+ ),
71480
+ /* @__PURE__ */ jsx("span", { children: "Edit" })
71481
+ ]
71482
+ }
71483
+ )
71484
+ ]
71485
+ },
71486
+ i
71487
+ ));
71488
+ })()
71343
71489
  ] }),
71344
71490
  /* @__PURE__ */ jsxs("div", { className: "chat-input-area", children: [
71345
71491
  attachError && /* @__PURE__ */ jsxs("div", { className: "image-error-banner", children: [
@@ -71442,12 +71588,30 @@ ${att.textContent}
71442
71588
  conversationUsage.toLocaleString(),
71443
71589
  " tokens"
71444
71590
  ] }),
71445
- /* @__PURE__ */ jsx(
71591
+ streaming ? /* @__PURE__ */ jsx(
71592
+ "button",
71593
+ {
71594
+ className: "chat-action-btn stop-btn",
71595
+ onClick: handleStop,
71596
+ "data-testid": "chat-stop-btn",
71597
+ title: "Stop generating",
71598
+ children: /* @__PURE__ */ jsx(
71599
+ "svg",
71600
+ {
71601
+ width: "16",
71602
+ height: "16",
71603
+ viewBox: "0 0 24 24",
71604
+ fill: "currentColor",
71605
+ children: /* @__PURE__ */ jsx("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" })
71606
+ }
71607
+ )
71608
+ }
71609
+ ) : /* @__PURE__ */ jsx(
71446
71610
  "button",
71447
71611
  {
71448
71612
  className: "chat-action-btn",
71449
71613
  onClick: handleSubmit,
71450
- disabled: streaming || !input.trim() && pendingAttachments.length === 0,
71614
+ disabled: !input.trim() && pendingAttachments.length === 0,
71451
71615
  "data-testid": "chat-send-btn",
71452
71616
  title: "Send",
71453
71617
  children: /* @__PURE__ */ jsxs(
@@ -78643,4 +78807,4 @@ export {
78643
78807
  loadAnimationSettings as l,
78644
78808
  useGraphData as u
78645
78809
  };
78646
- //# sourceMappingURL=SettingsDrawer-CS3GT8xp.js.map
78810
+ //# sourceMappingURL=SettingsDrawer-WL2RHvRb.js.map