@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.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 {
@@ -1016,6 +1020,20 @@ declare const CHAT_COMPONENT_TYPE = "Chat";
1016
1020
  * 一并注册,同样不覆盖已有同名类型。
1017
1021
  */
1018
1022
  declare function registerChatComponent(registry: Registry<unknown>): void;
1023
+ /**
1024
+ * 便捷工厂:创建含全部内置组件 + Chat / ChatShell 的组件注册表,
1025
+ * 直接作为 createPactorApplication 的 componentRegistry 选项:
1026
+ *
1027
+ * ```ts
1028
+ * import { createChatComponentRegistry } from '@pactor-app/chat';
1029
+ *
1030
+ * createPactorApplication({
1031
+ * appId: 'my-chat',
1032
+ * componentRegistry: createChatComponentRegistry,
1033
+ * });
1034
+ * ```
1035
+ */
1036
+ declare function createChatComponentRegistry(): Registry<unknown>;
1019
1037
 
1020
1038
  /**
1021
1039
  * ChatShell(spec: chat-dsl-component / ChatShell 外壳组件,设计决策 D6):
@@ -1463,7 +1481,7 @@ declare const CHAT_LAYOUT_TYPE = "chat";
1463
1481
  * 结构化 LayoutRegistry(兼容 @pactor-app/ui layout 层的 LayoutRegistry =
1464
1482
  * Registry<LayoutComponent>):结构化类型声明,调用方传入 ui layout 层
1465
1483
  * 的注册表即天然兼容(历史为零依赖约定而设;chat-icon-convergence 起
1466
- * chat 正式依赖 ui kit,本结构化声明保留以收窄本文件的 API 面)。
1484
+ * chat 正式依赖 @pactor-app/ui,本结构化声明保留以收窄本文件的 API 面)。
1467
1485
  */
1468
1486
  interface ChatLayoutRegistry {
1469
1487
  has(name: string): boolean;
@@ -1472,4 +1490,4 @@ interface ChatLayoutRegistry {
1472
1490
  /** 注册 chat 布局(已注册时不覆盖,与 createLayoutRegistry 约定一致) */
1473
1491
  declare function registerChatLayout(layoutRegistry: ChatLayoutRegistry): void;
1474
1492
 
1475
- export { ActivityTrace, type ActivityTraceProps, AppShell, type AppShellProps, ArtifactPanel, type ArtifactPanelProps, type ArtifactPatch, ArtifactStore, type BuiltinMessageAction, CHAT_COMPONENT_TYPE, CHAT_DENIED_TYPE, CHAT_LAYOUT_TYPE, CHAT_SHELL_COMPONENT_TYPE, type CapabilityActionDeps, type CapabilityHandler, Chat, type ChatArtifact, type ChatAttachment, type ChatCapabilityRegistry, type ChatConfirm, type ChatConfirmRequest, type ChatContextItem, type ChatContextSource, ChatDenied, type ChatEvent, type ChatHeaderAction, type ChatInput, ChatLayout, type ChatLayoutProps, type ChatLayoutRegistry, type ChatMenuItem, type ChatMessage, type ChatMessageAction, type ChatModelOption, type ChatPart, type ChatPermissionChecker, type ChatProps, type ChatRole, type ChatRuntime, type ChatScope, type ChatServices, ChatServicesProvider, ChatShell, type ChatShellProps, ChatSidebarMenu, type ChatSuggestion, type ChatTransport, type ChatTurnResult, ChatView, type ChatViewProps, type ChatWorkspaceProps, type ChatWorkspaceServices, Composer, type ComposerProps, ConversationStore, type CreateChatRuntimeOptions, DEFAULT_CHAT_GREETING, EmptyState, type EmptyStateProps, type ManualChatDriver, MessageActions, type MessageActionsProps, MessageBubble, type MessageBubbleProps, type MessagePatch, type MessageStatus, type PartPatch, type PartRef, PartRenderer, type PartRendererProps, type PartStatus, type RunChatTurnOptions, type StreamingMode, type ToolCallStatus, type ToolTraceEntry, type UseChatAutoScrollOptions, type UseChatAutoScrollResult, type UseSmoothStreamTextOptions, applyChatEvent, buildChatScope, collectMessageText, createChatId, createChatRuntime, createChatWorkspace, createManualDriver, createSafeRegistry, detectStreamingMode, guardChatDsl, pairToolTraces, registerCapabilityAction, registerChatComponent, registerChatLayout, registerChatShellComponent, runChatTurn, runRegenerateTurn, useChatAutoScroll, useChatServices, useSidebarDrawer, useSmoothStreamText };
1493
+ export { ActivityTrace, type ActivityTraceProps, AppShell, type AppShellProps, ArtifactPanel, type ArtifactPanelProps, type ArtifactPatch, ArtifactStore, type BuiltinMessageAction, CHAT_COMPONENT_TYPE, CHAT_DENIED_TYPE, CHAT_LAYOUT_TYPE, CHAT_SHELL_COMPONENT_TYPE, type CapabilityActionDeps, type CapabilityHandler, Chat, type ChatArtifact, type ChatAttachment, type ChatCapabilityRegistry, type ChatConfirm, type ChatConfirmRequest, type ChatContextItem, type ChatContextSource, ChatDenied, type ChatEvent, type ChatHeaderAction, type ChatInput, ChatLayout, type ChatLayoutProps, type ChatLayoutRegistry, type ChatMenuItem, type ChatMessage, type ChatMessageAction, type ChatModelOption, type ChatPart, type ChatPermissionChecker, type ChatProps, type ChatRole, type ChatRuntime, type ChatScope, type ChatServices, ChatServicesProvider, ChatShell, type ChatShellProps, ChatSidebarMenu, type ChatSuggestion, type ChatTransport, type ChatTurnResult, ChatView, type ChatViewProps, type ChatWorkspaceProps, type ChatWorkspaceServices, Composer, type ComposerProps, ConversationStore, type CreateChatRuntimeOptions, DEFAULT_CHAT_GREETING, EmptyState, type EmptyStateProps, type ManualChatDriver, MessageActions, type MessageActionsProps, MessageBubble, type MessageBubbleProps, type MessagePatch, type MessageStatus, type PartPatch, type PartRef, PartRenderer, type PartRendererProps, type PartStatus, type RunChatTurnOptions, type StreamingMode, type ToolCallStatus, type ToolTraceEntry, type UseChatAutoScrollOptions, type UseChatAutoScrollResult, type UseSmoothStreamTextOptions, applyChatEvent, buildChatScope, collectMessageText, createChatComponentRegistry, createChatId, createChatRuntime, createChatWorkspace, createManualDriver, createSafeRegistry, detectStreamingMode, guardChatDsl, pairToolTraces, registerCapabilityAction, registerChatComponent, registerChatLayout, registerChatShellComponent, runChatTurn, runRegenerateTurn, useChatAutoScroll, useChatServices, useSidebarDrawer, useSmoothStreamText };
package/dist/index.d.ts 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 {
@@ -1016,6 +1020,20 @@ declare const CHAT_COMPONENT_TYPE = "Chat";
1016
1020
  * 一并注册,同样不覆盖已有同名类型。
1017
1021
  */
1018
1022
  declare function registerChatComponent(registry: Registry<unknown>): void;
1023
+ /**
1024
+ * 便捷工厂:创建含全部内置组件 + Chat / ChatShell 的组件注册表,
1025
+ * 直接作为 createPactorApplication 的 componentRegistry 选项:
1026
+ *
1027
+ * ```ts
1028
+ * import { createChatComponentRegistry } from '@pactor-app/chat';
1029
+ *
1030
+ * createPactorApplication({
1031
+ * appId: 'my-chat',
1032
+ * componentRegistry: createChatComponentRegistry,
1033
+ * });
1034
+ * ```
1035
+ */
1036
+ declare function createChatComponentRegistry(): Registry<unknown>;
1019
1037
 
1020
1038
  /**
1021
1039
  * ChatShell(spec: chat-dsl-component / ChatShell 外壳组件,设计决策 D6):
@@ -1463,7 +1481,7 @@ declare const CHAT_LAYOUT_TYPE = "chat";
1463
1481
  * 结构化 LayoutRegistry(兼容 @pactor-app/ui layout 层的 LayoutRegistry =
1464
1482
  * Registry<LayoutComponent>):结构化类型声明,调用方传入 ui layout 层
1465
1483
  * 的注册表即天然兼容(历史为零依赖约定而设;chat-icon-convergence 起
1466
- * chat 正式依赖 ui kit,本结构化声明保留以收窄本文件的 API 面)。
1484
+ * chat 正式依赖 @pactor-app/ui,本结构化声明保留以收窄本文件的 API 面)。
1467
1485
  */
1468
1486
  interface ChatLayoutRegistry {
1469
1487
  has(name: string): boolean;
@@ -1472,4 +1490,4 @@ interface ChatLayoutRegistry {
1472
1490
  /** 注册 chat 布局(已注册时不覆盖,与 createLayoutRegistry 约定一致) */
1473
1491
  declare function registerChatLayout(layoutRegistry: ChatLayoutRegistry): void;
1474
1492
 
1475
- export { ActivityTrace, type ActivityTraceProps, AppShell, type AppShellProps, ArtifactPanel, type ArtifactPanelProps, type ArtifactPatch, ArtifactStore, type BuiltinMessageAction, CHAT_COMPONENT_TYPE, CHAT_DENIED_TYPE, CHAT_LAYOUT_TYPE, CHAT_SHELL_COMPONENT_TYPE, type CapabilityActionDeps, type CapabilityHandler, Chat, type ChatArtifact, type ChatAttachment, type ChatCapabilityRegistry, type ChatConfirm, type ChatConfirmRequest, type ChatContextItem, type ChatContextSource, ChatDenied, type ChatEvent, type ChatHeaderAction, type ChatInput, ChatLayout, type ChatLayoutProps, type ChatLayoutRegistry, type ChatMenuItem, type ChatMessage, type ChatMessageAction, type ChatModelOption, type ChatPart, type ChatPermissionChecker, type ChatProps, type ChatRole, type ChatRuntime, type ChatScope, type ChatServices, ChatServicesProvider, ChatShell, type ChatShellProps, ChatSidebarMenu, type ChatSuggestion, type ChatTransport, type ChatTurnResult, ChatView, type ChatViewProps, type ChatWorkspaceProps, type ChatWorkspaceServices, Composer, type ComposerProps, ConversationStore, type CreateChatRuntimeOptions, DEFAULT_CHAT_GREETING, EmptyState, type EmptyStateProps, type ManualChatDriver, MessageActions, type MessageActionsProps, MessageBubble, type MessageBubbleProps, type MessagePatch, type MessageStatus, type PartPatch, type PartRef, PartRenderer, type PartRendererProps, type PartStatus, type RunChatTurnOptions, type StreamingMode, type ToolCallStatus, type ToolTraceEntry, type UseChatAutoScrollOptions, type UseChatAutoScrollResult, type UseSmoothStreamTextOptions, applyChatEvent, buildChatScope, collectMessageText, createChatId, createChatRuntime, createChatWorkspace, createManualDriver, createSafeRegistry, detectStreamingMode, guardChatDsl, pairToolTraces, registerCapabilityAction, registerChatComponent, registerChatLayout, registerChatShellComponent, runChatTurn, runRegenerateTurn, useChatAutoScroll, useChatServices, useSidebarDrawer, useSmoothStreamText };
1493
+ export { ActivityTrace, type ActivityTraceProps, AppShell, type AppShellProps, ArtifactPanel, type ArtifactPanelProps, type ArtifactPatch, ArtifactStore, type BuiltinMessageAction, CHAT_COMPONENT_TYPE, CHAT_DENIED_TYPE, CHAT_LAYOUT_TYPE, CHAT_SHELL_COMPONENT_TYPE, type CapabilityActionDeps, type CapabilityHandler, Chat, type ChatArtifact, type ChatAttachment, type ChatCapabilityRegistry, type ChatConfirm, type ChatConfirmRequest, type ChatContextItem, type ChatContextSource, ChatDenied, type ChatEvent, type ChatHeaderAction, type ChatInput, ChatLayout, type ChatLayoutProps, type ChatLayoutRegistry, type ChatMenuItem, type ChatMessage, type ChatMessageAction, type ChatModelOption, type ChatPart, type ChatPermissionChecker, type ChatProps, type ChatRole, type ChatRuntime, type ChatScope, type ChatServices, ChatServicesProvider, ChatShell, type ChatShellProps, ChatSidebarMenu, type ChatSuggestion, type ChatTransport, type ChatTurnResult, ChatView, type ChatViewProps, type ChatWorkspaceProps, type ChatWorkspaceServices, Composer, type ComposerProps, ConversationStore, type CreateChatRuntimeOptions, DEFAULT_CHAT_GREETING, EmptyState, type EmptyStateProps, type ManualChatDriver, MessageActions, type MessageActionsProps, MessageBubble, type MessageBubbleProps, type MessagePatch, type MessageStatus, type PartPatch, type PartRef, PartRenderer, type PartRendererProps, type PartStatus, type RunChatTurnOptions, type StreamingMode, type ToolCallStatus, type ToolTraceEntry, type UseChatAutoScrollOptions, type UseChatAutoScrollResult, type UseSmoothStreamTextOptions, applyChatEvent, buildChatScope, collectMessageText, createChatComponentRegistry, createChatId, createChatRuntime, createChatWorkspace, createManualDriver, createSafeRegistry, detectStreamingMode, guardChatDsl, pairToolTraces, registerCapabilityAction, registerChatComponent, registerChatLayout, registerChatShellComponent, runChatTurn, runRegenerateTurn, useChatAutoScroll, useChatServices, useSidebarDrawer, useSmoothStreamText };
package/dist/index.js CHANGED
@@ -431,6 +431,16 @@ function pairToolTraces(parts) {
431
431
  }
432
432
 
433
433
  // src/transport.ts
434
+ function markCancelled(store, messageId) {
435
+ const message = store.getMessage(messageId);
436
+ if (!message) {
437
+ return;
438
+ }
439
+ const parts = message.parts.map(
440
+ (part) => "status" in part && (part.status === "streaming" || part.status === "pending") ? { ...part, status: "ready" } : part
441
+ );
442
+ store.updateMessage(messageId, { status: "cancelled", parts });
443
+ }
434
444
  async function runChatTurn(store, transport, input, options = {}) {
435
445
  const userMessageId = options.userMessageId ?? createChatId("msg");
436
446
  const assistantMessageId = options.assistantMessageId ?? createChatId("msg");
@@ -453,15 +463,15 @@ async function runChatTurn(store, transport, input, options = {}) {
453
463
  createdAt: Date.now()
454
464
  });
455
465
  try {
456
- for await (const event of transport.send(input)) {
466
+ for await (const event of transport.send(input, { signal: options.signal })) {
457
467
  if (options.signal?.aborted) {
458
- store.updateMessage(assistantMessageId, { status: "cancelled" });
468
+ markCancelled(store, assistantMessageId);
459
469
  return { userMessageId, assistantMessageId };
460
470
  }
461
471
  applyChatEvent(store, { ...event, messageId: assistantMessageId });
462
472
  }
463
473
  if (options.signal?.aborted) {
464
- store.updateMessage(assistantMessageId, { status: "cancelled" });
474
+ markCancelled(store, assistantMessageId);
465
475
  return { userMessageId, assistantMessageId };
466
476
  }
467
477
  const message = store.getMessage(assistantMessageId);
@@ -796,8 +806,8 @@ import { useRenderer as useRenderer4 } from "@pactor-app/runtime";
796
806
  import {
797
807
  Component as Component3,
798
808
  isValidElement,
799
- useRef as useRef12,
800
- useState as useState14,
809
+ useRef as useRef13,
810
+ useState as useState15,
801
811
  useSyncExternalStore as useSyncExternalStore2
802
812
  } from "react";
803
813
 
@@ -862,10 +872,10 @@ import { MarkdownRenderer as MarkdownRenderer2 } from "@pactor-app/markdown";
862
872
  import { DslRenderer as DslRenderer2, useRenderer as useRenderer3 } from "@pactor-app/runtime";
863
873
  import {
864
874
  Component as Component2,
865
- useEffect as useEffect2,
875
+ useEffect as useEffect3,
866
876
  useMemo,
867
- useRef as useRef3,
868
- useState as useState4
877
+ useRef as useRef4,
878
+ useState as useState5
869
879
  } from "react";
870
880
 
871
881
  // src/view/PartRenderer.tsx
@@ -873,6 +883,43 @@ import { Icon } from "@pactor-app/ui/components";
873
883
  import { MarkdownRenderer } from "@pactor-app/markdown";
874
884
  import { DslRenderer, useRenderer as useRenderer2 } from "@pactor-app/runtime";
875
885
 
886
+ // src/hooks/use-throttled-value.ts
887
+ import { useEffect as useEffect2, useRef as useRef3, useState as useState4 } from "react";
888
+ function useThrottledValue(value, intervalMs, enabled) {
889
+ const [shown, setShown] = useState4(value);
890
+ const lastUpdateRef = useRef3(0);
891
+ const timerRef = useRef3(void 0);
892
+ useEffect2(() => {
893
+ if (!enabled) {
894
+ if (timerRef.current) {
895
+ clearTimeout(timerRef.current);
896
+ timerRef.current = void 0;
897
+ }
898
+ setShown(value);
899
+ return;
900
+ }
901
+ const now = Date.now();
902
+ const elapsed = now - lastUpdateRef.current;
903
+ if (elapsed >= intervalMs) {
904
+ lastUpdateRef.current = now;
905
+ setShown(value);
906
+ return;
907
+ }
908
+ timerRef.current = setTimeout(() => {
909
+ lastUpdateRef.current = Date.now();
910
+ setShown(value);
911
+ timerRef.current = void 0;
912
+ }, intervalMs - elapsed);
913
+ return () => {
914
+ if (timerRef.current) {
915
+ clearTimeout(timerRef.current);
916
+ timerRef.current = void 0;
917
+ }
918
+ };
919
+ }, [value, intervalMs, enabled]);
920
+ return enabled ? shown : value;
921
+ }
922
+
876
923
  // src/view/chrome/artifact-card.tsx
877
924
  function buildArtifactCardFragment(artifact) {
878
925
  return {
@@ -1119,7 +1166,8 @@ function SmoothText({ content, streaming }) {
1119
1166
  }
1120
1167
  function SmoothMarkdown({ content, streaming }) {
1121
1168
  const shown = useSmoothStreamText(content, streaming);
1122
- return /* @__PURE__ */ jsx3(MarkdownRenderer, { content: shown });
1169
+ const throttled = useThrottledValue(shown, 100, streaming);
1170
+ return /* @__PURE__ */ jsx3(MarkdownRenderer, { content: throttled });
1123
1171
  }
1124
1172
  function PartRenderer({ part, runtime, scope, streaming = false, onOpenArtifact }) {
1125
1173
  switch (part.type) {
@@ -1331,13 +1379,13 @@ function ArtifactPanel({
1331
1379
  className
1332
1380
  }) {
1333
1381
  const runtime = useArtifactRuntime(safeRegistry, user);
1334
- const closeRef = useRef3(onClose);
1382
+ const closeRef = useRef4(onClose);
1335
1383
  closeRef.current = onClose;
1336
- useEffect2(() => {
1384
+ useEffect3(() => {
1337
1385
  setChatChromeHandlers({ panelClose: () => closeRef.current?.() });
1338
1386
  }, []);
1339
- const panelRef = useRef3(null);
1340
- const [width, setWidth] = useState4(null);
1387
+ const panelRef = useRef4(null);
1388
+ const [width, setWidth] = useState5(null);
1341
1389
  const handleResizeStart = (event) => {
1342
1390
  event.preventDefault();
1343
1391
  const panel = panelRef.current;
@@ -1414,18 +1462,18 @@ function ArtifactPanel({
1414
1462
  // src/view/ChatView.tsx
1415
1463
  import { Icon as Icon9 } from "@pactor-app/ui/components";
1416
1464
  import { Registry as Registry3 } from "@pactor-app/core";
1417
- import { useEffect as useEffect8, useMemo as useMemo3, useRef as useRef10, useState as useState12, useSyncExternalStore } from "react";
1465
+ import { useEffect as useEffect9, useMemo as useMemo3, useRef as useRef11, useState as useState13, useSyncExternalStore } from "react";
1418
1466
 
1419
1467
  // src/view/Composer.tsx
1420
1468
  import { Icon as Icon6 } from "@pactor-app/ui/components";
1421
- import { useLayoutEffect as useLayoutEffect3, useMemo as useMemo2, useRef as useRef6, useState as useState8 } from "react";
1469
+ import { useLayoutEffect as useLayoutEffect3, useMemo as useMemo2, useRef as useRef7, useState as useState9 } from "react";
1422
1470
 
1423
1471
  // src/view/AnchoredPopover.tsx
1424
1472
  import {
1425
- useEffect as useEffect3,
1473
+ useEffect as useEffect4,
1426
1474
  useLayoutEffect as useLayoutEffect2,
1427
- useRef as useRef4,
1428
- useState as useState5
1475
+ useRef as useRef5,
1476
+ useState as useState6
1429
1477
  } from "react";
1430
1478
  import { jsx as jsx6 } from "react/jsx-runtime";
1431
1479
  function AnchoredPopover({
@@ -1436,8 +1484,8 @@ function AnchoredPopover({
1436
1484
  role = "dialog",
1437
1485
  sideOffset = 8
1438
1486
  }) {
1439
- const ref = useRef4(null);
1440
- const [position, setPosition] = useState5(null);
1487
+ const ref = useRef5(null);
1488
+ const [position, setPosition] = useState6(null);
1441
1489
  useLayoutEffect2(() => {
1442
1490
  const element = ref.current;
1443
1491
  if (!element) return;
@@ -1456,7 +1504,7 @@ function AnchoredPopover({
1456
1504
  }
1457
1505
  );
1458
1506
  }, [anchorRect, sideOffset]);
1459
- useEffect3(() => {
1507
+ useEffect4(() => {
1460
1508
  const onKeyDown = (event) => {
1461
1509
  if (event.key !== "Escape") return;
1462
1510
  const element = ref.current;
@@ -1504,7 +1552,7 @@ function AnchoredPopover({
1504
1552
 
1505
1553
  // src/view/ModelChip.tsx
1506
1554
  import { Icon as Icon4 } from "@pactor-app/ui/components";
1507
- import { useEffect as useEffect4, useRef as useRef5, useState as useState6 } from "react";
1555
+ import { useEffect as useEffect5, useRef as useRef6, useState as useState7 } from "react";
1508
1556
 
1509
1557
  // src/view/chrome/model-select.tsx
1510
1558
  function buildModelSelectFragment(props) {
@@ -1536,8 +1584,8 @@ function ModelChip({
1536
1584
  model,
1537
1585
  onModelChange
1538
1586
  }) {
1539
- const chipRef = useRef5(null);
1540
- const [anchor, setAnchor] = useState6(null);
1587
+ const chipRef = useRef6(null);
1588
+ const [anchor, setAnchor] = useState7(null);
1541
1589
  const currentOption = models.find((option) => option.value === model) ?? models[0];
1542
1590
  return /* @__PURE__ */ jsxs3(Fragment4, { children: [
1543
1591
  /* @__PURE__ */ jsxs3(
@@ -1588,9 +1636,9 @@ function ModelSelect({
1588
1636
  model,
1589
1637
  onModelChange
1590
1638
  }) {
1591
- const changeRef = useRef5(onModelChange);
1639
+ const changeRef = useRef6(onModelChange);
1592
1640
  changeRef.current = onModelChange;
1593
- useEffect4(() => {
1641
+ useEffect5(() => {
1594
1642
  setChatChromeHandlers({
1595
1643
  modelChange: (value) => changeRef.current?.(value)
1596
1644
  });
@@ -1818,7 +1866,7 @@ function buildComposerFragment(props) {
1818
1866
  }
1819
1867
 
1820
1868
  // src/view/context-sources.tsx
1821
- import { useEffect as useEffect5, useState as useState7 } from "react";
1869
+ import { useEffect as useEffect6, useState as useState8 } from "react";
1822
1870
  import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
1823
1871
  function sourceCacheKey(source) {
1824
1872
  return `${source.service ?? ""}:${JSON.stringify(source.params ?? {})}`;
@@ -1854,8 +1902,8 @@ function ContextSourceList({
1854
1902
  cache,
1855
1903
  onSelect
1856
1904
  }) {
1857
- const [resolutions, setResolutions] = useState7([]);
1858
- useEffect5(() => {
1905
+ const [resolutions, setResolutions] = useState8([]);
1906
+ useEffect6(() => {
1859
1907
  let cancelled = false;
1860
1908
  const initial = sources.map(
1861
1909
  (source) => source.items ? { status: "ready", items: source.items } : { status: "loading" }
@@ -1955,12 +2003,12 @@ function Composer({
1955
2003
  composerFooter,
1956
2004
  hideToolbar = false
1957
2005
  }) {
1958
- const [text, setText] = useState8("");
1959
- const [dragOver, setDragOver] = useState8(false);
1960
- const inputRef = useRef6(null);
1961
- const fileInputRef = useRef6(null);
1962
- const plusButtonRef = useRef6(null);
1963
- const composingRef = useRef6(false);
2006
+ const [text, setText] = useState9("");
2007
+ const [dragOver, setDragOver] = useState9(false);
2008
+ const inputRef = useRef7(null);
2009
+ const fileInputRef = useRef7(null);
2010
+ const plusButtonRef = useRef7(null);
2011
+ const composingRef = useRef7(false);
1964
2012
  const modelChipEnabled = models !== void 0 && models.length > 0;
1965
2013
  const mentionSources = useMemo2(
1966
2014
  () => contextSources?.filter((source) => source.trigger === "@") ?? [],
@@ -1972,9 +2020,9 @@ function Composer({
1972
2020
  );
1973
2021
  const builtinMentionEnabled = onMentionTrigger === void 0 && mentionSources.length > 0;
1974
2022
  const builtinPlusEnabled = onPlusMenu === void 0 && plusSources.length > 0;
1975
- const [mentionState, setMentionState] = useState8(null);
1976
- const [plusAnchor, setPlusAnchor] = useState8(null);
1977
- const sourceCacheRef = useRef6(null);
2023
+ const [mentionState, setMentionState] = useState9(null);
2024
+ const [plusAnchor, setPlusAnchor] = useState9(null);
2025
+ const sourceCacheRef = useRef7(null);
1978
2026
  if (sourceCacheRef.current === null) {
1979
2027
  sourceCacheRef.current = /* @__PURE__ */ new Map();
1980
2028
  }
@@ -2008,7 +2056,7 @@ function Composer({
2008
2056
  onSend?.(value);
2009
2057
  setText("");
2010
2058
  };
2011
- const sendRef = useRef6(send);
2059
+ const sendRef = useRef7(send);
2012
2060
  sendRef.current = send;
2013
2061
  useLayoutEffect3(() => {
2014
2062
  setChatChromeHandlers({ composerSubmit: () => sendRef.current() });
@@ -2427,15 +2475,15 @@ function EmptyState({
2427
2475
  }
2428
2476
 
2429
2477
  // src/view/MessageBubble.tsx
2430
- import { useRef as useRef9, useState as useState11 } from "react";
2478
+ import { useRef as useRef10, useState as useState12 } from "react";
2431
2479
 
2432
2480
  // src/view/ActivityTrace.tsx
2433
2481
  import { Icon as Icon7 } from "@pactor-app/ui/components";
2434
2482
  import { MarkdownRenderer as MarkdownRenderer3 } from "@pactor-app/markdown";
2435
- import { useEffect as useEffect6, useRef as useRef7, useState as useState9 } from "react";
2483
+ import { useEffect as useEffect7, useRef as useRef8, useState as useState10 } from "react";
2436
2484
  import { Fragment as Fragment8, jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
2437
2485
  function usePinnedCollapse(autoOpen) {
2438
- const [state, setState] = useState9("auto");
2486
+ const [state, setState] = useState10("auto");
2439
2487
  const open = state === "auto" ? autoOpen : state === "pinned-open";
2440
2488
  const toggle = () => {
2441
2489
  setState(open ? "pinned-closed" : "pinned-open");
@@ -2461,8 +2509,8 @@ function statusText(message, mode, toolCount) {
2461
2509
  }
2462
2510
  function useElapsedSeconds(startedAt, active) {
2463
2511
  const compute = () => Math.max(0, Math.floor((Date.now() - startedAt) / 1e3));
2464
- const [elapsed, setElapsed] = useState9(compute);
2465
- useEffect6(() => {
2512
+ const [elapsed, setElapsed] = useState10(compute);
2513
+ useEffect7(() => {
2466
2514
  if (!active) {
2467
2515
  setElapsed(compute());
2468
2516
  return;
@@ -2513,7 +2561,7 @@ function formatDetail(value) {
2513
2561
  }
2514
2562
  }
2515
2563
  function TraceRow({ entry }) {
2516
- const [open, setOpen] = useState9(false);
2564
+ const [open, setOpen] = useState10(false);
2517
2565
  const summary = summarizeArgs(entry.args);
2518
2566
  return /* @__PURE__ */ jsxs8(
2519
2567
  "div",
@@ -2550,6 +2598,7 @@ function ThinkingCard({
2550
2598
  answerPhase
2551
2599
  }) {
2552
2600
  const partStreaming = part.status === "streaming";
2601
+ const shown = useThrottledValue(part.content, 120, partStreaming);
2553
2602
  const { open, toggle } = usePinnedCollapse(partStreaming && !answerPhase);
2554
2603
  return /* @__PURE__ */ jsxs8(
2555
2604
  "div",
@@ -2572,7 +2621,7 @@ function ThinkingCard({
2572
2621
  ]
2573
2622
  }
2574
2623
  ),
2575
- open && /* @__PURE__ */ jsx12("div", { className: "pactor-chat-thinking__body", children: /* @__PURE__ */ jsx12(MarkdownRenderer3, { content: part.content }) })
2624
+ open && /* @__PURE__ */ jsx12("div", { className: "pactor-chat-thinking__body", children: partStreaming ? /* @__PURE__ */ jsx12("div", { className: "pactor-chat-thinking__text", children: shown }) : /* @__PURE__ */ jsx12(MarkdownRenderer3, { content: part.content }) })
2576
2625
  ]
2577
2626
  }
2578
2627
  );
@@ -2586,7 +2635,7 @@ function ActivityTrace({ message }) {
2586
2635
  );
2587
2636
  const answerPhase = mode === "responding" || !streaming;
2588
2637
  const { open, toggle } = usePinnedCollapse(!answerPhase);
2589
- const mountTimeRef = useRef7(0);
2638
+ const mountTimeRef = useRef8(0);
2590
2639
  if (mountTimeRef.current === 0) {
2591
2640
  mountTimeRef.current = Date.now();
2592
2641
  }
@@ -2634,7 +2683,7 @@ function ActivityTrace({ message }) {
2634
2683
 
2635
2684
  // src/view/MessageActions.tsx
2636
2685
  import { Icon as Icon8 } from "@pactor-app/ui/components";
2637
- import { useEffect as useEffect7, useRef as useRef8, useState as useState10 } from "react";
2686
+ import { useEffect as useEffect8, useRef as useRef9, useState as useState11 } from "react";
2638
2687
 
2639
2688
  // src/view/chrome/message-actions.tsx
2640
2689
  function buildMessageActionsFragment(args) {
@@ -2760,13 +2809,13 @@ function MessageActions({
2760
2809
  callService,
2761
2810
  onMessageAction
2762
2811
  }) {
2763
- const [copied, setCopied] = useState10(false);
2764
- const dispatchRef = useRef8(
2812
+ const [copied, setCopied] = useState11(false);
2813
+ const dispatchRef = useRef9(
2765
2814
  () => {
2766
2815
  }
2767
2816
  );
2768
- const visibleActionsRef = useRef8([]);
2769
- useEffect7(() => {
2817
+ const visibleActionsRef = useRef9([]);
2818
+ useEffect8(() => {
2770
2819
  setMessageActionDispatcher(message.id, (actionId) => {
2771
2820
  const action = visibleActionsRef.current.find((a) => a.id === actionId);
2772
2821
  if (action) {
@@ -2880,7 +2929,7 @@ function MessageEditForm({
2880
2929
  onConfirm,
2881
2930
  onCancel
2882
2931
  }) {
2883
- const [text, setText] = useState11(initialText);
2932
+ const [text, setText] = useState12(initialText);
2884
2933
  const confirm = () => {
2885
2934
  const value = text.trim();
2886
2935
  if (!value) {
@@ -2944,7 +2993,7 @@ function MessageBubble({
2944
2993
  onMessageAction,
2945
2994
  onOpenArtifact
2946
2995
  }) {
2947
- const [editing, setEditing] = useState11(false);
2996
+ const [editing, setEditing] = useState12(false);
2948
2997
  const bubbleScope = {
2949
2998
  conversation: scope?.conversation ?? {},
2950
2999
  message: { id: message.id },
@@ -2952,7 +3001,7 @@ function MessageBubble({
2952
3001
  };
2953
3002
  const streaming = message.status === "streaming";
2954
3003
  const live = message.status === "pending" || message.status === "streaming";
2955
- const wasLiveRef = useRef9(live);
3004
+ const wasLiveRef = useRef10(live);
2956
3005
  if (live) {
2957
3006
  wasLiveRef.current = true;
2958
3007
  }
@@ -3087,8 +3136,8 @@ function ChatView({
3087
3136
  () => ({ conversation: { id: conversationId }, params }),
3088
3137
  [conversationId, params]
3089
3138
  );
3090
- const notifiedArtifactsRef = useRef10(/* @__PURE__ */ new Set());
3091
- useEffect8(() => {
3139
+ const notifiedArtifactsRef = useRef11(/* @__PURE__ */ new Set());
3140
+ useEffect9(() => {
3092
3141
  if (!onArtifact) {
3093
3142
  return;
3094
3143
  }
@@ -3107,7 +3156,7 @@ function ChatView({
3107
3156
  });
3108
3157
  }
3109
3158
  }, [messages, onArtifact]);
3110
- const abortRef = useRef10(null);
3159
+ const abortRef = useRef11(null);
3111
3160
  const { containerRef, isFollowing, scrollToBottom } = useChatAutoScroll({
3112
3161
  messageCount: messages.length,
3113
3162
  lastContentLength: lastContentLength(messages),
@@ -3118,7 +3167,7 @@ function ChatView({
3118
3167
  model,
3119
3168
  onModelChange
3120
3169
  });
3121
- const [contextChips, setContextChips] = useState12([]);
3170
+ const [contextChips, setContextChips] = useState13([]);
3122
3171
  const handleContextChipsChange = (chips) => {
3123
3172
  setContextChips(chips);
3124
3173
  onContextsChange?.(chips);
@@ -3150,11 +3199,11 @@ function ChatView({
3150
3199
  abortRef.current?.abort();
3151
3200
  abortRef.current = null;
3152
3201
  };
3153
- const handleSendRef = useRef10(handleSend);
3202
+ const handleSendRef = useRef11(handleSend);
3154
3203
  handleSendRef.current = handleSend;
3155
- const onOpenArtifactRef = useRef10(onOpenArtifact);
3204
+ const onOpenArtifactRef = useRef11(onOpenArtifact);
3156
3205
  onOpenArtifactRef.current = onOpenArtifact;
3157
- useEffect8(() => {
3206
+ useEffect9(() => {
3158
3207
  setChatChromeHandlers({
3159
3208
  sendSuggestion: (text) => handleSendRef.current(text),
3160
3209
  openArtifact: (artifact) => onOpenArtifactRef.current?.(artifact)
@@ -3261,7 +3310,7 @@ function ChatView({
3261
3310
 
3262
3311
  // src/view/WorkspaceHeader.tsx
3263
3312
  import { Icon as Icon10 } from "@pactor-app/ui/components";
3264
- import { useEffect as useEffect9, useRef as useRef11, useState as useState13 } from "react";
3313
+ import { useEffect as useEffect10, useRef as useRef12, useState as useState14 } from "react";
3265
3314
 
3266
3315
  // src/export-markdown.ts
3267
3316
  var ROLE_LABELS = {
@@ -3358,10 +3407,10 @@ function WorkspaceHeader({
3358
3407
  onTitleChange
3359
3408
  }) {
3360
3409
  const hasActions = headerActions !== void 0 && headerActions.length > 0;
3361
- const [overrideTitle, setOverrideTitle] = useState13(null);
3362
- const [editing, setEditing] = useState13(false);
3363
- const [draft, setDraft] = useState13("");
3364
- const prevTitleRef = useRef11(title);
3410
+ const [overrideTitle, setOverrideTitle] = useState14(null);
3411
+ const [editing, setEditing] = useState14(false);
3412
+ const [draft, setDraft] = useState14("");
3413
+ const prevTitleRef = useRef12(title);
3365
3414
  if (prevTitleRef.current !== title) {
3366
3415
  prevTitleRef.current = title;
3367
3416
  if (overrideTitle !== null) {
@@ -3369,7 +3418,7 @@ function WorkspaceHeader({
3369
3418
  }
3370
3419
  }
3371
3420
  const displayTitle = overrideTitle ?? title;
3372
- const [theme, setTheme] = useState13(readCurrentTheme);
3421
+ const [theme, setTheme] = useState14(readCurrentTheme);
3373
3422
  const commitTitle = () => {
3374
3423
  setEditing(false);
3375
3424
  const next = draft.trim();
@@ -3396,9 +3445,9 @@ function WorkspaceHeader({
3396
3445
  store.clear();
3397
3446
  onClear?.();
3398
3447
  };
3399
- const handlersRef = useRef11({ handleExport, handleTheme, handleClear });
3448
+ const handlersRef = useRef12({ handleExport, handleTheme, handleClear });
3400
3449
  handlersRef.current = { handleExport, handleTheme, handleClear };
3401
- useEffect9(() => {
3450
+ useEffect10(() => {
3402
3451
  setChatChromeHandlers({
3403
3452
  headerAction: (name) => {
3404
3453
  const handlers2 = handlersRef.current;
@@ -3582,12 +3631,12 @@ function ChatComposition({
3582
3631
  onArtifact,
3583
3632
  onOpenArtifact
3584
3633
  }) {
3585
- const storeRef = useRef12(null);
3634
+ const storeRef = useRef13(null);
3586
3635
  if (storeRef.current === null) {
3587
3636
  storeRef.current = services.store ?? services.storeFactory?.() ?? new ConversationStore();
3588
3637
  }
3589
3638
  const store = storeRef.current;
3590
- const artifactStoreRef = useRef12(null);
3639
+ const artifactStoreRef = useRef13(null);
3591
3640
  if (artifactStoreRef.current === null) {
3592
3641
  artifactStoreRef.current = services.artifactStore ?? new ArtifactStore();
3593
3642
  }
@@ -3611,7 +3660,7 @@ function ChatComposition({
3611
3660
  artifactStore.open(next);
3612
3661
  }
3613
3662
  };
3614
- const [attachments, setAttachments] = useState14([]);
3663
+ const [attachments, setAttachments] = useState15([]);
3615
3664
  const handleAttachFiles = services.onAttachFiles ? (files) => {
3616
3665
  void Promise.resolve(services.onAttachFiles(files)).then(
3617
3666
  (created) => {
@@ -3847,8 +3896,8 @@ function createChatWorkspace(services) {
3847
3896
  }
3848
3897
 
3849
3898
  // src/view/Chat.tsx
3850
- import { Icon as Icon11, RailTooltip } from "@pactor-app/ui/components";
3851
- import { Fragment as Fragment10, useEffect as useEffect10, useRef as useRef13 } from "react";
3899
+ import { createComponentRegistry, Icon as Icon11, RailTooltip } from "@pactor-app/ui/components";
3900
+ import { Fragment as Fragment10, useEffect as useEffect11, useRef as useRef14 } from "react";
3852
3901
 
3853
3902
  // src/view/chrome/menu.tsx
3854
3903
  function buildSidebarMenuFragment(props) {
@@ -3936,13 +3985,13 @@ function ChatSidebarMenu({
3936
3985
  onMenuSelect
3937
3986
  }) {
3938
3987
  const drawer = useSidebarDrawer();
3939
- const menuRef = useRef13(menu);
3988
+ const menuRef = useRef14(menu);
3940
3989
  menuRef.current = menu;
3941
- const selectRef = useRef13(onMenuSelect);
3990
+ const selectRef = useRef14(onMenuSelect);
3942
3991
  selectRef.current = onMenuSelect;
3943
- const drawerRef = useRef13(drawer);
3992
+ const drawerRef = useRef14(drawer);
3944
3993
  drawerRef.current = drawer;
3945
- useEffect10(() => {
3994
+ useEffect11(() => {
3946
3995
  setChatChromeHandlers({
3947
3996
  menuSelect: (key) => {
3948
3997
  const item = menuRef.current.find((entry) => entry.key === key);
@@ -4040,7 +4089,7 @@ function Chat({
4040
4089
  onMenuSelect
4041
4090
  }) {
4042
4091
  const services = useChatServices();
4043
- useEffect10(() => {
4092
+ useEffect11(() => {
4044
4093
  if (!services) {
4045
4094
  console.warn(
4046
4095
  "[@pactor-app/chat] Chat \u672A\u68C0\u6D4B\u5230 ChatServicesProvider\uFF1A\u8BF7\u5728\u5E94\u7528\u6839\u90E8\u4EE5 <ChatServicesProvider> \u6CE8\u5165 transport \u7B49\u670D\u52A1"
@@ -4144,6 +4193,11 @@ function registerChatComponent(registry) {
4144
4193
  }
4145
4194
  registerChatShellComponent(registry);
4146
4195
  }
4196
+ function createChatComponentRegistry() {
4197
+ const registry = createComponentRegistry();
4198
+ registerChatComponent(registry);
4199
+ return registry;
4200
+ }
4147
4201
 
4148
4202
  // src/view/ChatLayout.tsx
4149
4203
  import { jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
@@ -4185,6 +4239,7 @@ export {
4185
4239
  applyChatEvent,
4186
4240
  buildChatScope,
4187
4241
  collectMessageText,
4242
+ createChatComponentRegistry,
4188
4243
  createChatId,
4189
4244
  createChatRuntime,
4190
4245
  createChatWorkspace,