@pactor-app/chat 0.1.0 → 1.1.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
@@ -39,8 +39,6 @@ __export(index_exports, {
39
39
  ConversationStore: () => ConversationStore,
40
40
  DEFAULT_CHAT_GREETING: () => DEFAULT_CHAT_GREETING,
41
41
  EmptyState: () => EmptyState,
42
- MENU_ICONS: () => MENU_ICONS,
43
- MESSAGE_ACTION_ICONS: () => MESSAGE_ACTION_ICONS,
44
42
  MessageActions: () => MessageActions,
45
43
  MessageBubble: () => MessageBubble,
46
44
  PartRenderer: () => PartRenderer,
@@ -719,20 +717,34 @@ function createChatRuntime(options = {}) {
719
717
  }
720
718
 
721
719
  // src/dsl-guard.ts
720
+ var import_core2 = require("@pactor-app/core");
722
721
  var CHAT_DENIED_TYPE = "ChatDenied";
723
722
  function guardChatDsl(node, registry) {
724
- if (node.type !== CHAT_DENIED_TYPE && !registry.has(node.type)) {
723
+ const type = node.type ?? "(reference)";
724
+ if (type !== CHAT_DENIED_TYPE && !registry.has(type)) {
725
+ return {
726
+ type: CHAT_DENIED_TYPE,
727
+ props: { deniedType: type }
728
+ };
729
+ }
730
+ if (node.children === void 0) {
731
+ return node;
732
+ }
733
+ if (!Array.isArray(node.children)) {
725
734
  return {
726
735
  type: CHAT_DENIED_TYPE,
727
- props: { deniedType: node.type }
736
+ props: { deniedType: `${type}(remote)` }
728
737
  };
729
738
  }
730
- if (!node.children || node.children.length === 0) {
739
+ if (node.children.length === 0) {
731
740
  return node;
732
741
  }
733
742
  return {
734
743
  ...node,
735
- children: node.children.map((child) => guardChatDsl(child, registry))
744
+ // 数组中混排的引用元素同样无法静态过白名单:替换为受控占位
745
+ children: node.children.map(
746
+ (child) => (0, import_core2.isDslSourceRef)(child) ? { type: CHAT_DENIED_TYPE, props: { deniedType: "(remote)" } } : guardChatDsl(child, registry)
747
+ )
736
748
  };
737
749
  }
738
750
 
@@ -844,8 +856,8 @@ function useChatAutoScroll({
844
856
  }
845
857
 
846
858
  // src/chat-composition.tsx
847
- var import_runtime4 = require("@pactor-app/runtime");
848
- var import_react15 = require("react");
859
+ var import_runtime5 = require("@pactor-app/runtime");
860
+ var import_react16 = require("react");
849
861
 
850
862
  // src/hooks/use-chat-model.ts
851
863
  var import_react3 = require("react");
@@ -870,121 +882,274 @@ function useChatModel({
870
882
  }
871
883
 
872
884
  // src/view/AppShell.tsx
873
- var import_lucide_react = require("lucide-react");
874
- var import_react4 = require("react");
885
+ var import_components = require("@pactor-app/ui/components");
875
886
  var import_jsx_runtime = require("react/jsx-runtime");
876
- var SidebarDrawerContext = (0, import_react4.createContext)(null);
877
887
  function useSidebarDrawer() {
878
- return (0, import_react4.useContext)(SidebarDrawerContext);
888
+ return (0, import_components.useSidebarDrawer)();
879
889
  }
880
- var MOBILE_MEDIA_QUERY = "(max-width: 767px)";
881
- function useIsMobileViewport() {
882
- const [isMobile, setIsMobile] = (0, import_react4.useState)(
883
- () => typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia(MOBILE_MEDIA_QUERY).matches
884
- );
885
- (0, import_react4.useEffect)(() => {
886
- if (typeof window.matchMedia !== "function") {
887
- return;
888
- }
889
- const media = window.matchMedia(MOBILE_MEDIA_QUERY);
890
- const onChange = (event) => {
891
- setIsMobile(event.matches);
892
- };
893
- setIsMobile(media.matches);
894
- media.addEventListener("change", onChange);
895
- return () => media.removeEventListener("change", onChange);
896
- }, []);
897
- return isMobile;
898
- }
899
- function AppShell({ sidebar, brand, locationKey, sidebarFooter, collapsible = false, children, className }) {
900
- const [drawerOpen, setDrawerOpen] = (0, import_react4.useState)(false);
901
- const isMobile = useIsMobileViewport();
902
- const close = (0, import_react4.useCallback)(() => setDrawerOpen(false), []);
903
- const [collapsed, setCollapsed] = (0, import_react4.useState)(false);
904
- const collapsedEffective = collapsible && collapsed && !isMobile;
905
- const framed = brand !== void 0 || sidebarFooter !== void 0;
906
- const [trackedLocationKey, setTrackedLocationKey] = (0, import_react4.useState)(locationKey);
907
- if (trackedLocationKey !== locationKey) {
908
- setTrackedLocationKey(locationKey);
909
- setDrawerOpen(false);
910
- }
911
- (0, import_react4.useEffect)(() => {
912
- if (!drawerOpen) {
913
- return;
890
+ function AppShell({
891
+ sidebar,
892
+ brand,
893
+ locationKey,
894
+ sidebarFooter,
895
+ collapsible = false,
896
+ children,
897
+ className
898
+ }) {
899
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
900
+ import_components.Sidebar,
901
+ {
902
+ content: sidebar,
903
+ brand,
904
+ locationKey,
905
+ sidebarFooter,
906
+ collapsible,
907
+ className: `pactor-chat-shell h-full min-h-0${className ? ` ${className}` : ""}`,
908
+ children
914
909
  }
915
- const onKeyDown = (event) => {
916
- if (event.key === "Escape") {
917
- setDrawerOpen(false);
918
- }
919
- };
920
- window.addEventListener("keydown", onKeyDown);
921
- return () => window.removeEventListener("keydown", onKeyDown);
922
- }, [drawerOpen]);
923
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarDrawerContext.Provider, { value: { close, collapsed: collapsedEffective }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `pactor-chat-shell${className ? ` ${className}` : ""}`, children: [
924
- drawerOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pactor-chat-shell__scrim", "aria-hidden": true, onClick: close }) : null,
925
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
926
- "div",
927
- {
928
- className: `pactor-chat-shell__sidebar${framed ? " pactor-chat-shell__sidebar--framed" : ""}${collapsedEffective ? " pactor-chat-shell__sidebar--collapsed" : ""}`,
929
- "data-open": drawerOpen ? "true" : "false",
930
- inert: isMobile && !drawerOpen ? true : void 0,
931
- children: framed ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
932
- brand !== void 0 && // 桌面品牌行(决策 D3):brand 左、折叠钮右;移动端由
933
- // CSS 隐藏(品牌维持顶栏现状)
934
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pactor-chat-shell__sidebar-header", children: [
935
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pactor-chat-shell__brand", children: brand }),
936
- collapsible && !isMobile && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
937
- "button",
938
- {
939
- type: "button",
940
- className: "pactor-chat-icon-button pactor-chat-shell__collapse",
941
- "aria-label": collapsedEffective ? "\u5C55\u5F00\u4FA7\u680F" : "\u6298\u53E0\u4FA7\u680F",
942
- "aria-expanded": !collapsedEffective,
943
- title: collapsedEffective ? "\u5C55\u5F00\u4FA7\u680F" : "\u6298\u53E0\u4FA7\u680F",
944
- onClick: () => setCollapsed((value) => !value),
945
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.PanelLeft, { size: 16, strokeWidth: 1.7 })
946
- }
947
- )
948
- ] }),
949
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pactor-chat-shell__sidebar-body", children: sidebar }),
950
- sidebarFooter !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pactor-chat-shell__sidebar-footer", children: sidebarFooter })
951
- ] }) : sidebar
952
- }
953
- ),
954
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("main", { className: "pactor-chat-shell__main", children: [
955
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pactor-chat-shell__topbar", children: [
956
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
957
- "button",
958
- {
959
- type: "button",
960
- className: "pactor-chat-icon-button pactor-chat-shell__menu",
961
- "aria-label": "\u6253\u5F00\u5BFC\u822A",
962
- "aria-expanded": drawerOpen,
963
- onClick: () => setDrawerOpen(true),
964
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Menu, { size: 18, strokeWidth: 1.7 })
965
- }
966
- ),
967
- brand
968
- ] }),
969
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pactor-chat-shell__content", children })
970
- ] })
971
- ] }) });
910
+ );
972
911
  }
973
912
 
974
913
  // src/view/ArtifactPanel.tsx
975
- var import_lucide_react3 = require("lucide-react");
976
- var import_core2 = require("@pactor-app/core");
914
+ var import_components4 = require("@pactor-app/ui/components");
915
+ var import_core3 = require("@pactor-app/core");
977
916
  var import_markdown2 = require("@pactor-app/markdown");
978
- var import_runtime3 = require("@pactor-app/runtime");
917
+ var import_runtime4 = require("@pactor-app/runtime");
979
918
  var import_react5 = require("react");
980
919
 
981
920
  // src/view/PartRenderer.tsx
982
- var import_lucide_react2 = require("lucide-react");
921
+ var import_components2 = require("@pactor-app/ui/components");
983
922
  var import_markdown = require("@pactor-app/markdown");
923
+ var import_runtime3 = require("@pactor-app/runtime");
924
+
925
+ // src/view/chrome/artifact-card.tsx
926
+ function buildArtifactCardFragment(artifact) {
927
+ return {
928
+ requires: ["Card", "Flex", "Text", "Badge", "Button"],
929
+ node: {
930
+ type: "Card",
931
+ props: { className: "pactor-chat-artifact-card" },
932
+ children: [
933
+ {
934
+ type: "Flex",
935
+ props: { justify: "space-between", align: "center", gap: "small" },
936
+ children: [
937
+ {
938
+ type: "Text",
939
+ props: {
940
+ text: artifact.title,
941
+ strong: true,
942
+ className: "pactor-chat-artifact-card__title"
943
+ }
944
+ },
945
+ {
946
+ type: "Badge",
947
+ props: {
948
+ text: artifact.kind,
949
+ variant: "secondary",
950
+ className: "pactor-chat-artifact-card__kind"
951
+ }
952
+ },
953
+ {
954
+ type: "Button",
955
+ props: {
956
+ text: "\u5728\u9762\u677F\u4E2D\u6253\u5F00",
957
+ variant: "default",
958
+ size: "small",
959
+ className: "pactor-chat-artifact-card__open"
960
+ },
961
+ events: {
962
+ onClick: [
963
+ {
964
+ action: "chat.openArtifact",
965
+ params: { artifact }
966
+ }
967
+ ]
968
+ }
969
+ }
970
+ ]
971
+ }
972
+ ]
973
+ }
974
+ };
975
+ }
976
+
977
+ // src/view/chrome/ChromeSlot.tsx
984
978
  var import_runtime2 = require("@pactor-app/runtime");
979
+ var import_react4 = require("react");
980
+
981
+ // src/view/chrome/actions.ts
982
+ var handlers = {};
983
+ function setChatChromeHandlers(next) {
984
+ handlers = { ...handlers, ...next };
985
+ }
986
+ var messageActionDispatchers = /* @__PURE__ */ new Map();
987
+ function setMessageActionDispatcher(token, dispatch) {
988
+ messageActionDispatchers.set(token, dispatch);
989
+ }
990
+ function clearMessageActionDispatcher(token) {
991
+ messageActionDispatchers.delete(token);
992
+ }
993
+ var CHROME_ACTION_HANDLERS = {
994
+ "chat.sendSuggestion": (action) => {
995
+ const text = action.params?.["text"];
996
+ if (typeof text === "string") {
997
+ handlers.sendSuggestion?.(text);
998
+ }
999
+ return { ok: true };
1000
+ },
1001
+ "chat.openArtifact": (action) => {
1002
+ const artifact = action.params?.["artifact"];
1003
+ if (artifact) {
1004
+ handlers.openArtifact?.(artifact);
1005
+ }
1006
+ return { ok: true };
1007
+ },
1008
+ "chat.messageAction": (action) => {
1009
+ const token = action.params?.["token"];
1010
+ const actionId = action.params?.["actionId"];
1011
+ if (typeof token === "string" && typeof actionId === "string") {
1012
+ messageActionDispatchers.get(token)?.(actionId);
1013
+ }
1014
+ return { ok: true };
1015
+ },
1016
+ "chat.composerSubmit": (action) => {
1017
+ const value = action.params?.["value"];
1018
+ if (typeof value === "string") {
1019
+ handlers.composerSubmit?.(value);
1020
+ }
1021
+ return { ok: true };
1022
+ },
1023
+ "chat.menuSelect": (action) => {
1024
+ const key = action.params?.["key"];
1025
+ if (typeof key === "string") {
1026
+ handlers.menuSelect?.(key);
1027
+ }
1028
+ return { ok: true };
1029
+ },
1030
+ "chat.modelChange": (action) => {
1031
+ const value = action.params?.["value"];
1032
+ if (typeof value === "string") {
1033
+ handlers.modelChange?.(value);
1034
+ }
1035
+ return { ok: true };
1036
+ },
1037
+ "chat.headerAction": (action) => {
1038
+ const name = action.params?.["action"];
1039
+ if (typeof name === "string") {
1040
+ handlers.headerAction?.(name);
1041
+ }
1042
+ return { ok: true };
1043
+ },
1044
+ "chat.panelClose": () => {
1045
+ handlers.panelClose?.();
1046
+ return { ok: true };
1047
+ }
1048
+ };
1049
+ function ensureChatChromeActions(actions) {
1050
+ for (const [name, handler] of Object.entries(CHROME_ACTION_HANDLERS)) {
1051
+ if (!actions.has(name)) {
1052
+ actions.register(name, handler);
1053
+ }
1054
+ }
1055
+ }
1056
+ function invokeChatPanelClose() {
1057
+ handlers.panelClose?.();
1058
+ }
1059
+
1060
+ // src/view/chrome/ChromeSlot.tsx
985
1061
  var import_jsx_runtime2 = require("react/jsx-runtime");
1062
+ var warnedMissing = /* @__PURE__ */ new Set();
1063
+ function warnMissingOnce(key, detail) {
1064
+ if (!warnedMissing.has(key)) {
1065
+ warnedMissing.add(key);
1066
+ console.warn(`[@pactor-app/chat] ${detail}`);
1067
+ }
1068
+ }
1069
+ function ChromeSlotInner({
1070
+ fragment,
1071
+ fallback,
1072
+ components,
1073
+ wrap
1074
+ }) {
1075
+ let renderer;
1076
+ try {
1077
+ renderer = (0, import_runtime2.useRenderer)();
1078
+ } catch {
1079
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: fallback });
1080
+ }
1081
+ ensureChatChromeActions(renderer.context.actions);
1082
+ if (components) {
1083
+ for (const [name, component] of Object.entries(components)) {
1084
+ const existing = renderer.context.components.get(name);
1085
+ if (existing !== void 0 && existing !== component) {
1086
+ warnMissingOnce(
1087
+ `conflict:${name}`,
1088
+ `[@pactor-app/chat] chrome \u5185\u90E8\u7EC4\u4EF6\u540D\u300C${name}\u300D\u88AB\u5BBF\u4E3B\u5360\u7528\uFF0C\u5DF2\u56DE\u9000\u5185\u5EFA\u5B9E\u73B0`
1089
+ );
1090
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: fallback });
1091
+ }
1092
+ if (existing === void 0) {
1093
+ renderer.context.components.register(
1094
+ name,
1095
+ component
1096
+ );
1097
+ }
1098
+ }
1099
+ }
1100
+ const missing = fragment.requires.filter(
1101
+ (type) => !renderer.context.components.has(type)
1102
+ );
1103
+ if (missing.length > 0) {
1104
+ warnMissingOnce(
1105
+ `${fragment.node.type}:${missing.join(",")}`,
1106
+ `[@pactor-app/chat] chrome DSL \u7247\u6BB5\u6240\u9700\u7EC4\u4EF6\u7C7B\u578B\u7F3A\u5931\uFF08${missing.join(" / ")}\uFF09\uFF0C\u5DF2\u56DE\u9000\u5185\u5EFA\u5B9E\u73B0`
1107
+ );
1108
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: fallback });
1109
+ }
1110
+ const rendered = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "pactor-chat-chrome", children: renderer.renderChildren([fragment.node]) });
1111
+ return wrap ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: wrap(rendered) }) : rendered;
1112
+ }
1113
+ var ChromeSlotGuard = class extends import_react4.Component {
1114
+ constructor() {
1115
+ super(...arguments);
1116
+ this.state = { failed: false };
1117
+ }
1118
+ static getDerivedStateFromError() {
1119
+ return { failed: true };
1120
+ }
1121
+ render() {
1122
+ return this.state.failed ? this.props.fallback : this.props.children;
1123
+ }
1124
+ };
1125
+ function ChromeSlot(props) {
1126
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChromeSlotGuard, { fallback: props.fallback, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChromeSlotInner, { ...props }) });
1127
+ }
1128
+
1129
+ // src/view/chrome/feedback.tsx
1130
+ function buildErrorFragment(message) {
1131
+ return {
1132
+ requires: ["Alert"],
1133
+ node: {
1134
+ type: "Alert",
1135
+ props: { variant: "error", message }
1136
+ }
1137
+ };
1138
+ }
1139
+ function buildStatusFragment(text) {
1140
+ return {
1141
+ requires: ["Text"],
1142
+ node: {
1143
+ type: "Text",
1144
+ props: { type: "secondary", text }
1145
+ }
1146
+ };
1147
+ }
1148
+
1149
+ // src/view/PartRenderer.tsx
1150
+ var import_jsx_runtime3 = require("react/jsx-runtime");
986
1151
  function ChatDenied({ deniedType }) {
987
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "pactor-chat-denied", "data-pactor-chat-denied": deniedType, children: [
1152
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "pactor-chat-denied", "data-pactor-chat-denied": deniedType, children: [
988
1153
  "\u8BE5\u7EC4\u4EF6\u4E0D\u5141\u8BB8\u5728 Chat \u4E2D\u4F7F\u7528",
989
1154
  deniedType ? `\uFF1A${deniedType}` : ""
990
1155
  ] });
