@pactor-app/chat 1.2.0 → 1.3.0

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/index.cjs CHANGED
@@ -500,6 +500,16 @@ function pairToolTraces(parts) {
500
500
  }
501
501
 
502
502
  // src/transport.ts
503
+ function markCancelled(store, messageId) {
504
+ const message = store.getMessage(messageId);
505
+ if (!message) {
506
+ return;
507
+ }
508
+ const parts = message.parts.map(
509
+ (part) => "status" in part && (part.status === "streaming" || part.status === "pending") ? { ...part, status: "ready" } : part
510
+ );
511
+ store.updateMessage(messageId, { status: "cancelled", parts });
512
+ }
503
513
  async function runChatTurn(store, transport, input, options = {}) {
504
514
  const userMessageId = options.userMessageId ?? createChatId("msg");
505
515
  const assistantMessageId = options.assistantMessageId ?? createChatId("msg");
@@ -522,15 +532,15 @@ async function runChatTurn(store, transport, input, options = {}) {
522
532
  createdAt: Date.now()
523
533
  });
524
534
  try {
525
- for await (const event of transport.send(input)) {
535
+ for await (const event of transport.send(input, { signal: options.signal })) {
526
536
  if (options.signal?.aborted) {
527
- store.updateMessage(assistantMessageId, { status: "cancelled" });
537
+ markCancelled(store, assistantMessageId);
528
538
  return { userMessageId, assistantMessageId };
529
539
  }
530
540
  applyChatEvent(store, { ...event, messageId: assistantMessageId });
531
541
  }
532
542
  if (options.signal?.aborted) {
533
- store.updateMessage(assistantMessageId, { status: "cancelled" });
543
+ markCancelled(store, assistantMessageId);
534
544
  return { userMessageId, assistantMessageId };
535
545
  }
536
546
  const message = store.getMessage(assistantMessageId);
@@ -858,7 +868,7 @@ function useChatAutoScroll({
858
868
 
859
869
  // src/chat-composition.tsx
860
870
  var import_runtime5 = require("@pactor-app/runtime");
861
- var import_react16 = require("react");
871
+ var import_react17 = require("react");
862
872
 
863
873
  // src/hooks/use-chat-model.ts
864
874
  var import_react3 = require("react");
@@ -916,13 +926,50 @@ var import_components4 = require("@pactor-app/ui/components");
916
926
  var import_core3 = require("@pactor-app/core");
917
927
  var import_markdown2 = require("@pactor-app/markdown");
918
928
  var import_runtime4 = require("@pactor-app/runtime");
919
- var import_react5 = require("react");
929
+ var import_react6 = require("react");
920
930
 
921
931
  // src/view/PartRenderer.tsx
922
932
  var import_components2 = require("@pactor-app/ui/components");
923
933
  var import_markdown = require("@pactor-app/markdown");
924
934
  var import_runtime3 = require("@pactor-app/runtime");
925
935
 
936
+ // src/hooks/use-throttled-value.ts
937
+ var import_react4 = require("react");
938
+ function useThrottledValue(value, intervalMs, enabled) {
939
+ const [shown, setShown] = (0, import_react4.useState)(value);
940
+ const lastUpdateRef = (0, import_react4.useRef)(0);
941
+ const timerRef = (0, import_react4.useRef)(void 0);
942
+ (0, import_react4.useEffect)(() => {
943
+ if (!enabled) {
944
+ if (timerRef.current) {
945
+ clearTimeout(timerRef.current);
946
+ timerRef.current = void 0;
947
+ }
948
+ setShown(value);
949
+ return;
950
+ }
951
+ const now = Date.now();
952
+ const elapsed = now - lastUpdateRef.current;
953
+ if (elapsed >= intervalMs) {
954
+ lastUpdateRef.current = now;
955
+ setShown(value);
956
+ return;
957
+ }
958
+ timerRef.current = setTimeout(() => {
959
+ lastUpdateRef.current = Date.now();
960
+ setShown(value);
961
+ timerRef.current = void 0;
962
+ }, intervalMs - elapsed);
963
+ return () => {
964
+ if (timerRef.current) {
965
+ clearTimeout(timerRef.current);
966
+ timerRef.current = void 0;
967
+ }
968
+ };
969
+ }, [value, intervalMs, enabled]);
970
+ return enabled ? shown : value;
971
+ }
972
+
926
973
  // src/view/chrome/artifact-card.tsx
927
974
  function buildArtifactCardFragment(artifact) {
928
975
  return {
@@ -977,7 +1024,7 @@ function buildArtifactCardFragment(artifact) {
977
1024
 
978
1025
  // src/view/chrome/ChromeSlot.tsx
979
1026
  var import_runtime2 = require("@pactor-app/runtime");
980
- var import_react4 = require("react");
1027
+ var import_react5 = require("react");
981
1028
 
982
1029
  // src/view/chrome/actions.ts
983
1030
  var handlers = {};
@@ -1111,7 +1158,7 @@ function ChromeSlotInner({
1111
1158
  const rendered = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "pactor-chat-chrome", children: renderer.renderChildren([fragment.node]) });
1112
1159
  return wrap ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: wrap(rendered) }) : rendered;
1113
1160
  }
1114
- var ChromeSlotGuard = class extends import_react4.Component {
1161
+ var ChromeSlotGuard = class extends import_react5.Component {
1115
1162
  constructor() {
1116
1163
  super(...arguments);
1117
1164
  this.state = { failed: false };
@@ -1169,7 +1216,8 @@ function SmoothText({ content, streaming }) {
1169
1216
  }
1170
1217
  function SmoothMarkdown({ content, streaming }) {
1171
1218
  const shown = useSmoothStreamText(content, streaming);
1172
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_markdown.MarkdownRenderer, { content: shown });
1219
+ const throttled = useThrottledValue(shown, 100, streaming);
1220
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_markdown.MarkdownRenderer, { content: throttled });
1173
1221
  }
1174
1222
  function PartRenderer({ part, runtime, scope, streaming = false, onOpenArtifact }) {
1175
1223
  switch (part.type) {
@@ -1310,7 +1358,7 @@ function buildPanelHeaderFragment(props) {
1310
1358
  // src/view/ArtifactPanel.tsx
1311
1359
  var import_jsx_runtime5 = require("react/jsx-runtime");
1312
1360
  function useArtifactRuntime(safeRegistry, user) {
1313
- return (0, import_react5.useMemo)(() => {
1361
+ return (0, import_react6.useMemo)(() => {
1314
1362
  if (!safeRegistry) {
1315
1363
  return void 0;
1316
1364
  }
@@ -1322,7 +1370,7 @@ function useArtifactRuntime(safeRegistry, user) {
1322
1370
  return createChatRuntime({ safeRegistry: components, user });
1323
1371
  }, [safeRegistry, user]);
1324
1372
  }
1325
- var ArtifactDslGuard = class extends import_react5.Component {
1373
+ var ArtifactDslGuard = class extends import_react6.Component {
1326
1374
  constructor() {
1327
1375
  super(...arguments);
1328
1376
  this.state = { failed: false };
@@ -1381,13 +1429,13 @@ function ArtifactPanel({
1381
1429
  className
1382
1430
  }) {
1383
1431
  const runtime = useArtifactRuntime(safeRegistry, user);
1384
- const closeRef = (0, import_react5.useRef)(onClose);
1432
+ const closeRef = (0, import_react6.useRef)(onClose);
1385
1433
  closeRef.current = onClose;
1386
- (0, import_react5.useEffect)(() => {
1434
+ (0, import_react6.useEffect)(() => {
1387
1435
  setChatChromeHandlers({ panelClose: () => closeRef.current?.() });
1388
1436
  }, []);
1389
- const panelRef = (0, import_react5.useRef)(null);
1390
- const [width, setWidth] = (0, import_react5.useState)(null);
1437
+ const panelRef = (0, import_react6.useRef)(null);
1438
+ const [width, setWidth] = (0, import_react6.useState)(null);
1391
1439
  const handleResizeStart = (event) => {
1392
1440
  event.preventDefault();
1393
1441
  const panel = panelRef.current;
@@ -1464,14 +1512,14 @@ function ArtifactPanel({
1464
1512
  // src/view/ChatView.tsx
1465
1513
  var import_components10 = require("@pactor-app/ui/components");
1466
1514
  var import_core4 = require("@pactor-app/core");
1467
- var import_react14 = require("react");
1515
+ var import_react15 = require("react");
1468
1516
 
1469
1517
  // src/view/Composer.tsx
1470
1518
  var import_components7 = require("@pactor-app/ui/components");
1471
- var import_react10 = require("react");
1519
+ var import_react11 = require("react");
1472
1520
 
1473
1521
  // src/view/AnchoredPopover.tsx
1474
- var import_react6 = require("react");
1522
+ var import_react7 = require("react");
1475
1523
  var import_jsx_runtime6 = require("react/jsx-runtime");
1476
1524
  function AnchoredPopover({
1477
1525
  anchorRect,
@@ -1481,9 +1529,9 @@ function AnchoredPopover({
1481
1529
  role = "dialog",
1482
1530
  sideOffset = 8
1483
1531
  }) {
1484
- const ref = (0, import_react6.useRef)(null);
1485
- const [position, setPosition] = (0, import_react6.useState)(null);
1486
- (0, import_react6.useLayoutEffect)(() => {
1532
+ const ref = (0, import_react7.useRef)(null);
1533
+ const [position, setPosition] = (0, import_react7.useState)(null);
1534
+ (0, import_react7.useLayoutEffect)(() => {
1487
1535
  const element = ref.current;
1488
1536
  if (!element) return;
1489
1537
  const height = element.offsetHeight;
@@ -1501,7 +1549,7 @@ function AnchoredPopover({
1501
1549
  }
1502
1550
  );
1503
1551
  }, [anchorRect, sideOffset]);
1504
- (0, import_react6.useEffect)(() => {
1552
+ (0, import_react7.useEffect)(() => {
1505
1553
  const onKeyDown = (event) => {
1506
1554
  if (event.key !== "Escape") return;
1507
1555
  const element = ref.current;
@@ -1549,7 +1597,7 @@ function AnchoredPopover({
1549
1597
 
1550
1598
  // src/view/ModelChip.tsx
1551
1599
  var import_components5 = require("@pactor-app/ui/components");
1552
- var import_react7 = require("react");
1600
+ var import_react8 = require("react");
1553
1601
 
1554
1602
  // src/view/chrome/model-select.tsx
1555
1603
  function buildModelSelectFragment(props) {
@@ -1581,8 +1629,8 @@ function ModelChip({
1581
1629
  model,
1582
1630
  onModelChange
1583
1631
  }) {
1584
- const chipRef = (0, import_react7.useRef)(null);
1585
- const [anchor, setAnchor] = (0, import_react7.useState)(null);
1632
+ const chipRef = (0, import_react8.useRef)(null);
1633
+ const [anchor, setAnchor] = (0, import_react8.useState)(null);
1586
1634
  const currentOption = models.find((option) => option.value === model) ?? models[0];
1587
1635
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1588
1636
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
@@ -1633,9 +1681,9 @@ function ModelSelect({
1633
1681
  model,
1634
1682
  onModelChange
1635
1683
  }) {
1636
- const changeRef = (0, import_react7.useRef)(onModelChange);
1684
+ const changeRef = (0, import_react8.useRef)(onModelChange);
1637
1685
  changeRef.current = onModelChange;
1638
- (0, import_react7.useEffect)(() => {
1686
+ (0, import_react8.useEffect)(() => {
1639
1687
  setChatChromeHandlers({
1640
1688
  modelChange: (value) => changeRef.current?.(value)
1641
1689
  });
@@ -1651,13 +1699,13 @@ function ModelSelect({
1651
1699
 
1652
1700
  // src/view/chrome/composer.tsx
1653
1701
  var import_components6 = require("@pactor-app/ui/components");
1654
- var import_react8 = require("react");
1702
+ var import_react9 = require("react");
1655
1703
  var import_jsx_runtime8 = require("react/jsx-runtime");
1656
- var ComposerBridgeContext = (0, import_react8.createContext)(
1704
+ var ComposerBridgeContext = (0, import_react9.createContext)(
1657
1705
  null
1658
1706
  );
1659
1707
  function useComposerBridge() {
1660
- const bridge = (0, import_react8.useContext)(ComposerBridgeContext);
1708
+ const bridge = (0, import_react9.useContext)(ComposerBridgeContext);
1661
1709
  if (!bridge) {
1662
1710
  throw new Error(
1663
1711
  "[@pactor-app/chat] ChatComposer* \u5185\u90E8\u7EC4\u4EF6\u4EC5\u53EF\u5728 Composer chrome \u7247\u6BB5\u5185\u6E32\u67D3"
@@ -1863,7 +1911,7 @@ function buildComposerFragment(props) {
1863
1911
  }
1864
1912
 
1865
1913
  // src/view/context-sources.tsx
1866
- var import_react9 = require("react");
1914
+ var import_react10 = require("react");
1867
1915
  var import_jsx_runtime9 = require("react/jsx-runtime");
1868
1916
  function sourceCacheKey(source) {
1869
1917
  return `${source.service ?? ""}:${JSON.stringify(source.params ?? {})}`;
@@ -1899,8 +1947,8 @@ function ContextSourceList({
1899
1947
  cache,
1900
1948
  onSelect
1901
1949
  }) {
1902
- const [resolutions, setResolutions] = (0, import_react9.useState)([]);
1903
- (0, import_react9.useEffect)(() => {
1950
+ const [resolutions, setResolutions] = (0, import_react10.useState)([]);
1951
+ (0, import_react10.useEffect)(() => {
1904
1952
  let cancelled = false;
1905
1953
  const initial = sources.map(
1906
1954
  (source) => source.items ? { status: "ready", items: source.items } : { status: "loading" }
@@ -2000,26 +2048,26 @@ function Composer({
2000
2048
  composerFooter,
2001
2049
  hideToolbar = false
2002
2050
  }) {
2003
- const [text, setText] = (0, import_react10.useState)("");
2004
- const [dragOver, setDragOver] = (0, import_react10.useState)(false);
2005
- const inputRef = (0, import_react10.useRef)(null);
2006
- const fileInputRef = (0, import_react10.useRef)(null);
2007
- const plusButtonRef = (0, import_react10.useRef)(null);
2008
- const composingRef = (0, import_react10.useRef)(false);
2051
+ const [text, setText] = (0, import_react11.useState)("");
2052
+ const [dragOver, setDragOver] = (0, import_react11.useState)(false);
2053
+ const inputRef = (0, import_react11.useRef)(null);
2054
+ const fileInputRef = (0, import_react11.useRef)(null);
2055
+ const plusButtonRef = (0, import_react11.useRef)(null);
2056
+ const composingRef = (0, import_react11.useRef)(false);
2009
2057
  const modelChipEnabled = models !== void 0 && models.length > 0;
2010
- const mentionSources = (0, import_react10.useMemo)(
2058
+ const mentionSources = (0, import_react11.useMemo)(
2011
2059
  () => contextSources?.filter((source) => source.trigger === "@") ?? [],
2012
2060
  [contextSources]
2013
2061
  );
2014
- const plusSources = (0, import_react10.useMemo)(
2062
+ const plusSources = (0, import_react11.useMemo)(
2015
2063
  () => contextSources?.filter((source) => source.trigger === "+") ?? [],
2016
2064
  [contextSources]
2017
2065
  );
2018
2066
  const builtinMentionEnabled = onMentionTrigger === void 0 && mentionSources.length > 0;
2019
2067
  const builtinPlusEnabled = onPlusMenu === void 0 && plusSources.length > 0;
2020
- const [mentionState, setMentionState] = (0, import_react10.useState)(null);
2021
- const [plusAnchor, setPlusAnchor] = (0, import_react10.useState)(null);
2022
- const sourceCacheRef = (0, import_react10.useRef)(null);
2068
+ const [mentionState, setMentionState] = (0, import_react11.useState)(null);
2069
+ const [plusAnchor, setPlusAnchor] = (0, import_react11.useState)(null);
2070
+ const sourceCacheRef = (0, import_react11.useRef)(null);
2023
2071
  if (sourceCacheRef.current === null) {
2024
2072
  sourceCacheRef.current = /* @__PURE__ */ new Map();
2025
2073
  }
@@ -2038,7 +2086,7 @@ function Composer({
2038
2086
  setMentionState(null);
2039
2087
  setPlusAnchor(null);
2040
2088
  };
2041
- (0, import_react10.useLayoutEffect)(() => {
2089
+ (0, import_react11.useLayoutEffect)(() => {
2042
2090
  const input = inputRef.current;
2043
2091
  if (input) {
2044
2092
  input.style.height = "auto";
@@ -2053,9 +2101,9 @@ function Composer({
2053
2101
  onSend?.(value);
2054
2102
  setText("");
2055
2103
  };
2056
- const sendRef = (0, import_react10.useRef)(send);
2104
+ const sendRef = (0, import_react11.useRef)(send);
2057
2105
  sendRef.current = send;
2058
- (0, import_react10.useLayoutEffect)(() => {
2106
+ (0, import_react11.useLayoutEffect)(() => {
2059
2107
  setChatChromeHandlers({ composerSubmit: () => sendRef.current() });
2060
2108
  }, []);
2061
2109
  const syncText = (value, caret, rect) => {
@@ -2472,15 +2520,15 @@ function EmptyState({
2472
2520
  }
2473
2521
 
2474
2522
  // src/view/MessageBubble.tsx
2475
- var import_react13 = require("react");
2523
+ var import_react14 = require("react");
2476
2524
 
2477
2525
  // src/view/ActivityTrace.tsx
2478
2526
  var import_components8 = require("@pactor-app/ui/components");
2479
2527
  var import_markdown3 = require("@pactor-app/markdown");
2480
- var import_react11 = require("react");
2528
+ var import_react12 = require("react");
2481
2529
  var import_jsx_runtime12 = require("react/jsx-runtime");
2482
2530
  function usePinnedCollapse(autoOpen) {
2483
- const [state, setState] = (0, import_react11.useState)("auto");
2531
+ const [state, setState] = (0, import_react12.useState)("auto");
2484
2532
  const open = state === "auto" ? autoOpen : state === "pinned-open";
2485
2533
  const toggle = () => {
2486
2534
  setState(open ? "pinned-closed" : "pinned-open");
@@ -2506,8 +2554,8 @@ function statusText(message, mode, toolCount) {
2506
2554
  }
2507
2555
  function useElapsedSeconds(startedAt, active) {
2508
2556
  const compute = () => Math.max(0, Math.floor((Date.now() - startedAt) / 1e3));
2509
- const [elapsed, setElapsed] = (0, import_react11.useState)(compute);
2510
- (0, import_react11.useEffect)(() => {
2557
+ const [elapsed, setElapsed] = (0, import_react12.useState)(compute);
2558
+ (0, import_react12.useEffect)(() => {
2511
2559
  if (!active) {
2512
2560
  setElapsed(compute());
2513
2561
  return;
@@ -2558,7 +2606,7 @@ function formatDetail(value) {
2558
2606
  }
2559
2607
  }
2560
2608
  function TraceRow({ entry }) {
2561
- const [open, setOpen] = (0, import_react11.useState)(false);
2609
+ const [open, setOpen] = (0, import_react12.useState)(false);
2562
2610
  const summary = summarizeArgs(entry.args);
2563
2611
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2564
2612
  "div",
@@ -2595,6 +2643,7 @@ function ThinkingCard({
2595
2643
  answerPhase
2596
2644
  }) {
2597
2645
  const partStreaming = part.status === "streaming";
2646
+ const shown = useThrottledValue(part.content, 120, partStreaming);
2598
2647
  const { open, toggle } = usePinnedCollapse(partStreaming && !answerPhase);
2599
2648
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2600
2649
  "div",
@@ -2617,7 +2666,7 @@ function ThinkingCard({
2617
2666
  ]
2618
2667
  }
2619
2668
  ),
2620
- open && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "pactor-chat-thinking__body", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_markdown3.MarkdownRenderer, { content: part.content }) })
2669
+ open && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "pactor-chat-thinking__body", children: partStreaming ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "pactor-chat-thinking__text", children: shown }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_markdown3.MarkdownRenderer, { content: part.content }) })
2621
2670
  ]
2622
2671
  }
2623
2672
  );
@@ -2631,7 +2680,7 @@ function ActivityTrace({ message }) {
2631
2680
  );
2632
2681
  const answerPhase = mode === "responding" || !streaming;
2633
2682
  const { open, toggle } = usePinnedCollapse(!answerPhase);
2634
- const mountTimeRef = (0, import_react11.useRef)(0);
2683
+ const mountTimeRef = (0, import_react12.useRef)(0);
2635
2684
  if (mountTimeRef.current === 0) {
2636
2685
  mountTimeRef.current = Date.now();
2637
2686
  }
@@ -2679,7 +2728,7 @@ function ActivityTrace({ message }) {
2679
2728
 
2680
2729
  // src/view/MessageActions.tsx
2681
2730
  var import_components9 = require("@pactor-app/ui/components");
2682
- var import_react12 = require("react");
2731
+ var import_react13 = require("react");
2683
2732
 
2684
2733
  // src/view/chrome/message-actions.tsx
2685
2734
  function buildMessageActionsFragment(args) {
@@ -2805,13 +2854,13 @@ function MessageActions({
2805
2854
  callService,
2806
2855
  onMessageAction
2807
2856
  }) {
2808
- const [copied, setCopied] = (0, import_react12.useState)(false);
2809
- const dispatchRef = (0, import_react12.useRef)(
2857
+ const [copied, setCopied] = (0, import_react13.useState)(false);
2858
+ const dispatchRef = (0, import_react13.useRef)(
2810
2859
  () => {
2811
2860
  }
2812
2861
  );
2813
- const visibleActionsRef = (0, import_react12.useRef)([]);
2814
- (0, import_react12.useEffect)(() => {
2862
+ const visibleActionsRef = (0, import_react13.useRef)([]);
2863
+ (0, import_react13.useEffect)(() => {
2815
2864
  setMessageActionDispatcher(message.id, (actionId) => {
2816
2865
  const action = visibleActionsRef.current.find((a) => a.id === actionId);
2817
2866
  if (action) {
@@ -2925,7 +2974,7 @@ function MessageEditForm({
2925
2974
  onConfirm,
2926
2975
  onCancel
2927
2976
  }) {
2928
- const [text, setText] = (0, import_react13.useState)(initialText);
2977
+ const [text, setText] = (0, import_react14.useState)(initialText);
2929
2978
  const confirm = () => {
2930
2979
  const value = text.trim();
2931
2980
  if (!value) {
@@ -2989,7 +3038,7 @@ function MessageBubble({
2989
3038
  onMessageAction,
2990
3039
  onOpenArtifact
2991
3040
  }) {
2992
- const [editing, setEditing] = (0, import_react13.useState)(false);
3041
+ const [editing, setEditing] = (0, import_react14.useState)(false);
2993
3042
  const bubbleScope = {
2994
3043
  conversation: scope?.conversation ?? {},
2995
3044
  message: { id: message.id },
@@ -2997,7 +3046,7 @@ function MessageBubble({
2997
3046
  };
2998
3047
  const streaming = message.status === "streaming";
2999
3048
  const live = message.status === "pending" || message.status === "streaming";
3000
- const wasLiveRef = (0, import_react13.useRef)(live);
3049
+ const wasLiveRef = (0, import_react14.useRef)(live);
3001
3050
  if (live) {
3002
3051
  wasLiveRef.current = true;
3003
3052
  }
@@ -3104,14 +3153,14 @@ function ChatView({
3104
3153
  onMessageAction,
3105
3154
  composerBottom
3106
3155
  }) {
3107
- const messages = (0, import_react14.useSyncExternalStore)(
3156
+ const messages = (0, import_react15.useSyncExternalStore)(
3108
3157
  (listener) => store.subscribe(listener),
3109
3158
  () => store.getMessages()
3110
3159
  );
3111
3160
  const isStreaming = messages.some(
3112
3161
  (message) => message.status === "pending" || message.status === "streaming"
3113
3162
  );
3114
- const runtime = (0, import_react14.useMemo)(() => {
3163
+ const runtime = (0, import_react15.useMemo)(() => {
3115
3164
  if (!safeRegistry) {
3116
3165
  return void 0;
3117
3166
  }
@@ -3128,12 +3177,12 @@ function ChatView({
3128
3177
  user
3129
3178
  });
3130
3179
  }, [safeRegistry, capabilities, permission, confirm, user]);
3131
- const scope = (0, import_react14.useMemo)(
3180
+ const scope = (0, import_react15.useMemo)(
3132
3181
  () => ({ conversation: { id: conversationId }, params }),
3133
3182
  [conversationId, params]
3134
3183
  );
3135
- const notifiedArtifactsRef = (0, import_react14.useRef)(/* @__PURE__ */ new Set());
3136
- (0, import_react14.useEffect)(() => {
3184
+ const notifiedArtifactsRef = (0, import_react15.useRef)(/* @__PURE__ */ new Set());
3185
+ (0, import_react15.useEffect)(() => {
3137
3186
  if (!onArtifact) {
3138
3187
  return;
3139
3188
  }
@@ -3152,7 +3201,7 @@ function ChatView({
3152
3201
  });
3153
3202
  }
3154
3203
  }, [messages, onArtifact]);
3155
- const abortRef = (0, import_react14.useRef)(null);
3204
+ const abortRef = (0, import_react15.useRef)(null);
3156
3205
  const { containerRef, isFollowing, scrollToBottom } = useChatAutoScroll({
3157
3206
  messageCount: messages.length,
3158
3207
  lastContentLength: lastContentLength(messages),
@@ -3163,7 +3212,7 @@ function ChatView({
3163
3212
  model,
3164
3213
  onModelChange
3165
3214
  });
3166
- const [contextChips, setContextChips] = (0, import_react14.useState)([]);
3215
+ const [contextChips, setContextChips] = (0, import_react15.useState)([]);
3167
3216
  const handleContextChipsChange = (chips) => {
3168
3217
  setContextChips(chips);
3169
3218
  onContextsChange?.(chips);
@@ -3195,11 +3244,11 @@ function ChatView({
3195
3244
  abortRef.current?.abort();
3196
3245
  abortRef.current = null;
3197
3246
  };
3198
- const handleSendRef = (0, import_react14.useRef)(handleSend);
3247
+ const handleSendRef = (0, import_react15.useRef)(handleSend);
3199
3248
  handleSendRef.current = handleSend;
3200
- const onOpenArtifactRef = (0, import_react14.useRef)(onOpenArtifact);
3249
+ const onOpenArtifactRef = (0, import_react15.useRef)(onOpenArtifact);
3201
3250
  onOpenArtifactRef.current = onOpenArtifact;
3202
- (0, import_react14.useEffect)(() => {
3251
+ (0, import_react15.useEffect)(() => {
3203
3252
  setChatChromeHandlers({
3204
3253
  sendSuggestion: (text) => handleSendRef.current(text),
3205
3254
  openArtifact: (artifact) => onOpenArtifactRef.current?.(artifact)
@@ -3306,7 +3355,7 @@ function ChatView({
3306
3355
 
3307
3356
  // src/view/WorkspaceHeader.tsx
3308
3357
  var import_components11 = require("@pactor-app/ui/components");
3309
- var import_react15 = require("react");
3358
+ var import_react16 = require("react");
3310
3359
 
3311
3360
  // src/export-markdown.ts
3312
3361
  var ROLE_LABELS = {
@@ -3403,10 +3452,10 @@ function WorkspaceHeader({
3403
3452
  onTitleChange
3404
3453
  }) {
3405
3454
  const hasActions = headerActions !== void 0 && headerActions.length > 0;
3406
- const [overrideTitle, setOverrideTitle] = (0, import_react15.useState)(null);
3407
- const [editing, setEditing] = (0, import_react15.useState)(false);
3408
- const [draft, setDraft] = (0, import_react15.useState)("");
3409
- const prevTitleRef = (0, import_react15.useRef)(title);
3455
+ const [overrideTitle, setOverrideTitle] = (0, import_react16.useState)(null);
3456
+ const [editing, setEditing] = (0, import_react16.useState)(false);
3457
+ const [draft, setDraft] = (0, import_react16.useState)("");
3458
+ const prevTitleRef = (0, import_react16.useRef)(title);
3410
3459
  if (prevTitleRef.current !== title) {
3411
3460
  prevTitleRef.current = title;
3412
3461
  if (overrideTitle !== null) {
@@ -3414,7 +3463,7 @@ function WorkspaceHeader({
3414
3463
  }
3415
3464
  }
3416
3465
  const displayTitle = overrideTitle ?? title;
3417
- const [theme, setTheme] = (0, import_react15.useState)(readCurrentTheme);
3466
+ const [theme, setTheme] = (0, import_react16.useState)(readCurrentTheme);
3418
3467
  const commitTitle = () => {
3419
3468
  setEditing(false);
3420
3469
  const next = draft.trim();
@@ -3441,9 +3490,9 @@ function WorkspaceHeader({
3441
3490
  store.clear();
3442
3491
  onClear?.();
3443
3492
  };
3444
- const handlersRef = (0, import_react15.useRef)({ handleExport, handleTheme, handleClear });
3493
+ const handlersRef = (0, import_react16.useRef)({ handleExport, handleTheme, handleClear });
3445
3494
  handlersRef.current = { handleExport, handleTheme, handleClear };
3446
- (0, import_react15.useEffect)(() => {
3495
+ (0, import_react16.useEffect)(() => {
3447
3496
  setChatChromeHandlers({
3448
3497
  headerAction: (name) => {
3449
3498
  const handlers2 = handlersRef.current;
@@ -3555,7 +3604,7 @@ function DslNodeSlotInner({ node }) {
3555
3604
  const { renderChildren } = (0, import_runtime5.useRenderer)();
3556
3605
  return renderChildren([node]);
3557
3606
  }
3558
- var DslSlotGuard = class extends import_react16.Component {
3607
+ var DslSlotGuard = class extends import_react17.Component {
3559
3608
  constructor() {
3560
3609
  super(...arguments);
3561
3610
  this.state = { failed: false };
@@ -3576,7 +3625,7 @@ function DslNodeSlot({ node }) {
3576
3625
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DslSlotGuard, { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DslNodeSlotInner, { node }) });
3577
3626
  }
3578
3627
  function isDslNodeValue(value) {
3579
- return typeof value === "object" && value !== null && !Array.isArray(value) && !(0, import_react16.isValidElement)(value) && typeof value.type === "string";
3628
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !(0, import_react17.isValidElement)(value) && typeof value.type === "string";
3580
3629
  }
3581
3630
  function resolveSlot(slot) {
3582
3631
  if (slot === void 0 || slot === null) {
@@ -3627,18 +3676,18 @@ function ChatComposition({
3627
3676
  onArtifact,
3628
3677
  onOpenArtifact
3629
3678
  }) {
3630
- const storeRef = (0, import_react16.useRef)(null);
3679
+ const storeRef = (0, import_react17.useRef)(null);
3631
3680
  if (storeRef.current === null) {
3632
3681
  storeRef.current = services.store ?? services.storeFactory?.() ?? new ConversationStore();
3633
3682
  }
3634
3683
  const store = storeRef.current;
3635
- const artifactStoreRef = (0, import_react16.useRef)(null);
3684
+ const artifactStoreRef = (0, import_react17.useRef)(null);
3636
3685
  if (artifactStoreRef.current === null) {
3637
3686
  artifactStoreRef.current = services.artifactStore ?? new ArtifactStore();
3638
3687
  }
3639
3688
  const artifactStore = artifactStoreRef.current;
3640
3689
  const panelControlled = panelOpen !== void 0 || artifact !== void 0;
3641
- const storeArtifact = (0, import_react16.useSyncExternalStore)(
3690
+ const storeArtifact = (0, import_react17.useSyncExternalStore)(
3642
3691
  (listener) => artifactStore.subscribe(listener),
3643
3692
  () => artifactStore.get()
3644
3693
  );
@@ -3656,7 +3705,7 @@ function ChatComposition({
3656
3705
  artifactStore.open(next);
3657
3706
  }
3658
3707
  };
3659
- const [attachments, setAttachments] = (0, import_react16.useState)([]);
3708
+ const [attachments, setAttachments] = (0, import_react17.useState)([]);
3660
3709
  const handleAttachFiles = services.onAttachFiles ? (files) => {
3661
3710
  void Promise.resolve(services.onAttachFiles(files)).then(
3662
3711
  (created) => {
@@ -3796,9 +3845,9 @@ function ChatComposition({
3796
3845
  }
3797
3846
 
3798
3847
  // src/chat-services.tsx
3799
- var import_react17 = require("react");
3848
+ var import_react18 = require("react");
3800
3849
  var import_jsx_runtime18 = require("react/jsx-runtime");
3801
- var ChatServicesContext = (0, import_react17.createContext)(null);
3850
+ var ChatServicesContext = (0, import_react18.createContext)(null);
3802
3851
  function ChatServicesProvider({
3803
3852
  value,
3804
3853
  children
@@ -3806,7 +3855,7 @@ function ChatServicesProvider({
3806
3855
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChatServicesContext.Provider, { value, children });
3807
3856
  }
3808
3857
  function useChatServices() {
3809
- return (0, import_react17.useContext)(ChatServicesContext);
3858
+ return (0, import_react18.useContext)(ChatServicesContext);
3810
3859
  }
3811
3860
 
3812
3861
  // src/chat-workspace.tsx
@@ -3893,7 +3942,7 @@ function createChatWorkspace(services) {
3893
3942
 
3894
3943
  // src/view/Chat.tsx
3895
3944
  var import_components12 = require("@pactor-app/ui/components");
3896
- var import_react18 = require("react");
3945
+ var import_react19 = require("react");
3897
3946
 
3898
3947
  // src/view/chrome/menu.tsx
3899
3948
  function buildSidebarMenuFragment(props) {
@@ -3981,13 +4030,13 @@ function ChatSidebarMenu({
3981
4030
  onMenuSelect
3982
4031
  }) {
3983
4032
  const drawer = useSidebarDrawer();
3984
- const menuRef = (0, import_react18.useRef)(menu);
4033
+ const menuRef = (0, import_react19.useRef)(menu);
3985
4034
  menuRef.current = menu;
3986
- const selectRef = (0, import_react18.useRef)(onMenuSelect);
4035
+ const selectRef = (0, import_react19.useRef)(onMenuSelect);
3987
4036
  selectRef.current = onMenuSelect;
3988
- const drawerRef = (0, import_react18.useRef)(drawer);
4037
+ const drawerRef = (0, import_react19.useRef)(drawer);
3989
4038
  drawerRef.current = drawer;
3990
- (0, import_react18.useEffect)(() => {
4039
+ (0, import_react19.useEffect)(() => {
3991
4040
  setChatChromeHandlers({
3992
4041
  menuSelect: (key) => {
3993
4042
  const item = menuRef.current.find((entry) => entry.key === key);
@@ -4028,7 +4077,7 @@ function ChatSidebarMenu({
4028
4077
  ]
4029
4078
  }
4030
4079
  );
4031
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_react18.Fragment, { children: [
4080
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_react19.Fragment, { children: [
4032
4081
  groupLabel !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "pactor-chat-menu__group", children: groupLabel }) : null,
4033
4082
  collapsed ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_components12.RailTooltip, { label: item.label, children: button }) : button
4034
4083
  ] }, item.key);
@@ -4085,7 +4134,7 @@ function Chat({
4085
4134
  onMenuSelect
4086
4135
  }) {
4087
4136
  const services = useChatServices();
4088
- (0, import_react18.useEffect)(() => {
4137
+ (0, import_react19.useEffect)(() => {
4089
4138
  if (!services) {
4090
4139
  console.warn(
4091
4140
  "[@pactor-app/chat] Chat \u672A\u68C0\u6D4B\u5230 ChatServicesProvider\uFF1A\u8BF7\u5728\u5E94\u7528\u6839\u90E8\u4EE5 <ChatServicesProvider> \u6CE8\u5165 transport \u7B49\u670D\u52A1"
package/dist/index.d.cts CHANGED
@@ -410,9 +410,13 @@ interface ChatInput {
410
410
  */
411
411
  contexts?: ChatContextItem[];
412
412
  }
413
+ /** 传输层发送选项 */
414
+ interface ChatTransportSendOptions {
415
+ signal?: AbortSignal;
416
+ }
413
417
  /** Chat 传输层:发送输入,返回流式事件 */
414
418
  interface ChatTransport {
415
- send(input: ChatInput): AsyncIterable<ChatEvent>;
419
+ send(input: ChatInput, options?: ChatTransportSendOptions): AsyncIterable<ChatEvent>;
416
420
  }
417
421
  /** runChatTurn 的可选定制(id 生成等,测试可注入确定性 id) */
418
422
  interface RunChatTurnOptions {