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