@@ -993,64 +1158,90 @@ function ChatDslNode({
993
1158
  node,
994
1159
  scope
995
1160
  }) {
996
- const renderer = (0, import_runtime2.useRenderer)();
1161
+ const renderer = (0, import_runtime3.useRenderer)();
997
1162
  const extraScope = scope ? buildChatScope(scope) : void 0;
998
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: renderer.renderChildren([node], extraScope) });
1163
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: renderer.renderChildren([node], extraScope) });
999
1164
  }
1000
1165
  function SmoothText({ content, streaming }) {
1001
1166
  const shown = useSmoothStreamText(content, streaming);
1002
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "pactor-chat-text", children: shown });
1167
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "pactor-chat-text", children: shown });
1003
1168
  }
1004
1169
  function SmoothMarkdown({ content, streaming }) {
1005
1170
  const shown = useSmoothStreamText(content, streaming);
1006
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_markdown.MarkdownRenderer, { content: shown });
1171
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_markdown.MarkdownRenderer, { content: shown });
1007
1172
  }
1008
1173
  function PartRenderer({ part, runtime, scope, streaming = false, onOpenArtifact }) {
1009
1174
  switch (part.type) {
1010
1175
  case "text":
1011
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SmoothText, { content: part.content, streaming });
1176
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SmoothText, { content: part.content, streaming });
1012
1177
  case "markdown":
1013
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SmoothMarkdown, { content: part.content, streaming });
1178
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SmoothMarkdown, { content: part.content, streaming });
1014
1179
  case "thinking":
1015
1180
  case "tool-call":
1016
1181
  case "tool-result":
1017
1182
  return null;
1018
1183
  case "dsl": {
1019
1184
  if (!part.schema || !runtime) {
1020
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "pactor-chat-dsl-pending", children: part.schema ? "DSL \u6E32\u67D3\u4E0D\u53EF\u7528\uFF08\u672A\u6CE8\u5165 Safe Registry\uFF09" : "\u5185\u5BB9\u751F\u6210\u4E2D\u2026" });
1185
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1186
+ ChromeSlot,
1187
+ {
1188
+ fragment: buildStatusFragment(
1189
+ part.schema ? "DSL \u6E32\u67D3\u4E0D\u53EF\u7528\uFF08\u672A\u6CE8\u5165 Safe Registry\uFF09" : "\u5185\u5BB9\u751F\u6210\u4E2D\u2026"
1190
+ ),
1191
+ fallback: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "pactor-chat-dsl-pending", children: part.schema ? "DSL \u6E32\u67D3\u4E0D\u53EF\u7528\uFF08\u672A\u6CE8\u5165 Safe Registry\uFF09" : "\u5185\u5BB9\u751F\u6210\u4E2D\u2026" })
1192
+ }
1193
+ );
1021
1194
  }
1022
1195
  const guarded = guardChatDsl(part.schema, runtime.context.components);
1023
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "pactor-chat-dsl", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_runtime2.DslRenderer, { context: runtime.context, engine: runtime.engine, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChatDslNode, { node: guarded, scope }) }) });
1196
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "pactor-chat-dsl", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_runtime3.DslRenderer, { context: runtime.context, engine: runtime.engine, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChatDslNode, { node: guarded, scope }) }) });
1024
1197
  }
1025
1198
  case "error":
1026
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "pactor-chat-error", role: "alert", children: [
1027
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.CircleAlert, { size: 14 }),
1028
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: part.message })
1029
- ] });
1199
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1200
+ ChromeSlot,
1201
+ {
1202
+ fragment: buildErrorFragment(part.message),
1203
+ fallback: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "pactor-chat-error", role: "alert", children: [
1204
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_components2.Icon, { name: "error", size: 14 }),
1205
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: part.message })
1206
+ ] })
1207
+ }
1208
+ );
1030
1209
  case "status":
1031
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "pactor-chat-status", children: part.text });
1210
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1211
+ ChromeSlot,
1212
+ {
1213
+ fragment: buildStatusFragment(part.text),
1214
+ fallback: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "pactor-chat-status", children: part.text })
1215
+ }
1216
+ );
1032
1217
  case "artifact":
1033
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1034
- "div",
1218
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1219
+ ChromeSlot,
1035
1220
  {
1036
- className: "pactor-chat-artifact-card",
1037
- "data-artifact-kind": part.artifact.kind,
1038
- children: [
1039
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "pactor-chat-artifact-card__title", children: part.artifact.title }),
1040
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "pactor-chat-artifact-card__kind", children: part.artifact.kind }),
1041
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1042
- "button",
1043
- {
1044
- type: "button",
1045
- className: "pactor-chat-artifact-card__open",
1046
- onClick: () => onOpenArtifact?.(part.artifact),
1047
- children: [
1048
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.PanelRightOpen, { size: 13, strokeWidth: 1.7 }),
1049
- "\u5728\u9762\u677F\u4E2D\u6253\u5F00"
1050
- ]
1051
- }
1052
- )
1053
- ]
1221
+ fragment: buildArtifactCardFragment(part.artifact),
1222
+ fallback: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1223
+ "div",
1224
+ {
1225
+ className: "pactor-chat-artifact-card",
1226
+ "data-artifact-kind": part.artifact.kind,
1227
+ children: [
1228
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "pactor-chat-artifact-card__title", children: part.artifact.title }),
1229
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "pactor-chat-artifact-card__kind", children: part.artifact.kind }),
1230
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1231
+ "button",
1232
+ {
1233
+ type: "button",
1234
+ className: "pactor-chat-artifact-card__open",
1235
+ onClick: () => onOpenArtifact?.(part.artifact),
1236
+ children: [
1237
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_components2.Icon, { name: "panel-right-open", size: 13, strokeWidth: 1.7 }),
1238
+ "\u5728\u9762\u677F\u4E2D\u6253\u5F00"
1239
+ ]
1240
+ }
1241
+ )
1242
+ ]
1243
+ }
1244
+ )
1054
1245
  }
1055
1246
  );
1056
1247
  default:
@@ -1058,14 +1249,71 @@ function PartRenderer({ part, runtime, scope, streaming = false, onOpenArtifact
1058
1249
  }
1059
1250
  }
1060
1251
 
1252
+ // src/view/chrome/panel-header.tsx
1253
+ var import_components3 = require("@pactor-app/ui/components");
1254
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1255
+ function ChatPanelCloseButton() {
1256
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1257
+ "button",
1258
+ {
1259
+ type: "button",
1260
+ className: "pactor-chat-icon-button pactor-chat-inspector__close",
1261
+ title: "\u5173\u95ED\u9762\u677F",
1262
+ "aria-label": "\u5173\u95ED\u9762\u677F",
1263
+ onClick: () => invokeChatPanelClose(),
1264
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_components3.Icon, { name: "x", size: 16 })
1265
+ }
1266
+ );
1267
+ }
1268
+ var PANEL_HEADER_INTERNAL_COMPONENTS = { ChatPanelCloseButton };
1269
+ function buildPanelHeaderFragment(props) {
1270
+ return {
1271
+ requires: ["Flex", "Text", "Badge", "ChatPanelCloseButton"],
1272
+ node: {
1273
+ type: "Flex",
1274
+ props: {
1275
+ justify: "space-between",
1276
+ align: "center",
1277
+ gap: "small",
1278
+ className: "pactor-chat-inspector__header-dsl"
1279
+ },
1280
+ children: [
1281
+ {
1282
+ type: "Flex",
1283
+ props: { align: "center", gap: "small" },
1284
+ children: [
1285
+ {
1286
+ type: "Text",
1287
+ props: {
1288
+ text: props.title,
1289
+ strong: true,
1290
+ className: "pactor-chat-inspector__title"
1291
+ }
1292
+ },
1293
+ {
1294
+ type: "Badge",
1295
+ props: {
1296
+ text: props.kind,
1297
+ variant: "secondary",
1298
+ className: "pactor-chat-inspector__kind"
1299
+ }
1300
+ }
1301
+ ]
1302
+ },
1303
+ { type: "ChatPanelCloseButton" }
1304
+ ]
1305
+ }
1306
+ };
1307
+ }
1308
+
1061
1309
  // src/view/ArtifactPanel.tsx
1062
- var import_jsx_runtime3 = require("react/jsx-runtime");
1310
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1063
1311
  function useArtifactRuntime(safeRegistry, user) {
1064
1312
  return (0, import_react5.useMemo)(() => {
1065
1313
  if (!safeRegistry) {
1066
1314
  return void 0;
1067
1315
  }
1068
- const components = new import_core2.Registry("ChatArtifactRegistry");
1316
+ const components = new import_core3.Registry("ChatArtifactRegistry");
1069
1317
  components.register(CHAT_DENIED_TYPE, ChatDenied);
1070
1318
  for (const name of safeRegistry.list()) {
1071
1319
  components.register(name, safeRegistry.get(name));
@@ -1085,12 +1333,12 @@ var ArtifactDslGuard = class extends import_react5.Component {
1085
1333
  console.warn("[@pactor-app/chat] DSL \u4EA7\u51FA\u7269\u6E32\u67D3\u5931\u8D25\uFF0C\u5DF2\u964D\u7EA7\u5C55\u793A", error);
1086
1334
  }
1087
1335
  render() {
1088
- return this.state.failed ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "pactor-chat-denied", children: "DSL \u4EA7\u51FA\u7269\u6E32\u67D3\u5931\u8D25\uFF0C\u5DF2\u964D\u7EA7\u5C55\u793A" }) : this.props.children;
1336
+ return this.state.failed ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pactor-chat-denied", children: "DSL \u4EA7\u51FA\u7269\u6E32\u67D3\u5931\u8D25\uFF0C\u5DF2\u964D\u7EA7\u5C55\u793A" }) : this.props.children;
1089
1337
  }
1090
1338
  };
1091
1339
  function ArtifactDslNode({ node }) {
1092
- const { renderChildren } = (0, import_runtime3.useRenderer)();
1093
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: renderChildren([node]) });
1340
+ const { renderChildren } = (0, import_runtime4.useRenderer)();
1341
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: renderChildren([node]) });
1094
1342
  }
1095
1343
  function ArtifactDslContent({
1096
1344
  content,
@@ -1098,10 +1346,10 @@ function ArtifactDslContent({
1098
1346
  }) {
1099
1347
  const node = typeof content === "object" && content !== null && !Array.isArray(content) && typeof content.type === "string" ? content : void 0;
1100
1348
  if (!node || !runtime) {
1101
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "pactor-chat-denied", children: node ? "DSL \u9884\u89C8\u4E0D\u53EF\u7528\uFF08\u672A\u6CE8\u5165 Safe Registry\uFF09" : "DSL \u4EA7\u51FA\u7269\u5185\u5BB9\u65E0\u6548" });
1349
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pactor-chat-denied", children: node ? "DSL \u9884\u89C8\u4E0D\u53EF\u7528\uFF08\u672A\u6CE8\u5165 Safe Registry\uFF09" : "DSL \u4EA7\u51FA\u7269\u5185\u5BB9\u65E0\u6548" });
1102
1350
  }
1103
1351
  const guarded = guardChatDsl(node, runtime.context.components);
1104
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_runtime3.DslRenderer, { context: runtime.context, engine: runtime.engine, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ArtifactDslGuard, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ArtifactDslNode, { node: guarded }) }) });
1352
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_runtime4.DslRenderer, { context: runtime.context, engine: runtime.engine, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ArtifactDslGuard, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ArtifactDslNode, { node: guarded }) }) });
1105
1353
  }
1106
1354
  function ArtifactContent({
1107
1355
  artifact,
@@ -1109,16 +1357,16 @@ function ArtifactContent({
1109
1357
  }) {
1110
1358
  switch (artifact.kind) {
1111
1359
  case "markdown":
1112
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_markdown2.MarkdownRenderer, { content: String(artifact.content) });
1360
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_markdown2.MarkdownRenderer, { content: String(artifact.content) });
1113
1361
  case "code":
1114
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "pactor-chat-inspector__code", children: [
1115
- artifact.language ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "pactor-chat-inspector__code-lang", children: artifact.language }) : null,
1116
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("pre", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("code", { children: String(artifact.content) }) })
1362
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "pactor-chat-inspector__code", children: [
1363
+ artifact.language ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "pactor-chat-inspector__code-lang", children: artifact.language }) : null,
1364
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("pre", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("code", { children: String(artifact.content) }) })
1117
1365
  ] });
1118
1366
  case "text":
1119
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("pre", { className: "pactor-chat-inspector__text", children: String(artifact.content) });
1367
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("pre", { className: "pactor-chat-inspector__text", children: String(artifact.content) });
1120
1368
  case "dsl":
1121
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ArtifactDslContent, { content: artifact.content, runtime });
1369
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ArtifactDslContent, { content: artifact.content, runtime });
1122
1370
  default:
1123
1371
  return null;
1124
1372
  }
@@ -1132,6 +1380,11 @@ function ArtifactPanel({
1132
1380
  className
1133
1381
  }) {
1134
1382
  const runtime = useArtifactRuntime(safeRegistry, user);
1383
+ const closeRef = (0, import_react5.useRef)(onClose);
1384
+ closeRef.current = onClose;
1385
+ (0, import_react5.useEffect)(() => {
1386
+ setChatChromeHandlers({ panelClose: () => closeRef.current?.() });
1387
+ }, []);
1135
1388
  const panelRef = (0, import_react5.useRef)(null);
1136
1389
  const [width, setWidth] = (0, import_react5.useState)(null);
1137
1390
  const handleResizeStart = (event) => {
@@ -1159,7 +1412,7 @@ function ArtifactPanel({
1159
1412
  document.addEventListener("pointermove", onMove);
1160
1413
  document.addEventListener("pointerup", onUp);
1161
1414
  };
1162
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1415
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1163
1416
  "aside",
1164
1417
  {
1165
1418
  ref: panelRef,
@@ -1167,7 +1420,7 @@ function ArtifactPanel({
1167
1420
  "data-kind": artifact?.kind,
1168
1421
  style: width !== null ? { width: `${width}px` } : void 0,
1169
1422
  children: [
1170
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1423
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1171
1424
  "div",
1172
1425
  {
1173
1426
  className: "pactor-chat-inspector__resize",
@@ -1177,38 +1430,48 @@ function ArtifactPanel({
1177
1430
  onPointerDown: handleResizeStart
1178
1431
  }
1179
1432
  ),
1180
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "pactor-chat-inspector__header", children: [
1181
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "pactor-chat-inspector__title", children: artifact?.title ?? "\u4EA7\u51FA\u7269" }),
1182
- artifact ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "pactor-chat-inspector__kind", children: artifact.kind }) : null,
1183
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1184
- "button",
1185
- {
1186
- type: "button",
1187
- className: "pactor-chat-icon-button pactor-chat-inspector__close",
1188
- "aria-label": "\u5173\u95ED\u9762\u677F",
1189
- onClick: onClose,
1190
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.X, { size: 16, strokeWidth: 1.7 })
1191
- }
1192
- )
1193
- ] }),
1194
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "pactor-chat-inspector__body", children: inspector !== void 0 && inspector !== null ? inspector : artifact ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ArtifactContent, { artifact, runtime }) : null })
1433
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pactor-chat-inspector__header", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1434
+ ChromeSlot,
1435
+ {
1436
+ fragment: buildPanelHeaderFragment({
1437
+ title: artifact?.title ?? "\u4EA7\u51FA\u7269",
1438
+ kind: artifact?.kind ?? "text"
1439
+ }),
1440
+ components: PANEL_HEADER_INTERNAL_COMPONENTS,
1441
+ fallback: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1442
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "pactor-chat-inspector__title", children: artifact?.title ?? "\u4EA7\u51FA\u7269" }),
1443
+ artifact ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "pactor-chat-inspector__kind", children: artifact.kind }) : null,
1444
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1445
+ "button",
1446
+ {
1447
+ type: "button",
1448
+ className: "pactor-chat-icon-button pactor-chat-inspector__close",
1449
+ "aria-label": "\u5173\u95ED\u9762\u677F",
1450
+ onClick: onClose,
1451
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_components4.Icon, { name: "x", size: 16, strokeWidth: 1.7 })
1452
+ }
1453
+ )
1454
+ ] })
1455
+ }
1456
+ ) }),
1457
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pactor-chat-inspector__body", children: inspector !== void 0 && inspector !== null ? inspector : artifact ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ArtifactContent, { artifact, runtime }) : null })
1195
1458
  ]
1196
1459
  }
1197
1460
  );
1198
1461
  }
1199
1462
 
