@pactor-app/ui 1.44.0 → 1.46.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.
@@ -32,19 +32,15 @@ var components_exports = {};
32
32
  __export(components_exports, {
33
33
  Accordion: () => Accordion2,
34
34
  Affix: () => Affix,
35
- AgentStatus: () => AgentStatus,
36
35
  Alert: () => Alert2,
37
36
  AlertDialog: () => AlertDialog2,
38
37
  Anchor: () => Anchor,
39
- ArtifactPanel: () => ArtifactPanel,
40
38
  AspectRatio: () => AspectRatio2,
41
- Attachment: () => Attachment,
42
39
  Audio: () => Audio,
43
40
  Avatar: () => Avatar2,
44
41
  BackTop: () => BackTop,
45
42
  Badge: () => Badge2,
46
43
  Breadcrumb: () => Breadcrumb2,
47
- Bubble: () => Bubble2,
48
44
  Button: () => Button2,
49
45
  ButtonGroup: () => ButtonGroup2,
50
46
  CONTROL_SIZE_AREA: () => CONTROL_SIZE_AREA,
@@ -57,12 +53,9 @@ __export(components_exports, {
57
53
  Carousel: () => Carousel2,
58
54
  Cascader: () => Cascader,
59
55
  ChatInput: () => ChatInput,
60
- ChatMessage: () => ChatMessage,
61
- ChatPage: () => ChatPage,
62
56
  Checkbox: () => Checkbox2,
63
57
  ChipFilter: () => ChipFilter,
64
58
  ChipNode: () => ChipNode,
65
- Citation: () => Citation,
66
59
  ClearHoverButton: () => ClearHoverButton,
67
60
  CodeBlock: () => CodeBlock,
68
61
  Col: () => Col,
@@ -70,11 +63,9 @@ __export(components_exports, {
70
63
  ColorPicker: () => ColorPicker,
71
64
  Combobox: () => Combobox2,
72
65
  Command: () => Command2,
73
- Composer: () => Composer,
74
66
  Container: () => Container,
75
67
  Content: () => Content,
76
68
  ContextMenu: () => ContextMenu2,
77
- ConversationList: () => ConversationList,
78
69
  DataTable: () => DataTable,
79
70
  DatePicker: () => DatePicker,
80
71
  Descriptions: () => Descriptions,
@@ -115,7 +106,6 @@ __export(components_exports, {
115
106
  Menu: () => Menu2,
116
107
  Menubar: () => Menubar2,
117
108
  Message: () => Message,
118
- MessageList: () => MessageList,
119
109
  NavigationMenu: () => NavigationMenu2,
120
110
  Notification: () => Notification,
121
111
  NumberInput: () => NumberInput,
@@ -124,7 +114,6 @@ __export(components_exports, {
124
114
  Popconfirm: () => Popconfirm,
125
115
  Popover: () => Popover2,
126
116
  Progress: () => Progress2,
127
- Prompt: () => Prompt,
128
117
  QRCode: () => QRCode,
129
118
  RadioGroup: () => RadioGroup2,
130
119
  RailTooltip: () => RailTooltip,
@@ -136,7 +125,6 @@ __export(components_exports, {
136
125
  ScrollArea: () => ScrollArea2,
137
126
  Segmented: () => Segmented,
138
127
  Select: () => Select2,
139
- Sender: () => Sender,
140
128
  Separator: () => Separator2,
141
129
  Sheet: () => Sheet2,
142
130
  Sidebar: () => Sidebar,
@@ -149,21 +137,16 @@ __export(components_exports, {
149
137
  Statistic: () => Statistic,
150
138
  Status: () => Status,
151
139
  Steps: () => Steps,
152
- StepsPanel: () => StepsPanel,
153
- StreamingText: () => StreamingText,
154
- Suggestion: () => Suggestion,
155
140
  Switch: () => Switch2,
156
141
  Table: () => Table3,
157
142
  Tabs: () => Tabs2,
158
143
  Tag: () => Tag2,
159
144
  Text: () => Text,
160
145
  Textarea: () => Textarea2,
161
- Thinking: () => Thinking,
162
146
  TimePicker: () => TimePicker,
163
147
  Timeline: () => Timeline,
164
148
  Toggle: () => Toggle2,
165
149
  ToggleGroup: () => ToggleGroup2,
166
- ToolCall: () => ToolCall,
167
150
  Tooltip: () => Tooltip2,
168
151
  Tour: () => Tour,
169
152
  Transfer: () => Transfer,
@@ -173,8 +156,6 @@ __export(components_exports, {
173
156
  Upload: () => Upload2,
174
157
  Video: () => Video2,
175
158
  Watermark: () => Watermark,
176
- Welcome: () => Welcome,
177
- buildArtifactTree: () => buildArtifactTree,
178
159
  builtinComponents: () => builtinComponents,
179
160
  createComponentRegistry: () => createComponentRegistry,
180
161
  diffLines: () => diffLines,
@@ -182,12 +163,13 @@ __export(components_exports, {
182
163
  normalizeIconName: () => normalizeIconName,
183
164
  registerBuiltinComponents: () => registerBuiltinComponents,
184
165
  useFormContext: () => useFormContext,
166
+ useOptionalRenderer: () => useOptionalRenderer,
185
167
  useSidebarDrawer: () => useSidebarDrawer
186
168
  });
187
169
  module.exports = __toCommonJS(components_exports);
188
170
 
189
171
  // src/components/register.ts
190
- var import_core3 = require("@pactor-app/core");
172
+ var import_core2 = require("@pactor-app/core");
191
173
 
192
174
  // src/components/components/Accordion/index.tsx
193
175
  var import_runtime = require("@pactor-app/runtime");
@@ -505,6 +487,8 @@ var ICONS = {
505
487
  "chart-column": import_lucide_react2.ChartColumn,
506
488
  "layout-grid": import_lucide_react2.LayoutGrid,
507
489
  "layout-template": import_lucide_react2.LayoutTemplate,
490
+ "maximize-2": import_lucide_react2.Maximize2,
491
+ "minimize-2": import_lucide_react2.Minimize2,
508
492
  palette: import_lucide_react2.Palette,
509
493
  paintbrush: import_lucide_react2.Paintbrush,
510
494
  funnel: import_lucide_react2.Funnel,
@@ -3278,12 +3262,21 @@ function Textarea({
3278
3262
 
3279
3263
  // src/components/components/ChatInput/index.tsx
3280
3264
  var import_jsx_runtime51 = require("react/jsx-runtime");
3265
+ function useOptionalRenderer() {
3266
+ try {
3267
+ return (0, import_runtime13.useRenderer)();
3268
+ } catch {
3269
+ return null;
3270
+ }
3271
+ }
3281
3272
  function ChatInput({
3282
3273
  value,
3283
3274
  placeholder,
3284
3275
  disabled,
3285
3276
  rows = 3,
3286
3277
  hideSend,
3278
+ streaming = false,
3279
+ onStop,
3287
3280
  toolbarStart,
3288
3281
  toolbarEnd,
3289
3282
  header,
@@ -3294,14 +3287,14 @@ function ChatInput({
3294
3287
  className,
3295
3288
  style
3296
3289
  }) {
3297
- const { renderChildren } = (0, import_runtime13.useRenderer)();
3290
+ const renderer = useOptionalRenderer();
3298
3291
  const controlled = value !== void 0;
3299
3292
  const [draft, setDraft] = (0, import_react14.useState)("");
3300
3293
  const { t } = (0, import_runtime12.useI18n)();
3301
3294
  const text = controlled ? value : draft;
3302
3295
  const canSubmit = !disabled && text.trim() !== "";
3303
3296
  const customField = field !== void 0 && field !== null;
3304
- const renderSlot = (slot) => (0, import_react14.isValidElement)(slot) || typeof slot !== "object" || slot === null ? slot : renderChildren([slot]);
3297
+ const renderSlot = (slot) => (0, import_react14.isValidElement)(slot) || typeof slot !== "object" || slot === null ? slot : renderer?.renderChildren([slot]) ?? null;
3305
3298
  const composingRef = (0, import_react14.useRef)(false);
3306
3299
  const handleChange = (next) => {
3307
3300
  if (!controlled) {
@@ -3361,11 +3354,11 @@ function ChatInput({
3361
3354
  {
3362
3355
  type: "button",
3363
3356
  size: "icon",
3364
- "aria-label": t("pactor.chatInput.send"),
3365
- disabled: !canSubmit,
3366
- onClick: submit,
3357
+ "aria-label": streaming ? t("pactor.chatInput.stop") : t("pactor.chatInput.send"),
3358
+ disabled: streaming ? false : !canSubmit,
3359
+ onClick: streaming ? () => onStop?.() : submit,
3367
3360
  className: "size-8 rounded-full",
3368
- children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_lucide_react13.ArrowUp, {})
3361
+ children: streaming ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_lucide_react13.Square, {}) : /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_lucide_react13.ArrowUp, {})
3369
3362
  }
3370
3363
  )
3371
3364
  ] }) : null,
@@ -7480,7 +7473,7 @@ function useSidebarDrawer() {
7480
7473
  return null;
7481
7474
  }
7482
7475
  }
7483
- function useOptionalRenderer() {
7476
+ function useOptionalRenderer2() {
7484
7477
  try {
7485
7478
  return (0, import_runtime31.useRenderer)();
7486
7479
  } catch {
@@ -7488,7 +7481,7 @@ function useOptionalRenderer() {
7488
7481
  }
7489
7482
  }
7490
7483
  function SidebarContentSlot({ content }) {
7491
- const renderer = useOptionalRenderer();
7484
+ const renderer = useOptionalRenderer2();
7492
7485
  if (renderer !== null && typeof content === "object" && content !== null && !("$$typeof" in content) && "type" in content) {
7493
7486
  return /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(import_jsx_runtime108.Fragment, { children: renderer.renderChildren([content]) });
7494
7487
  }
@@ -10971,16 +10964,26 @@ var tabsListVariants = (0, import_class_variance_authority7.cva)(
10971
10964
  default: "h-9 rounded-lg bg-muted p-[3px]",
10972
10965
  // line:页签栏通栏(下划线轨道占满容器宽),触发器自然宽度靠左排列
10973
10966
  line: "h-auto w-full justify-start gap-1 rounded-none border-b border-border bg-transparent p-0"
10967
+ },
10968
+ /** 尺寸(对齐 Button 的 size 语义):sm 经 compoundVariants 仅压缩胶囊变体 */
10969
+ size: {
10970
+ default: "",
10971
+ sm: ""
10974
10972
  }
10975
10973
  },
10974
+ compoundVariants: [
10975
+ { variant: "default", size: "sm", class: "h-8" }
10976
+ ],
10976
10977
  defaultVariants: {
10977
- variant: "default"
10978
+ variant: "default",
10979
+ size: "default"
10978
10980
  }
10979
10981
  }
10980
10982
  );
10981
10983
  function TabsList({
10982
10984
  className,
10983
10985
  variant,
10986
+ size,
10984
10987
  ...props
10985
10988
  }) {
10986
10989
  return /* @__PURE__ */ (0, import_jsx_runtime153.jsx)(
@@ -10988,7 +10991,8 @@ function TabsList({
10988
10991
  {
10989
10992
  "data-slot": "tabs-list",
10990
10993
  "data-variant": variant ?? "default",
10991
- className: cn(tabsListVariants({ variant }), className),
10994
+ "data-size": size ?? "default",
10995
+ className: cn(tabsListVariants({ variant, size }), className),
10992
10996
  ...props
10993
10997
  }
10994
10998
  );
@@ -11009,6 +11013,8 @@ function TabsTrigger({
11009
11013
  //(叠在 List 的底边轨道上),无底色/圆角/阴影;通栏 List 下取消
11010
11014
  // flex-1 拉伸,触发器保持自然宽度
11011
11015
  "group-data-[variant=line]/tabs-list:-mb-px group-data-[variant=line]/tabs-list:h-9 group-data-[variant=line]/tabs-list:flex-none group-data-[variant=line]/tabs-list:rounded-none group-data-[variant=line]/tabs-list:border-x-0 group-data-[variant=line]/tabs-list:border-t-0 group-data-[variant=line]/tabs-list:border-b-2 group-data-[variant=line]/tabs-list:py-1.5 group-data-[variant=line]/tabs-list:text-muted-foreground group-data-[variant=line]/tabs-list:data-active:border-b-primary group-data-[variant=line]/tabs-list:data-active:text-foreground",
11016
+ // size=sm:小字号 + 收紧纵向 padding(高度随 List h-8 自动收缩)
11017
+ "group-data-[size=sm]/tabs-list:py-0.5 group-data-[size=sm]/tabs-list:text-xs",
11012
11018
  className
11013
11019
  ),
11014
11020
  ...props
@@ -12939,2460 +12945,6 @@ function Watermark({
12939
12945
  );
12940
12946
  }
12941
12947
 
12942
- // src/components/chat/AgentStatus/index.tsx
12943
- var import_jsx_runtime174 = require("react/jsx-runtime");
12944
- var statusMeta = {
12945
- running: { icon: "loader-circle", className: "text-info", spin: true },
12946
- waiting: { icon: "clock", className: "text-warning" },
12947
- done: { icon: "circle-check", className: "text-success" },
12948
- error: { icon: "circle-x", className: "text-destructive" }
12949
- };
12950
- function AgentStatus({ status = "running", text, elapsed, className, style }) {
12951
- const meta = statusMeta[status];
12952
- return /* @__PURE__ */ (0, import_jsx_runtime174.jsxs)(
12953
- "div",
12954
- {
12955
- "data-slot": "agent-status",
12956
- "data-status": status,
12957
- className: cn("flex items-center gap-2 text-sm", className),
12958
- style,
12959
- children: [
12960
- /* @__PURE__ */ (0, import_jsx_runtime174.jsx)("span", { className: cn(meta.className, meta.spin && "animate-spin motion-reduce:animate-none"), children: /* @__PURE__ */ (0, import_jsx_runtime174.jsx)(Icon, { name: meta.icon, size: "sm" }) }),
12961
- text ? /* @__PURE__ */ (0, import_jsx_runtime174.jsx)("span", { "data-slot": "agent-status-text", children: text }) : null,
12962
- elapsed !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime174.jsxs)("span", { "data-slot": "agent-status-elapsed", className: "text-xs text-muted-foreground", children: [
12963
- "\u5DF2\u7528\u65F6 ",
12964
- elapsed,
12965
- "s"
12966
- ] }) : null
12967
- ]
12968
- }
12969
- );
12970
- }
12971
-
12972
- // src/components/chat/ArtifactPanel/index.tsx
12973
- var import_react62 = require("react");
12974
-
12975
- // src/components/chat/ArtifactPanel/artifact.ts
12976
- var ARTIFACT_IMAGE_EXTENSIONS = [
12977
- "png",
12978
- "jpg",
12979
- "jpeg",
12980
- "gif",
12981
- "svg",
12982
- "webp",
12983
- "avif",
12984
- "bmp",
12985
- "ico"
12986
- ];
12987
- function resolveArtifactFileType(artifact) {
12988
- const declared = artifact.fileType?.trim().toLowerCase().replace(/^\./, "");
12989
- if (declared) {
12990
- return declared;
12991
- }
12992
- const match = /\.([A-Za-z0-9]+)$/.exec(artifact.title.trim());
12993
- return match ? match[1].toLowerCase() : null;
12994
- }
12995
- function isImageArtifact(artifact) {
12996
- const ext = resolveArtifactFileType(artifact);
12997
- return ext !== null && ARTIFACT_IMAGE_EXTENSIONS.includes(ext);
12998
- }
12999
- var ARTIFACT_ICON_BY_EXTENSION = {
13000
- // 表格数据
13001
- csv: "table",
13002
- tsv: "table",
13003
- xls: "table",
13004
- xlsx: "table",
13005
- // 数据 / 数据库
13006
- json: "database",
13007
- sql: "database",
13008
- // 脚本
13009
- sh: "terminal",
13010
- bash: "terminal",
13011
- zsh: "terminal",
13012
- // 代码 / 配置
13013
- js: "code",
13014
- mjs: "code",
13015
- cjs: "code",
13016
- jsx: "code",
13017
- ts: "code",
13018
- mts: "code",
13019
- cts: "code",
13020
- tsx: "code",
13021
- py: "code",
13022
- rb: "code",
13023
- go: "code",
13024
- rs: "code",
13025
- java: "code",
13026
- kt: "code",
13027
- c: "code",
13028
- h: "code",
13029
- cc: "code",
13030
- cpp: "code",
13031
- cs: "code",
13032
- swift: "code",
13033
- php: "code",
13034
- css: "code",
13035
- scss: "code",
13036
- less: "code",
13037
- html: "code",
13038
- vue: "code",
13039
- svelte: "code",
13040
- yaml: "code",
13041
- yml: "code",
13042
- toml: "code",
13043
- xml: "code",
13044
- // 文档 / 文本
13045
- md: "file-text",
13046
- markdown: "file-text",
13047
- mdx: "file-text",
13048
- txt: "file-text",
13049
- text: "file-text",
13050
- log: "file-text",
13051
- rst: "file-text",
13052
- pdf: "file-text",
13053
- doc: "file-text",
13054
- docx: "file-text"
13055
- };
13056
- function resolveArtifactIconName(artifact) {
13057
- const ext = resolveArtifactFileType(artifact);
13058
- if (ext !== null) {
13059
- if (ARTIFACT_IMAGE_EXTENSIONS.includes(ext)) {
13060
- return "image";
13061
- }
13062
- const name = ARTIFACT_ICON_BY_EXTENSION[ext];
13063
- if (name !== void 0) {
13064
- return name;
13065
- }
13066
- }
13067
- if (artifact.kind === "code") {
13068
- return "code";
13069
- }
13070
- if (artifact.kind === "dsl") {
13071
- return "layout-grid";
13072
- }
13073
- return ext === null ? "file-text" : "file";
13074
- }
13075
- function buildArtifactTree(artifacts) {
13076
- const root = { name: "", path: "", children: [] };
13077
- for (const artifact of artifacts) {
13078
- const segments = artifact.id.split("/").filter(Boolean);
13079
- let node = root;
13080
- for (let i = 0; i < segments.length - 1; i++) {
13081
- const path = segments.slice(0, i + 1).join("/");
13082
- let child = node.children.find(
13083
- (item) => item.path === path && item.artifact === void 0
13084
- );
13085
- if (!child) {
13086
- child = { name: segments[i], path, children: [] };
13087
- node.children.push(child);
13088
- }
13089
- node = child;
13090
- }
13091
- node.children.push({
13092
- name: artifact.title,
13093
- path: artifact.id,
13094
- artifact,
13095
- children: []
13096
- });
13097
- }
13098
- const sortLevel = (nodes) => {
13099
- nodes.sort((a, b) => {
13100
- const dirDelta = Number(b.artifact === void 0) - Number(a.artifact === void 0);
13101
- return dirDelta !== 0 ? dirDelta : a.name.localeCompare(b.name);
13102
- });
13103
- for (const item of nodes) {
13104
- sortLevel(item.children);
13105
- }
13106
- };
13107
- sortLevel(root.children);
13108
- return root.children;
13109
- }
13110
-
13111
- // src/components/chat/ArtifactPanel/download.ts
13112
- var KIND_EXTENSIONS = {
13113
- markdown: "md",
13114
- text: "txt",
13115
- code: "txt",
13116
- dsl: "json"
13117
- };
13118
- function resolveArtifactFilename(artifact) {
13119
- const title = artifact.title.trim() || "artifact";
13120
- if (/\.[A-Za-z0-9]+$/.test(title)) {
13121
- return title;
13122
- }
13123
- const ext = resolveArtifactFileType(artifact) ?? KIND_EXTENSIONS[artifact.kind];
13124
- return `${title}.${ext}`;
13125
- }
13126
- function dataUrlToBlob(dataUrl) {
13127
- const comma = dataUrl.indexOf(",");
13128
- const meta = dataUrl.slice(0, comma);
13129
- const payload = dataUrl.slice(comma + 1);
13130
- const mime = /^data:([^;,]+)/.exec(meta)?.[1] ?? "application/octet-stream";
13131
- if (meta.includes(";base64")) {
13132
- const binary = atob(payload);
13133
- const bytes = new Uint8Array(binary.length);
13134
- for (let i = 0; i < binary.length; i++) {
13135
- bytes[i] = binary.charCodeAt(i);
13136
- }
13137
- return new Blob([bytes], { type: mime });
13138
- }
13139
- return new Blob([decodeURIComponent(payload)], { type: mime });
13140
- }
13141
- function isRemoteUrl(raw) {
13142
- return /^https?:\/\//.test(raw.trim());
13143
- }
13144
- function buildArtifactBlob(artifact) {
13145
- if (artifact.kind === "dsl" && typeof artifact.content === "object") {
13146
- return new Blob([JSON.stringify(artifact.content, null, 2)], {
13147
- type: "application/json"
13148
- });
13149
- }
13150
- const raw = String(artifact.content);
13151
- if (isImageArtifact(artifact)) {
13152
- const trimmed = raw.trim();
13153
- if (trimmed.startsWith("data:")) {
13154
- return dataUrlToBlob(trimmed);
13155
- }
13156
- if (trimmed.startsWith("<svg") || trimmed.startsWith("<?xml")) {
13157
- return new Blob([raw], { type: "image/svg+xml" });
13158
- }
13159
- if (isRemoteUrl(trimmed)) {
13160
- return null;
13161
- }
13162
- }
13163
- const ext = resolveArtifactFileType(artifact);
13164
- const mime = ext === "md" || artifact.kind === "markdown" ? "text/markdown" : "text/plain";
13165
- return new Blob([raw], { type: `${mime};charset=utf-8` });
13166
- }
13167
- function saveBlob(blob, filename) {
13168
- const url = URL.createObjectURL(blob);
13169
- const anchor = document.createElement("a");
13170
- anchor.href = url;
13171
- anchor.download = filename;
13172
- document.body.appendChild(anchor);
13173
- anchor.click();
13174
- anchor.remove();
13175
- setTimeout(() => URL.revokeObjectURL(url), 1e3);
13176
- }
13177
- async function downloadArtifact(artifact) {
13178
- const filename = resolveArtifactFilename(artifact);
13179
- const raw = String(artifact.content ?? "").trim();
13180
- if (isImageArtifact(artifact) && isRemoteUrl(raw)) {
13181
- try {
13182
- const response = await fetch(raw);
13183
- if (!response.ok) {
13184
- throw new Error(`HTTP ${response.status}`);
13185
- }
13186
- saveBlob(await response.blob(), filename);
13187
- return;
13188
- } catch {
13189
- window.open(raw, "_blank", "noopener");
13190
- return;
13191
- }
13192
- }
13193
- const blob = buildArtifactBlob(artifact);
13194
- if (blob) {
13195
- saveBlob(blob, filename);
13196
- }
13197
- }
13198
-
13199
- // src/components/chat/ArtifactPanel/dsl-preview.tsx
13200
- var import_core2 = require("@pactor-app/core");
13201
- var import_runtime50 = require("@pactor-app/runtime");
13202
- var import_react61 = require("react");
13203
- var import_jsx_runtime175 = require("react/jsx-runtime");
13204
- var ARTIFACT_DENIED_TYPE = "ChatDenied";
13205
- function ArtifactDenied({ deniedType }) {
13206
- return /* @__PURE__ */ (0, import_jsx_runtime175.jsxs)(
13207
- "div",
13208
- {
13209
- "data-slot": "artifact-denied",
13210
- "data-denied-type": deniedType,
13211
- className: "rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground",
13212
- children: [
13213
- "\u8BE5\u7EC4\u4EF6\u4E0D\u5141\u8BB8\u5728\u4EA7\u7269\u9884\u89C8\u4E2D\u4F7F\u7528",
13214
- deniedType ? `\uFF1A${deniedType}` : ""
13215
- ]
13216
- }
13217
- );
13218
- }
13219
- function guardArtifactDsl(node, registry) {
13220
- const type = node.type ?? "(reference)";
13221
- if (type !== ARTIFACT_DENIED_TYPE && !registry.has(type)) {
13222
- return {
13223
- type: ARTIFACT_DENIED_TYPE,
13224
- props: { deniedType: type }
13225
- };
13226
- }
13227
- if (node.children === void 0) {
13228
- return node;
13229
- }
13230
- if (!Array.isArray(node.children)) {
13231
- return {
13232
- type: ARTIFACT_DENIED_TYPE,
13233
- props: { deniedType: `${type}(remote)` }
13234
- };
13235
- }
13236
- if (node.children.length === 0) {
13237
- return node;
13238
- }
13239
- return {
13240
- ...node,
13241
- // 数组中混排的引用元素同样无法静态过白名单:替换为受控占位
13242
- children: node.children.map(
13243
- (child) => (0, import_core2.isDslSourceRef)(child) ? { type: ARTIFACT_DENIED_TYPE, props: { deniedType: "(remote)" } } : guardArtifactDsl(child, registry)
13244
- )
13245
- };
13246
- }
13247
- function useArtifactPreviewRuntime(safeRegistry, user) {
13248
- return (0, import_react61.useMemo)(() => {
13249
- if (!safeRegistry) {
13250
- return void 0;
13251
- }
13252
- const components = new import_core2.Registry("ArtifactPreviewRegistry");
13253
- components.register(ARTIFACT_DENIED_TYPE, ArtifactDenied);
13254
- for (const name of safeRegistry.list()) {
13255
- components.register(name, safeRegistry.get(name));
13256
- }
13257
- const context = (0, import_runtime50.createRuntimeContext)({ components, user });
13258
- (0, import_runtime50.registerBuiltinActions)(context.actions);
13259
- return { context, engine: new import_runtime50.ActionEngine(context) };
13260
- }, [safeRegistry, user]);
13261
- }
13262
- var ArtifactDslGuard = class extends import_react61.Component {
13263
- constructor() {
13264
- super(...arguments);
13265
- this.state = { failed: false };
13266
- }
13267
- static getDerivedStateFromError() {
13268
- return { failed: true };
13269
- }
13270
- componentDidCatch(error) {
13271
- console.warn("[@pactor-app/ui] DSL \u4EA7\u7269\u6E32\u67D3\u5931\u8D25\uFF0C\u5DF2\u964D\u7EA7\u5C55\u793A", error);
13272
- }
13273
- render() {
13274
- return this.state.failed ? /* @__PURE__ */ (0, import_jsx_runtime175.jsx)(
13275
- "div",
13276
- {
13277
- "data-slot": "artifact-denied",
13278
- className: "rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground",
13279
- children: "DSL \u4EA7\u7269\u6E32\u67D3\u5931\u8D25\uFF0C\u5DF2\u964D\u7EA7\u5C55\u793A"
13280
- }
13281
- ) : this.props.children;
13282
- }
13283
- };
13284
- function ArtifactDslNode({ node }) {
13285
- const { renderChildren } = (0, import_runtime50.useRenderer)();
13286
- return /* @__PURE__ */ (0, import_jsx_runtime175.jsx)(import_jsx_runtime175.Fragment, { children: renderChildren([node]) });
13287
- }
13288
- function ArtifactDslContent({
13289
- content,
13290
- runtime,
13291
- style
13292
- }) {
13293
- const node = typeof content === "object" && content !== null && !Array.isArray(content) && typeof content.type === "string" ? content : void 0;
13294
- if (!node || !runtime) {
13295
- return /* @__PURE__ */ (0, import_jsx_runtime175.jsx)(
13296
- "div",
13297
- {
13298
- "data-slot": "artifact-denied",
13299
- className: "rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground",
13300
- style,
13301
- children: node ? "DSL \u9884\u89C8\u4E0D\u53EF\u7528\uFF08\u672A\u6CE8\u5165 Safe Registry\uFF09" : "DSL \u4EA7\u7269\u5185\u5BB9\u65E0\u6548"
13302
- }
13303
- );
13304
- }
13305
- const guarded = guardArtifactDsl(node, runtime.context.components);
13306
- return /* @__PURE__ */ (0, import_jsx_runtime175.jsx)(import_runtime50.DslRenderer, { context: runtime.context, engine: runtime.engine, children: /* @__PURE__ */ (0, import_jsx_runtime175.jsx)(ArtifactDslGuard, { children: /* @__PURE__ */ (0, import_jsx_runtime175.jsx)(ArtifactDslNode, { node: guarded }) }) });
13307
- }
13308
-
13309
- // src/components/chat/ArtifactPanel/index.tsx
13310
- var import_jsx_runtime176 = require("react/jsx-runtime");
13311
- function ArtifactKindIcon({ artifact }) {
13312
- return /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(Icon, { name: resolveArtifactIconName(artifact), size: 13 });
13313
- }
13314
- function ArtifactListView({
13315
- nodes,
13316
- activeId,
13317
- onSelect
13318
- }) {
13319
- return /* @__PURE__ */ (0, import_jsx_runtime176.jsx)("ul", { "data-slot": "artifact-panel-list", className: "flex flex-col gap-0.5", children: nodes.map((node) => /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(
13320
- ArtifactTreeRow,
13321
- {
13322
- node,
13323
- activeId,
13324
- onSelect
13325
- },
13326
- node.path
13327
- )) });
13328
- }
13329
- function ArtifactTreeRow({
13330
- node,
13331
- activeId,
13332
- onSelect
13333
- }) {
13334
- const [open, setOpen] = (0, import_react62.useState)(true);
13335
- if (node.artifact) {
13336
- return /* @__PURE__ */ (0, import_jsx_runtime176.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime176.jsxs)(
13337
- "button",
13338
- {
13339
- type: "button",
13340
- "data-slot": "artifact-panel-list-item",
13341
- "data-active": node.artifact.id === activeId,
13342
- className: "flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm hover:bg-accent data-[active=true]:bg-accent",
13343
- onClick: () => onSelect?.(node.artifact.id),
13344
- children: [
13345
- /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(ArtifactKindIcon, { artifact: node.artifact }),
13346
- /* @__PURE__ */ (0, import_jsx_runtime176.jsx)("span", { className: "min-w-0 flex-1 truncate", children: node.artifact.title }),
13347
- /* @__PURE__ */ (0, import_jsx_runtime176.jsx)("span", { className: "shrink-0 text-xs text-muted-foreground", children: node.artifact.kind })
13348
- ]
13349
- }
13350
- ) });
13351
- }
13352
- return /* @__PURE__ */ (0, import_jsx_runtime176.jsxs)("li", { children: [
13353
- /* @__PURE__ */ (0, import_jsx_runtime176.jsxs)(
13354
- "button",
13355
- {
13356
- type: "button",
13357
- "data-slot": "artifact-panel-list-folder",
13358
- "aria-expanded": open,
13359
- "data-open": open,
13360
- className: "flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm hover:bg-accent",
13361
- onClick: () => setOpen((value) => !value),
13362
- children: [
13363
- /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(
13364
- "span",
13365
- {
13366
- className: "inline-flex transition-transform data-[open=true]:rotate-90",
13367
- "data-open": open,
13368
- children: /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(Icon, { name: "chevron-right", size: 12 })
13369
- }
13370
- ),
13371
- /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(Icon, { name: "folder", size: 13 }),
13372
- /* @__PURE__ */ (0, import_jsx_runtime176.jsx)("span", { className: "min-w-0 flex-1 truncate", children: node.name })
13373
- ]
13374
- }
13375
- ),
13376
- open && /* @__PURE__ */ (0, import_jsx_runtime176.jsx)("div", { className: "pl-4", children: /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(
13377
- ArtifactListView,
13378
- {
13379
- nodes: node.children,
13380
- activeId,
13381
- onSelect
13382
- }
13383
- ) })
13384
- ] });
13385
- }
13386
- function ArtifactImageContent({ artifact }) {
13387
- const raw = String(artifact.content).trim();
13388
- const src = raw.startsWith("<svg") || raw.startsWith("<?xml") ? `data:image/svg+xml;utf8,${encodeURIComponent(raw)}` : raw;
13389
- return /* @__PURE__ */ (0, import_jsx_runtime176.jsx)("div", { "data-slot": "artifact-panel-image", className: "flex justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime176.jsx)("img", { src, alt: artifact.title, className: "max-w-full rounded-md" }) });
13390
- }
13391
- function ArtifactContent({
13392
- artifact,
13393
- runtime,
13394
- viewers
13395
- }) {
13396
- const ext = resolveArtifactFileType(artifact);
13397
- const custom = (ext ? viewers?.[ext] : void 0) ?? viewers?.[artifact.kind];
13398
- if (custom) {
13399
- return /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(import_jsx_runtime176.Fragment, { children: custom(artifact) });
13400
- }
13401
- if (isImageArtifact(artifact)) {
13402
- return /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(ArtifactImageContent, { artifact });
13403
- }
13404
- switch (artifact.kind) {
13405
- case "markdown":
13406
- return /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(import_markdown2.MarkdownDslComponent, { content: String(artifact.content) });
13407
- case "code":
13408
- return /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(
13409
- CodeBlock,
13410
- {
13411
- code: String(artifact.content),
13412
- language: artifact.language
13413
- }
13414
- );
13415
- case "text":
13416
- return /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(
13417
- "pre",
13418
- {
13419
- "data-slot": "artifact-panel-text",
13420
- className: "text-sm break-words whitespace-pre-wrap",
13421
- children: String(artifact.content)
13422
- }
13423
- );
13424
- case "dsl":
13425
- return /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(ArtifactDslContent, { content: artifact.content, runtime });
13426
- default:
13427
- return null;
13428
- }
13429
- }
13430
- function ArtifactPanel({
13431
- artifact,
13432
- artifacts,
13433
- activeArtifactId,
13434
- onSelectArtifact,
13435
- title = "\u4EA7\u7269",
13436
- emptyText = "\u6682\u65E0\u4EA7\u7269",
13437
- onClose,
13438
- onDownload,
13439
- safeRegistry,
13440
- user,
13441
- artifactViewers,
13442
- className,
13443
- style
13444
- }) {
13445
- const runtime = useArtifactPreviewRuntime(safeRegistry, user);
13446
- const [view, setView] = (0, import_react62.useState)("content");
13447
- const activeId = activeArtifactId ?? artifact?.id;
13448
- (0, import_react62.useEffect)(() => {
13449
- setView("content");
13450
- }, [activeId]);
13451
- const hasArtifacts = artifacts !== void 0 && artifacts.length > 0;
13452
- const headerTitle = view === "list" ? "\u4EA7\u7269\u76EE\u5F55" : artifact?.title ?? title;
13453
- return /* @__PURE__ */ (0, import_jsx_runtime176.jsxs)(
13454
- "aside",
13455
- {
13456
- "data-slot": "artifact-panel",
13457
- "data-kind": artifact?.kind,
13458
- className: cn("flex h-full min-w-0 flex-col", className),
13459
- style,
13460
- children: [
13461
- /* @__PURE__ */ (0, import_jsx_runtime176.jsxs)("div", { className: "flex shrink-0 items-center gap-1 border-b border-border px-2 py-2.5", children: [
13462
- hasArtifacts ? /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(
13463
- Button2,
13464
- {
13465
- icon: "list",
13466
- variant: "text",
13467
- tooltip: view === "list" ? "\u8FD4\u56DE\u5185\u5BB9" : "\u4EA7\u7269\u76EE\u5F55",
13468
- onClick: () => setView((value) => value === "list" ? "content" : "list")
13469
- }
13470
- ) : null,
13471
- hasArtifacts ? /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(
13472
- DropdownMenu2,
13473
- {
13474
- items: artifacts.map((item) => ({
13475
- label: item.title,
13476
- value: item.id,
13477
- icon: resolveArtifactIconName(item)
13478
- })),
13479
- onSelect: (id) => onSelectArtifact?.(id),
13480
- children: /* @__PURE__ */ (0, import_jsx_runtime176.jsxs)(
13481
- "span",
13482
- {
13483
- "data-slot": "artifact-panel-menu-trigger",
13484
- className: "flex min-w-0 cursor-pointer items-center gap-1 rounded-md px-1.5 py-1 text-sm font-medium hover:bg-accent",
13485
- children: [
13486
- view !== "list" && artifact ? /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(ArtifactKindIcon, { artifact }) : null,
13487
- /* @__PURE__ */ (0, import_jsx_runtime176.jsx)("span", { className: "truncate", children: headerTitle }),
13488
- /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(Icon, { name: "chevron-down", size: 12 })
13489
- ]
13490
- }
13491
- )
13492
- }
13493
- ) : /* @__PURE__ */ (0, import_jsx_runtime176.jsx)("span", { className: "min-w-0 flex-1 truncate px-1.5 text-sm font-medium", children: headerTitle }),
13494
- view !== "list" && artifact ? /* @__PURE__ */ (0, import_jsx_runtime176.jsx)("span", { className: "shrink-0 rounded-md bg-accent px-1.5 py-0.5 text-xs text-muted-foreground", children: artifact.kind }) : null,
13495
- /* @__PURE__ */ (0, import_jsx_runtime176.jsx)("span", { className: "flex-1" }),
13496
- artifact ? /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(
13497
- Button2,
13498
- {
13499
- icon: "download",
13500
- variant: "text",
13501
- tooltip: "\u4E0B\u8F7D\u4EA7\u7269",
13502
- className: "px-2",
13503
- onClick: () => onDownload ? onDownload(artifact) : void downloadArtifact(artifact)
13504
- }
13505
- ) : null,
13506
- /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(
13507
- Button2,
13508
- {
13509
- icon: "x",
13510
- variant: "text",
13511
- tooltip: "\u6536\u8D77\u4EA7\u7269\u533A",
13512
- className: "px-2",
13513
- onClick: () => onClose?.()
13514
- }
13515
- )
13516
- ] }),
13517
- /* @__PURE__ */ (0, import_jsx_runtime176.jsx)("div", { className: "scrollbar-subtle min-h-0 flex-1 overflow-auto p-4", children: view === "list" && artifacts !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(
13518
- ArtifactListView,
13519
- {
13520
- nodes: buildArtifactTree(artifacts),
13521
- activeId,
13522
- onSelect: onSelectArtifact
13523
- }
13524
- ) : artifact ? /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(
13525
- ArtifactContent,
13526
- {
13527
- artifact,
13528
- runtime,
13529
- viewers: artifactViewers
13530
- }
13531
- ) : /* @__PURE__ */ (0, import_jsx_runtime176.jsx)("p", { className: "text-sm text-muted-foreground", children: emptyText }) })
13532
- ]
13533
- }
13534
- );
13535
- }
13536
-
13537
- // src/components/chat/Attachment/index.tsx
13538
- var import_jsx_runtime177 = require("react/jsx-runtime");
13539
- var kindIcon2 = { image: "image", text: "file-text", video: "video", audio: "music", other: "file" };
13540
- function formatSize3(bytes) {
13541
- if (bytes < 1024) return `${bytes} B`;
13542
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
13543
- return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
13544
- }
13545
- function Attachment({ items = [], onRemove, onRetry, className, style }) {
13546
- return /* @__PURE__ */ (0, import_jsx_runtime177.jsx)("div", { "data-slot": "attachment", className: cn("flex flex-wrap gap-2", className), style, children: items.map((item, index) => {
13547
- const key = item.id ?? index;
13548
- const kind = inferKind(item.name, item.fileType);
13549
- const status = item.status ?? "done";
13550
- return /* @__PURE__ */ (0, import_jsx_runtime177.jsxs)(
13551
- "div",
13552
- {
13553
- "data-slot": "attachment-item",
13554
- "data-status": status,
13555
- className: "flex w-56 flex-col rounded-md border border-border px-2 py-1.5",
13556
- children: [
13557
- /* @__PURE__ */ (0, import_jsx_runtime177.jsxs)("div", { className: "flex items-center gap-1.5", children: [
13558
- /* @__PURE__ */ (0, import_jsx_runtime177.jsx)("span", { className: "shrink-0 text-muted-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime177.jsx)(Icon, { name: kindIcon2[kind], size: "sm" }) }),
13559
- /* @__PURE__ */ (0, import_jsx_runtime177.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm", children: item.name }),
13560
- status === "uploading" ? /* @__PURE__ */ (0, import_jsx_runtime177.jsx)("span", { className: "shrink-0 animate-spin text-muted-foreground motion-reduce:animate-none", children: /* @__PURE__ */ (0, import_jsx_runtime177.jsx)(Icon, { name: "loader-circle", size: "sm" }) }) : null,
13561
- status === "error" && onRetry ? /* @__PURE__ */ (0, import_jsx_runtime177.jsx)(
13562
- "button",
13563
- {
13564
- type: "button",
13565
- "data-slot": "attachment-retry",
13566
- "aria-label": `\u91CD\u8BD5 ${item.name}`,
13567
- className: "shrink-0 text-destructive hover:opacity-80",
13568
- onClick: () => onRetry(key),
13569
- children: /* @__PURE__ */ (0, import_jsx_runtime177.jsx)(Icon, { name: "refresh-ccw", size: "sm" })
13570
- }
13571
- ) : null,
13572
- onRemove ? /* @__PURE__ */ (0, import_jsx_runtime177.jsx)(
13573
- "button",
13574
- {
13575
- type: "button",
13576
- "data-slot": "attachment-remove",
13577
- "aria-label": `\u79FB\u9664 ${item.name}`,
13578
- className: "shrink-0 text-muted-foreground hover:text-foreground",
13579
- onClick: () => onRemove(key),
13580
- children: /* @__PURE__ */ (0, import_jsx_runtime177.jsx)(Icon, { name: "x", size: "sm" })
13581
- }
13582
- ) : null
13583
- ] }),
13584
- /* @__PURE__ */ (0, import_jsx_runtime177.jsxs)("div", { className: "mt-0.5 flex items-center gap-1.5 pl-5 text-xs", children: [
13585
- item.fileSize !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime177.jsx)("span", { className: "text-muted-foreground", children: formatSize3(item.fileSize) }) : null,
13586
- status === "error" ? /* @__PURE__ */ (0, import_jsx_runtime177.jsx)("span", { className: "text-destructive", children: "\u4E0A\u4F20\u5931\u8D25" }) : null
13587
- ] }),
13588
- status === "uploading" && item.progress !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime177.jsx)("div", { "data-slot": "attachment-progress", className: "mt-1 ml-5 h-1 overflow-hidden rounded-full bg-muted", children: /* @__PURE__ */ (0, import_jsx_runtime177.jsx)("div", { className: "h-full bg-primary transition-[width]", style: { width: `${item.progress}%` } }) }) : null
13589
- ]
13590
- },
13591
- key
13592
- );
13593
- }) });
13594
- }
13595
-
13596
- // src/ui/bubble.tsx
13597
- var React7 = __toESM(require("react"), 1);
13598
- var import_class_variance_authority10 = require("class-variance-authority");
13599
- var import_jsx_runtime178 = require("react/jsx-runtime");
13600
- var bubbleVariants = (0, import_class_variance_authority10.cva)(
13601
- "group/bubble relative flex w-fit max-w-[80%] min-w-0 flex-col gap-1 group-data-[align=end]/message:self-end data-[align=end]:self-end data-[variant=ghost]:max-w-full",
13602
- {
13603
- variants: {
13604
- variant: {
13605
- default: "*:data-[slot=bubble-content]:bg-primary *:data-[slot=bubble-content]:text-primary-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-primary/80",
13606
- secondary: "*:data-[slot=bubble-content]:bg-secondary *:data-[slot=bubble-content]:text-secondary-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)]",
13607
- muted: "*:data-[slot=bubble-content]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[color-mix(in_oklch,var(--muted),var(--foreground)_5%)]",
13608
- tinted: "*:data-[slot=bubble-content]:bg-(--bubble-background) *:data-[slot=bubble-content]:text-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-(--bubble-hover-background)",
13609
- outline: "*:data-[slot=bubble-content]:border-border *:data-[slot=bubble-content]:bg-card [&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:text-foreground dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-input/30",
13610
- ghost: "border-none *:data-[slot=bubble-content]:rounded-none *:data-[slot=bubble-content]:bg-transparent *:data-[slot=bubble-content]:p-0 [&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:text-foreground dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted/50",
13611
- destructive: "*:data-[slot=bubble-content]:bg-destructive/10 *:data-[slot=bubble-content]:text-destructive dark:*:data-[slot=bubble-content]:bg-destructive/20 [&>[data-slot=bubble-content]:is(button,a):hover]:bg-destructive/20 dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-destructive/30"
13612
- }
13613
- },
13614
- defaultVariants: {
13615
- variant: "default"
13616
- }
13617
- }
13618
- );
13619
- function Bubble({
13620
- variant = "default",
13621
- align = "start",
13622
- className,
13623
- ...props
13624
- }) {
13625
- return /* @__PURE__ */ (0, import_jsx_runtime178.jsx)(
13626
- "div",
13627
- {
13628
- "data-slot": "bubble",
13629
- "data-variant": variant,
13630
- "data-align": align,
13631
- className: cn(bubbleVariants({ variant }), className),
13632
- ...props
13633
- }
13634
- );
13635
- }
13636
- function BubbleContent({
13637
- asChild = false,
13638
- className,
13639
- children,
13640
- ...props
13641
- }) {
13642
- const cls = cn(
13643
- "w-fit max-w-full min-w-0 overflow-hidden rounded-xl border border-transparent px-3 py-2 text-sm leading-relaxed wrap-break-word group-data-[align=end]/bubble:self-end [button]:text-left [button,a]:transition-colors [button,a]:outline-none [button,a]:focus-visible:border-ring [button,a]:focus-visible:ring-3 [button,a]:focus-visible:ring-ring/50",
13644
- className
13645
- );
13646
- if (asChild && React7.isValidElement(children)) {
13647
- return React7.cloneElement(children, {
13648
- "data-slot": "bubble-content",
13649
- className: cn(cls, children.props.className),
13650
- ...props
13651
- });
13652
- }
13653
- return /* @__PURE__ */ (0, import_jsx_runtime178.jsx)(
13654
- "div",
13655
- {
13656
- "data-slot": "bubble-content",
13657
- className: cls,
13658
- ...props,
13659
- children
13660
- }
13661
- );
13662
- }
13663
- var bubbleReactionsVariants = (0, import_class_variance_authority10.cva)(
13664
- "absolute z-10 flex w-fit shrink-0 items-center justify-center gap-1 rounded-full bg-muted px-1.5 py-0.5 text-sm ring-3 ring-card has-[button]:p-0",
13665
- {
13666
- variants: {
13667
- side: {
13668
- top: "top-0 -translate-y-3/4",
13669
- bottom: "bottom-0 translate-y-3/4"
13670
- },
13671
- align: {
13672
- start: "left-3",
13673
- end: "right-3"
13674
- }
13675
- },
13676
- defaultVariants: {
13677
- side: "bottom",
13678
- align: "end"
13679
- }
13680
- }
13681
- );
13682
-
13683
- // src/components/chat/Bubble/index.tsx
13684
- var import_jsx_runtime179 = require("react/jsx-runtime");
13685
- function Bubble2({ align = "start", variant = "muted", className, style, children }) {
13686
- return /* @__PURE__ */ (0, import_jsx_runtime179.jsx)(Bubble, { variant, align, className, style, children: /* @__PURE__ */ (0, import_jsx_runtime179.jsx)(BubbleContent, { children }) });
13687
- }
13688
-
13689
- // src/components/chat/Sender/index.tsx
13690
- var import_jsx_runtime180 = require("react/jsx-runtime");
13691
- function Sender({ name, avatar, avatarText, role, time, align = "start", className, style }) {
13692
- return /* @__PURE__ */ (0, import_jsx_runtime180.jsxs)(
13693
- "div",
13694
- {
13695
- "data-slot": "sender",
13696
- className: cn("flex items-center gap-2", align === "end" && "flex-row-reverse", className),
13697
- style,
13698
- children: [
13699
- /* @__PURE__ */ (0, import_jsx_runtime180.jsx)(Avatar2, { src: avatar, fallback: avatarText ?? name?.slice(0, 1) ?? "?", className: "size-7" }),
13700
- /* @__PURE__ */ (0, import_jsx_runtime180.jsxs)("div", { className: cn("flex min-w-0 items-baseline gap-1.5", align === "end" && "flex-row-reverse"), children: [
13701
- name ? /* @__PURE__ */ (0, import_jsx_runtime180.jsx)("span", { "data-slot": "sender-name", className: "truncate text-sm font-medium", children: name }) : null,
13702
- role ? /* @__PURE__ */ (0, import_jsx_runtime180.jsx)("span", { "data-slot": "sender-role", className: "shrink-0 text-xs text-muted-foreground", children: role }) : null,
13703
- time ? /* @__PURE__ */ (0, import_jsx_runtime180.jsx)("span", { "data-slot": "sender-time", className: "shrink-0 text-xs text-muted-foreground", children: time }) : null
13704
- ] })
13705
- ]
13706
- }
13707
- );
13708
- }
13709
-
13710
- // src/components/chat/StreamingText/index.tsx
13711
- var import_react63 = require("react");
13712
- var import_jsx_runtime181 = require("react/jsx-runtime");
13713
- function StreamingText({ content = "", streaming, typing, speed = 2, className, style }) {
13714
- const [revealed, setRevealed] = (0, import_react63.useState)(typing ? 0 : content.length);
13715
- (0, import_react63.useEffect)(() => {
13716
- if (!typing) {
13717
- setRevealed(content.length);
13718
- return;
13719
- }
13720
- setRevealed(0);
13721
- const timer = window.setInterval(() => {
13722
- setRevealed((value) => {
13723
- if (value >= content.length) {
13724
- window.clearInterval(timer);
13725
- return value;
13726
- }
13727
- return Math.min(content.length, value + speed);
13728
- });
13729
- }, 24);
13730
- return () => window.clearInterval(timer);
13731
- }, [content, typing, speed]);
13732
- const typingDone = !typing || revealed >= content.length;
13733
- const showCursor = streaming || typing && !typingDone;
13734
- return /* @__PURE__ */ (0, import_jsx_runtime181.jsxs)("span", { "data-slot": "streaming-text", className: cn("whitespace-pre-wrap", className), style, children: [
13735
- typing ? content.slice(0, revealed) : content,
13736
- showCursor ? /* @__PURE__ */ (0, import_jsx_runtime181.jsx)(
13737
- "span",
13738
- {
13739
- "data-slot": "streaming-text-cursor",
13740
- "aria-hidden": "true",
13741
- className: "ml-0.5 inline-block animate-pulse text-primary motion-reduce:animate-none",
13742
- children: "\u258D"
13743
- }
13744
- ) : null
13745
- ] });
13746
- }
13747
-
13748
- // src/components/chat/Thinking/index.tsx
13749
- var import_react64 = require("react");
13750
- var import_jsx_runtime182 = require("react/jsx-runtime");
13751
- function Thinking({
13752
- content,
13753
- status = "thinking",
13754
- title,
13755
- expanded,
13756
- defaultExpanded,
13757
- onToggle,
13758
- className,
13759
- style
13760
- }) {
13761
- const [innerExpanded, setInnerExpanded] = (0, import_react64.useState)(defaultExpanded ?? status === "thinking");
13762
- const isExpanded = expanded ?? innerExpanded;
13763
- const resolvedTitle = title ?? (status === "thinking" ? "\u601D\u8003\u4E2D\u2026" : "\u601D\u8003\u5B8C\u6210");
13764
- const toggle = () => {
13765
- if (expanded === void 0) {
13766
- setInnerExpanded(!isExpanded);
13767
- }
13768
- onToggle?.(!isExpanded);
13769
- };
13770
- return /* @__PURE__ */ (0, import_jsx_runtime182.jsxs)(
13771
- "div",
13772
- {
13773
- "data-slot": "thinking",
13774
- "data-status": status,
13775
- className: cn("w-full", className),
13776
- style,
13777
- children: [
13778
- /* @__PURE__ */ (0, import_jsx_runtime182.jsxs)(
13779
- "button",
13780
- {
13781
- type: "button",
13782
- "data-slot": "thinking-trigger",
13783
- "aria-expanded": isExpanded,
13784
- className: "flex w-full cursor-pointer items-center gap-2 py-1 text-left text-sm",
13785
- onClick: toggle,
13786
- children: [
13787
- /* @__PURE__ */ (0, import_jsx_runtime182.jsx)("span", { className: cn("text-muted-foreground", status === "thinking" && "animate-pulse motion-reduce:animate-none"), children: /* @__PURE__ */ (0, import_jsx_runtime182.jsx)(Icon, { name: "brain", size: "sm" }) }),
13788
- /* @__PURE__ */ (0, import_jsx_runtime182.jsx)("span", { "data-slot": "thinking-title", className: "text-muted-foreground", children: resolvedTitle }),
13789
- /* @__PURE__ */ (0, import_jsx_runtime182.jsx)("span", { className: "text-muted-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime182.jsx)(Icon, { name: isExpanded ? "chevron-down" : "chevron-right", size: "sm" }) })
13790
- ]
13791
- }
13792
- ),
13793
- isExpanded && content ? /* @__PURE__ */ (0, import_jsx_runtime182.jsx)(
13794
- "div",
13795
- {
13796
- "data-slot": "thinking-content",
13797
- className: "py-1 pl-6 text-sm whitespace-pre-wrap text-muted-foreground",
13798
- children: content
13799
- }
13800
- ) : null
13801
- ]
13802
- }
13803
- );
13804
- }
13805
-
13806
- // src/components/chat/ToolCall/index.tsx
13807
- var import_react65 = require("react");
13808
- var import_jsx_runtime183 = require("react/jsx-runtime");
13809
- var statusIcon3 = {
13810
- running: { icon: "loader-circle", className: "text-info", spin: true },
13811
- done: { icon: "circle-check", className: "text-success" },
13812
- error: { icon: "circle-x", className: "text-destructive" }
13813
- };
13814
- function renderValue(value) {
13815
- if (value === void 0 || value === null) return "";
13816
- if (typeof value === "string") return value;
13817
- try {
13818
- return JSON.stringify(value, null, 2);
13819
- } catch {
13820
- return String(value);
13821
- }
13822
- }
13823
- function ToolCall({ name, params, status = "done", result, durationMs, defaultExpanded, className, style }) {
13824
- const [expanded, setExpanded] = (0, import_react65.useState)(defaultExpanded ?? status === "running");
13825
- const meta = statusIcon3[status];
13826
- const paramsText = renderValue(params);
13827
- const hasDetail = Boolean(paramsText) || Boolean(result);
13828
- const headerInner = /* @__PURE__ */ (0, import_jsx_runtime183.jsxs)(import_jsx_runtime183.Fragment, { children: [
13829
- /* @__PURE__ */ (0, import_jsx_runtime183.jsx)("span", { className: "shrink-0 text-muted-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime183.jsx)(Icon, { name: "terminal", size: "sm" }) }),
13830
- /* @__PURE__ */ (0, import_jsx_runtime183.jsx)("span", { "data-slot": "tool-call-name", className: "min-w-0 truncate font-medium", children: name ?? "\u5DE5\u5177\u8C03\u7528" }),
13831
- /* @__PURE__ */ (0, import_jsx_runtime183.jsx)("span", { className: cn("shrink-0", meta.className, meta.spin && "animate-spin motion-reduce:animate-none"), children: /* @__PURE__ */ (0, import_jsx_runtime183.jsx)(Icon, { name: meta.icon, size: "sm" }) }),
13832
- durationMs !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime183.jsxs)("span", { className: "shrink-0 text-xs text-muted-foreground", children: [
13833
- (durationMs / 1e3).toFixed(1),
13834
- "s"
13835
- ] }) : null,
13836
- hasDetail ? /* @__PURE__ */ (0, import_jsx_runtime183.jsx)("span", { className: "shrink-0 text-muted-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime183.jsx)(Icon, { name: expanded ? "chevron-down" : "chevron-right", size: "sm" }) }) : null
13837
- ] });
13838
- return /* @__PURE__ */ (0, import_jsx_runtime183.jsxs)(
13839
- "div",
13840
- {
13841
- "data-slot": "tool-call",
13842
- "data-status": status,
13843
- className: cn("w-full", className),
13844
- style,
13845
- children: [
13846
- hasDetail ? /* @__PURE__ */ (0, import_jsx_runtime183.jsx)(
13847
- "button",
13848
- {
13849
- type: "button",
13850
- "data-slot": "tool-call-trigger",
13851
- "aria-expanded": expanded,
13852
- className: "flex w-full cursor-pointer items-center gap-2 py-1 text-left text-sm",
13853
- onClick: () => setExpanded((value) => !value),
13854
- children: headerInner
13855
- }
13856
- ) : /* @__PURE__ */ (0, import_jsx_runtime183.jsx)(
13857
- "div",
13858
- {
13859
- "data-slot": "tool-call-trigger",
13860
- className: "flex w-full items-center gap-2 py-1 text-left text-sm",
13861
- children: headerInner
13862
- }
13863
- ),
13864
- hasDetail && expanded ? /* @__PURE__ */ (0, import_jsx_runtime183.jsxs)("div", { "data-slot": "tool-call-body", className: "flex flex-col gap-2 py-1 pl-6", children: [
13865
- paramsText ? /* @__PURE__ */ (0, import_jsx_runtime183.jsxs)("div", { children: [
13866
- /* @__PURE__ */ (0, import_jsx_runtime183.jsx)("div", { className: "text-xs font-medium text-muted-foreground", children: "\u53C2\u6570" }),
13867
- /* @__PURE__ */ (0, import_jsx_runtime183.jsx)("pre", { "data-slot": "tool-call-params", className: "mt-1 overflow-x-auto text-xs whitespace-pre-wrap text-muted-foreground", children: paramsText })
13868
- ] }) : null,
13869
- result ? /* @__PURE__ */ (0, import_jsx_runtime183.jsxs)("div", { children: [
13870
- /* @__PURE__ */ (0, import_jsx_runtime183.jsx)("div", { className: "text-xs font-medium text-muted-foreground", children: "\u7ED3\u679C" }),
13871
- /* @__PURE__ */ (0, import_jsx_runtime183.jsx)("pre", { "data-slot": "tool-call-result", className: "mt-1 overflow-x-auto text-xs whitespace-pre-wrap text-muted-foreground", children: result })
13872
- ] }) : null
13873
- ] }) : null
13874
- ]
13875
- }
13876
- );
13877
- }
13878
-
13879
- // src/components/chat/ChatMessage/index.tsx
13880
- var import_jsx_runtime184 = require("react/jsx-runtime");
13881
- function PartView({
13882
- part,
13883
- role,
13884
- onOpenArtifacts
13885
- }) {
13886
- if (role === "system" && (part.type === "text" || part.type === "markdown")) {
13887
- return /* @__PURE__ */ (0, import_jsx_runtime184.jsx)(
13888
- "div",
13889
- {
13890
- "data-slot": "chat-message-system-part",
13891
- className: "w-full text-center text-xs text-muted-foreground",
13892
- children: part.type === "markdown" ? /* @__PURE__ */ (0, import_jsx_runtime184.jsx)(import_markdown2.MarkdownDslComponent, { content: part.content }) : part.content
13893
- }
13894
- );
13895
- }
13896
- switch (part.type) {
13897
- case "text":
13898
- return /* @__PURE__ */ (0, import_jsx_runtime184.jsx)(Bubble2, { align: role === "user" ? "end" : "start", variant: role === "user" ? "default" : "muted", children: /* @__PURE__ */ (0, import_jsx_runtime184.jsx)("span", { className: "text-sm whitespace-pre-wrap", children: part.content }) });
13899
- case "markdown":
13900
- return /* @__PURE__ */ (0, import_jsx_runtime184.jsx)(Bubble2, { align: role === "user" ? "end" : "start", variant: role === "user" ? "default" : "ghost", children: /* @__PURE__ */ (0, import_jsx_runtime184.jsx)(import_markdown2.MarkdownDslComponent, { content: part.content }) });
13901
- case "thinking":
13902
- return /* @__PURE__ */ (0, import_jsx_runtime184.jsx)(Thinking, { content: part.content, status: part.status ?? "done" });
13903
- case "tool-call":
13904
- return /* @__PURE__ */ (0, import_jsx_runtime184.jsx)(ToolCall, { name: part.name, params: part.params, status: part.status ?? "done", result: part.result });
13905
- case "tool-result":
13906
- return /* @__PURE__ */ (0, import_jsx_runtime184.jsx)("pre", { "data-slot": "chat-message-tool-result", className: "overflow-x-auto rounded-md bg-muted p-2 text-xs whitespace-pre-wrap", children: part.content });
13907
- case "status":
13908
- return /* @__PURE__ */ (0, import_jsx_runtime184.jsxs)("div", { "data-slot": "chat-message-status", className: "flex w-fit items-center gap-1.5 text-muted-foreground", children: [
13909
- /* @__PURE__ */ (0, import_jsx_runtime184.jsx)(Icon, { name: "clock", size: "sm" }),
13910
- part.text
13911
- ] });
13912
- case "artifacts": {
13913
- const artifactsClass = "flex w-fit items-center gap-1.5 text-muted-foreground";
13914
- const label = `\u5DF2\u751F\u6210 ${part.count} \u4E2A\u4EA7\u7269`;
13915
- return onOpenArtifacts ? /* @__PURE__ */ (0, import_jsx_runtime184.jsxs)(
13916
- "button",
13917
- {
13918
- type: "button",
13919
- "data-slot": "chat-message-artifacts",
13920
- className: cn(artifactsClass, "cursor-pointer hover:text-foreground"),
13921
- onClick: onOpenArtifacts,
13922
- children: [
13923
- /* @__PURE__ */ (0, import_jsx_runtime184.jsx)(Icon, { name: "package", size: "sm" }),
13924
- label,
13925
- /* @__PURE__ */ (0, import_jsx_runtime184.jsx)(Icon, { name: "chevron-right", size: 12 })
13926
- ]
13927
- }
13928
- ) : /* @__PURE__ */ (0, import_jsx_runtime184.jsxs)("div", { "data-slot": "chat-message-artifacts", className: artifactsClass, children: [
13929
- /* @__PURE__ */ (0, import_jsx_runtime184.jsx)(Icon, { name: "package", size: "sm" }),
13930
- label
13931
- ] });
13932
- }
13933
- case "error":
13934
- return /* @__PURE__ */ (0, import_jsx_runtime184.jsxs)("div", { "data-slot": "chat-message-error", className: "flex items-center gap-1.5 text-sm text-destructive", children: [
13935
- /* @__PURE__ */ (0, import_jsx_runtime184.jsx)(Icon, { name: "circle-alert", size: "sm" }),
13936
- part.message
13937
- ] });
13938
- default:
13939
- return null;
13940
- }
13941
- }
13942
- var ACTION_ICON_BY_NAME = {
13943
- copy: "copy",
13944
- retry: "rotate-ccw",
13945
- regenerate: "refresh-ccw",
13946
- edit: "pencil",
13947
- like: "thumbs-up",
13948
- dislike: "thumbs-down",
13949
- share: "share",
13950
- delete: "trash"
13951
- };
13952
- function ChatMessage({
13953
- role = "assistant",
13954
- parts = [],
13955
- status = "completed",
13956
- agent,
13957
- time,
13958
- showSender = false,
13959
- actions = [],
13960
- onAction,
13961
- onOpenArtifacts,
13962
- className,
13963
- style
13964
- }) {
13965
- const isUser = role === "user";
13966
- const isSystem = role === "system";
13967
- const senderName = isUser ? "\u6211" : agent?.name ?? "\u52A9\u624B";
13968
- return /* @__PURE__ */ (0, import_jsx_runtime184.jsxs)(
13969
- "div",
13970
- {
13971
- "data-slot": "chat-message",
13972
- "data-role": role,
13973
- "data-status": status,
13974
- className: cn(
13975
- "flex w-full flex-col gap-1.5",
13976
- isUser && "items-end",
13977
- // system 居中弱提示(参考 @pactor-app/chat MessageBubble)
13978
- isSystem && "items-center text-muted-foreground",
13979
- className
13980
- ),
13981
- style,
13982
- children: [
13983
- showSender ? /* @__PURE__ */ (0, import_jsx_runtime184.jsx)(
13984
- Sender,
13985
- {
13986
- name: senderName,
13987
- avatarText: isUser ? void 0 : agent?.icon ? void 0 : senderName.slice(0, 1),
13988
- time,
13989
- align: isUser ? "end" : "start"
13990
- }
13991
- ) : null,
13992
- /* @__PURE__ */ (0, import_jsx_runtime184.jsxs)("div", { className: cn("flex min-w-0 w-full max-w-full flex-col gap-2", isUser && "items-end", isSystem && "items-center"), children: [
13993
- parts.map((part, index) => /* @__PURE__ */ (0, import_jsx_runtime184.jsx)(PartView, { part, role, onOpenArtifacts }, index)),
13994
- status === "streaming" ? /* @__PURE__ */ (0, import_jsx_runtime184.jsx)(StreamingText, { content: "", streaming: true }) : null
13995
- ] }),
13996
- actions.length > 0 && (status === "completed" || status === "error") ? /* @__PURE__ */ (0, import_jsx_runtime184.jsx)("div", { "data-slot": "chat-message-actions", className: "flex items-center gap-1 mt-2", children: actions.map((action) => {
13997
- const label = action.label ?? action.name;
13998
- const iconName = action.icon ?? ACTION_ICON_BY_NAME[action.name];
13999
- if (iconName) {
14000
- return /* @__PURE__ */ (0, import_jsx_runtime184.jsx)(Tooltip2, { content: label, side: "top", children: /* @__PURE__ */ (0, import_jsx_runtime184.jsx)(
14001
- "button",
14002
- {
14003
- type: "button",
14004
- "data-slot": "chat-message-action",
14005
- "data-action": action.name,
14006
- "aria-label": label,
14007
- className: "flex cursor-pointer items-center rounded-sm p-1 text-muted-foreground hover:bg-accent hover:text-foreground",
14008
- onClick: () => onAction?.(action.name),
14009
- children: /* @__PURE__ */ (0, import_jsx_runtime184.jsx)(Icon, { name: iconName, size: "sm" })
14010
- }
14011
- ) }, action.name);
14012
- }
14013
- return /* @__PURE__ */ (0, import_jsx_runtime184.jsx)(
14014
- "button",
14015
- {
14016
- type: "button",
14017
- "data-slot": "chat-message-action",
14018
- "data-action": action.name,
14019
- className: "flex items-center cursor-pointer gap-1 rounded-sm px-1.5 py-0.5 text-xs text-muted-foreground hover:bg-accent hover:text-foreground",
14020
- onClick: () => onAction?.(action.name),
14021
- children: label
14022
- },
14023
- action.name
14024
- );
14025
- }) }) : null
14026
- ]
14027
- }
14028
- );
14029
- }
14030
-
14031
- // src/components/chat/ChatPage/index.tsx
14032
- var import_runtime51 = require("@pactor-app/runtime");
14033
- var import_react70 = require("react");
14034
-
14035
- // src/components/chat/Composer/index.tsx
14036
- var import_react67 = require("react");
14037
-
14038
- // src/components/chat/Composer/mention-input.tsx
14039
- var import_react66 = require("react");
14040
- var import_server = require("react-dom/server");
14041
- var import_jsx_runtime185 = require("react/jsx-runtime");
14042
- var NBSP = "\xA0";
14043
- var CHIP_ATTR = "data-mention-chip";
14044
- function escapeRegExp(text) {
14045
- return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
14046
- }
14047
- function isChipElement(node) {
14048
- return node instanceof HTMLElement && node.hasAttribute(CHIP_ATTR);
14049
- }
14050
- function serializeNode(node) {
14051
- let out = "";
14052
- node.childNodes.forEach((child) => {
14053
- if (child.nodeType === Node.TEXT_NODE) {
14054
- out += (child.textContent ?? "").replace(/\u00A0/g, " ");
14055
- } else if (isChipElement(child)) {
14056
- out += `${child.dataset["trigger"] ?? ""}${child.dataset["label"] ?? ""}`;
14057
- } else if (child instanceof HTMLBRElement) {
14058
- out += "\n";
14059
- } else if (child instanceof HTMLElement) {
14060
- const inner = serializeNode(child).replace(/\n$/, "");
14061
- out += (out === "" || out.endsWith("\n") ? "" : "\n") + inner + "\n";
14062
- }
14063
- });
14064
- return out;
14065
- }
14066
- function collectChips(field) {
14067
- return Array.from(field.querySelectorAll(`[${CHIP_ATTR}]`)).map((el) => ({
14068
- id: el.dataset["id"] ?? "",
14069
- label: el.dataset["label"] ?? "",
14070
- trigger: el.dataset["trigger"] ?? ""
14071
- }));
14072
- }
14073
- function detectActiveMention(field, sources) {
14074
- const selection = window.getSelection();
14075
- if (!selection || selection.rangeCount === 0 || !selection.isCollapsed) {
14076
- return null;
14077
- }
14078
- const anchor = selection.anchorNode;
14079
- if (!anchor || anchor.nodeType !== Node.TEXT_NODE || !field.contains(anchor)) {
14080
- return null;
14081
- }
14082
- const caretOffset = selection.anchorOffset;
14083
- const beforeCaret = (anchor.textContent ?? "").slice(0, caretOffset);
14084
- for (const source of sources) {
14085
- const trigger = source.trigger;
14086
- const match = new RegExp(
14087
- `(?:^|\\s)${escapeRegExp(trigger)}([^\\s${escapeRegExp(trigger)}]*)$`
14088
- ).exec(beforeCaret);
14089
- if (match) {
14090
- const query = match[1] ?? "";
14091
- return {
14092
- source,
14093
- query,
14094
- node: anchor,
14095
- triggerStart: caretOffset - query.length - trigger.length,
14096
- caretOffset
14097
- };
14098
- }
14099
- }
14100
- return null;
14101
- }
14102
- function chipIcon(item, source) {
14103
- return item.icon ?? source.icon ?? (source.trigger === "@" ? "user" : "file-text");
14104
- }
14105
- var MentionInput = (0, import_react66.forwardRef)(
14106
- function MentionInput2({
14107
- value,
14108
- placeholder,
14109
- disabled,
14110
- sources,
14111
- chips,
14112
- onChipsChange,
14113
- onChange,
14114
- onSubmit,
14115
- className,
14116
- style
14117
- }, ref) {
14118
- const fieldRef = (0, import_react66.useRef)(null);
14119
- const [menu, setMenu] = (0, import_react66.useState)(null);
14120
- const [empty, setEmpty] = (0, import_react66.useState)(true);
14121
- const suppressSyncRef = (0, import_react66.useRef)(false);
14122
- const composingRef = (0, import_react66.useRef)(false);
14123
- const emitChips = () => {
14124
- if (suppressSyncRef.current || !fieldRef.current) {
14125
- return;
14126
- }
14127
- onChipsChange?.(collectChips(fieldRef.current));
14128
- };
14129
- const serialize = () => fieldRef.current ? serializeNode(fieldRef.current).replace(/\n$/, "") : "";
14130
- const buildChipElement = (item, source) => {
14131
- const chip = document.createElement("span");
14132
- chip.setAttribute(CHIP_ATTR, "");
14133
- chip.dataset["id"] = item.id;
14134
- chip.dataset["label"] = item.label;
14135
- chip.dataset["trigger"] = item.trigger ?? source.trigger;
14136
- chip.contentEditable = "false";
14137
- chip.className = "mx-0.5 inline-flex items-center gap-1 rounded-md bg-muted px-1.5 py-0.5 align-middle text-xs text-foreground";
14138
- const iconMarkup = (0, import_server.renderToStaticMarkup)(
14139
- /* @__PURE__ */ (0, import_jsx_runtime185.jsx)(Icon, { name: chipIcon(item, source), size: 12 })
14140
- );
14141
- const label = document.createElement("span");
14142
- label.textContent = item.label;
14143
- chip.innerHTML = iconMarkup;
14144
- chip.appendChild(label);
14145
- return chip;
14146
- };
14147
- const insertChipElement = (item, source, replace) => {
14148
- const field = fieldRef.current;
14149
- if (!field) {
14150
- return;
14151
- }
14152
- const chip = buildChipElement(item, source);
14153
- let caretNode;
14154
- if (replace) {
14155
- const { node, start, end } = replace;
14156
- const text = node.textContent ?? "";
14157
- node.textContent = text.slice(0, start);
14158
- caretNode = document.createTextNode(`${NBSP}${text.slice(end)}`);
14159
- node.after(chip, caretNode);
14160
- } else {
14161
- caretNode = document.createTextNode(NBSP);
14162
- const selection2 = window.getSelection();
14163
- if (selection2 && selection2.rangeCount > 0 && selection2.anchorNode !== null && field.contains(selection2.anchorNode)) {
14164
- const range = selection2.getRangeAt(0);
14165
- range.deleteContents();
14166
- range.insertNode(caretNode);
14167
- range.insertNode(chip);
14168
- } else {
14169
- field.append(chip, caretNode);
14170
- }
14171
- }
14172
- const selection = window.getSelection();
14173
- if (selection) {
14174
- const range = document.createRange();
14175
- range.setStart(caretNode, 1);
14176
- range.collapse(true);
14177
- selection.removeAllRanges();
14178
- selection.addRange(range);
14179
- }
14180
- setEmpty(false);
14181
- onChange?.(serialize());
14182
- emitChips();
14183
- };
14184
- (0, import_react66.useImperativeHandle)(ref, () => ({
14185
- insertChip: (item, source) => {
14186
- fieldRef.current?.focus();
14187
- insertChipElement(item, source);
14188
- },
14189
- focus: () => fieldRef.current?.focus()
14190
- }));
14191
- (0, import_react66.useEffect)(() => {
14192
- const field = fieldRef.current;
14193
- if (field && value === "" && serializeNode(field).trim() !== "") {
14194
- suppressSyncRef.current = true;
14195
- field.innerHTML = "";
14196
- setEmpty(true);
14197
- setMenu(null);
14198
- suppressSyncRef.current = false;
14199
- }
14200
- }, [value]);
14201
- (0, import_react66.useEffect)(() => {
14202
- const field = fieldRef.current;
14203
- if (!field) {
14204
- return;
14205
- }
14206
- const keep = new Set(chips.map((chip) => `${chip.trigger}:${chip.id}`));
14207
- const stale = Array.from(field.querySelectorAll(`[${CHIP_ATTR}]`)).filter(
14208
- (el) => !keep.has(
14209
- `${el.dataset["trigger"]}:${el.dataset["id"]}`
14210
- )
14211
- );
14212
- if (stale.length > 0) {
14213
- suppressSyncRef.current = true;
14214
- stale.forEach((el) => el.remove());
14215
- suppressSyncRef.current = false;
14216
- setEmpty(serializeNode(field).trim() === "");
14217
- }
14218
- }, [chips]);
14219
- const closeMenu = () => setMenu(null);
14220
- const filteredItems = (state) => state.query ? state.source.items.filter((item) => item.label.includes(state.query)) : state.source.items;
14221
- const refreshMenu = () => {
14222
- const field = fieldRef.current;
14223
- if (!field) {
14224
- return;
14225
- }
14226
- const active = detectActiveMention(field, sources);
14227
- if (!active) {
14228
- setMenu(null);
14229
- return;
14230
- }
14231
- const range = document.createRange();
14232
- range.setStart(active.node, active.triggerStart);
14233
- range.setEnd(active.node, active.caretOffset);
14234
- const rect = typeof range.getBoundingClientRect === "function" ? range.getBoundingClientRect() : field.getBoundingClientRect();
14235
- setMenu((prev) => ({
14236
- source: active.source,
14237
- query: active.query,
14238
- left: rect.left,
14239
- top: rect.top,
14240
- // 过滤结果变化后候选下标钳制在有效范围内(DSL 每次渲染产出新
14241
- // props 对象,source 按 trigger 比较而非引用)
14242
- activeIndex: Math.min(
14243
- prev?.source.trigger === active.source.trigger ? prev.activeIndex : 0,
14244
- Math.max(filteredItems({ source: active.source, query: active.query, left: 0, top: 0, activeIndex: 0 }).length - 1, 0)
14245
- )
14246
- }));
14247
- };
14248
- const selectMenuItem = (item) => {
14249
- const field = fieldRef.current;
14250
- if (!field || !menu) {
14251
- return;
14252
- }
14253
- const active = detectActiveMention(field, sources);
14254
- insertChipElement(
14255
- { ...item, trigger: item.trigger ?? menu.source.trigger },
14256
- menu.source,
14257
- active && active.source.trigger === menu.source.trigger ? { node: active.node, start: active.triggerStart, end: active.caretOffset } : void 0
14258
- );
14259
- setMenu(null);
14260
- };
14261
- const handleInput = () => {
14262
- const field = fieldRef.current;
14263
- if (!field) {
14264
- return;
14265
- }
14266
- if ((field.textContent ?? "") === "" && field.querySelector(`[${CHIP_ATTR}]`) === null) {
14267
- field.innerHTML = "";
14268
- }
14269
- setEmpty(serializeNode(field).trim() === "");
14270
- onChange?.(serialize());
14271
- refreshMenu();
14272
- };
14273
- const handleKeyDown = (event) => {
14274
- if (menu) {
14275
- const items = filteredItems(menu);
14276
- if (event.key === "ArrowDown" || event.key === "ArrowUp") {
14277
- event.preventDefault();
14278
- if (items.length > 0) {
14279
- const delta = event.key === "ArrowDown" ? 1 : -1;
14280
- setMenu({
14281
- ...menu,
14282
- activeIndex: (menu.activeIndex + delta + items.length) % items.length
14283
- });
14284
- }
14285
- return;
14286
- }
14287
- if (event.key === "Enter" && !composingRef.current && !event.nativeEvent.isComposing) {
14288
- event.preventDefault();
14289
- const item = items[Math.min(menu.activeIndex, items.length - 1)];
14290
- if (item) {
14291
- selectMenuItem(item);
14292
- } else {
14293
- closeMenu();
14294
- }
14295
- return;
14296
- }
14297
- if (event.key === "Escape") {
14298
- event.preventDefault();
14299
- closeMenu();
14300
- return;
14301
- }
14302
- }
14303
- if (event.key === "Enter" && !event.shiftKey && !composingRef.current && !event.nativeEvent.isComposing) {
14304
- event.preventDefault();
14305
- const text = serialize();
14306
- if (text.trim() !== "" && !disabled) {
14307
- onSubmit?.(text);
14308
- }
14309
- return;
14310
- }
14311
- if (event.key === "Backspace") {
14312
- const selection = window.getSelection();
14313
- const field = fieldRef.current;
14314
- if (!selection || !selection.isCollapsed || !field) {
14315
- return;
14316
- }
14317
- let chip = null;
14318
- const anchor = selection.anchorNode;
14319
- if (anchor === field && selection.anchorOffset > 0) {
14320
- const before = field.childNodes[selection.anchorOffset - 1];
14321
- if (isChipElement(before)) {
14322
- chip = before;
14323
- }
14324
- } else if (anchor?.nodeType === Node.TEXT_NODE && selection.anchorOffset === 0) {
14325
- if (isChipElement(anchor.previousSibling)) {
14326
- chip = anchor.previousSibling;
14327
- }
14328
- }
14329
- if (chip) {
14330
- event.preventDefault();
14331
- chip.remove();
14332
- onChange?.(serialize());
14333
- emitChips();
14334
- setEmpty(serializeNode(field).trim() === "");
14335
- }
14336
- }
14337
- };
14338
- const menuItems = menu ? filteredItems(menu) : [];
14339
- const activeIndex = Math.min(menu?.activeIndex ?? 0, Math.max(menuItems.length - 1, 0));
14340
- return /* @__PURE__ */ (0, import_jsx_runtime185.jsxs)("div", { className: "relative", children: [
14341
- /* @__PURE__ */ (0, import_jsx_runtime185.jsx)(
14342
- "div",
14343
- {
14344
- ref: fieldRef,
14345
- "data-slot": "mention-input",
14346
- role: "textbox",
14347
- "aria-multiline": "true",
14348
- "aria-placeholder": placeholder,
14349
- contentEditable: !disabled,
14350
- suppressContentEditableWarning: true,
14351
- className: cn(
14352
- "min-h-[60px] w-full px-4 pt-3 pb-1 text-sm whitespace-pre-wrap break-words outline-none",
14353
- className
14354
- ),
14355
- style,
14356
- onInput: handleInput,
14357
- onKeyDown: handleKeyDown,
14358
- onCompositionStart: () => {
14359
- composingRef.current = true;
14360
- },
14361
- onCompositionEnd: () => {
14362
- composingRef.current = false;
14363
- },
14364
- onPaste: (event) => {
14365
- event.preventDefault();
14366
- const text = event.clipboardData?.getData("text/plain") ?? "";
14367
- if (text) {
14368
- document.execCommand("insertText", false, text);
14369
- }
14370
- }
14371
- }
14372
- ),
14373
- empty && placeholder !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime185.jsx)("span", { className: "pointer-events-none absolute top-3 left-4 text-sm text-muted-foreground", children: placeholder }) : null,
14374
- menu ? /* @__PURE__ */ (0, import_jsx_runtime185.jsxs)(import_jsx_runtime185.Fragment, { children: [
14375
- /* @__PURE__ */ (0, import_jsx_runtime185.jsx)(
14376
- "div",
14377
- {
14378
- className: "fixed inset-0 z-40",
14379
- onMouseDown: (event) => {
14380
- event.preventDefault();
14381
- closeMenu();
14382
- }
14383
- }
14384
- ),
14385
- /* @__PURE__ */ (0, import_jsx_runtime185.jsxs)(
14386
- "div",
14387
- {
14388
- "data-slot": "mention-menu",
14389
- role: "listbox",
14390
- className: "scrollbar-subtle fixed z-50 max-h-72 w-64 overflow-y-auto rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md",
14391
- style: {
14392
- left: menu.left,
14393
- bottom: typeof window === "undefined" ? void 0 : window.innerHeight - menu.top + 6
14394
- },
14395
- children: [
14396
- /* @__PURE__ */ (0, import_jsx_runtime185.jsx)("div", { className: "px-2 py-1 text-xs text-muted-foreground", children: menu.source.label }),
14397
- menuItems.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime185.jsx)("div", { className: "px-2 py-2 text-xs text-muted-foreground", children: "\u65E0\u5339\u914D\u9879" }) : menuItems.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime185.jsxs)(
14398
- "button",
14399
- {
14400
- type: "button",
14401
- role: "option",
14402
- "aria-selected": index === activeIndex,
14403
- className: cn(
14404
- "flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm",
14405
- index === activeIndex ? "bg-accent" : "hover:bg-accent"
14406
- ),
14407
- onMouseDown: (event) => {
14408
- event.preventDefault();
14409
- selectMenuItem(item);
14410
- },
14411
- children: [
14412
- /* @__PURE__ */ (0, import_jsx_runtime185.jsx)(Icon, { name: chipIcon(item, menu.source), size: "sm" }),
14413
- /* @__PURE__ */ (0, import_jsx_runtime185.jsx)("span", { className: "min-w-0 flex-1 truncate", children: item.label })
14414
- ]
14415
- },
14416
- item.id
14417
- ))
14418
- ]
14419
- }
14420
- )
14421
- ] }) : null
14422
- ] });
14423
- }
14424
- );
14425
-
14426
- // src/components/chat/Composer/index.tsx
14427
- var import_jsx_runtime186 = require("react/jsx-runtime");
14428
- var toolButtonClass3 = "flex cursor-pointer items-center rounded-md p-1.5 text-muted-foreground hover:bg-accent hover:text-foreground";
14429
- var popoverPanelClass = "absolute bottom-full left-0 z-50 mb-2 max-h-72 w-64 overflow-y-auto rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md";
14430
- var popoverItemClass = "flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm hover:bg-accent";
14431
- function Composer({
14432
- value,
14433
- placeholder,
14434
- disabled,
14435
- loading,
14436
- rows,
14437
- attachments = [],
14438
- onRemoveAttachment,
14439
- onRetryAttachment,
14440
- onAttachFiles,
14441
- onChange,
14442
- onSubmit,
14443
- models,
14444
- model,
14445
- onModelChange,
14446
- contexts,
14447
- contextItems,
14448
- onContextItemsChange,
14449
- footer,
14450
- className,
14451
- style
14452
- }) {
14453
- const textControlled = value !== void 0;
14454
- const [innerText, setInnerText] = (0, import_react67.useState)("");
14455
- const text = textControlled ? value : innerText;
14456
- const [dragOver, setDragOver] = (0, import_react67.useState)(false);
14457
- const [plusOpen, setPlusOpen] = (0, import_react67.useState)(false);
14458
- const [modelOpen, setModelOpen] = (0, import_react67.useState)(false);
14459
- const fileInputRef = (0, import_react67.useRef)(null);
14460
- const mentionRef = (0, import_react67.useRef)(null);
14461
- const sources = contexts ?? [];
14462
- const rich = sources.length > 0;
14463
- const plusSources = sources.filter((s) => s.trigger === "+");
14464
- const plusEnabled = plusSources.length > 0;
14465
- const modelEnabled = models !== void 0 && models.length > 0;
14466
- const currentModel = models?.find((m) => m.value === model) ?? models?.[0];
14467
- const [innerChips, setInnerChips] = (0, import_react67.useState)([]);
14468
- const chipsControlled = contextItems !== void 0;
14469
- const chips = chipsControlled ? contextItems : innerChips;
14470
- const handleChipsChange = (next) => {
14471
- if (!chipsControlled) {
14472
- setInnerChips(next);
14473
- }
14474
- onContextItemsChange?.(next);
14475
- };
14476
- const closePopovers = () => {
14477
- setPlusOpen(false);
14478
- setModelOpen(false);
14479
- };
14480
- const handleChange = (next) => {
14481
- if (!textControlled) {
14482
- setInnerText(next);
14483
- }
14484
- onChange?.(next);
14485
- };
14486
- const handleSubmit = (submitted) => {
14487
- onSubmit?.(submitted);
14488
- if (!textControlled) {
14489
- setInnerText("");
14490
- }
14491
- closePopovers();
14492
- };
14493
- const selectContext = (item, source) => {
14494
- if (rich) {
14495
- mentionRef.current?.insertChip(item, source);
14496
- } else {
14497
- const chip = { ...item, trigger: item.trigger ?? source.trigger };
14498
- const duplicated = chips.some(
14499
- (existing) => existing.id === chip.id && existing.trigger === chip.trigger
14500
- );
14501
- if (!duplicated) {
14502
- handleChipsChange([...chips, chip]);
14503
- }
14504
- }
14505
- closePopovers();
14506
- };
14507
- const emitFiles = (files) => {
14508
- if (files.length > 0 && onAttachFiles) {
14509
- onAttachFiles(files);
14510
- }
14511
- };
14512
- const handleDrop = (event) => {
14513
- if (!onAttachFiles) {
14514
- return;
14515
- }
14516
- event.preventDefault();
14517
- setDragOver(false);
14518
- emitFiles(Array.from(event.dataTransfer?.files ?? []));
14519
- };
14520
- const toolbarStart = plusEnabled || onAttachFiles ? /* @__PURE__ */ (0, import_jsx_runtime186.jsxs)(import_jsx_runtime186.Fragment, { children: [
14521
- plusEnabled ? /* @__PURE__ */ (0, import_jsx_runtime186.jsx)(
14522
- "button",
14523
- {
14524
- type: "button",
14525
- "aria-label": "\u6DFB\u52A0\u4E0A\u4E0B\u6587",
14526
- title: "\u6DFB\u52A0\u4E0A\u4E0B\u6587",
14527
- className: toolButtonClass3,
14528
- onClick: () => {
14529
- setPlusOpen((prev) => !prev);
14530
- setModelOpen(false);
14531
- },
14532
- children: /* @__PURE__ */ (0, import_jsx_runtime186.jsx)(Icon, { name: "plus", size: "sm" })
14533
- }
14534
- ) : null,
14535
- onAttachFiles ? /* @__PURE__ */ (0, import_jsx_runtime186.jsx)(
14536
- "button",
14537
- {
14538
- type: "button",
14539
- "aria-label": "\u6DFB\u52A0\u9644\u4EF6",
14540
- title: "\u6DFB\u52A0\u9644\u4EF6",
14541
- className: toolButtonClass3,
14542
- onClick: () => fileInputRef.current?.click(),
14543
- children: /* @__PURE__ */ (0, import_jsx_runtime186.jsx)(Icon, { name: "paperclip", size: "sm" })
14544
- }
14545
- ) : null
14546
- ] }) : void 0;
14547
- const toolbarEnd = modelEnabled ? /* @__PURE__ */ (0, import_jsx_runtime186.jsxs)(
14548
- "button",
14549
- {
14550
- type: "button",
14551
- "aria-label": "\u9009\u62E9\u6A21\u578B",
14552
- title: "\u9009\u62E9\u6A21\u578B",
14553
- className: "flex cursor-pointer items-center gap-1 rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-accent hover:text-foreground",
14554
- onClick: () => {
14555
- setModelOpen((prev) => !prev);
14556
- setPlusOpen(false);
14557
- },
14558
- children: [
14559
- currentModel?.label,
14560
- /* @__PURE__ */ (0, import_jsx_runtime186.jsx)(Icon, { name: "chevrons-up-down", size: 12 })
14561
- ]
14562
- }
14563
- ) : void 0;
14564
- const activePopover = plusOpen ? "plus" : modelOpen ? "model" : null;
14565
- return /* @__PURE__ */ (0, import_jsx_runtime186.jsxs)(
14566
- "div",
14567
- {
14568
- "data-slot": "composer",
14569
- className: cn("relative flex w-full flex-col gap-2", className),
14570
- style,
14571
- onDragOver: (event) => {
14572
- if (onAttachFiles) {
14573
- event.preventDefault();
14574
- setDragOver(true);
14575
- }
14576
- },
14577
- onDragLeave: () => setDragOver(false),
14578
- onDrop: handleDrop,
14579
- onPaste: (event) => {
14580
- const files = Array.from(event.clipboardData?.files ?? []);
14581
- if (files.length > 0 && onAttachFiles) {
14582
- event.preventDefault();
14583
- emitFiles(files);
14584
- }
14585
- },
14586
- children: [
14587
- attachments.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime186.jsx)(Attachment, { items: attachments, onRemove: onRemoveAttachment, onRetry: onRetryAttachment }) : null,
14588
- /* @__PURE__ */ (0, import_jsx_runtime186.jsx)("div", { className: cn(dragOver && "rounded-xl ring-2 ring-ring/50"), children: /* @__PURE__ */ (0, import_jsx_runtime186.jsx)(
14589
- ChatInput,
14590
- {
14591
- value: text,
14592
- placeholder,
14593
- disabled: disabled || loading,
14594
- rows,
14595
- toolbarStart,
14596
- toolbarEnd,
14597
- footer,
14598
- field: rich ? /* @__PURE__ */ (0, import_jsx_runtime186.jsx)(
14599
- MentionInput,
14600
- {
14601
- ref: mentionRef,
14602
- value: text,
14603
- placeholder,
14604
- disabled: disabled || loading,
14605
- sources,
14606
- chips,
14607
- onChipsChange: handleChipsChange,
14608
- onChange: handleChange,
14609
- onSubmit: handleSubmit
14610
- }
14611
- ) : void 0,
14612
- onChange: handleChange,
14613
- onSubmit: handleSubmit
14614
- }
14615
- ) }),
14616
- onAttachFiles ? /* @__PURE__ */ (0, import_jsx_runtime186.jsx)(
14617
- "input",
14618
- {
14619
- ref: fileInputRef,
14620
- type: "file",
14621
- multiple: true,
14622
- hidden: true,
14623
- onChange: (event) => {
14624
- emitFiles(Array.from(event.target.files ?? []));
14625
- event.target.value = "";
14626
- }
14627
- }
14628
- ) : null,
14629
- activePopover !== null ? /* @__PURE__ */ (0, import_jsx_runtime186.jsxs)(import_jsx_runtime186.Fragment, { children: [
14630
- /* @__PURE__ */ (0, import_jsx_runtime186.jsx)("div", { className: "fixed inset-0 z-40", onClick: closePopovers }),
14631
- activePopover === "model" ? /* @__PURE__ */ (0, import_jsx_runtime186.jsx)("div", { "data-slot": "composer-model-popover", className: cn(popoverPanelClass, "right-0 left-auto w-48"), role: "listbox", children: models.map((option) => /* @__PURE__ */ (0, import_jsx_runtime186.jsxs)(
14632
- "button",
14633
- {
14634
- type: "button",
14635
- role: "option",
14636
- "aria-selected": option.value === currentModel?.value,
14637
- className: popoverItemClass,
14638
- onClick: () => {
14639
- onModelChange?.(option.value);
14640
- closePopovers();
14641
- },
14642
- children: [
14643
- /* @__PURE__ */ (0, import_jsx_runtime186.jsx)("span", { className: "min-w-0 flex-1 truncate", children: option.label }),
14644
- option.value === currentModel?.value ? /* @__PURE__ */ (0, import_jsx_runtime186.jsx)(Icon, { name: "check", size: 14 }) : null
14645
- ]
14646
- },
14647
- option.value
14648
- )) }) : /* @__PURE__ */ (0, import_jsx_runtime186.jsx)("div", { "data-slot": "composer-context-popover", className: popoverPanelClass, role: "listbox", children: plusSources.map(
14649
- (source) => source.items.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime186.jsxs)("div", { children: [
14650
- /* @__PURE__ */ (0, import_jsx_runtime186.jsx)("div", { className: "px-2 py-1 text-xs text-muted-foreground", children: source.label }),
14651
- source.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime186.jsxs)(
14652
- "button",
14653
- {
14654
- type: "button",
14655
- className: popoverItemClass,
14656
- onClick: () => selectContext(item, source),
14657
- children: [
14658
- /* @__PURE__ */ (0, import_jsx_runtime186.jsx)(Icon, { name: item.icon ?? source.icon ?? "file-text", size: "sm" }),
14659
- /* @__PURE__ */ (0, import_jsx_runtime186.jsx)("span", { className: "min-w-0 flex-1 truncate", children: item.label })
14660
- ]
14661
- },
14662
- item.id
14663
- ))
14664
- ] }, source.trigger)
14665
- ) })
14666
- ] }) : null
14667
- ]
14668
- }
14669
- );
14670
- }
14671
-
14672
- // src/components/chat/ConversationList/index.tsx
14673
- var import_react68 = require("react");
14674
- var import_jsx_runtime187 = require("react/jsx-runtime");
14675
- function ConversationList({ items = [], activeId, searchable, onSelect, onDelete, className, style }) {
14676
- const [innerActive, setInnerActive] = (0, import_react68.useState)();
14677
- const [keyword, setKeyword] = (0, import_react68.useState)("");
14678
- const active = activeId ?? innerActive;
14679
- const filtered = (0, import_react68.useMemo)(() => {
14680
- const query = keyword.trim().toLowerCase();
14681
- if (!query) return items;
14682
- return items.filter(
14683
- (item) => item.title.toLowerCase().includes(query) || (item.description ?? "").toLowerCase().includes(query)
14684
- );
14685
- }, [items, keyword]);
14686
- const select = (id) => {
14687
- if (activeId === void 0) {
14688
- setInnerActive(id);
14689
- }
14690
- onSelect?.(id);
14691
- };
14692
- return /* @__PURE__ */ (0, import_jsx_runtime187.jsxs)("div", { "data-slot": "conversation-list", className: cn("flex w-full flex-col gap-1", className), style, children: [
14693
- searchable ? /* @__PURE__ */ (0, import_jsx_runtime187.jsxs)("div", { className: "relative mb-1", children: [
14694
- /* @__PURE__ */ (0, import_jsx_runtime187.jsx)("span", { className: "absolute top-1/2 left-2 -translate-y-1/2 text-muted-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime187.jsx)(Icon, { name: "search", size: "sm" }) }),
14695
- /* @__PURE__ */ (0, import_jsx_runtime187.jsx)(
14696
- "input",
14697
- {
14698
- "data-slot": "conversation-list-search",
14699
- value: keyword,
14700
- placeholder: "\u641C\u7D22\u4F1A\u8BDD",
14701
- className: "h-8 w-full rounded-md border border-border bg-background pr-2 pl-7 text-sm outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50",
14702
- onChange: (event) => setKeyword(event.target.value)
14703
- }
14704
- )
14705
- ] }) : null,
14706
- filtered.map((item) => /* @__PURE__ */ (0, import_jsx_runtime187.jsxs)(
14707
- "div",
14708
- {
14709
- "data-slot": "conversation-list-item",
14710
- "data-active": item.id === active || void 0,
14711
- className: cn(
14712
- "group flex cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 hover:bg-accent",
14713
- item.id === active && "bg-accent"
14714
- ),
14715
- onClick: () => select(item.id),
14716
- children: [
14717
- /* @__PURE__ */ (0, import_jsx_runtime187.jsxs)("div", { className: "min-w-0 flex-1", children: [
14718
- /* @__PURE__ */ (0, import_jsx_runtime187.jsx)("div", { className: cn("truncate text-sm", item.id === active && "font-medium"), children: item.title }),
14719
- item.description ? /* @__PURE__ */ (0, import_jsx_runtime187.jsx)("div", { className: "truncate text-xs text-muted-foreground", children: item.description }) : null
14720
- ] }),
14721
- item.time ? /* @__PURE__ */ (0, import_jsx_runtime187.jsx)("span", { className: "shrink-0 text-xs text-muted-foreground", children: item.time }) : null,
14722
- onDelete ? /* @__PURE__ */ (0, import_jsx_runtime187.jsx)(
14723
- "button",
14724
- {
14725
- type: "button",
14726
- "data-slot": "conversation-list-delete",
14727
- "aria-label": `\u5220\u9664 ${item.title}`,
14728
- className: "shrink-0 text-muted-foreground opacity-0 group-hover:opacity-100 hover:text-destructive",
14729
- onClick: (event) => {
14730
- event.stopPropagation();
14731
- onDelete(item.id);
14732
- },
14733
- children: /* @__PURE__ */ (0, import_jsx_runtime187.jsx)(Icon, { name: "trash-2", size: "sm" })
14734
- }
14735
- ) : null
14736
- ]
14737
- },
14738
- item.id
14739
- )),
14740
- filtered.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime187.jsx)("div", { className: "px-2 py-4 text-center text-sm text-muted-foreground", children: "\u65E0\u5339\u914D\u4F1A\u8BDD" }) : null
14741
- ] });
14742
- }
14743
-
14744
- // src/components/chat/MessageList/index.tsx
14745
- var import_react69 = require("react");
14746
- var import_jsx_runtime188 = require("react/jsx-runtime");
14747
- function MessageList({ messages = [], autoScroll = true, onAction, onOpenArtifacts, className, style }) {
14748
- const listRef = (0, import_react69.useRef)(null);
14749
- (0, import_react69.useEffect)(() => {
14750
- if (!autoScroll) return;
14751
- const el = listRef.current;
14752
- if (el) {
14753
- el.scrollTop = el.scrollHeight;
14754
- }
14755
- }, [messages, autoScroll]);
14756
- return /* @__PURE__ */ (0, import_jsx_runtime188.jsx)(
14757
- "div",
14758
- {
14759
- ref: listRef,
14760
- "data-slot": "message-list",
14761
- className: cn("flex w-full flex-col gap-4 overflow-y-auto", className),
14762
- style,
14763
- children: messages.map((message, index) => /* @__PURE__ */ (0, import_jsx_runtime188.jsx)(
14764
- ChatMessage,
14765
- {
14766
- ...message,
14767
- onAction: (action) => onAction?.({ id: message.id, action }),
14768
- onOpenArtifacts: onOpenArtifacts ? () => onOpenArtifacts({ id: message.id }) : void 0
14769
- },
14770
- message.id ?? index
14771
- ))
14772
- }
14773
- );
14774
- }
14775
-
14776
- // src/components/chat/Prompt/index.tsx
14777
- var import_jsx_runtime189 = require("react/jsx-runtime");
14778
- function Prompt({ title, description, icon, value, disabled, onSelect, className, style }) {
14779
- return /* @__PURE__ */ (0, import_jsx_runtime189.jsxs)(
14780
- "button",
14781
- {
14782
- type: "button",
14783
- "data-slot": "prompt",
14784
- disabled,
14785
- className: cn(
14786
- "flex items-start gap-2.5 rounded-md border border-border p-3 text-left transition-colors hover:border-primary disabled:pointer-events-none disabled:opacity-50",
14787
- className
14788
- ),
14789
- style,
14790
- onClick: () => onSelect?.(value ?? title ?? ""),
14791
- children: [
14792
- icon ? /* @__PURE__ */ (0, import_jsx_runtime189.jsx)("span", { className: "mt-0.5 shrink-0 text-primary", children: /* @__PURE__ */ (0, import_jsx_runtime189.jsx)(Icon, { name: icon, size: "sm" }) }) : null,
14793
- /* @__PURE__ */ (0, import_jsx_runtime189.jsxs)("span", { className: "min-w-0", children: [
14794
- /* @__PURE__ */ (0, import_jsx_runtime189.jsx)("span", { "data-slot": "prompt-title", className: "block text-sm font-medium", children: title }),
14795
- description ? /* @__PURE__ */ (0, import_jsx_runtime189.jsx)("span", { "data-slot": "prompt-description", className: "mt-0.5 block text-xs text-muted-foreground", children: description }) : null
14796
- ] })
14797
- ]
14798
- }
14799
- );
14800
- }
14801
-
14802
- // src/components/chat/Welcome/index.tsx
14803
- var import_jsx_runtime190 = require("react/jsx-runtime");
14804
- function Welcome({
14805
- title = "\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u4F60\uFF1F",
14806
- description,
14807
- icon = "chat",
14808
- prompts = [],
14809
- onSelect,
14810
- className,
14811
- style
14812
- }) {
14813
- return /* @__PURE__ */ (0, import_jsx_runtime190.jsxs)(
14814
- "div",
14815
- {
14816
- "data-slot": "welcome",
14817
- className: cn("flex w-full flex-col items-center gap-4 py-10 text-center", className),
14818
- style,
14819
- children: [
14820
- /* @__PURE__ */ (0, import_jsx_runtime190.jsx)("span", { "data-slot": "welcome-icon", className: "text-primary", children: /* @__PURE__ */ (0, import_jsx_runtime190.jsx)(Icon, { name: icon, size: "lg" }) }),
14821
- /* @__PURE__ */ (0, import_jsx_runtime190.jsxs)("div", { children: [
14822
- /* @__PURE__ */ (0, import_jsx_runtime190.jsx)("div", { "data-slot": "welcome-title", className: "text-xl font-semibold", children: title }),
14823
- description ? /* @__PURE__ */ (0, import_jsx_runtime190.jsx)("div", { "data-slot": "welcome-description", className: "mt-1 text-sm text-muted-foreground", children: description }) : null
14824
- ] }),
14825
- prompts.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime190.jsx)("div", { "data-slot": "welcome-prompts", className: "grid w-full max-w-xl grid-cols-1 gap-3 text-left sm:grid-cols-2", children: prompts.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime190.jsx)(
14826
- Prompt,
14827
- {
14828
- title: item.title,
14829
- description: item.description,
14830
- icon: item.icon,
14831
- value: item.value,
14832
- onSelect
14833
- },
14834
- `${item.title}-${index}`
14835
- )) }) : null
14836
- ]
14837
- }
14838
- );
14839
- }
14840
-
14841
- // src/components/chat/ChatPage/index.tsx
14842
- var import_jsx_runtime191 = require("react/jsx-runtime");
14843
- function ChatPage({
14844
- brand,
14845
- menuItems = [],
14846
- activeMenuKey,
14847
- railItems = [],
14848
- railFooterItems = [],
14849
- menuAction,
14850
- onMenuSelect,
14851
- onMenuAction,
14852
- conversationSectionTitle,
14853
- conversationSectionCollapsed,
14854
- defaultConversationSectionCollapsed = false,
14855
- onConversationSectionToggle,
14856
- conversations = [],
14857
- activeConversationId,
14858
- onSelectConversation,
14859
- onDeleteConversation,
14860
- moreLabel,
14861
- onMore,
14862
- footerSectionTitle,
14863
- footerItems = [],
14864
- footerSectionCollapsed,
14865
- defaultFooterSectionCollapsed = false,
14866
- onFooterSectionToggle,
14867
- conversationTitle,
14868
- conversationHeaderAction,
14869
- onConversationHeaderAction,
14870
- conversationRailItems = [],
14871
- hideArtifactRailItem = false,
14872
- activeConversationRailKey,
14873
- onConversationRailChange,
14874
- messages = [],
14875
- sending,
14876
- composerPlaceholder,
14877
- welcome,
14878
- onSend,
14879
- onMessageAction,
14880
- models,
14881
- model,
14882
- onModelChange,
14883
- contexts,
14884
- contextItems,
14885
- onContextItemsChange,
14886
- attachments,
14887
- onAttachFiles,
14888
- onRemoveAttachment,
14889
- onRetryAttachment,
14890
- composerFooter,
14891
- composerBottom,
14892
- artifactTitle = "\u4EA7\u7269",
14893
- artifacts = [],
14894
- activeArtifactId,
14895
- onSelectArtifact,
14896
- artifactEmptyText = "\u6682\u65E0\u4EA7\u7269\uFF0C\u53D1\u9001\u6D88\u606F\u540E\u751F\u6210\u7684\u6587\u4EF6\u5C06\u5C55\u793A\u5728\u8FD9\u91CC",
14897
- safeRegistry,
14898
- user,
14899
- artifactViewers,
14900
- onDownloadArtifact,
14901
- artifactOpen,
14902
- defaultArtifactOpen = false,
14903
- onArtifactOpenChange,
14904
- content,
14905
- className,
14906
- style
14907
- }) {
14908
- const controlled = artifactOpen !== void 0;
14909
- const [innerOpen, setInnerOpen] = (0, import_react70.useState)(defaultArtifactOpen);
14910
- const open = controlled ? artifactOpen === true : innerOpen;
14911
- const setOpen = (next) => {
14912
- if (!controlled) setInnerOpen(next);
14913
- onArtifactOpenChange?.(next);
14914
- };
14915
- const railControlled = activeConversationRailKey !== void 0;
14916
- const [activeRailInner, setActiveRailInner] = (0, import_react70.useState)(null);
14917
- const activeRailKey = railControlled ? activeConversationRailKey : activeRailInner;
14918
- const setActiveRailKey = (next) => {
14919
- if (!railControlled) setActiveRailInner(next);
14920
- onConversationRailChange?.(next);
14921
- };
14922
- const visibleRailItems = conversationRailItems.filter((item) => item.hidden !== true);
14923
- const activeRailItem = visibleRailItems.find(
14924
- (item) => item.key === activeRailKey
14925
- );
14926
- const activePanelItem = activeRailItem?.display === "panel" && activeRailItem.content !== void 0 ? activeRailItem : void 0;
14927
- const activePopoverItem = activeRailItem !== void 0 && activeRailItem.display !== "panel" && activeRailItem.content !== void 0 ? activeRailItem : void 0;
14928
- const toggleRailItem = (item) => {
14929
- const next = activeRailKey === item.key ? null : item.key;
14930
- if (next !== null && item.display === "panel") {
14931
- setOpen(false);
14932
- }
14933
- setActiveRailKey(next);
14934
- };
14935
- const toggleArtifact = () => {
14936
- if (!open && activePanelItem) {
14937
- setActiveRailKey(null);
14938
- }
14939
- setOpen(!open);
14940
- };
14941
- (0, import_react70.useEffect)(() => {
14942
- if (!activePopoverItem) return;
14943
- const onKeyDown = (event) => {
14944
- if (event.key === "Escape") setActiveRailKey(null);
14945
- };
14946
- const onPointerDown = (event) => {
14947
- const target = event.target;
14948
- if (target.closest("[data-slot=chat-page-rail-popover]") || target.closest("[data-slot=chat-page-conv-rail]")) {
14949
- return;
14950
- }
14951
- setActiveRailKey(null);
14952
- };
14953
- document.addEventListener("keydown", onKeyDown);
14954
- document.addEventListener("mousedown", onPointerDown);
14955
- return () => {
14956
- document.removeEventListener("keydown", onKeyDown);
14957
- document.removeEventListener("mousedown", onPointerDown);
14958
- };
14959
- });
14960
- const { renderChildren } = (0, import_runtime51.useRenderer)();
14961
- const renderSlot = (slot) => (0, import_react70.isValidElement)(slot) || typeof slot !== "object" || slot === null ? slot : renderChildren([slot]);
14962
- const convCollControlled = conversationSectionCollapsed !== void 0;
14963
- const [convCollapsedInner, setConvCollapsedInner] = (0, import_react70.useState)(
14964
- defaultConversationSectionCollapsed
14965
- );
14966
- const convCollapsed = convCollControlled ? conversationSectionCollapsed === true : convCollapsedInner;
14967
- const toggleConvSection = () => {
14968
- const next = !convCollapsed;
14969
- if (!convCollControlled) setConvCollapsedInner(next);
14970
- onConversationSectionToggle?.(next);
14971
- };
14972
- const footerCollControlled = footerSectionCollapsed !== void 0;
14973
- const [footerCollapsedInner, setFooterCollapsedInner] = (0, import_react70.useState)(
14974
- defaultFooterSectionCollapsed
14975
- );
14976
- const footerCollapsed = footerCollControlled ? footerSectionCollapsed === true : footerCollapsedInner;
14977
- const toggleFooterSection = () => {
14978
- const next = !footerCollapsed;
14979
- if (!footerCollControlled) setFooterCollapsedInner(next);
14980
- onFooterSectionToggle?.(next);
14981
- };
14982
- const railVisible = railItems.length > 0 || railFooterItems.length > 0;
14983
- const railButton = (item) => /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(Tooltip2, { content: item.label, side: "right", children: /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(
14984
- Button2,
14985
- {
14986
- icon: item.icon,
14987
- variant: "text",
14988
- onClick: () => onMenuSelect?.(item.key)
14989
- }
14990
- ) }, item.key);
14991
- const convRailButton = (item, active, onClick) => {
14992
- const button = /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(
14993
- Button2,
14994
- {
14995
- icon: item.icon,
14996
- variant: "text",
14997
- className: cn(active ? "bg-accent text-accent-foreground" : void 0, "px-2"),
14998
- onClick
14999
- }
15000
- );
15001
- return item.label !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(Tooltip2, { content: item.label, side: "left", children: button }, item.key) : /* @__PURE__ */ (0, import_jsx_runtime191.jsx)("span", { children: button }, item.key);
15002
- };
15003
- const convRailVisible = !hideArtifactRailItem || visibleRailItems.length > 0;
15004
- const convRail = convRailVisible ? /* @__PURE__ */ (0, import_jsx_runtime191.jsxs)(
15005
- "div",
15006
- {
15007
- "data-slot": "chat-page-conv-rail",
15008
- className: "relative flex w-(--pactor-chat-rail-width) shrink-0 flex-col items-center gap-1 px-1 py-2",
15009
- children: [
15010
- !hideArtifactRailItem ? convRailButton(
15011
- {
15012
- key: "artifact",
15013
- icon: open ? "panel-right-close" : "package",
15014
- label: open ? "\u6536\u8D77\u4EA7\u7269\u533A" : artifactTitle
15015
- },
15016
- open,
15017
- toggleArtifact
15018
- ) : null,
15019
- visibleRailItems.map(
15020
- (item) => convRailButton(item, activeRailKey === item.key, () => toggleRailItem(item))
15021
- ),
15022
- activePopoverItem ? /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(
15023
- "div",
15024
- {
15025
- "data-slot": "chat-page-rail-popover",
15026
- className: "absolute right-full top-0 z-10 mr-2 overflow-auto rounded-lg border border-border bg-popover shadow-md",
15027
- style: {
15028
- width: "var(--pactor-chat-rail-popover-width)",
15029
- maxWidth: "calc(100cqw - 2rem)",
15030
- maxHeight: "calc(100% - 1rem)"
15031
- },
15032
- children: renderSlot(activePopoverItem.content)
15033
- }
15034
- ) : null
15035
- ]
15036
- }
15037
- ) : null;
15038
- const chatArea = /* @__PURE__ */ (0, import_jsx_runtime191.jsxs)("div", { "data-slot": "chat-page-conversation", className: "flex h-full min-w-0 flex-1 flex-col", children: [
15039
- /* @__PURE__ */ (0, import_jsx_runtime191.jsxs)(
15040
- "div",
15041
- {
15042
- "data-slot": "chat-page-conversation-header",
15043
- className: "flex shrink-0 items-center justify-between border-b border-border px-4 py-2.5",
15044
- children: [
15045
- /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(Text, { text: conversationTitle, strong: true }),
15046
- conversationHeaderAction ? /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(Tooltip2, { content: conversationHeaderAction.tooltip ?? conversationHeaderAction.label, side: "bottom", children: /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(
15047
- Button2,
15048
- {
15049
- text: conversationHeaderAction.label,
15050
- icon: conversationHeaderAction.icon,
15051
- variant: "text",
15052
- onClick: () => onConversationHeaderAction?.()
15053
- }
15054
- ) }) : null
15055
- ]
15056
- }
15057
- ),
15058
- /* @__PURE__ */ (0, import_jsx_runtime191.jsxs)("div", { className: "flex min-h-0 flex-1", children: [
15059
- /* @__PURE__ */ (0, import_jsx_runtime191.jsxs)(
15060
- "div",
15061
- {
15062
- "data-slot": "chat-page-conversation-body",
15063
- className: "mx-auto flex min-h-0 w-[61.8cqw] max-w-full flex-col",
15064
- children: [
15065
- messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(
15066
- Welcome,
15067
- {
15068
- title: welcome?.title,
15069
- description: welcome?.description,
15070
- prompts: welcome?.prompts,
15071
- style: { flex: 1 },
15072
- onSelect: (value) => onSend?.(value)
15073
- }
15074
- ) : /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(
15075
- MessageList,
15076
- {
15077
- messages,
15078
- className: "scrollbar-subtle",
15079
- style: { flex: 1, minHeight: 0, padding: 16 },
15080
- onAction: onMessageAction,
15081
- onOpenArtifacts: () => setOpen(true)
15082
- }
15083
- ),
15084
- /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(
15085
- Composer,
15086
- {
15087
- placeholder: composerPlaceholder,
15088
- loading: sending,
15089
- models,
15090
- model,
15091
- contexts,
15092
- contextItems,
15093
- attachments,
15094
- footer: composerFooter,
15095
- style: { padding: 16, paddingBottom: composerBottom ? 8 : 16, flexShrink: 0 },
15096
- onModelChange,
15097
- onContextItemsChange,
15098
- onAttachFiles,
15099
- onRemoveAttachment,
15100
- onRetryAttachment,
15101
- onSubmit: (text) => onSend?.(text)
15102
- }
15103
- ),
15104
- composerBottom ? /* @__PURE__ */ (0, import_jsx_runtime191.jsx)("div", { "data-slot": "chat-page-composer-bottom", className: "shrink-0 px-4 pb-2 text-center", children: renderSlot(composerBottom) }) : null
15105
- ]
15106
- }
15107
- ),
15108
- convRail
15109
- ] })
15110
- ] });
15111
- const activeArtifact = artifacts.find((item) => item.id === activeArtifactId) ?? (activeArtifactId === void 0 || activeArtifactId === "" ? artifacts[artifacts.length - 1] : void 0);
15112
- const artifactPanel = /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(
15113
- "div",
15114
- {
15115
- "data-slot": "chat-page-artifact",
15116
- className: "flex h-full min-w-0 flex-col border-l border-border bg-muted",
15117
- children: /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(
15118
- ArtifactPanel,
15119
- {
15120
- artifact: activeArtifact,
15121
- artifacts,
15122
- activeArtifactId,
15123
- title: artifactTitle,
15124
- emptyText: artifactEmptyText,
15125
- safeRegistry,
15126
- user,
15127
- artifactViewers,
15128
- onSelectArtifact: (id) => onSelectArtifact?.(id),
15129
- onDownload: onDownloadArtifact,
15130
- onClose: () => setOpen(false)
15131
- }
15132
- )
15133
- }
15134
- );
15135
- return /* @__PURE__ */ (0, import_jsx_runtime191.jsxs)(
15136
- "div",
15137
- {
15138
- "data-slot": "chat-page",
15139
- className: cn("flex w-full overflow-hidden", className),
15140
- style: { height: "100vh", ...style },
15141
- children: [
15142
- railVisible ? /* @__PURE__ */ (0, import_jsx_runtime191.jsxs)(
15143
- "div",
15144
- {
15145
- "data-slot": "chat-page-rail",
15146
- className: "flex h-full w-[52px] shrink-0 flex-col items-center gap-1 border-r border-border py-2.5",
15147
- children: [
15148
- railItems.map(railButton),
15149
- /* @__PURE__ */ (0, import_jsx_runtime191.jsx)("div", { className: "flex-1" }),
15150
- railFooterItems.map(railButton)
15151
- ]
15152
- }
15153
- ) : null,
15154
- /* @__PURE__ */ (0, import_jsx_runtime191.jsxs)(
15155
- "div",
15156
- {
15157
- "data-slot": "chat-page-menu",
15158
- className: "flex h-full w-[250px] shrink-0 flex-col gap-2 overflow-hidden border-r border-border px-2 py-3",
15159
- children: [
15160
- brand ? /* @__PURE__ */ (0, import_jsx_runtime191.jsxs)("div", { className: "flex shrink-0 items-center justify-between px-2", children: [
15161
- /* @__PURE__ */ (0, import_jsx_runtime191.jsxs)("div", { className: "flex flex-col gap-0.5", children: [
15162
- /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(Text, { text: brand.title, strong: true, size: "large" }),
15163
- brand.version !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(Text, { text: brand.version, type: "secondary", size: "small" }) : null
15164
- ] }),
15165
- menuAction ? /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(
15166
- Button2,
15167
- {
15168
- text: menuAction.label,
15169
- icon: menuAction.icon,
15170
- size: "small",
15171
- onClick: () => onMenuAction?.()
15172
- }
15173
- ) : null
15174
- ] }) : null,
15175
- /* @__PURE__ */ (0, import_jsx_runtime191.jsx)("div", { className: "scrollbar-subtle max-h-[40%] min-h-0 shrink-0 overflow-y-auto", children: /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(
15176
- Menu2,
15177
- {
15178
- items: menuItems,
15179
- selectedKey: activeMenuKey,
15180
- onSelect: (key) => onMenuSelect?.(key)
15181
- }
15182
- ) }),
15183
- conversationSectionTitle !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime191.jsxs)(
15184
- "button",
15185
- {
15186
- type: "button",
15187
- "data-slot": "chat-page-section-toggle",
15188
- "aria-expanded": !convCollapsed,
15189
- className: "flex w-full shrink-0 cursor-pointer items-center justify-between px-2 pt-2",
15190
- onClick: toggleConvSection,
15191
- children: [
15192
- /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(
15193
- Text,
15194
- {
15195
- text: `${conversationSectionTitle} (${conversations.length})`,
15196
- type: "secondary",
15197
- className: "text-sm",
15198
- strong: true
15199
- }
15200
- ),
15201
- /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(Icon, { name: convCollapsed ? "chevron-right" : "chevron-down", size: 14 })
15202
- ]
15203
- }
15204
- ) : null,
15205
- !convCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime191.jsxs)("div", { "data-slot": "chat-page-section-body", className: "scrollbar-subtle flex min-h-0 flex-1 flex-col overflow-y-auto", children: [
15206
- /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(
15207
- ConversationList,
15208
- {
15209
- items: conversations,
15210
- activeId: activeConversationId,
15211
- onSelect: (id) => onSelectConversation?.(id),
15212
- onDelete: (id) => onDeleteConversation?.(id)
15213
- }
15214
- ),
15215
- moreLabel !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime191.jsx)("div", { className: "flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(Button2, { text: `${moreLabel} >>`, variant: "link", size: "small", onClick: () => onMore?.() }) }) : null
15216
- ] }) : null,
15217
- footerSectionTitle !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime191.jsxs)(
15218
- "button",
15219
- {
15220
- type: "button",
15221
- "data-slot": "chat-page-section-toggle",
15222
- "aria-expanded": !footerCollapsed,
15223
- className: "flex w-full shrink-0 cursor-pointer items-center justify-between px-2 pt-2",
15224
- onClick: toggleFooterSection,
15225
- children: [
15226
- /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(Text, { text: footerSectionTitle, type: "secondary", className: "text-sm", strong: true }),
15227
- /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(Icon, { name: footerCollapsed ? "chevron-right" : "chevron-down", size: 14 })
15228
- ]
15229
- }
15230
- ) : null,
15231
- !footerCollapsed && footerItems.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime191.jsx)("div", { "data-slot": "chat-page-section-body", className: "scrollbar-subtle min-h-0 flex-1 overflow-y-auto", children: /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(
15232
- Menu2,
15233
- {
15234
- items: footerItems,
15235
- onSelect: (key) => onMenuSelect?.(key)
15236
- }
15237
- ) }) : null
15238
- ]
15239
- }
15240
- ),
15241
- /* @__PURE__ */ (0, import_jsx_runtime191.jsx)("div", { className: "@container h-full min-w-0 flex-1", children: content !== void 0 ? renderSlot(content) : open || activePanelItem ? /* @__PURE__ */ (0, import_jsx_runtime191.jsxs)(PanelGroup, { orientation: "horizontal", style: { width: "100%", height: "100%" }, children: [
15242
- /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(Panel, { defaultSize: "62%", children: chatArea }),
15243
- /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(ResizableHandle, { withHandle: true }),
15244
- /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(Panel, { defaultSize: "38%", minSize: "380px", maxSize: "760px", children: activePanelItem ? /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(
15245
- "div",
15246
- {
15247
- "data-slot": "chat-page-rail-panel",
15248
- className: "flex h-full min-w-0 flex-col border-l border-border bg-muted",
15249
- children: renderSlot(activePanelItem.content)
15250
- }
15251
- ) : artifactPanel })
15252
- ] }) : chatArea })
15253
- ]
15254
- }
15255
- );
15256
- }
15257
-
15258
- // src/components/chat/Citation/index.tsx
15259
- var import_jsx_runtime192 = require("react/jsx-runtime");
15260
- function Citation({ items = [], start = 1, className, style }) {
15261
- return /* @__PURE__ */ (0, import_jsx_runtime192.jsx)("span", { "data-slot": "citation", className: cn("inline-flex gap-1 align-super", className), style, children: items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime192.jsxs)(HoverCard, { children: [
15262
- /* @__PURE__ */ (0, import_jsx_runtime192.jsxs)(
15263
- HoverCardTrigger,
15264
- {
15265
- "data-slot": "citation-marker",
15266
- className: "cursor-pointer rounded-sm border border-border px-1 text-xs leading-4 text-primary hover:bg-accent",
15267
- children: [
15268
- "[",
15269
- start + index,
15270
- "]"
15271
- ]
15272
- }
15273
- ),
15274
- /* @__PURE__ */ (0, import_jsx_runtime192.jsxs)(HoverCardContent, { "data-slot": "citation-card", className: "w-72", children: [
15275
- /* @__PURE__ */ (0, import_jsx_runtime192.jsx)("div", { className: "text-sm font-medium", children: item.title }),
15276
- item.snippet ? /* @__PURE__ */ (0, import_jsx_runtime192.jsx)("div", { className: "mt-1 line-clamp-3 text-xs text-muted-foreground", children: item.snippet }) : null,
15277
- item.url ? /* @__PURE__ */ (0, import_jsx_runtime192.jsx)(
15278
- "a",
15279
- {
15280
- href: item.url,
15281
- target: "_blank",
15282
- rel: "noreferrer",
15283
- className: "mt-1 block truncate text-xs text-primary hover:underline",
15284
- children: item.url
15285
- }
15286
- ) : null
15287
- ] })
15288
- ] }, `${item.title}-${index}`)) });
15289
- }
15290
-
15291
- // src/components/chat/StepsPanel/index.tsx
15292
- var import_jsx_runtime193 = require("react/jsx-runtime");
15293
- function StepsPanel({
15294
- title = "\u6267\u884C\u6B65\u9AA4",
15295
- steps = [],
15296
- emptyText = "\u5F53\u524D\u4F1A\u8BDD\u6682\u65E0\u6267\u884C\u6B65\u9AA4",
15297
- className,
15298
- style
15299
- }) {
15300
- return /* @__PURE__ */ (0, import_jsx_runtime193.jsxs)(
15301
- "div",
15302
- {
15303
- "data-slot": "steps-panel",
15304
- className: cn("px-3.5 py-3 text-foreground", className),
15305
- style,
15306
- children: [
15307
- /* @__PURE__ */ (0, import_jsx_runtime193.jsx)("div", { className: "mb-1 text-[13px] font-semibold", children: title }),
15308
- steps.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime193.jsx)("div", { className: "py-4 text-center text-[13px] text-muted-foreground", children: emptyText }) : /* @__PURE__ */ (0, import_jsx_runtime193.jsx)("ol", { children: steps.map((step, index) => {
15309
- const status = step.status ?? "pending";
15310
- return /* @__PURE__ */ (0, import_jsx_runtime193.jsxs)(
15311
- "li",
15312
- {
15313
- "data-status": status,
15314
- className: cn(
15315
- "relative flex gap-2 py-1.5",
15316
- // 步骤之间的连接线(对齐 16px 图标中心)
15317
- 'not-last:before:absolute not-last:before:left-[7.5px] not-last:before:top-7 not-last:before:-bottom-1 not-last:before:w-px not-last:before:bg-border not-last:before:content-[""]'
15318
- ),
15319
- children: [
15320
- /* @__PURE__ */ (0, import_jsx_runtime193.jsx)(
15321
- "span",
15322
- {
15323
- className: cn(
15324
- "flex h-5 w-4 shrink-0 items-center justify-center",
15325
- status !== "pending" && "text-primary"
15326
- ),
15327
- children: status === "done" ? /* @__PURE__ */ (0, import_jsx_runtime193.jsx)(Icon, { name: "check-circle", size: 16 }) : status === "running" ? /* @__PURE__ */ (0, import_jsx_runtime193.jsx)(
15328
- Icon,
15329
- {
15330
- name: "loading",
15331
- size: 16,
15332
- className: "animate-spin motion-reduce:animate-none"
15333
- }
15334
- ) : /* @__PURE__ */ (0, import_jsx_runtime193.jsx)("span", { className: "size-1.5 rounded-full bg-muted-foreground/50" })
15335
- }
15336
- ),
15337
- /* @__PURE__ */ (0, import_jsx_runtime193.jsxs)("div", { className: "min-w-0 flex-1", children: [
15338
- /* @__PURE__ */ (0, import_jsx_runtime193.jsx)(
15339
- "div",
15340
- {
15341
- className: cn(
15342
- "text-[13px] leading-5",
15343
- status === "pending" && "text-muted-foreground"
15344
- ),
15345
- children: step.title
15346
- }
15347
- ),
15348
- step.description ? /* @__PURE__ */ (0, import_jsx_runtime193.jsx)("div", { className: "mt-0.5 text-xs leading-[18px] text-muted-foreground", children: step.description }) : null
15349
- ] }),
15350
- step.duration ? /* @__PURE__ */ (0, import_jsx_runtime193.jsx)("span", { className: "shrink-0 text-xs leading-5 text-muted-foreground", children: step.duration }) : null
15351
- ]
15352
- },
15353
- `${step.title}-${index}`
15354
- );
15355
- }) })
15356
- ]
15357
- }
15358
- );
15359
- }
15360
-
15361
- // src/components/chat/Suggestion/index.tsx
15362
- var import_jsx_runtime194 = require("react/jsx-runtime");
15363
- function normalize(item) {
15364
- if (typeof item === "string") return { label: item, value: item };
15365
- return { label: item.label, value: item.value ?? item.label, icon: item.icon };
15366
- }
15367
- function Suggestion({ items = [], justify = "start", onSelect, className, style }) {
15368
- return /* @__PURE__ */ (0, import_jsx_runtime194.jsx)(
15369
- "div",
15370
- {
15371
- "data-slot": "suggestion",
15372
- className: cn("flex flex-wrap gap-2", justify === "center" && "justify-center", className),
15373
- style,
15374
- children: items.map((item, index) => {
15375
- const { label, value, icon } = normalize(item);
15376
- return /* @__PURE__ */ (0, import_jsx_runtime194.jsxs)(
15377
- "button",
15378
- {
15379
- type: "button",
15380
- "data-slot": "suggestion-item",
15381
- "data-value": value,
15382
- className: "flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm transition-colors hover:bg-accent",
15383
- onClick: () => onSelect?.(value),
15384
- children: [
15385
- icon ? /* @__PURE__ */ (0, import_jsx_runtime194.jsx)(Icon, { name: icon, size: "sm" }) : null,
15386
- label
15387
- ]
15388
- },
15389
- `${value}-${index}`
15390
- );
15391
- })
15392
- }
15393
- );
15394
- }
15395
-
15396
12948
  // src/components/register.ts
15397
12949
  var builtinComponents = {
15398
12950
  Page,
@@ -15523,26 +13075,7 @@ var builtinComponents = {
15523
13075
  Markdown: import_markdown2.MarkdownDslComponent,
15524
13076
  CodeBlock,
15525
13077
  DiffViewer,
15526
- JSONViewer,
15527
- // AI 与会话类扩展组件(第八批:chat 子目录)
15528
- Bubble: Bubble2,
15529
- Sender,
15530
- Thinking,
15531
- AgentStatus,
15532
- StreamingText,
15533
- Suggestion,
15534
- Prompt,
15535
- Welcome,
15536
- Citation,
15537
- Attachment,
15538
- ToolCall,
15539
- ChatMessage,
15540
- ChatPage,
15541
- ArtifactPanel,
15542
- MessageList,
15543
- Composer,
15544
- ConversationList,
15545
- StepsPanel
13078
+ JSONViewer
15546
13079
  };
15547
13080
  function registerBuiltinComponents(registry) {
15548
13081
  for (const [type, component] of Object.entries(builtinComponents)) {
@@ -15550,7 +13083,7 @@ function registerBuiltinComponents(registry) {
15550
13083
  }
15551
13084
  }
15552
13085
  function createComponentRegistry() {
15553
- const registry = new import_core3.Registry("ComponentRegistry");
13086
+ const registry = new import_core2.Registry("ComponentRegistry");
15554
13087
  registerBuiltinComponents(registry);
15555
13088
  return registry;
15556
13089
  }
@@ -15558,19 +13091,15 @@ function createComponentRegistry() {
15558
13091
  0 && (module.exports = {
15559
13092
  Accordion,
15560
13093
  Affix,
15561
- AgentStatus,
15562
13094
  Alert,
15563
13095
  AlertDialog,
15564
13096
  Anchor,
15565
- ArtifactPanel,
15566
13097
  AspectRatio,
15567
- Attachment,
15568
13098
  Audio,
15569
13099
  Avatar,
15570
13100
  BackTop,
15571
13101
  Badge,
15572
13102
  Breadcrumb,
15573
- Bubble,
15574
13103
  Button,
15575
13104
  ButtonGroup,
15576
13105
  CONTROL_SIZE_AREA,
@@ -15583,12 +13112,9 @@ function createComponentRegistry() {
15583
13112
  Carousel,
15584
13113
  Cascader,
15585
13114
  ChatInput,
15586
- ChatMessage,
15587
- ChatPage,
15588
13115
  Checkbox,
15589
13116
  ChipFilter,
15590
13117
  ChipNode,
15591
- Citation,
15592
13118
  ClearHoverButton,
15593
13119
  CodeBlock,
15594
13120
  Col,
@@ -15596,11 +13122,9 @@ function createComponentRegistry() {
15596
13122
  ColorPicker,
15597
13123
  Combobox,
15598
13124
  Command,
15599
- Composer,
15600
13125
  Container,
15601
13126
  Content,
15602
13127
  ContextMenu,
15603
- ConversationList,
15604
13128
  DataTable,
15605
13129
  DatePicker,
15606
13130
  Descriptions,
@@ -15641,7 +13165,6 @@ function createComponentRegistry() {
15641
13165
  Menu,
15642
13166
  Menubar,
15643
13167
  Message,
15644
- MessageList,
15645
13168
  NavigationMenu,
15646
13169
  Notification,
15647
13170
  NumberInput,
@@ -15650,7 +13173,6 @@ function createComponentRegistry() {
15650
13173
  Popconfirm,
15651
13174
  Popover,
15652
13175
  Progress,
15653
- Prompt,
15654
13176
  QRCode,
15655
13177
  RadioGroup,
15656
13178
  RailTooltip,
@@ -15662,7 +13184,6 @@ function createComponentRegistry() {
15662
13184
  ScrollArea,
15663
13185
  Segmented,
15664
13186
  Select,
15665
- Sender,
15666
13187
  Separator,
15667
13188
  Sheet,
15668
13189
  Sidebar,
@@ -15675,21 +13196,16 @@ function createComponentRegistry() {
15675
13196
  Statistic,
15676
13197
  Status,
15677
13198
  Steps,
15678
- StepsPanel,
15679
- StreamingText,
15680
- Suggestion,
15681
13199
  Switch,
15682
13200
  Table,
15683
13201
  Tabs,
15684
13202
  Tag,
15685
13203
  Text,
15686
13204
  Textarea,
15687
- Thinking,
15688
13205
  TimePicker,
15689
13206
  Timeline,
15690
13207
  Toggle,
15691
13208
  ToggleGroup,
15692
- ToolCall,
15693
13209
  Tooltip,
15694
13210
  Tour,
15695
13211
  Transfer,
@@ -15699,8 +13215,6 @@ function createComponentRegistry() {
15699
13215
  Upload,
15700
13216
  Video,
15701
13217
  Watermark,
15702
- Welcome,
15703
- buildArtifactTree,
15704
13218
  builtinComponents,
15705
13219
  createComponentRegistry,
15706
13220
  diffLines,
@@ -15708,5 +13222,6 @@ function createComponentRegistry() {
15708
13222
  normalizeIconName,
15709
13223
  registerBuiltinComponents,
15710
13224
  useFormContext,
13225
+ useOptionalRenderer,
15711
13226
  useSidebarDrawer
15712
13227
  });