1200
1463
  // src/view/ChatView.tsx
1201
- var import_lucide_react8 = require("lucide-react");
1202
- var import_core3 = require("@pactor-app/core");
1203
- var import_react13 = require("react");
1464
+ var import_components10 = require("@pactor-app/ui/components");
1465
+ var import_core4 = require("@pactor-app/core");
1466
+ var import_react14 = require("react");
1204
1467
 
1205
1468
  // src/view/Composer.tsx
1206
- var import_lucide_react5 = require("lucide-react");
1207
- var import_react9 = require("react");
1469
+ var import_components7 = require("@pactor-app/ui/components");
1470
+ var import_react10 = require("react");
1208
1471
 
1209
1472
  // src/view/AnchoredPopover.tsx
1210
1473
  var import_react6 = require("react");
1211
- var import_jsx_runtime4 = require("react/jsx-runtime");
1474
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1212
1475
  function AnchoredPopover({
1213
1476
  anchorRect,
1214
1477
  onClose,
@@ -1265,7 +1528,7 @@ function AnchoredPopover({
1265
1528
  position?.below ? "pactor-chat-popover--below" : "",
1266
1529
  className ?? ""
1267
1530
  ].filter(Boolean).join(" ");
1268
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1531
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1269
1532
  "div",
1270
1533
  {
1271
1534
  ref,
@@ -1284,9 +1547,34 @@ function AnchoredPopover({
1284
1547
  }
1285
1548
 
1286
1549
  // src/view/ModelChip.tsx
1287
- var import_lucide_react4 = require("lucide-react");
1550
+ var import_components5 = require("@pactor-app/ui/components");
1288
1551
  var import_react7 = require("react");
1289
- var import_jsx_runtime5 = require("react/jsx-runtime");
1552
+
1553
+ // src/view/chrome/model-select.tsx
1554
+ function buildModelSelectFragment(props) {
1555
+ return {
1556
+ requires: ["Select"],
1557
+ node: {
1558
+ type: "Select",
1559
+ props: {
1560
+ options: props.models.map((option) => ({
1561
+ value: option.value,
1562
+ label: option.label
1563
+ })),
1564
+ value: props.model,
1565
+ className: "pactor-chat-model-select"
1566
+ },
1567
+ events: {
1568
+ onChange: [
1569
+ { action: "chat.modelChange", params: { value: "${event}" } }
1570
+ ]
1571
+ }
1572
+ }
1573
+ };
1574
+ }
1575
+
1576
+ // src/view/ModelChip.tsx
1577
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1290
1578
  function ModelChip({
1291
1579
  models,
1292
1580
  model,
@@ -1295,8 +1583,8 @@ function ModelChip({
1295
1583
  const chipRef = (0, import_react7.useRef)(null);
1296
1584
  const [anchor, setAnchor] = (0, import_react7.useState)(null);
1297
1585
  const currentOption = models.find((option) => option.value === model) ?? models[0];
1298
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1299
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1586
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1587
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1300
1588
  "button",
1301
1589
  {
1302
1590
  ref: chipRef,
@@ -1306,18 +1594,18 @@ function ModelChip({
1306
1594
  "aria-label": "\u9009\u62E9\u6A21\u578B",
1307
1595
  onClick: () => setAnchor(chipRef.current.getBoundingClientRect()),
1308
1596
  children: [
1309
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "pactor-chat-model-chip__label", children: currentOption.label }),
1310
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.ChevronsUpDown, { size: 12 })
1597
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "pactor-chat-model-chip__label", children: currentOption.label }),
1598
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_components5.Icon, { name: "chevrons-up-down", size: 12 })
1311
1599
  ]
1312
1600
  }
1313
1601
  ),
1314
- anchor && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1602
+ anchor && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1315
1603
  AnchoredPopover,
1316
1604
  {
1317
1605
  anchorRect: anchor,
1318
1606
  onClose: () => setAnchor(null),
1319
1607
  role: "listbox",
1320
- children: models.map((option) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1608
+ children: models.map((option) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1321
1609
  "button",
1322
1610
  {
1323
1611
  type: "button",
@@ -1329,8 +1617,8 @@ function ModelChip({
1329
1617
  setAnchor(null);
1330
1618
  },
1331
1619
  children: [
1332
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "pactor-chat-popover__item-label", children: option.label }),
1333
- option.value === currentOption.value && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Check, { size: 13, className: "pactor-chat-popover__item-check" })
1620
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "pactor-chat-popover__item-label", children: option.label }),
1621
+ option.value === currentOption.value && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_components5.Icon, { name: "check", size: 13, className: "pactor-chat-popover__item-check" })
1334
1622
  ]
1335
1623
  },
1336
1624
  option.value
@@ -1339,10 +1627,243 @@ function ModelChip({
1339
1627
  )
1340
1628
  ] });
1341
1629
  }
1630
+ function ModelSelect({
1631
+ models,
1632
+ model,
1633
+ onModelChange
1634
+ }) {
1635
+ const changeRef = (0, import_react7.useRef)(onModelChange);
1636
+ changeRef.current = onModelChange;
1637
+ (0, import_react7.useEffect)(() => {
1638
+ setChatChromeHandlers({
1639
+ modelChange: (value) => changeRef.current?.(value)
1640
+ });
1641
+ }, []);
1642
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1643
+ ChromeSlot,
1644
+ {
1645
+ fragment: buildModelSelectFragment({ models, model }),
1646
+ fallback: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ModelChip, { models, model, onModelChange })
1647
+ }
1648
+ );
1649
+ }
1342
1650
 
1343
- // src/view/context-sources.tsx
1651
+ // src/view/chrome/composer.tsx
1652
+ var import_components6 = require("@pactor-app/ui/components");
1344
1653
  var import_react8 = require("react");
1345
- var import_jsx_runtime6 = require("react/jsx-runtime");
1654
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1655
+ var ComposerBridgeContext = (0, import_react8.createContext)(
1656
+ null
1657
+ );
1658
+ function useComposerBridge() {
1659
+ const bridge = (0, import_react8.useContext)(ComposerBridgeContext);
1660
+ if (!bridge) {
1661
+ throw new Error(
1662
+ "[@pactor-app/chat] ChatComposer* \u5185\u90E8\u7EC4\u4EF6\u4EC5\u53EF\u5728 Composer chrome \u7247\u6BB5\u5185\u6E32\u67D3"
1663
+ );
1664
+ }
1665
+ return bridge;
1666
+ }
1667
+ function formatSize(bytes) {
1668
+ if (bytes < 1024) {
1669
+ return `${bytes} B`;
1670
+ }
1671
+ if (bytes < 1024 * 1024) {
1672
+ return `${(bytes / 1024).toFixed(1)} KB`;
1673
+ }
1674
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1675
+ }
1676
+ function ComposerAttachments({
1677
+ attachments,
1678
+ onRemove,
1679
+ onClick
1680
+ }) {
1681
+ if (attachments.length === 0) {
1682
+ return null;
1683
+ }
1684
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "pactor-chat-composer__attachments", children: attachments.map((attachment) => {
1685
+ const isImage = attachment.mimeType.startsWith("image/");
1686
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1687
+ "div",
1688
+ {
1689
+ className: "pactor-chat-composer__attachment",
1690
+ "data-attachment-id": attachment.id,
1691
+ onClick: () => onClick?.(attachment),
1692
+ children: [
1693
+ isImage && attachment.url ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "pactor-chat-composer__attachment-thumb", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: attachment.url, alt: attachment.name }) }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "pactor-chat-composer__attachment-info", children: [
1694
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_components6.Icon, { name: "file-text", size: 14 }),
1695
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "pactor-chat-composer__attachment-name", children: attachment.name }),
1696
+ attachment.size !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "pactor-chat-composer__attachment-size", children: formatSize(attachment.size) })
1697
+ ] }),
1698
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1699
+ "button",
1700
+ {
1701
+ type: "button",
1702
+ className: "pactor-chat-icon-button pactor-chat-composer__attachment-remove",
1703
+ title: "\u79FB\u9664\u9644\u4EF6",
1704
+ "aria-label": "\u79FB\u9664\u9644\u4EF6",
1705
+ onClick: (event) => {
1706
+ event.stopPropagation();
1707
+ onRemove(attachment.id);
1708
+ },
1709
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_components6.Icon, { name: "x", size: 12 })
1710
+ }
1711
+ )
1712
+ ]
1713
+ },
1714
+ attachment.id
1715
+ );
1716
+ }) });
1717
+ }
1718
+ function ComposerContextChips({
1719
+ chips,
1720
+ onRemove
1721
+ }) {
1722
+ if (chips.length === 0) {
1723
+ return null;
1724
+ }
1725
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "pactor-chat-composer__contexts", children: chips.map((chip) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1726
+ "span",
1727
+ {
1728
+ className: "pactor-chat-context-chip",
1729
+ "data-context-id": chip.id,
1730
+ children: [
1731
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "pactor-chat-context-chip__label", children: chip.label }),
1732
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1733
+ "button",
1734
+ {
1735
+ type: "button",
1736
+ className: "pactor-chat-icon-button pactor-chat-context-chip__remove",
1737
+ title: `\u79FB\u9664\u4E0A\u4E0B\u6587 ${chip.label}`,
1738
+ "aria-label": `\u79FB\u9664\u4E0A\u4E0B\u6587 ${chip.label}`,
1739
+ onClick: () => onRemove(chip),
1740
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_components6.Icon, { name: "x", size: 12 })
1741
+ }
1742
+ )
1743
+ ]
1744
+ },
1745
+ `${chip.trigger ?? ""}:${chip.id}`
1746
+ )) });
1747
+ }
1748
+ function ChatComposerHeader() {
1749
+ const bridge = useComposerBridge();
1750
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1751
+ bridge.attachments !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1752
+ ComposerAttachments,
1753
+ {
1754
+ attachments: bridge.attachments,
1755
+ onRemove: bridge.removeAttachment,
1756
+ onClick: bridge.attachmentClick
1757
+ }
1758
+ ),
1759
+ bridge.contextChips !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1760
+ ComposerContextChips,
1761
+ {
1762
+ chips: bridge.contextChips,
1763
+ onRemove: bridge.removeChip
1764
+ }
1765
+ )
1766
+ ] });
1767
+ }
1768
+ function ChatComposerToolbarStart() {
1769
+ const bridge = useComposerBridge();
1770
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1771
+ bridge.toolbarStart,
1772
+ bridge.showPlus && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1773
+ "button",
1774
+ {
1775
+ type: "button",
1776
+ className: "pactor-chat-icon-button",
1777
+ title: "\u66F4\u591A\u529F\u80FD",
1778
+ "aria-label": "\u66F4\u591A\u529F\u80FD",
1779
+ onClick: (event) => bridge.plusClick(event.currentTarget.getBoundingClientRect()),
1780
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_components6.Icon, { name: "plus", size: 14 })
1781
+ }
1782
+ ),
1783
+ bridge.showAttach && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1784
+ "button",
1785
+ {
1786
+ type: "button",
1787
+ className: "pactor-chat-icon-button",
1788
+ title: "\u6DFB\u52A0\u9644\u4EF6",
1789
+ "aria-label": "\u6DFB\u52A0\u9644\u4EF6",
1790
+ onClick: bridge.openFilePicker,
1791
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_components6.Icon, { name: "paperclip", size: 14 })
1792
+ }
1793
+ )
1794
+ ] });
1795
+ }
1796
+ function ChatComposerToolbarEnd() {
1797
+ const bridge = useComposerBridge();
1798
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1799
+ bridge.modelChipEnabled && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1800
+ ModelSelect,
1801
+ {
1802
+ models: bridge.models,
1803
+ model: bridge.model,
1804
+ onModelChange: bridge.modelChange
1805
+ }
1806
+ ),
1807
+ bridge.toolbarEnd,
1808
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1809
+ "button",
1810
+ {
1811
+ type: "button",
1812
+ className: `pactor-chat-composer__button${bridge.streaming ? " pactor-chat-composer__button--stop" : ""}`,
1813
+ title: bridge.streaming ? "\u505C\u6B62\u751F\u6210" : "\u53D1\u9001",
1814
+ "aria-label": bridge.streaming ? "\u505C\u6B62\u751F\u6210" : "\u53D1\u9001",
1815
+ disabled: bridge.streaming ? false : bridge.disabled || !bridge.text.trim(),
1816
+ onClick: bridge.streaming ? bridge.stop : bridge.send,
1817
+ children: bridge.streaming ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_components6.Icon, { name: "square", size: 14, className: "pactor-chat-icon-fill" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_components6.Icon, { name: "arrow-up", size: 16 })
1818
+ }
1819
+ )
1820
+ ] });
1821
+ }
1822
+ function ChatComposerFooter() {
1823
+ const bridge = useComposerBridge();
1824
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_jsx_runtime8.Fragment, { children: bridge.composerFooter });
1825
+ }
1826
+ var COMPOSER_INTERNAL_COMPONENTS = {
1827
+ ChatComposerHeader,
1828
+ ChatComposerToolbarStart,
1829
+ ChatComposerToolbarEnd,
1830
+ ChatComposerFooter
1831
+ };
1832
+ function buildComposerFragment(props) {
1833
+ const hideToolbar = props.hideToolbar === true;
1834
+ return {
1835
+ requires: [
1836
+ "ChatInput",
1837
+ "ChatComposerHeader",
1838
+ ...hideToolbar ? [] : ["ChatComposerToolbarStart", "ChatComposerToolbarEnd"],
1839
+ "ChatComposerFooter"
1840
+ ],
1841
+ node: {
1842
+ type: "ChatInput",
1843
+ props: {
1844
+ placeholder: props.placeholder,
1845
+ disabled: props.disabled,
1846
+ rows: 1,
1847
+ hideSend: true,
1848
+ header: { type: "ChatComposerHeader" },
1849
+ ...hideToolbar ? {} : {
1850
+ toolbarStart: { type: "ChatComposerToolbarStart" },
1851
+ toolbarEnd: { type: "ChatComposerToolbarEnd" }
1852
+ },
1853
+ ...props.hasFooter === true ? { footer: { type: "ChatComposerFooter" } } : {}
1854
+ },
1855
+ events: {
1856
+ onSubmit: [
1857
+ { action: "chat.composerSubmit", params: { value: "${event}" } }
1858
+ ]
1859
+ }
1860
+ }
1861
+ };
1862
+ }
1863
+
1864
+ // src/view/context-sources.tsx
1865
+ var import_react9 = require("react");
1866
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1346
1867
  function sourceCacheKey(source) {
1347
1868
  return `${source.service ?? ""}:${JSON.stringify(source.params ?? {})}`;
1348
1869
  }
@@ -1377,8 +1898,8 @@ function ContextSourceList({
1377
1898
  cache,
1378
1899
  onSelect
1379
1900
  }) {
1380
- const [resolutions, setResolutions] = (0, import_react8.useState)([]);
1381
- (0, import_react8.useEffect)(() => {
1901
+ const [resolutions, setResolutions] = (0, import_react9.useState)([]);
1902
+ (0, import_react9.useEffect)(() => {
1382
1903
  let cancelled = false;
1383
1904
  const initial = sources.map(
1384
1905
  (source) => source.items ? { status: "ready", items: source.items } : { status: "loading" }
@@ -1423,12 +1944,12 @@ function ContextSourceList({
1423
1944
  totalMatched += items.length;
1424
1945
  return { source, resolution, items };
1425
1946
  });
1426
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "pactor-chat-source-list", children: [
1427
- groups.map(({ source, resolution, items }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "pactor-chat-source-list__group", children: [
1428
- sources.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "pactor-chat-source-list__group-label", children: source.label }),
1429
- resolution.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "pactor-chat-source-list__hint", children: "\u52A0\u8F7D\u4E2D\u2026" }),
1430
- resolution.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "pactor-chat-source-list__error", role: "alert", children: resolution.message }),
1431
- items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1947
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "pactor-chat-source-list", children: [
1948
+ groups.map(({ source, resolution, items }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "pactor-chat-source-list__group", children: [
1949
+ sources.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "pactor-chat-source-list__group-label", children: source.label }),
1950
+ resolution.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "pactor-chat-source-list__hint", children: "\u52A0\u8F7D\u4E2D\u2026" }),
1951
+ resolution.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "pactor-chat-source-list__error", role: "alert", children: resolution.message }),
1952
+ items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1432
1953
  "button",
1433
1954
  {
1434
1955
  type: "button",
@@ -1439,22 +1960,13 @@ function ContextSourceList({
1439
1960
  item.id
1440
1961
  ))
1441
1962
  ] }, `${source.trigger}:${source.label}`)),
1442
- totalMatched === 0 && groups.every(({ resolution }) => resolution.status === "ready") && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "pactor-chat-source-list__hint", children: "\u65E0\u5339\u914D\u9879" })
1963
+ totalMatched === 0 && groups.every(({ resolution }) => resolution.status === "ready") && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "pactor-chat-source-list__hint", children: "\u65E0\u5339\u914D\u9879" })
1443
1964
  ] });
1444
1965
  }
1445
1966
 
1446
1967
  // src/view/Composer.tsx
1447
- var import_jsx_runtime7 = require("react/jsx-runtime");
1968
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1448
1969
  var MAX_INPUT_HEIGHT = 200;
1449
- function formatSize(bytes) {
1450
- if (bytes < 1024) {
1451
- return `${bytes} B`;
1452
- }
1453
- if (bytes < 1024 * 1024) {
1454
- return `${(bytes / 1024).toFixed(1)} KB`;
1455
- }
1456
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1457
- }
1458
1970
  function detectMention(text, caret) {
1459
1971
  const beforeCaret = text.slice(0, caret);
1460
1972
  const match = /(?:^|\s)@([^\s@]*)$/.exec(beforeCaret);
@@ -1483,28 +1995,30 @@ function Composer({
1483
1995
  contextSources,
1484
1996
  contextChips,
1485
1997
  onContextChipsChange,
1486
- callService
1998
+ callService,
1999
+ composerFooter,
2000
+ hideToolbar = false
1487
2001
  }) {
1488
- const [text, setText] = (0, import_react9.useState)("");
1489
- const [dragOver, setDragOver] = (0, import_react9.useState)(false);
1490
- const inputRef = (0, import_react9.useRef)(null);
1491
- const fileInputRef = (0, import_react9.useRef)(null);
1492
- const plusButtonRef = (0, import_react9.useRef)(null);
1493
- const composingRef = (0, import_react9.useRef)(false);
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);
1494
2008
  const modelChipEnabled = models !== void 0 && models.length > 0;
1495
- const mentionSources = (0, import_react9.useMemo)(
2009
+ const mentionSources = (0, import_react10.useMemo)(
1496
2010
  () => contextSources?.filter((source) => source.trigger === "@") ?? [],
1497
2011
  [contextSources]
1498
2012
  );
1499
- const plusSources = (0, import_react9.useMemo)(
2013
+ const plusSources = (0, import_react10.useMemo)(
1500
2014
  () => contextSources?.filter((source) => source.trigger === "+") ?? [],
1501
2015
  [contextSources]
1502
2016
  );
1503
2017
  const builtinMentionEnabled = onMentionTrigger === void 0 && mentionSources.length > 0;
1504
2018
  const builtinPlusEnabled = onPlusMenu === void 0 && plusSources.length > 0;
1505
- const [mentionState, setMentionState] = (0, import_react9.useState)(null);
1506
- const [plusAnchor, setPlusAnchor] = (0, import_react9.useState)(null);
1507
- const sourceCacheRef = (0, import_react9.useRef)(null);
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);
1508
2022
  if (sourceCacheRef.current === null) {
1509
2023
  sourceCacheRef.current = /* @__PURE__ */ new Map();
1510
2024
  }
@@ -1523,7 +2037,7 @@ function Composer({
1523
2037
  setMentionState(null);
1524
2038
  setPlusAnchor(null);
1525
2039
  };
1526
- (0, import_react9.useLayoutEffect)(() => {
2040
+ (0, import_react10.useLayoutEffect)(() => {
1527
2041
  const input = inputRef.current;
1528
2042
  if (input) {
1529
2043
  input.style.height = "auto";
@@ -1538,105 +2052,147 @@ function Composer({
1538
2052
  onSend?.(value);
1539
2053
  setText("");
1540
2054
  };
1541
- const handleChange = (event) => {
1542
- const value = event.target.value;
2055
+ const sendRef = (0, import_react10.useRef)(send);
2056
+ sendRef.current = send;
2057
+ (0, import_react10.useLayoutEffect)(() => {
2058
+ setChatChromeHandlers({ composerSubmit: () => sendRef.current() });
2059
+ }, []);
2060
+ const syncText = (value, caret, rect) => {
1543
2061
  setText(value);
1544
2062
  if (onMentionTrigger || builtinMentionEnabled) {
1545
- const caret = event.target.selectionStart ?? value.length;
1546
2063
  const query = detectMention(value, caret);
1547
2064
  if (onMentionTrigger) {
1548
2065
  if (query !== null) {
1549
- onMentionTrigger(query, event.target.getBoundingClientRect());
2066
+ onMentionTrigger(query, rect);
1550
2067
  }
1551
2068
  } else {
1552
- setMentionState(
1553
- query !== null ? { query, anchorRect: event.target.getBoundingClientRect() } : null
1554
- );
2069
+ setMentionState(query !== null ? { query, anchorRect: rect } : null);
2070
+ }
2071
+ }
2072
+ };
2073
+ const handleChange = (event) => {
2074
+ syncText(
2075
+ event.target.value,
2076
+ event.target.selectionStart ?? event.target.value.length,
2077
+ event.target.getBoundingClientRect()
2078
+ );
2079
+ };
2080
+ const handleBubbleChange = (event) => {
2081
+ const target = event.target;
2082
+ if (!(target instanceof HTMLTextAreaElement)) {
2083
+ return;
2084
+ }
2085
+ syncText(
2086
+ target.value,
2087
+ target.selectionStart ?? target.value.length,
2088
+ target.getBoundingClientRect()
2089
+ );
2090
+ };
2091
+ const handlePasteFiles = (clipboardData) => {
2092
+ const files = Array.from(clipboardData.files);
2093
+ if (files.length > 0 && onAttachFiles) {
2094
+ onAttachFiles(files);
2095
+ return true;
2096
+ }
2097
+ return false;
2098
+ };
2099
+ const dragHandlers = {
2100
+ onDragOver: (event) => {
2101
+ event.preventDefault();
2102
+ setDragOver(true);
2103
+ },
2104
+ onDragLeave: () => setDragOver(false),
2105
+ onDrop: (event) => {
2106
+ event.preventDefault();
2107
+ setDragOver(false);
2108
+ const files = collectDropFiles(event);
2109
+ if (files.length > 0 && onAttachFiles) {
2110
+ onAttachFiles(files);
1555
2111
  }
1556
2112
  }
1557
2113
  };
1558
- const showToolbar = toolbarStart !== void 0 || toolbarEnd !== void 0 || onPlusMenu !== void 0 || builtinPlusEnabled || onAttachFiles !== void 0 || modelChipEnabled;
1559
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2114
+ const showToolbar = !hideToolbar && (toolbarStart !== void 0 || toolbarEnd !== void 0 || onPlusMenu !== void 0 || builtinPlusEnabled || onAttachFiles !== void 0 || modelChipEnabled);
2115
+ const fileInputNode = onAttachFiles ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2116
+ "input",
2117
+ {
2118
+ ref: fileInputRef,
2119
+ type: "file",
2120
+ multiple: true,
2121
+ hidden: true,
2122
+ onChange: (event) => {
2123
+ const files = event.target.files ? Array.from(event.target.files) : [];
2124
+ if (files.length > 0) {
2125
+ onAttachFiles(files);
2126
+ }
2127
+ event.target.value = "";
2128
+ }
2129
+ }
2130
+ ) : null;
2131
+ const popoverNodes = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
2132
+ builtinMentionEnabled && mentionState && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2133
+ AnchoredPopover,
2134
+ {
2135
+ anchorRect: mentionState.anchorRect,
2136
+ onClose: () => setMentionState(null),
2137
+ role: "listbox",
2138
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2139
+ ContextSourceList,
2140
+ {
2141
+ sources: mentionSources,
2142
+ query: mentionState.query,
2143
+ callService,
2144
+ cache: sourceCacheRef.current,
2145
+ onSelect: handleSelectContext
2146
+ }
2147
+ )
2148
+ }
2149
+ ),
2150
+ builtinPlusEnabled && plusAnchor && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2151
+ AnchoredPopover,
2152
+ {
2153
+ anchorRect: plusAnchor,
2154
+ onClose: () => setPlusAnchor(null),
2155
+ role: "listbox",
2156
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2157
+ ContextSourceList,
2158
+ {
2159
+ sources: plusSources,
2160
+ query: "",
2161
+ callService,
2162
+ cache: sourceCacheRef.current,
2163
+ onSelect: handleSelectContext
2164
+ }
2165
+ )
2166
+ }
2167
+ )
2168
+ ] });
2169
+ const fallback = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1560
2170
  "div",
1561
2171
  {
1562
2172
  className: `pactor-chat-composer${dragOver ? " pactor-chat-composer--dragover" : ""}`,
1563
- onDragOver: (event) => {
1564
- event.preventDefault();
1565
- setDragOver(true);
1566
- },
1567
- onDragLeave: () => setDragOver(false),
1568
- onDrop: (event) => {
1569
- event.preventDefault();
1570
- setDragOver(false);
1571
- const files = collectDropFiles(event);
1572
- if (files.length > 0 && onAttachFiles) {
1573
- onAttachFiles(files);
1574
- }
1575
- },
2173
+ ...dragHandlers,
1576
2174
  children: [
1577
- attachments !== void 0 && attachments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "pactor-chat-composer__attachments", children: attachments.map((attachment) => {
1578
- const isImage = attachment.mimeType.startsWith("image/");
1579
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1580
- "div",
1581
- {
1582
- className: "pactor-chat-composer__attachment",
1583
- "data-attachment-id": attachment.id,
1584
- onClick: () => onAttachmentClick?.(attachment),
1585
- children: [
1586
- isImage && attachment.url ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "pactor-chat-composer__attachment-thumb", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("img", { src: attachment.url, alt: attachment.name }) }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "pactor-chat-composer__attachment-info", children: [
1587
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react5.FileText, { size: 14 }),
1588
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "pactor-chat-composer__attachment-name", children: attachment.name }),
1589
- attachment.size !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "pactor-chat-composer__attachment-size", children: formatSize(attachment.size) })
1590
- ] }),
1591
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1592
- "button",
1593
- {
1594
- type: "button",
1595
- className: "pactor-chat-icon-button pactor-chat-composer__attachment-remove",
1596
- title: "\u79FB\u9664\u9644\u4EF6",
1597
- "aria-label": "\u79FB\u9664\u9644\u4EF6",
1598
- onClick: (event) => {
1599
- event.stopPropagation();
1600
- onAttachmentsChange?.(
1601
- attachments.filter((item) => item.id !== attachment.id)
1602
- );
1603
- },
1604
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react5.X, { size: 12 })
1605
- }
1606
- )
1607
- ]
1608
- },
1609
- attachment.id
1610
- );
1611
- }) }),
1612
- contextChips !== void 0 && contextChips.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "pactor-chat-composer__contexts", children: contextChips.map((chip) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1613
- "span",
2175
+ attachments !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2176
+ ComposerAttachments,
1614
2177
  {
1615
- className: "pactor-chat-context-chip",
1616
- "data-context-id": chip.id,
1617
- children: [
1618
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "pactor-chat-context-chip__label", children: chip.label }),
1619
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1620
- "button",
1621
- {
1622
- type: "button",
1623
- className: "pactor-chat-icon-button pactor-chat-context-chip__remove",
1624
- title: `\u79FB\u9664\u4E0A\u4E0B\u6587 ${chip.label}`,
1625
- "aria-label": `\u79FB\u9664\u4E0A\u4E0B\u6587 ${chip.label}`,
1626
- onClick: () => onContextChipsChange?.(
1627
- contextChips.filter(
1628
- (existing) => !(existing.id === chip.id && existing.trigger === chip.trigger)
1629
- )
1630
- ),
1631
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react5.X, { size: 12 })
1632
- }
2178
+ attachments,
2179
+ onRemove: (id) => onAttachmentsChange?.(attachments.filter((a) => a.id !== id)),
2180
+ onClick: onAttachmentClick
2181
+ }
2182
+ ),
2183
+ contextChips !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2184
+ ComposerContextChips,
2185
+ {
2186
+ chips: contextChips,
2187
+ onRemove: (chip) => onContextChipsChange?.(
2188
+ contextChips.filter(
2189
+ (existing) => !(existing.id === chip.id && existing.trigger === chip.trigger)
1633
2190
  )
1634
- ]
1635
- },
1636
- `${chip.trigger ?? ""}:${chip.id}`
1637
- )) }),
1638
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "pactor-chat-composer__row", children: [
1639
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2191
+ )
2192
+ }
2193
+ ),
2194
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "pactor-chat-composer__row", children: [
2195
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1640
2196
  "textarea",
1641
2197
  {
1642
2198
  ref: inputRef,
@@ -1653,10 +2209,8 @@ function Composer({
1653
2209
  composingRef.current = false;
1654
2210
  },
1655
2211
  onPaste: (event) => {
1656
- const files = Array.from(event.clipboardData.files);
1657
- if (files.length > 0 && onAttachFiles) {
2212
+ if (handlePasteFiles(event.clipboardData)) {
1658
2213
  event.preventDefault();
1659
- onAttachFiles(files);
1660
2214
  }
1661
2215
  },
1662
2216
  onKeyDown: (event) => {
@@ -1670,7 +2224,7 @@ function Composer({
1670
2224
  }
1671
2225
  }
1672
2226
  ),
1673
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2227
+ hideToolbar ? null : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1674
2228
  "button",
1675
2229
  {
1676
2230
  type: "button",
@@ -1679,14 +2233,14 @@ function Composer({
1679
2233
  "aria-label": streaming ? "\u505C\u6B62\u751F\u6210" : "\u53D1\u9001",
1680
2234
  disabled: streaming ? false : disabled || !text.trim(),
1681
2235
  onClick: streaming ? onStop : send,
1682
- children: streaming ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react5.Square, { size: 14, fill: "currentColor" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react5.ArrowUp, { size: 16 })
2236
+ children: streaming ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_components7.Icon, { name: "square", size: 14, className: "pactor-chat-icon-fill" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_components7.Icon, { name: "arrow-up", size: 16 })
1683
2237
  }
1684
2238
  )
1685
2239
  ] }),
1686
- showToolbar && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "pactor-chat-composer__toolbar", children: [
2240
+ showToolbar && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "pactor-chat-composer__toolbar", children: [
1687
2241
  toolbarStart,
1688
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "pactor-chat-composer__toolbar-spacer" }),
1689
- (onPlusMenu || builtinPlusEnabled) && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2242
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "pactor-chat-composer__toolbar-spacer" }),
2243
+ (onPlusMenu || builtinPlusEnabled) && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1690
2244
  "button",
1691
2245
  {
1692
2246
  ref: plusButtonRef,
@@ -1702,10 +2256,10 @@ function Composer({
1702
2256
  setPlusAnchor(rect);
1703
2257
  }
1704
2258
  },
1705
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react5.Plus, { size: 14 })
2259
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_components7.Icon, { name: "plus", size: 14 })
1706
2260
  }
1707
2261
  ),
1708
- onAttachFiles && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2262
+ onAttachFiles && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1709
2263
  "button",
1710
2264
  {
1711
2265
  type: "button",
@@ -1713,11 +2267,11 @@ function Composer({
1713
2267
  title: "\u6DFB\u52A0\u9644\u4EF6",
1714
2268
  "aria-label": "\u6DFB\u52A0\u9644\u4EF6",
1715
2269
  onClick: () => fileInputRef.current?.click(),
1716
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react5.Paperclip, { size: 14 })
2270
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_components7.Icon, { name: "paperclip", size: 14 })
1717
2271
  }
1718
2272
  ),
1719
- modelChipEnabled && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1720
- ModelChip,
2273
+ modelChipEnabled && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2274
+ ModelSelect,
1721
2275
  {
1722
2276
  models,
1723
2277
  model,
@@ -1726,104 +2280,206 @@ function Composer({
1726
2280
  ),
1727
2281
  toolbarEnd
1728
2282
  ] }),
1729
- onAttachFiles && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1730
- "input",
1731
- {
1732
- ref: fileInputRef,
1733
- type: "file",
1734
- multiple: true,
1735
- hidden: true,
1736
- onChange: (event) => {
1737
- const files = event.target.files ? Array.from(event.target.files) : [];
1738
- if (files.length > 0) {
1739
- onAttachFiles(files);
1740
- }
1741
- event.target.value = "";
1742
- }
1743
- }
1744
- ),
1745
- builtinMentionEnabled && mentionState && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1746
- AnchoredPopover,
1747
- {
1748
- anchorRect: mentionState.anchorRect,
1749
- onClose: () => setMentionState(null),
1750
- role: "listbox",
1751
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1752
- ContextSourceList,
1753
- {
1754
- sources: mentionSources,
1755
- query: mentionState.query,
1756
- callService,
1757
- cache: sourceCacheRef.current,
1758
- onSelect: handleSelectContext
1759
- }
1760
- )
1761
- }
1762
- ),
1763
- builtinPlusEnabled && plusAnchor && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1764
- AnchoredPopover,
1765
- {
1766
- anchorRect: plusAnchor,
1767
- onClose: () => setPlusAnchor(null),
1768
- role: "listbox",
1769
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1770
- ContextSourceList,
1771
- {
1772
- sources: plusSources,
1773
- query: "",
1774
- callService,
1775
- cache: sourceCacheRef.current,
1776
- onSelect: handleSelectContext
1777
- }
1778
- )
1779
- }
1780
- )
2283
+ composerFooter !== void 0 && composerFooter !== null ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "pactor-chat-composer__footer", children: composerFooter }) : null,
2284
+ fileInputNode,
2285
+ popoverNodes
1781
2286
  ]
1782
2287
  }
1783
2288
  );
2289
+ const bridge = {
2290
+ text,
2291
+ streaming,
2292
+ disabled,
2293
+ attachments,
2294
+ contextChips,
2295
+ toolbarStart,
2296
+ toolbarEnd,
2297
+ composerFooter,
2298
+ models,
2299
+ model,
2300
+ showPlus: onPlusMenu !== void 0 || builtinPlusEnabled,
2301
+ showAttach: onAttachFiles !== void 0,
2302
+ modelChipEnabled,
2303
+ send,
2304
+ stop: onStop,
2305
+ openFilePicker: () => fileInputRef.current?.click(),
2306
+ plusClick: (rect) => {
2307
+ if (onPlusMenu) {
2308
+ onPlusMenu(rect);
2309
+ } else {
2310
+ setPlusAnchor(rect);
2311
+ }
2312
+ },
2313
+ removeAttachment: (id) => onAttachmentsChange?.((attachments ?? []).filter((a) => a.id !== id)),
2314
+ attachmentClick: onAttachmentClick,
2315
+ removeChip: (chip) => onContextChipsChange?.(
2316
+ (contextChips ?? []).filter(
2317
+ (existing) => !(existing.id === chip.id && existing.trigger === chip.trigger)
2318
+ )
2319
+ ),
2320
+ modelChange: onModelChange
2321
+ };
2322
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2323
+ ChromeSlot,
2324
+ {
2325
+ fragment: buildComposerFragment({
2326
+ placeholder,
2327
+ disabled,
2328
+ hasFooter: composerFooter !== void 0 && composerFooter !== null,
2329
+ hideToolbar
2330
+ }),
2331
+ components: COMPOSER_INTERNAL_COMPONENTS,
2332
+ fallback,
2333
+ wrap: (children) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ComposerBridgeContext.Provider, { value: bridge, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2334
+ "div",
2335
+ {
2336
+ className: `pactor-chat-composer pactor-chat-composer--dsl${dragOver ? " pactor-chat-composer--dragover" : ""}`,
2337
+ ...dragHandlers,
2338
+ onChange: handleBubbleChange,
2339
+ onPaste: (event) => {
2340
+ if (handlePasteFiles(event.clipboardData)) {
2341
+ event.preventDefault();
2342
+ }
2343
+ },
2344
+ children: [
2345
+ children,
2346
+ fileInputNode,
2347
+ popoverNodes
2348
+ ]
2349
+ }
2350
+ ) })
2351
+ }
2352
+ );
1784
2353
  }
1785
2354
 
1786
- // src/view/EmptyState.tsx
1787
- var import_jsx_runtime8 = require("react/jsx-runtime");
1788
- var DEFAULT_CHAT_GREETING = "\u4F60\u597D\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u4F60\uFF1F";
2355
+ // src/view/chrome/empty-state.tsx
1789
2356
  function suggestionLabel(suggestion) {
1790
2357
  return typeof suggestion === "string" ? suggestion : suggestion.label;
1791
2358
  }
1792
2359
  function suggestionPrompt(suggestion) {
1793
2360
  return typeof suggestion === "string" ? suggestion : suggestion.prompt ?? suggestion.label;
1794
2361
  }
1795
- function EmptyState({
1796
- greeting = DEFAULT_CHAT_GREETING,
2362
+ function buildEmptyStateFragment(props) {
2363
+ const children = [
2364
+ {
2365
+ type: "Typography",
2366
+ props: {
2367
+ variant: "h3",
2368
+ text: props.greeting,
2369
+ className: "pactor-chat-empty__greeting"
2370
+ }
2371
+ }
2372
+ ];
2373
+ if (props.subtitle) {
2374
+ children.push({
2375
+ type: "Text",
2376
+ props: {
2377
+ type: "secondary",
2378
+ text: props.subtitle,
2379
+ className: "pactor-chat-empty__subtitle"
2380
+ }
2381
+ });
2382
+ }
2383
+ if (props.suggestions && props.suggestions.length > 0) {
2384
+ children.push({
2385
+ type: "Flex",
2386
+ props: {
2387
+ gap: "small",
2388
+ justify: "center",
2389
+ className: "pactor-chat-empty__suggestions"
2390
+ },
2391
+ children: props.suggestions.map((suggestion) => ({
2392
+ type: "Button",
2393
+ props: {
2394
+ text: suggestionLabel(suggestion),
2395
+ variant: "default",
2396
+ size: "middle",
2397
+ className: "pactor-chat-empty__suggestion"
2398
+ },
2399
+ events: {
2400
+ onClick: [
2401
+ {
2402
+ action: "chat.sendSuggestion",
2403
+ params: { text: suggestionPrompt(suggestion) }
2404
+ }
2405
+ ]
2406
+ }
2407
+ }))
2408
+ });
2409
+ }
2410
+ return {
2411
+ requires: ["Flex", "Typography", "Text", "Button"],
2412
+ node: {
2413
+ type: "Flex",
2414
+ props: { direction: "vertical", align: "center", gap: "middle" },
2415
+ children
2416
+ }
2417
+ };
2418
+ }
2419
+
2420
+ // src/view/EmptyState.tsx
2421
+ var import_jsx_runtime11 = require("react/jsx-runtime");
2422
+ var DEFAULT_CHAT_GREETING = "\u4F60\u597D\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u4F60\uFF1F";
2423
+ function suggestionLabel2(suggestion) {
2424
+ return typeof suggestion === "string" ? suggestion : suggestion.label;
2425
+ }
2426
+ function suggestionPrompt2(suggestion) {
2427
+ return typeof suggestion === "string" ? suggestion : suggestion.prompt ?? suggestion.label;
2428
+ }
2429
+ function EmptyStateFallback({
1797
2430
  subtitle,
1798
2431
  suggestions,
1799
- onSuggestionClick
2432
+ onSuggestionClick,
2433
+ greeting
1800
2434
  }) {
1801
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "pactor-chat-empty", children: [
1802
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "pactor-chat-empty__greeting", children: greeting }),
1803
- subtitle && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "pactor-chat-empty__subtitle", children: subtitle }),
1804
- suggestions && suggestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "pactor-chat-empty__suggestions", children: suggestions.map((suggestion) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2435
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
2436
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "pactor-chat-empty__greeting", children: greeting }),
2437
+ subtitle && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "pactor-chat-empty__subtitle", children: subtitle }),
2438
+ suggestions && suggestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "pactor-chat-empty__suggestions", children: suggestions.map((suggestion) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1805
2439
  "button",
1806
2440
  {
1807
2441
  type: "button",
1808
2442
  className: "pactor-chat-empty__suggestion",
1809
- onClick: () => onSuggestionClick?.(suggestionPrompt(suggestion)),
1810
- children: suggestionLabel(suggestion)
2443
+ onClick: () => onSuggestionClick?.(suggestionPrompt2(suggestion)),
2444
+ children: suggestionLabel2(suggestion)
1811
2445
  },
1812
- suggestionLabel(suggestion)
2446
+ suggestionLabel2(suggestion)
1813
2447
  )) })
1814
2448
  ] });
1815
2449
  }
2450
+ function EmptyState({
2451
+ greeting = DEFAULT_CHAT_GREETING,
2452
+ subtitle,
2453
+ suggestions,
2454
+ onSuggestionClick
2455
+ }) {
2456
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "pactor-chat-empty", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2457
+ ChromeSlot,
2458
+ {
2459
+ fragment: buildEmptyStateFragment({ greeting, subtitle, suggestions }),
2460
+ fallback: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2461
+ EmptyStateFallback,
2462
+ {
2463
+ greeting,
2464
+ subtitle,
2465
+ suggestions,
2466
+ onSuggestionClick
2467
+ }
2468
+ )
2469
+ }
2470
+ ) });
2471
+ }
1816
2472
 
1817
2473
  // src/view/MessageBubble.tsx
1818
- var import_react12 = require("react");
2474
+ var import_react13 = require("react");
1819
2475
 
1820
2476
  // src/view/ActivityTrace.tsx
1821
- var import_lucide_react6 = require("lucide-react");
2477
+ var import_components8 = require("@pactor-app/ui/components");
1822
2478
  var import_markdown3 = require("@pactor-app/markdown");
1823
- var import_react10 = require("react");
1824
- var import_jsx_runtime9 = require("react/jsx-runtime");
2479
+ var import_react11 = require("react");
2480
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1825
2481
  function usePinnedCollapse(autoOpen) {
1826
- const [state, setState] = (0, import_react10.useState)("auto");
2482
+ const [state, setState] = (0, import_react11.useState)("auto");
1827
2483
  const open = state === "auto" ? autoOpen : state === "pinned-open";
1828
2484
  const toggle = () => {
1829
2485
  setState(open ? "pinned-closed" : "pinned-open");
@@ -1849,8 +2505,8 @@ function statusText(message, mode, toolCount) {
1849
2505
  }
1850
2506
  function useElapsedSeconds(startedAt, active) {
1851
2507
  const compute = () => Math.max(0, Math.floor((Date.now() - startedAt) / 1e3));
1852
- const [elapsed, setElapsed] = (0, import_react10.useState)(compute);
1853
- (0, import_react10.useEffect)(() => {
2508
+ const [elapsed, setElapsed] = (0, import_react11.useState)(compute);
2509
+ (0, import_react11.useEffect)(() => {
1854
2510
  if (!active) {
1855
2511
  setElapsed(compute());
1856
2512
  return;
@@ -1864,15 +2520,15 @@ function useElapsedSeconds(startedAt, active) {
1864
2520
  }
1865
2521
  function TraceStatusIcon({ status }) {
1866
2522
  if (status === "running") {
1867
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react6.Loader2, { size: 13, className: "pactor-chat-spin" });
2523
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "loading", size: 13, className: "pactor-chat-spin" });
1868
2524
  }
1869
2525
  if (status === "success") {
1870
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react6.CheckCircle2, { size: 13 });
2526
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "check-circle", size: 13 });
1871
2527
  }
1872
2528
  if (status === "failed") {
1873
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react6.XCircle, { size: 13 });
2529
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "x-circle", size: 13 });
1874
2530
  }
1875
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react6.Clock, { size: 13 });
2531
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "clock", size: 13 });
1876
2532
  }
1877
2533
  function summarizeArgs(args) {
1878
2534
  if (args === void 0) {
@@ -1901,16 +2557,16 @@ function formatDetail(value) {
1901
2557
  }
1902
2558
  }
1903
2559
  function TraceRow({ entry }) {
1904
- const [open, setOpen] = (0, import_react10.useState)(false);
2560
+ const [open, setOpen] = (0, import_react11.useState)(false);
1905
2561
  const summary = summarizeArgs(entry.args);
1906
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2562
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1907
2563
  "div",
1908
2564
  {
1909
2565
  className: "pactor-chat-trace__entry",
1910
2566
  "data-status": entry.status,
1911
2567
  "data-open": open,
1912
2568
  children: [
1913
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2569
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1914
2570
  "button",
1915
2571
  {
1916
2572
  type: "button",
@@ -1918,16 +2574,16 @@ function TraceRow({ entry }) {
1918
2574
  "aria-expanded": open,
1919
2575
  onClick: () => setOpen((value) => !value),
1920
2576
  children: [
1921
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "pactor-chat-trace__chevron", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react6.ChevronRight, { size: 12 }) }),
1922
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "pactor-chat-trace__entry-icon", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(TraceStatusIcon, { status: entry.status }) }),
1923
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "pactor-chat-trace__entry-title", children: entry.title ?? entry.name }),
1924
- summary && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "pactor-chat-trace__entry-args", children: summary })
2577
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "pactor-chat-trace__chevron", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "chevron-right", size: 12 }) }),
2578
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "pactor-chat-trace__entry-icon", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TraceStatusIcon, { status: entry.status }) }),
2579
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "pactor-chat-trace__entry-title", children: entry.title ?? entry.name }),
2580
+ summary && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "pactor-chat-trace__entry-args", children: summary })
1925
2581
  ]
1926
2582
  }
1927
2583
  ),
1928
- open && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "pactor-chat-trace__entry-body", children: [
1929
- entry.args !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("pre", { className: "pactor-chat-trace__entry-detail", children: formatDetail(entry.args) }),
1930
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("pre", { className: "pactor-chat-trace__entry-detail", children: entry.status === "failed" ? entry.error ?? "\u6267\u884C\u5931\u8D25" : formatDetail(entry.result) })
2584
+ open && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "pactor-chat-trace__entry-body", children: [
2585
+ entry.args !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("pre", { className: "pactor-chat-trace__entry-detail", children: formatDetail(entry.args) }),
2586
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("pre", { className: "pactor-chat-trace__entry-detail", children: entry.status === "failed" ? entry.error ?? "\u6267\u884C\u5931\u8D25" : formatDetail(entry.result) })
1931
2587
  ] })
1932
2588
  ]
1933
2589
  }
@@ -1939,14 +2595,14 @@ function ThinkingCard({
1939
2595
  }) {
1940
2596
  const partStreaming = part.status === "streaming";
1941
2597
  const { open, toggle } = usePinnedCollapse(partStreaming && !answerPhase);
1942
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2598
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1943
2599
  "div",
1944
2600
  {
1945
2601
  className: "pactor-chat-thinking",
1946
2602
  "data-open": open,
1947
2603
  "data-streaming": partStreaming,
1948
2604
  children: [
1949
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2605
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1950
2606
  "button",
1951
2607
  {
1952
2608
  type: "button",
@@ -1954,13 +2610,13 @@ function ThinkingCard({
1954
2610
  "aria-expanded": open,
1955
2611
  onClick: toggle,
1956
2612
  children: [
1957
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "pactor-chat-trace__chevron", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react6.ChevronRight, { size: 12 }) }),
1958
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "pactor-chat-thinking__icon", children: partStreaming ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react6.Loader2, { size: 13, className: "pactor-chat-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react6.Brain, { size: 13 }) }),
1959
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "\u601D\u8003\u8FC7\u7A0B" })
2613
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "pactor-chat-trace__chevron", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "chevron-right", size: 12 }) }),
2614
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "pactor-chat-thinking__icon", children: partStreaming ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "loading", size: 13, className: "pactor-chat-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "brain", size: 13 }) }),
2615
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: "\u601D\u8003\u8FC7\u7A0B" })
1960
2616
  ]
1961
2617
  }
1962
2618
  ),
1963
- open && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "pactor-chat-thinking__body", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_markdown3.MarkdownRenderer, { content: part.content }) })
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 }) })
1964
2620
  ]
1965
2621
  }
1966
2622
  );
@@ -1974,7 +2630,7 @@ function ActivityTrace({ message }) {
1974
2630
  );
1975
2631
  const answerPhase = mode === "responding" || !streaming;
1976
2632
  const { open, toggle } = usePinnedCollapse(!answerPhase);
1977
- const mountTimeRef = (0, import_react10.useRef)(0);
2633
+ const mountTimeRef = (0, import_react11.useRef)(0);
1978
2634
  if (mountTimeRef.current === 0) {
1979
2635
  mountTimeRef.current = Date.now();
1980
2636
  }
@@ -1982,23 +2638,23 @@ function ActivityTrace({ message }) {
1982
2638
  message.createdAt ?? mountTimeRef.current,
1983
2639
  streaming
1984
2640
  );
1985
- const header = /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
1986
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "pactor-chat-trace__status-icon", children: streaming ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react6.Loader2, { size: 13, className: "pactor-chat-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react6.CheckCircle2, { size: 13 }) }),
1987
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2641
+ const header = /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
2642
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "pactor-chat-trace__status-icon", children: streaming ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "loading", size: 13, className: "pactor-chat-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "check-circle", size: 13 }) }),
2643
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1988
2644
  "span",
1989
2645
  {
1990
2646
  className: `pactor-chat-trace__status-text${streaming ? " pactor-chat-trace__status-text--breathing" : ""}`,
1991
2647
  children: statusText(message, mode, entries.length)
1992
2648
  }
1993
2649
  ),
1994
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "pactor-chat-trace__elapsed", children: [
2650
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "pactor-chat-trace__elapsed", children: [
1995
2651
  elapsed,
1996
2652
  "s"
1997
2653
  ] }),
1998
- entries.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "pactor-chat-trace__chevron", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react6.ChevronRight, { size: 12 }) })
2654
+ entries.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "pactor-chat-trace__chevron", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "chevron-right", size: 12 }) })
1999
2655
  ] });
2000
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "pactor-chat-trace", "data-open": open, children: [
2001
- entries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2656
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "pactor-chat-trace", "data-open": open, children: [
2657
+ entries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2002
2658
  "button",
2003
2659
  {
2004
2660
  type: "button",
@@ -2007,8 +2663,8 @@ function ActivityTrace({ message }) {
2007
2663
  onClick: toggle,
2008
2664
  children: header
2009
2665
  }
2010
- ) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "pactor-chat-trace__header", children: header }),
2011
- thinkingParts.map((part, index) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2666
+ ) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "pactor-chat-trace__header", children: header }),
2667
+ thinkingParts.map((part, index) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2012
2668
  ThinkingCard,
2013
2669
  {
2014
2670
  part,
@@ -2016,25 +2672,66 @@ function ActivityTrace({ message }) {
2016
2672
  },
2017
2673
  part.id ?? index
2018
2674
  )),
2019
- open && entries.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "pactor-chat-trace__entries", children: entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(TraceRow, { entry }, entry.callIndex)) })
2675
+ open && entries.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "pactor-chat-trace__entries", children: entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TraceRow, { entry }, entry.callIndex)) })
2020
2676
  ] });
2021
2677
  }
2022
2678
 
2023
2679
  // src/view/MessageActions.tsx
2024
- var import_lucide_react7 = require("lucide-react");
2025
- var import_react11 = require("react");
2026
- var import_jsx_runtime10 = require("react/jsx-runtime");
2027
- var MESSAGE_ACTION_ICONS = {
2028
- copy: import_lucide_react7.Copy,
2029
- check: import_lucide_react7.Check,
2030
- "rotate-ccw": import_lucide_react7.RotateCcw,
2031
- pencil: import_lucide_react7.Pencil,
2032
- quote: import_lucide_react7.Quote,
2033
- "thumbs-up": import_lucide_react7.ThumbsUp,
2034
- "thumbs-down": import_lucide_react7.ThumbsDown,
2035
- share: import_lucide_react7.Share2,
2036
- bookmark: import_lucide_react7.Bookmark
2037
- };
2680
+ var import_components9 = require("@pactor-app/ui/components");
2681
+ var import_react12 = require("react");
2682
+
2683
+ // src/view/chrome/message-actions.tsx
2684
+ function buildMessageActionsFragment(args) {
2685
+ const { actions, copied, token } = args;
2686
+ return {
2687
+ requires: ["Flex", "Tooltip", "Button", "Icon"],
2688
+ node: {
2689
+ type: "Flex",
2690
+ props: { gap: "small" },
2691
+ children: actions.map((action) => {
2692
+ const copiedFeedback = action.builtin === "copy" && copied;
2693
+ const label = copiedFeedback ? "\u5DF2\u590D\u5236" : action.label;
2694
+ const iconName = copiedFeedback ? "check" : action.icon;
2695
+ return {
2696
+ type: "Tooltip",
2697
+ props: { content: label },
2698
+ children: [
2699
+ {
2700
+ type: "Button",
2701
+ props: {
2702
+ variant: "text",
2703
+ size: "small",
2704
+ className: "pactor-chat-icon-button"
2705
+ },
2706
+ events: {
2707
+ onClick: [
2708
+ {
2709
+ action: "chat.messageAction",
2710
+ params: { token, actionId: action.id }
2711
+ }
2712
+ ]
2713
+ },
2714
+ children: [
2715
+ {
2716
+ type: "Icon",
2717
+ props: {
2718
+ // 契约图标名直通 ui Icon 白名单(spec: chat-icon-convergence;
2719
+ // 未知名由 ui Icon 渲染占位)
2720
+ name: iconName ?? "help-circle",
2721
+ size: 14
2722
+ }
2723
+ }
2724
+ ]
2725
+ }
2726
+ ]
2727
+ };
2728
+ })
2729
+ }
2730
+ };
2731
+ }
2732
+
2733
+ // src/view/MessageActions.tsx
2734
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2038
2735
  var BUILTIN_ACTION_DEFAULTS = {
2039
2736
  copy: {
2040
2737
  label: "\u590D\u5236",
@@ -2107,7 +2804,21 @@ function MessageActions({
2107
2804
  callService,
2108
2805
  onMessageAction
2109
2806
  }) {
2110
- const [copied, setCopied] = (0, import_react11.useState)(false);
2807
+ const [copied, setCopied] = (0, import_react12.useState)(false);
2808
+ const dispatchRef = (0, import_react12.useRef)(
2809
+ () => {
2810
+ }
2811
+ );
2812
+ const visibleActionsRef = (0, import_react12.useRef)([]);
2813
+ (0, import_react12.useEffect)(() => {
2814
+ setMessageActionDispatcher(message.id, (actionId) => {
2815
+ const action = visibleActionsRef.current.find((a) => a.id === actionId);
2816
+ if (action) {
2817
+ dispatchRef.current(action);
2818
+ }
2819
+ });
2820
+ return () => clearMessageActionDispatcher(message.id);
2821
+ }, [message.id]);
2111
2822
  const copy = async () => {
2112
2823
  try {
2113
2824
  await navigator.clipboard.writeText(collectMessageText(message));
@@ -2167,41 +2878,53 @@ function MessageActions({
2167
2878
  }
2168
2879
  onMessageAction?.(action.id, message);
2169
2880
  };
2170
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2881
+ dispatchRef.current = dispatch;
2882
+ visibleActionsRef.current = visibleActions;
2883
+ const buttons = visibleActions.map((action) => {
2884
+ const copiedFeedback = action.builtin === "copy" && copied;
2885
+ const label = copiedFeedback ? "\u5DF2\u590D\u5236" : action.label;
2886
+ const iconName = copiedFeedback ? "check" : action.icon;
2887
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2888
+ "button",
2889
+ {
2890
+ type: "button",
2891
+ className: "pactor-chat-icon-button",
2892
+ title: label,
2893
+ "aria-label": label,
2894
+ "data-message-action": action.id,
2895
+ onClick: () => dispatch(action),
2896
+ children: iconName !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_components9.Icon, { name: iconName, size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_components9.Icon, { name: "help-circle", size: 14 })
2897
+ },
2898
+ action.id
2899
+ );
2900
+ });
2901
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2171
2902
  "div",
2172
2903
  {
2173
2904
  className: `pactor-chat-message__actions${forceVisible ? " pactor-chat-message__actions--visible" : ""}`,
2174
- children: visibleActions.map((action) => {
2175
- const copiedFeedback = action.builtin === "copy" && copied;
2176
- const label = copiedFeedback ? "\u5DF2\u590D\u5236" : action.label;
2177
- const iconName = copiedFeedback ? "check" : action.icon;
2178
- const Icon = iconName !== void 0 ? MESSAGE_ACTION_ICONS[iconName] : void 0;
2179
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2180
- "button",
2181
- {
2182
- type: "button",
2183
- className: "pactor-chat-icon-button",
2184
- title: label,
2185
- "aria-label": label,
2186
- "data-message-action": action.id,
2187
- onClick: () => dispatch(action),
2188
- children: Icon ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Icon, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react7.HelpCircle, { size: 14, "data-icon-placeholder": "true" })
2189
- },
2190
- action.id
2191
- );
2192
- })
2905
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2906
+ ChromeSlot,
2907
+ {
2908
+ fragment: buildMessageActionsFragment({
2909
+ actions: visibleActions,
2910
+ copied,
2911
+ token: message.id
2912
+ }),
2913
+ fallback: buttons
2914
+ }
2915
+ )
2193
2916
  }
2194
2917
  );
2195
2918
  }
2196
2919
 
2197
2920
  // src/view/MessageBubble.tsx
2198
- var import_jsx_runtime11 = require("react/jsx-runtime");
2921
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2199
2922
  function MessageEditForm({
2200
2923
  initialText,
2201
2924
  onConfirm,
2202
2925
  onCancel
2203
2926
  }) {
2204
- const [text, setText] = (0, import_react12.useState)(initialText);
2927
+ const [text, setText] = (0, import_react13.useState)(initialText);
2205
2928
  const confirm = () => {
2206
2929
  const value = text.trim();
2207
2930
  if (!value) {
@@ -2209,8 +2932,8 @@ function MessageEditForm({
2209
2932
  }
2210
2933
  onConfirm(value);
2211
2934
  };
2212
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "pactor-chat-message__edit", children: [
2213
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2935
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "pactor-chat-message__edit", children: [
2936
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2214
2937
  "textarea",
2215
2938
  {
2216
2939
  className: "pactor-chat-message__edit-input",
@@ -2228,8 +2951,8 @@ function MessageEditForm({
2228
2951
  }
2229
2952
  }
2230
2953
  ),
2231
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "pactor-chat-message__edit-actions", children: [
2232
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2954
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "pactor-chat-message__edit-actions", children: [
2955
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2233
2956
  "button",
2234
2957
  {
2235
2958
  type: "button",
@@ -2239,7 +2962,7 @@ function MessageEditForm({
2239
2962
  children: "\u53D1\u9001"
2240
2963
  }
2241
2964
  ),
2242
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2965
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2243
2966
  "button",
2244
2967
  {
2245
2968
  type: "button",
@@ -2265,7 +2988,7 @@ function MessageBubble({
2265
2988
  onMessageAction,
2266
2989
  onOpenArtifact
2267
2990
  }) {
2268
- const [editing, setEditing] = (0, import_react12.useState)(false);
2991
+ const [editing, setEditing] = (0, import_react13.useState)(false);
2269
2992
  const bubbleScope = {
2270
2993
  conversation: scope?.conversation ?? {},
2271
2994
  message: { id: message.id },
@@ -2273,21 +2996,21 @@ function MessageBubble({
2273
2996
  };
2274
2997
  const streaming = message.status === "streaming";
2275
2998
  const live = message.status === "pending" || message.status === "streaming";
2276
- const wasLiveRef = (0, import_react12.useRef)(live);
2999
+ const wasLiveRef = (0, import_react13.useRef)(live);
2277
3000
  if (live) {
2278
3001
  wasLiveRef.current = true;
2279
3002
  }
2280
3003
  const showTrace = message.role === "assistant" && (wasLiveRef.current || message.parts.some(
2281
3004
  (part) => part.type === "thinking" || part.type === "tool-call" || part.type === "tool-result"
2282
3005
  ));
2283
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
3006
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2284
3007
  "div",
2285
3008
  {
2286
3009
  className: `pactor-chat-message pactor-chat-message--${message.role}`,
2287
3010
  "data-message-id": message.id,
2288
3011
  "data-message-status": message.status,
2289
3012
  children: [
2290
- editing ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3013
+ editing ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2291
3014
  MessageEditForm,
2292
3015
  {
2293
3016
  initialText: collectMessageText(message),
@@ -2297,9 +3020,9 @@ function MessageBubble({
2297
3020
  },
2298
3021
  onCancel: () => setEditing(false)
2299
3022
  }
2300
- ) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "pactor-chat-message__body", children: [
2301
- showTrace && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ActivityTrace, { message }),
2302
- message.parts.length === 0 && message.status === "pending" && message.role !== "assistant" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "pactor-chat-message__pending", children: "\u2026" }) : message.parts.map((part, index) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3023
+ ) : /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "pactor-chat-message__body", children: [
3024
+ showTrace && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ActivityTrace, { message }),
3025
+ message.parts.length === 0 && message.status === "pending" && message.role !== "assistant" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "pactor-chat-message__pending", children: "\u2026" }) : message.parts.map((part, index) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2303
3026
  PartRenderer,
2304
3027
  {
2305
3028
  part,
@@ -2311,7 +3034,7 @@ function MessageBubble({
2311
3034
  part.id ?? index
2312
3035
  ))
2313
3036
  ] }),
2314
- !editing && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3037
+ !editing && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2315
3038
  MessageActions,
2316
3039
  {
2317
3040
  message,
@@ -2331,7 +3054,7 @@ function MessageBubble({
2331
3054
  }
2332
3055
 
2333
3056
  // src/view/ChatView.tsx
2334
- var import_jsx_runtime12 = require("react/jsx-runtime");
3057
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2335
3058
  function lastContentLength(messages) {
2336
3059
  const last = messages[messages.length - 1];
2337
3060
  if (!last) {
@@ -2365,6 +3088,8 @@ function ChatView({
2365
3088
  onAttachmentClick,
2366
3089
  toolbarStart,
2367
3090
  toolbarEnd,
3091
+ composerFooter,
3092
+ hideToolbar,
2368
3093
  onMentionTrigger,
2369
3094
  onPlusMenu,
2370
3095
  models,
@@ -2375,20 +3100,21 @@ function ChatView({
2375
3100
  onContextsChange,
2376
3101
  callService,
2377
3102
  messageActions,
2378
- onMessageAction
3103
+ onMessageAction,
3104
+ composerBottom
2379
3105
  }) {
2380
- const messages = (0, import_react13.useSyncExternalStore)(
3106
+ const messages = (0, import_react14.useSyncExternalStore)(
2381
3107
  (listener) => store.subscribe(listener),
2382
3108
  () => store.getMessages()
2383
3109
  );
2384
3110
  const isStreaming = messages.some(
2385
3111
  (message) => message.status === "pending" || message.status === "streaming"
2386
3112
  );
2387
- const runtime = (0, import_react13.useMemo)(() => {
3113
+ const runtime = (0, import_react14.useMemo)(() => {
2388
3114
  if (!safeRegistry) {
2389
3115
  return void 0;
2390
3116
  }
2391
- const components = new import_core3.Registry("ChatComponentRegistry");
3117
+ const components = new import_core4.Registry("ChatComponentRegistry");
2392
3118
  components.register(CHAT_DENIED_TYPE, ChatDenied);
2393
3119
  for (const name of safeRegistry.list()) {
2394
3120
  components.register(name, safeRegistry.get(name));
@@ -2401,12 +3127,12 @@ function ChatView({
2401
3127
  user
2402
3128
  });
2403
3129
  }, [safeRegistry, capabilities, permission, confirm, user]);
2404
- const scope = (0, import_react13.useMemo)(
3130
+ const scope = (0, import_react14.useMemo)(
2405
3131
  () => ({ conversation: { id: conversationId }, params }),
2406
3132
  [conversationId, params]
2407
3133
  );
2408
- const notifiedArtifactsRef = (0, import_react13.useRef)(/* @__PURE__ */ new Set());
2409
- (0, import_react13.useEffect)(() => {
3134
+ const notifiedArtifactsRef = (0, import_react14.useRef)(/* @__PURE__ */ new Set());
3135
+ (0, import_react14.useEffect)(() => {
2410
3136
  if (!onArtifact) {
2411
3137
  return;
2412
3138
  }
@@ -2425,7 +3151,7 @@ function ChatView({
2425
3151
  });
2426
3152
  }
2427
3153
  }, [messages, onArtifact]);
2428
- const abortRef = (0, import_react13.useRef)(null);
3154
+ const abortRef = (0, import_react14.useRef)(null);
2429
3155
  const { containerRef, isFollowing, scrollToBottom } = useChatAutoScroll({
2430
3156
  messageCount: messages.length,
2431
3157
  lastContentLength: lastContentLength(messages),
@@ -2436,7 +3162,7 @@ function ChatView({
2436
3162
  model,
2437
3163
  onModelChange
2438
3164
  });
2439
- const [contextChips, setContextChips] = (0, import_react13.useState)([]);
3165
+ const [contextChips, setContextChips] = (0, import_react14.useState)([]);
2440
3166
  const handleContextChipsChange = (chips) => {
2441
3167
  setContextChips(chips);
2442
3168
  onContextsChange?.(chips);
@@ -2468,6 +3194,16 @@ function ChatView({
2468
3194
  abortRef.current?.abort();
2469
3195
  abortRef.current = null;
2470
3196
  };
3197
+ const handleSendRef = (0, import_react14.useRef)(handleSend);
3198
+ handleSendRef.current = handleSend;
3199
+ const onOpenArtifactRef = (0, import_react14.useRef)(onOpenArtifact);
3200
+ onOpenArtifactRef.current = onOpenArtifact;
3201
+ (0, import_react14.useEffect)(() => {
3202
+ setChatChromeHandlers({
3203
+ sendSuggestion: (text) => handleSendRef.current(text),
3204
+ openArtifact: (artifact) => onOpenArtifactRef.current?.(artifact)
3205
+ });
3206
+ }, []);
2471
3207
  const handleRegenerate = () => {
2472
3208
  if (!transport) {
2473
3209
  return;
@@ -2485,7 +3221,7 @@ function ChatView({
2485
3221
  const lastMessage = messages[messages.length - 1];
2486
3222
  const regenerableMessageId = lastMessage?.role === "assistant" && lastMessage.status === "completed" ? lastMessage.id : void 0;
2487
3223
  const isEmpty = messages.length === 0;
2488
- const composerNode = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3224
+ const composerNode = /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2489
3225
  Composer,
2490
3226
  {
2491
3227
  onSend: handleSend,
@@ -2497,6 +3233,8 @@ function ChatView({
2497
3233
  onAttachmentClick,
2498
3234
  toolbarStart,
2499
3235
  toolbarEnd,
3236
+ composerFooter,
3237
+ hideToolbar,
2500
3238
  onMentionTrigger,
2501
3239
  onPlusMenu,
2502
3240
  models: modelPlacement === "header" ? void 0 : models,
@@ -2508,14 +3246,18 @@ function ChatView({
2508
3246
  callService
2509
3247
  }
2510
3248
  );
2511
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3249
+ const composerDock = /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "pactor-chat-view__composer-dock", children: [
3250
+ composerNode,
3251
+ composerBottom !== void 0 && composerBottom !== null ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "pactor-chat-view__composer-bottom", children: composerBottom }) : null
3252
+ ] });
3253
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
2512
3254
  "div",
2513
3255
  {
2514
3256
  className: `pactor-chat-view${isEmpty ? " pactor-chat-view--empty" : ""}${className ? ` ${className}` : ""}`,
2515
3257
  children: [
2516
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "pactor-chat-messages", ref: containerRef, children: [
2517
- isEmpty ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "pactor-chat-view__empty-stack", children: [
2518
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3258
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "pactor-chat-messages", ref: containerRef, children: [
3259
+ isEmpty ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "pactor-chat-view__empty-stack", children: [
3260
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2519
3261
  EmptyState,
2520
3262
  {
2521
3263
  greeting,
@@ -2524,8 +3266,8 @@ function ChatView({
2524
3266
  onSuggestionClick: handleSend
2525
3267
  }
2526
3268
  ),
2527
- composerNode
2528
- ] }) : messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3269
+ composerDock
3270
+ ] }) : messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2529
3271
  MessageBubble,
2530
3272
  {
2531
3273
  message,
@@ -2542,28 +3284,28 @@ function ChatView({
2542
3284
  },
2543
3285
  message.id
2544
3286
  )),
2545
- !isFollowing && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3287
+ !isFollowing && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
2546
3288
  "button",
2547
3289
  {
2548
3290
  type: "button",
2549
3291
  className: "pactor-chat-scroll-bottom",
2550
3292
  onClick: scrollToBottom,
2551
3293
  children: [
2552
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react8.ArrowDown, { size: 13 }),
3294
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_components10.Icon, { name: "arrow-down", size: 13 }),
2553
3295
  "\u56DE\u5230\u5E95\u90E8"
2554
3296
  ]
2555
3297
  }
2556
3298
  )
2557
3299
  ] }),
2558
- !isEmpty && composerNode
3300
+ !isEmpty && composerDock
2559
3301
  ]
2560
3302
  }
2561
3303
  );
2562
3304
  }
2563
3305
 
2564
3306
  // src/view/WorkspaceHeader.tsx
2565
- var import_lucide_react9 = require("lucide-react");
2566
- var import_react14 = require("react");
3307
+ var import_components11 = require("@pactor-app/ui/components");
3308
+ var import_react15 = require("react");
2567
3309
 
2568
3310
  // src/export-markdown.ts
2569
3311
  var ROLE_LABELS = {
@@ -2595,8 +3337,52 @@ function downloadMessagesAsMarkdown(title, messages) {
2595
3337
  URL.revokeObjectURL(url);
2596
3338
  }
2597
3339
 
3340
+ // src/view/chrome/header-actions.tsx
3341
+ function buildHeaderActionsFragment(props) {
3342
+ const meta = {
3343
+ export: { label: "\u5BFC\u51FA Markdown", icon: "download" },
3344
+ theme: {
3345
+ label: props.theme === "dark" ? "\u5207\u6362\u4E3A\u6D45\u8272" : "\u5207\u6362\u4E3A\u6DF1\u8272",
3346
+ icon: props.theme === "dark" ? "sun" : "moon"
3347
+ },
3348
+ clear: { label: "\u6E05\u7A7A\u4F1A\u8BDD", icon: "trash2" }
3349
+ };
3350
+ return {
3351
+ requires: ["Flex", "Tooltip", "Button", "Icon"],
3352
+ node: {
3353
+ type: "Flex",
3354
+ props: { gap: "small", align: "center" },
3355
+ children: props.actions.map((action) => ({
3356
+ type: "Tooltip",
3357
+ props: { content: meta[action].label },
3358
+ children: [
3359
+ {
3360
+ type: "Button",
3361
+ props: {
3362
+ variant: "text",
3363
+ size: "small",
3364
+ className: "pactor-chat-icon-button"
3365
+ },
3366
+ events: {
3367
+ onClick: [
3368
+ { action: "chat.headerAction", params: { action } }
3369
+ ]
3370
+ },
3371
+ children: [
3372
+ {
3373
+ type: "Icon",
3374
+ props: { name: meta[action].icon, size: 15 }
3375
+ }
3376
+ ]
3377
+ }
3378
+ ]
3379
+ }))
3380
+ }
3381
+ };
3382
+ }
3383
+
2598
3384
  // src/view/WorkspaceHeader.tsx
2599
- var import_jsx_runtime13 = require("react/jsx-runtime");
3385
+ var import_jsx_runtime16 = require("react/jsx-runtime");
2600
3386
  function readCurrentTheme() {
2601
3387
  if (typeof document === "undefined") {
2602
3388
  return "light";
@@ -2616,10 +3402,10 @@ function WorkspaceHeader({
2616
3402
  onTitleChange
2617
3403
  }) {
2618
3404
  const hasActions = headerActions !== void 0 && headerActions.length > 0;
2619
- const [overrideTitle, setOverrideTitle] = (0, import_react14.useState)(null);
2620
- const [editing, setEditing] = (0, import_react14.useState)(false);
2621
- const [draft, setDraft] = (0, import_react14.useState)("");
2622
- const prevTitleRef = (0, import_react14.useRef)(title);
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);
2623
3409
  if (prevTitleRef.current !== title) {
2624
3410
  prevTitleRef.current = title;
2625
3411
  if (overrideTitle !== null) {
@@ -2627,7 +3413,7 @@ function WorkspaceHeader({
2627
3413
  }
2628
3414
  }
2629
3415
  const displayTitle = overrideTitle ?? title;
2630
- const [theme, setTheme] = (0, import_react14.useState)(readCurrentTheme);
3416
+ const [theme, setTheme] = (0, import_react15.useState)(readCurrentTheme);
2631
3417
  const commitTitle = () => {
2632
3418
  setEditing(false);
2633
3419
  const next = draft.trim();
@@ -2654,8 +3440,24 @@ function WorkspaceHeader({
2654
3440
  store.clear();
2655
3441
  onClear?.();
2656
3442
  };
3443
+ const handlersRef = (0, import_react15.useRef)({ handleExport, handleTheme, handleClear });
3444
+ handlersRef.current = { handleExport, handleTheme, handleClear };
3445
+ (0, import_react15.useEffect)(() => {
3446
+ setChatChromeHandlers({
3447
+ headerAction: (name) => {
3448
+ const handlers2 = handlersRef.current;
3449
+ if (name === "export") {
3450
+ handlers2.handleExport();
3451
+ } else if (name === "theme") {
3452
+ handlers2.handleTheme();
3453
+ } else if (name === "clear") {
3454
+ handlers2.handleClear();
3455
+ }
3456
+ }
3457
+ });
3458
+ }, []);
2657
3459
  const actionButtons = {
2658
- export: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3460
+ export: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2659
3461
  "button",
2660
3462
  {
2661
3463
  type: "button",
@@ -2663,11 +3465,11 @@ function WorkspaceHeader({
2663
3465
  title: "\u5BFC\u51FA Markdown",
2664
3466
  "aria-label": "\u5BFC\u51FA Markdown",
2665
3467
  onClick: handleExport,
2666
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.Download, { size: 15 })
3468
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_components11.Icon, { name: "download", size: 15 })
2667
3469
  },
2668
3470
  "export"
2669
3471
  ),
2670
- theme: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3472
+ theme: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2671
3473
  "button",
2672
3474
  {
2673
3475
  type: "button",
@@ -2675,11 +3477,11 @@ function WorkspaceHeader({
2675
3477
  title: theme === "dark" ? "\u5207\u6362\u4E3A\u6D45\u8272" : "\u5207\u6362\u4E3A\u6DF1\u8272",
2676
3478
  "aria-label": "\u5207\u6362\u660E\u6697\u4E3B\u9898",
2677
3479
  onClick: handleTheme,
2678
- children: theme === "dark" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.Sun, { size: 15 }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.Moon, { size: 15 })
3480
+ children: theme === "dark" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_components11.Icon, { name: "sun", size: 15 }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_components11.Icon, { name: "moon", size: 15 })
2679
3481
  },
2680
3482
  "theme"
2681
3483
  ),
2682
- clear: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3484
+ clear: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2683
3485
  "button",
2684
3486
  {
2685
3487
  type: "button",
@@ -2687,12 +3489,12 @@ function WorkspaceHeader({
2687
3489
  title: "\u6E05\u7A7A\u4F1A\u8BDD",
2688
3490
  "aria-label": "\u6E05\u7A7A\u4F1A\u8BDD",
2689
3491
  onClick: handleClear,
2690
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.Trash2, { size: 15 })
3492
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_components11.Icon, { name: "trash", size: 15 })
2691
3493
  },
2692
3494
  "clear"
2693
3495
  )
2694
3496
  };
2695
- const titleNode = editableTitle ? editing ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3497
+ const titleNode = editableTitle ? editing ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2696
3498
  "input",
2697
3499
  {
2698
3500
  className: "pactor-chat-workspace__title-input",
@@ -2709,7 +3511,7 @@ function WorkspaceHeader({
2709
3511
  }
2710
3512
  }
2711
3513
  }
2712
- ) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3514
+ ) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2713
3515
  "button",
2714
3516
  {
2715
3517
  type: "button",
@@ -2721,29 +3523,38 @@ function WorkspaceHeader({
2721
3523
  },
2722
3524
  children: displayTitle
2723
3525
  }
2724
- ) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "pactor-chat-workspace__title", children: title });
2725
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("header", { className: "pactor-chat-workspace__header", children: [
3526
+ ) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "pactor-chat-workspace__title", children: title });
3527
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("header", { className: "pactor-chat-workspace__header", children: [
2726
3528
  headerStart !== void 0 ? (
2727
3529
  // header 位模型 chip(决策 D2):与 title 组成左端组,chip 在前
2728
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "pactor-chat-workspace__header-left", children: [
3530
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "pactor-chat-workspace__header-left", children: [
2729
3531
  headerStart,
2730
3532
  titleNode
2731
3533
  ] })
2732
3534
  ) : titleNode,
2733
- hasActions ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "pactor-chat-workspace__header-right", children: [
2734
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "pactor-chat-workspace__header-actions", children: headerActions.map((action) => actionButtons[action]) }),
2735
- headerExtra !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "pactor-chat-workspace__header-extra", children: headerExtra }) : null
2736
- ] }) : headerExtra !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "pactor-chat-workspace__header-extra", children: headerExtra }) : null
3535
+ hasActions ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "pactor-chat-workspace__header-right", children: [
3536
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "pactor-chat-workspace__header-actions", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3537
+ ChromeSlot,
3538
+ {
3539
+ fragment: buildHeaderActionsFragment({
3540
+ actions: headerActions,
3541
+ theme
3542
+ }),
3543
+ fallback: headerActions.map((action) => actionButtons[action])
3544
+ }
3545
+ ) }),
3546
+ headerExtra !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "pactor-chat-workspace__header-extra", children: headerExtra }) : null
3547
+ ] }) : headerExtra !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "pactor-chat-workspace__header-extra", children: headerExtra }) : null
2737
3548
  ] });
2738
3549
  }
2739
3550
 
2740
3551
  // src/chat-composition.tsx
2741
- var import_jsx_runtime14 = require("react/jsx-runtime");
3552
+ var import_jsx_runtime17 = require("react/jsx-runtime");
2742
3553
  function DslNodeSlotInner({ node }) {
2743
- const { renderChildren } = (0, import_runtime4.useRenderer)();
3554
+ const { renderChildren } = (0, import_runtime5.useRenderer)();
2744
3555
  return renderChildren([node]);
2745
3556
  }
2746
- var DslSlotGuard = class extends import_react15.Component {
3557
+ var DslSlotGuard = class extends import_react16.Component {
2747
3558
  constructor() {
2748
3559
  super(...arguments);
2749
3560
  this.state = { failed: false };
@@ -2761,17 +3572,17 @@ var DslSlotGuard = class extends import_react15.Component {
2761
3572
  }
2762
3573
  };
2763
3574
  function DslNodeSlot({ node }) {
2764
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DslSlotGuard, { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DslNodeSlotInner, { node }) });
3575
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DslSlotGuard, { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DslNodeSlotInner, { node }) });
2765
3576
  }
2766
3577
  function isDslNodeValue(value) {
2767
- return typeof value === "object" && value !== null && !Array.isArray(value) && !(0, import_react15.isValidElement)(value) && typeof value.type === "string";
3578
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !(0, import_react16.isValidElement)(value) && typeof value.type === "string";
2768
3579
  }
2769
3580
  function resolveSlot(slot) {
2770
3581
  if (slot === void 0 || slot === null) {
2771
3582
  return void 0;
2772
3583
  }
2773
3584
  if (isDslNodeValue(slot)) {
2774
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DslNodeSlot, { node: slot });
3585
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DslNodeSlot, { node: slot });
2775
3586
  }
2776
3587
  return slot;
2777
3588
  }
@@ -2801,7 +3612,11 @@ function ChatComposition({
2801
3612
  messageActions,
2802
3613
  onMessageAction,
2803
3614
  sidebar,
3615
+ toolbarStart,
2804
3616
  toolbarEnd,
3617
+ composerFooter,
3618
+ composerBottom,
3619
+ hideToolbar,
2805
3620
  className,
2806
3621
  onSend,
2807
3622
  artifact,
@@ -2811,18 +3626,18 @@ function ChatComposition({
2811
3626
  onArtifact,
2812
3627
  onOpenArtifact
2813
3628
  }) {
2814
- const storeRef = (0, import_react15.useRef)(null);
3629
+ const storeRef = (0, import_react16.useRef)(null);
2815
3630
  if (storeRef.current === null) {
2816
3631
  storeRef.current = services.store ?? services.storeFactory?.() ?? new ConversationStore();
2817
3632
  }
2818
3633
  const store = storeRef.current;
2819
- const artifactStoreRef = (0, import_react15.useRef)(null);
3634
+ const artifactStoreRef = (0, import_react16.useRef)(null);
2820
3635
  if (artifactStoreRef.current === null) {
2821
3636
  artifactStoreRef.current = services.artifactStore ?? new ArtifactStore();
2822
3637
  }
2823
3638
  const artifactStore = artifactStoreRef.current;
2824
3639
  const panelControlled = panelOpen !== void 0 || artifact !== void 0;
2825
- const storeArtifact = (0, import_react15.useSyncExternalStore)(
3640
+ const storeArtifact = (0, import_react16.useSyncExternalStore)(
2826
3641
  (listener) => artifactStore.subscribe(listener),
2827
3642
  () => artifactStore.get()
2828
3643
  );
@@ -2840,7 +3655,7 @@ function ChatComposition({
2840
3655
  artifactStore.open(next);
2841
3656
  }
2842
3657
  };
2843
- const [attachments, setAttachments] = (0, import_react15.useState)([]);
3658
+ const [attachments, setAttachments] = (0, import_react16.useState)([]);
2844
3659
  const handleAttachFiles = services.onAttachFiles ? (files) => {
2845
3660
  void Promise.resolve(services.onAttachFiles(files)).then(
2846
3661
  (created) => {
@@ -2860,24 +3675,38 @@ function ChatComposition({
2860
3675
  model,
2861
3676
  onModelChange
2862
3677
  });
2863
- const headerModelChip = modelPlacement === "header" && modelsEnabled ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2864
- ModelChip,
3678
+ const headerModelChip = modelPlacement === "header" && modelsEnabled ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3679
+ ModelSelect,
2865
3680
  {
2866
3681
  models,
2867
3682
  model: currentModel,
2868
3683
  onModelChange: handleModelChange
2869
3684
  }
2870
3685
  ) : void 0;
2871
- const mergedToolbarEnd = services.toolbarEnd !== void 0 || toolbarEnd !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
3686
+ const mergedToolbarEnd = services.toolbarEnd !== void 0 || toolbarEnd !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
2872
3687
  services.toolbarEnd,
2873
3688
  toolbarEnd
2874
3689
  ] }) : void 0;
2875
- const workspace = /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
3690
+ const mergedToolbarStart = services.toolbarStart !== void 0 || toolbarStart !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
3691
+ services.toolbarStart,
3692
+ toolbarStart
3693
+ ] }) : void 0;
3694
+ const mergedComposerFooter = services.composerFooter !== void 0 || composerFooter !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
3695
+ services.composerFooter,
3696
+ composerFooter
3697
+ ] }) : void 0;
3698
+ const mergedComposerBottom = services.composerBottom !== void 0 || composerBottom !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
3699
+ services.composerBottom,
3700
+ composerBottom
3701
+ ] }) : void 0;
3702
+ const hasHeaderContent = header !== void 0 && header !== null ? true : title !== void 0 || headerExtra !== void 0 && headerExtra !== null || headerActions !== void 0 && headerActions.length > 0 || headerModelChip !== void 0;
3703
+ const renderHeader = showHeader !== false && hasHeaderContent;
3704
+ const workspace = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
2876
3705
  "div",
2877
3706
  {
2878
3707
  className: `pactor-chat-workspace${className ? ` ${className}` : ""}`,
2879
3708
  children: [
2880
- showHeader ? header ?? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3709
+ renderHeader ? header ?? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2881
3710
  WorkspaceHeader,
2882
3711
  {
2883
3712
  title,
@@ -2892,7 +3721,7 @@ function ChatComposition({
2892
3721
  onTitleChange
2893
3722
  }
2894
3723
  ) : null,
2895
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3724
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2896
3725
  ChatView,
2897
3726
  {
2898
3727
  store,
@@ -2912,8 +3741,11 @@ function ChatComposition({
2912
3741
  onAttachmentsChange: setAttachments,
2913
3742
  onAttachFiles: handleAttachFiles,
2914
3743
  onAttachmentClick: services.onAttachmentClick,
2915
- toolbarStart: services.toolbarStart,
3744
+ toolbarStart: mergedToolbarStart,
2916
3745
  toolbarEnd: mergedToolbarEnd,
3746
+ composerFooter: mergedComposerFooter,
3747
+ composerBottom: mergedComposerBottom,
3748
+ hideToolbar,
2917
3749
  onMentionTrigger: services.onMentionTrigger,
2918
3750
  onPlusMenu: services.onPlusMenu,
2919
3751
  onArtifact,
@@ -2932,15 +3764,15 @@ function ChatComposition({
2932
3764
  ]
2933
3765
  }
2934
3766
  );
2935
- const main = sidebar ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(AppShell, { sidebar, children: workspace }) : workspace;
3767
+ const main = sidebar ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(AppShell, { sidebar, children: workspace }) : workspace;
2936
3768
  const panelFeatureUsed = artifact !== void 0 || panelOpen !== void 0 || inspector !== void 0 || onPanelClose !== void 0 || onArtifact !== void 0 || onOpenArtifact !== void 0 || services.artifactStore !== void 0;
2937
3769
  if (!panelFeatureUsed && !effectivePanelOpen) {
2938
3770
  return main;
2939
3771
  }
2940
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "pactor-chat-inspector-host", children: [
3772
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "pactor-chat-inspector-host", children: [
2941
3773
  main,
2942
- effectivePanelOpen ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
2943
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3774
+ effectivePanelOpen ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
3775
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2944
3776
  "div",
2945
3777
  {
2946
3778
  className: "pactor-chat-inspector__scrim",
@@ -2948,7 +3780,7 @@ function ChatComposition({
2948
3780
  onClick: handlePanelClose
2949
3781
  }
2950
3782
  ),
2951
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3783
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2952
3784
  ArtifactPanel,
2953
3785
  {
2954
3786
  artifact: effectiveArtifact,
@@ -2963,21 +3795,21 @@ function ChatComposition({
2963
3795
  }
2964
3796
 
2965
3797
  // src/chat-services.tsx
2966
- var import_react16 = require("react");
2967
- var import_jsx_runtime15 = require("react/jsx-runtime");
2968
- var ChatServicesContext = (0, import_react16.createContext)(null);
3798
+ var import_react17 = require("react");
3799
+ var import_jsx_runtime18 = require("react/jsx-runtime");
3800
+ var ChatServicesContext = (0, import_react17.createContext)(null);
2969
3801
  function ChatServicesProvider({
2970
3802
  value,
2971
3803
  children
2972
3804
  }) {
2973
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ChatServicesContext.Provider, { value, children });
3805
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChatServicesContext.Provider, { value, children });
2974
3806
  }
2975
3807
  function useChatServices() {
2976
- return (0, import_react16.useContext)(ChatServicesContext);
3808
+ return (0, import_react17.useContext)(ChatServicesContext);
2977
3809
  }
2978
3810
 
2979
3811
  // src/chat-workspace.tsx
2980
- var import_jsx_runtime16 = require("react/jsx-runtime");
3812
+ var import_jsx_runtime19 = require("react/jsx-runtime");
2981
3813
  function createChatWorkspace(services) {
2982
3814
  function ChatWorkspace({
2983
3815
  title,
@@ -2987,7 +3819,11 @@ function createChatWorkspace(services) {
2987
3819
  conversationId,
2988
3820
  params,
2989
3821
  sidebar,
3822
+ toolbarStart,
2990
3823
  toolbarEnd,
3824
+ composerFooter,
3825
+ composerBottom,
3826
+ hideToolbar,
2991
3827
  artifact,
2992
3828
  panelOpen,
2993
3829
  onPanelClose,
@@ -3010,7 +3846,7 @@ function createChatWorkspace(services) {
3010
3846
  onMessageAction,
3011
3847
  className
3012
3848
  }) {
3013
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ChatServicesProvider, { value: services, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3849
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChatServicesProvider, { value: services, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3014
3850
  ChatComposition,
3015
3851
  {
3016
3852
  services,
@@ -3021,15 +3857,17 @@ function createChatWorkspace(services) {
3021
3857
  conversationId,
3022
3858
  params,
3023
3859
  className,
3024
- showHeader: title !== void 0 || services.headerExtra !== void 0 || // header 位模型 chip(决策 D2):仅配置 models 也渲染极简头部
3025
- modelPlacement === "header" && models !== void 0 && models.length > 0,
3026
3860
  headerExtra: services.headerExtra,
3027
- sidebar: sidebar ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DslNodeSlot, { node: sidebar }) : void 0,
3028
- toolbarEnd: toolbarEnd ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DslNodeSlot, { node: toolbarEnd }) : void 0,
3861
+ sidebar: sidebar ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DslNodeSlot, { node: sidebar }) : void 0,
3862
+ toolbarStart: toolbarStart ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DslNodeSlot, { node: toolbarStart }) : void 0,
3863
+ toolbarEnd: toolbarEnd ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DslNodeSlot, { node: toolbarEnd }) : void 0,
3864
+ composerFooter: composerFooter ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DslNodeSlot, { node: composerFooter }) : void 0,
3865
+ composerBottom: composerBottom ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DslNodeSlot, { node: composerBottom }) : void 0,
3866
+ hideToolbar,
3029
3867
  artifact,
3030
3868
  panelOpen,
3031
3869
  onPanelClose,
3032
- inspector: inspector ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DslNodeSlot, { node: inspector }) : void 0,
3870
+ inspector: inspector ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DslNodeSlot, { node: inspector }) : void 0,
3033
3871
  onArtifact,
3034
3872
  onOpenArtifact,
3035
3873
  headerActions,
@@ -3053,11 +3891,37 @@ function createChatWorkspace(services) {
3053
3891
  }
3054
3892
 
3055
3893
  // src/view/Chat.tsx
3056
- var import_lucide_react10 = require("lucide-react");
3057
- var import_react17 = require("react");
3894
+ var import_components12 = require("@pactor-app/ui/components");
3895
+ var import_react18 = require("react");
3896
+
3897
+ // src/view/chrome/menu.tsx
3898
+ function buildSidebarMenuFragment(props) {
3899
+ return {
3900
+ requires: ["Menu"],
3901
+ node: {
3902
+ type: "Menu",
3903
+ props: {
3904
+ mode: "inline",
3905
+ selectedKey: props.activeMenuKey,
3906
+ items: props.menu.map((item) => ({
3907
+ key: item.key,
3908
+ label: item.label,
3909
+ ...item.icon !== void 0 ? { icon: item.icon } : {},
3910
+ ...item.badge !== void 0 ? { badge: item.badge } : {},
3911
+ ...item.dot === true ? { dot: true } : {},
3912
+ ...item.group !== void 0 ? { group: item.group } : {},
3913
+ ...item.disabled === true ? { disabled: true } : {}
3914
+ }))
3915
+ },
3916
+ events: {
3917
+ onSelect: [{ action: "chat.menuSelect", params: { key: "${event}" } }]
3918
+ }
3919
+ }
3920
+ };
3921
+ }
3058
3922
 
3059
3923
  // src/view/ChatShell.tsx
3060
- var import_jsx_runtime17 = require("react/jsx-runtime");
3924
+ var import_jsx_runtime20 = require("react/jsx-runtime");
3061
3925
  function ChatShell({
3062
3926
  menu,
3063
3927
  activeMenuKey,
@@ -3069,7 +3933,7 @@ function ChatShell({
3069
3933
  className,
3070
3934
  onMenuSelect
3071
3935
  }) {
3072
- const sidebarContent = resolveSlot(sidebar) ?? (menu && menu.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3936
+ const sidebarContent = resolveSlot(sidebar) ?? (menu && menu.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3073
3937
  ChatSidebarMenu,
3074
3938
  {
3075
3939
  menu,
@@ -3077,7 +3941,7 @@ function ChatShell({
3077
3941
  onMenuSelect
3078
3942
  }
3079
3943
  ) : null);
3080
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3944
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3081
3945
  AppShell,
3082
3946
  {
3083
3947
  sidebar: sidebarContent,
@@ -3094,7 +3958,7 @@ function ChatShellDslAdapter({
3094
3958
  onMenuSelect,
3095
3959
  ...rest
3096
3960
  }) {
3097
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3961
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3098
3962
  ChatShell,
3099
3963
  {
3100
3964
  ...rest,
@@ -3109,39 +3973,45 @@ function registerChatShellComponent(registry) {
3109
3973
  }
3110
3974
 
3111
3975
  // src/view/Chat.tsx
3112
- var import_jsx_runtime18 = require("react/jsx-runtime");
3113
- var MENU_ICONS = {
3114
- plus: import_lucide_react10.Plus,
3115
- chat: import_lucide_react10.MessageSquare,
3116
- history: import_lucide_react10.History,
3117
- settings: import_lucide_react10.Settings,
3118
- star: import_lucide_react10.Star,
3119
- search: import_lucide_react10.Search,
3120
- user: import_lucide_react10.User,
3121
- folder: import_lucide_react10.Folder
3122
- };
3976
+ var import_jsx_runtime21 = require("react/jsx-runtime");
3123
3977
  function ChatSidebarMenu({
3124
3978
  menu,
3125
3979
  activeMenuKey,
3126
3980
  onMenuSelect
3127
3981
  }) {
3128
3982
  const drawer = useSidebarDrawer();
3129
- let lastGroup;
3130
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("nav", { className: "pactor-chat-menu", "aria-label": "\u4F1A\u8BDD\u5BFC\u822A", children: menu.map((item) => {
3131
- const groupLabel = item.group !== void 0 && item.group !== lastGroup ? item.group : void 0;
3132
- lastGroup = item.group;
3133
- const Icon = item.icon !== void 0 ? MENU_ICONS[item.icon] : void 0;
3134
- const active = item.key === activeMenuKey;
3135
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react17.Fragment, { children: [
3136
- groupLabel !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "pactor-chat-menu__group", children: groupLabel }) : null,
3137
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
3983
+ const menuRef = (0, import_react18.useRef)(menu);
3984
+ menuRef.current = menu;
3985
+ const selectRef = (0, import_react18.useRef)(onMenuSelect);
3986
+ selectRef.current = onMenuSelect;
3987
+ const drawerRef = (0, import_react18.useRef)(drawer);
3988
+ drawerRef.current = drawer;
3989
+ (0, import_react18.useEffect)(() => {
3990
+ setChatChromeHandlers({
3991
+ menuSelect: (key) => {
3992
+ const item = menuRef.current.find((entry) => entry.key === key);
3993
+ if (item === void 0 || item.disabled === true) {
3994
+ return;
3995
+ }
3996
+ selectRef.current?.(key, item);
3997
+ drawerRef.current?.close();
3998
+ }
3999
+ });
4000
+ }, []);
4001
+ const fallback = (() => {
4002
+ let lastGroup;
4003
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("nav", { className: "pactor-chat-menu", "aria-label": "\u4F1A\u8BDD\u5BFC\u822A", children: menu.map((item) => {
4004
+ const groupLabel = item.group !== void 0 && item.group !== lastGroup ? item.group : void 0;
4005
+ lastGroup = item.group;
4006
+ const active = item.key === activeMenuKey;
4007
+ const collapsed = drawer?.collapsed === true;
4008
+ const button = /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
3138
4009
  "button",
3139
4010
  {
3140
4011
  type: "button",
3141
4012
  className: `pactor-chat-menu__item${active ? " pactor-chat-menu__item--active" : ""}`,
3142
4013
  disabled: item.disabled === true,
3143
4014
  "aria-current": active ? "true" : void 0,
3144
- title: drawer?.collapsed ? item.label : void 0,
3145
4015
  onClick: () => {
3146
4016
  if (item.disabled) {
3147
4017
  return;
@@ -3150,21 +4020,26 @@ function ChatSidebarMenu({
3150
4020
  drawer?.close();
3151
4021
  },
3152
4022
  children: [
3153
- item.icon !== void 0 ? Icon ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "pactor-chat-menu__icon", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Icon, { size: 15, strokeWidth: 1.7 }) }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3154
- "span",
3155
- {
3156
- className: "pactor-chat-menu__icon-placeholder",
3157
- "aria-hidden": true
3158
- }
3159
- ) : null,
3160
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "pactor-chat-menu__label", children: item.label }),
3161
- item.dot === true ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "pactor-chat-menu__dot", "aria-hidden": true }) : null,
3162
- item.badge !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "pactor-chat-menu__badge", children: item.badge }) : null
4023
+ item.icon !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "pactor-chat-menu__icon", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_components12.Icon, { name: item.icon, size: 15, strokeWidth: 1.7 }) }) : null,
4024
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "pactor-chat-menu__label", children: item.label }),
4025
+ item.dot === true ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "pactor-chat-menu__dot", "aria-hidden": true }) : null,
4026
+ item.badge !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "pactor-chat-menu__badge", children: item.badge }) : null
3163
4027
  ]
3164
4028
  }
3165
- )
3166
- ] }, item.key);
3167
- }) });
4029
+ );
4030
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_react18.Fragment, { children: [
4031
+ groupLabel !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "pactor-chat-menu__group", children: groupLabel }) : null,
4032
+ collapsed ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_components12.RailTooltip, { label: item.label, children: button }) : button
4033
+ ] }, item.key);
4034
+ }) });
4035
+ })();
4036
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4037
+ ChromeSlot,
4038
+ {
4039
+ fragment: buildSidebarMenuFragment({ menu, activeMenuKey }),
4040
+ fallback
4041
+ }
4042
+ );
3168
4043
  }
3169
4044
  function Chat({
3170
4045
  title,
@@ -3177,6 +4052,11 @@ function Chat({
3177
4052
  menu,
3178
4053
  activeMenuKey,
3179
4054
  sidebar,
4055
+ toolbarStart,
4056
+ toolbarEnd,
4057
+ composerFooter,
4058
+ composerBottom,
4059
+ hideToolbar,
3180
4060
  headerExtra,
3181
4061
  header,
3182
4062
  headerActions,
@@ -3204,7 +4084,7 @@ function Chat({
3204
4084
  onMenuSelect
3205
4085
  }) {
3206
4086
  const services = useChatServices();
3207
- (0, import_react17.useEffect)(() => {
4087
+ (0, import_react18.useEffect)(() => {
3208
4088
  if (!services) {
3209
4089
  console.warn(
3210
4090
  "[@pactor-app/chat] Chat \u672A\u68C0\u6D4B\u5230 ChatServicesProvider\uFF1A\u8BF7\u5728\u5E94\u7528\u6839\u90E8\u4EE5 <ChatServicesProvider> \u6CE8\u5165 transport \u7B49\u670D\u52A1"
@@ -3212,10 +4092,10 @@ function Chat({
3212
4092
  }
3213
4093
  }, [services]);
3214
4094
  if (!services) {
3215
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "pactor-chat-notice", role: "note", children: "\u672A\u914D\u7F6E\u804A\u5929\u670D\u52A1\uFF1A\u8BF7\u5728 Chat \u5916\u5C42\u4F7F\u7528 ChatServicesProvider \u6CE8\u5165 transport \u7B49\u670D\u52A1\u540E\u91CD\u8BD5\u3002" });
4095
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "pactor-chat-notice", role: "note", children: "\u672A\u914D\u7F6E\u804A\u5929\u670D\u52A1\uFF1A\u8BF7\u5728 Chat \u5916\u5C42\u4F7F\u7528 ChatServicesProvider \u6CE8\u5165 transport \u7B49\u670D\u52A1\u540E\u91CD\u8BD5\u3002" });
3216
4096
  }
3217
4097
  const sidebarSlot = resolveSlot(sidebar);
3218
- const sidebarContent = sidebarSlot ?? (menu && menu.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4098
+ const sidebarContent = sidebarSlot ?? (menu && menu.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3219
4099
  ChatSidebarMenu,
3220
4100
  {
3221
4101
  menu,
@@ -3223,7 +4103,7 @@ function Chat({
3223
4103
  onMenuSelect
3224
4104
  }
3225
4105
  ) : void 0);
3226
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4106
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3227
4107
  ChatComposition,
3228
4108
  {
3229
4109
  services,
@@ -3234,9 +4114,14 @@ function Chat({
3234
4114
  conversationId,
3235
4115
  params,
3236
4116
  className,
3237
- showHeader: showHeader !== false,
4117
+ showHeader,
3238
4118
  header: resolveSlot(header),
3239
4119
  headerExtra: resolveSlot(headerExtra) ?? services.headerExtra,
4120
+ toolbarStart: resolveSlot(toolbarStart),
4121
+ toolbarEnd: resolveSlot(toolbarEnd),
4122
+ composerFooter: resolveSlot(composerFooter),
4123
+ composerBottom: resolveSlot(composerBottom),
4124
+ hideToolbar,
3240
4125
  headerActions,
3241
4126
  editableTitle,
3242
4127
  onExport,
@@ -3278,7 +4163,7 @@ function ChatDslAdapter({
3278
4163
  onTitleChange,
3279
4164
  ...rest
3280
4165
  }) {
3281
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4166
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3282
4167
  Chat,
3283
4168
  {
3284
4169
  ...rest,
@@ -3305,11 +4190,11 @@ function registerChatComponent(registry) {
3305
4190
  }
3306
4191
 
3307
4192
  // src/view/ChatLayout.tsx
3308
- var import_jsx_runtime19 = require("react/jsx-runtime");
4193
+ var import_jsx_runtime22 = require("react/jsx-runtime");
3309
4194
  function ChatLayout({ title, children }) {
3310
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "pactor-chat-layout", children: [
3311
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("header", { className: "pactor-chat-layout__header", children: title ?? "Chat" }),
3312
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("main", { className: "pactor-chat-layout__main", children })
4195
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "pactor-chat-layout", children: [
4196
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("header", { className: "pactor-chat-layout__header", children: title ?? "Chat" }),
4197
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("main", { className: "pactor-chat-layout__main", children })
3313
4198
  ] });
3314
4199
  }
3315
4200
  var CHAT_LAYOUT_TYPE = "chat";
@@ -3339,8 +4224,6 @@ function registerChatLayout(layoutRegistry) {
3339
4224
  ConversationStore,
3340
4225
  DEFAULT_CHAT_GREETING,
3341
4226
  EmptyState,
3342
- MENU_ICONS,
3343
- MESSAGE_ACTION_ICONS,
3344
4227
  MessageActions,
3345
4228
  MessageBubble,
3346
4229
  PartRenderer,