@huyooo/ai-chat-frontend-vue 0.2.13 → 0.2.14

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.
Files changed (142) hide show
  1. package/README.md +85 -77
  2. package/dist/_baseUniq-Berj5otS.js +477 -0
  3. package/dist/arc-D4b-De5i.js +83 -0
  4. package/dist/architecture-U656AL7Q-B1R4o7wt.js +5 -0
  5. package/dist/architectureDiagram-VXUJARFQ-0H1LzSrf.js +4668 -0
  6. package/dist/blockDiagram-VD42YOAC-Dd6DMjLf.js +2265 -0
  7. package/dist/c4Diagram-YG6GDRKO-c6mpIonb.js +1581 -0
  8. package/dist/channel-CQSBI1fo.js +5 -0
  9. package/dist/chunk-4BX2VUAB-CfWC7lTG.js +9 -0
  10. package/dist/chunk-55IACEB6-joM8m2Zv.js +8 -0
  11. package/dist/chunk-B4BG7PRW-DN94Y3mS.js +1376 -0
  12. package/dist/chunk-DI55MBZ5-CPRGoFPG.js +1382 -0
  13. package/dist/chunk-FMBD7UC4-dUKl2TcN.js +19 -0
  14. package/dist/chunk-QN33PNHL-o3WmqquO.js +20 -0
  15. package/dist/chunk-QZHKN3VN-Btf62lRF.js +15 -0
  16. package/dist/chunk-TZMSLE5B-QCPpnm8Y.js +64 -0
  17. package/dist/classDiagram-2ON5EDUG-ERF9RE9K.js +16 -0
  18. package/dist/classDiagram-v2-WZHVMYZB-ERF9RE9K.js +16 -0
  19. package/dist/clone-B_B1yyxj.js +8 -0
  20. package/dist/components/ChatPanel.vue.d.ts +11 -7
  21. package/dist/components/ChatPanel.vue.d.ts.map +1 -1
  22. package/dist/components/common/SettingsPanel.vue.d.ts +10 -1
  23. package/dist/components/common/SettingsPanel.vue.d.ts.map +1 -1
  24. package/dist/components/common/ToggleSwitch.vue.d.ts.map +1 -1
  25. package/dist/components/input/ChatInput.vue.d.ts +4 -1
  26. package/dist/components/input/ChatInput.vue.d.ts.map +1 -1
  27. package/dist/components/input/DropdownSelector.vue.d.ts +10 -0
  28. package/dist/components/input/DropdownSelector.vue.d.ts.map +1 -1
  29. package/dist/components/message/MessageBubble.vue.d.ts +2 -2
  30. package/dist/components/message/MessageBubble.vue.d.ts.map +1 -1
  31. package/dist/components/message/PartsRenderer.vue.d.ts +6 -0
  32. package/dist/components/message/PartsRenderer.vue.d.ts.map +1 -1
  33. package/dist/components/message/parts/CollapsibleCard.vue.d.ts.map +1 -1
  34. package/dist/components/message/parts/ImagePart.vue.d.ts.map +1 -1
  35. package/dist/components/message/parts/SearchPart.vue.d.ts.map +1 -1
  36. package/dist/components/message/parts/TextPart.vue.d.ts +3 -1
  37. package/dist/components/message/parts/TextPart.vue.d.ts.map +1 -1
  38. package/dist/components/message/parts/ToolCallPart.vue.d.ts +9 -4
  39. package/dist/components/message/parts/ToolCallPart.vue.d.ts.map +1 -1
  40. package/dist/components/message/parts/index.d.ts +5 -1
  41. package/dist/components/message/parts/index.d.ts.map +1 -1
  42. package/dist/components/message/parts/visual-predicate.d.ts +7 -0
  43. package/dist/components/message/parts/visual-predicate.d.ts.map +1 -0
  44. package/dist/components/message/parts/visual-render.d.ts +2 -0
  45. package/dist/components/message/parts/visual-render.d.ts.map +1 -0
  46. package/dist/components/message/parts/visual.d.ts +3 -0
  47. package/dist/components/message/parts/visual.d.ts.map +1 -0
  48. package/dist/composables/useChat.d.ts +34 -10
  49. package/dist/composables/useChat.d.ts.map +1 -1
  50. package/dist/composables/useImageUpload.d.ts +7 -0
  51. package/dist/composables/useImageUpload.d.ts.map +1 -1
  52. package/dist/cose-bilkent-S5V4N54A-C-CkEnIH.js +2608 -0
  53. package/dist/cytoscape.esm-CjI2IsL8.js +18735 -0
  54. package/dist/dagre-6UL2VRFP-BbmZysl9.js +446 -0
  55. package/dist/defaultLocale-D7EN2tov.js +171 -0
  56. package/dist/diagram-PSM6KHXK-CMnope5e.js +533 -0
  57. package/dist/diagram-QEK2KX5R-Bc87skpo.js +221 -0
  58. package/dist/diagram-S2PKOQOG-BtkzC5-o.js +143 -0
  59. package/dist/erDiagram-Q2GNP2WA-CwQLdjh9.js +842 -0
  60. package/dist/flowDiagram-NV44I4VS-8ehMD0M4.js +1627 -0
  61. package/dist/ganttDiagram-JELNMOA3-dLX8Pbvy.js +2670 -0
  62. package/dist/gitGraph-F6HP7TQM-BBk5qfnx.js +5 -0
  63. package/dist/gitGraphDiagram-NY62KEGX-DG_fvWrq.js +712 -0
  64. package/dist/graph-B7BnGhuR.js +381 -0
  65. package/dist/index-Cr3tTH72.js +83883 -0
  66. package/dist/index.d.ts +7 -7
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +19 -56433
  69. package/dist/info-NVLQJR56-eZr6nVr1.js +5 -0
  70. package/dist/infoDiagram-WHAUD3N6-BcVYTft_.js +24 -0
  71. package/dist/init-DjUOC4st.js +16 -0
  72. package/dist/journeyDiagram-XKPGCS4Q-D4C72gYB.js +834 -0
  73. package/dist/kanban-definition-3W4ZIXB7-BuSjAlm6.js +721 -0
  74. package/dist/layout-LjYblgUu.js +1441 -0
  75. package/dist/linear-DtPqj295.js +259 -0
  76. package/dist/mermaid-parser.core-DdOZxjrw.js +15189 -0
  77. package/dist/min-DYbgfxu0.js +38 -0
  78. package/dist/mindmap-definition-VGOIOE7T-BZxsr4M_.js +787 -0
  79. package/dist/ordinal-DfAQgscy.js +61 -0
  80. package/dist/packet-BFZMPI3H-XfNBjRzf.js +5 -0
  81. package/dist/pie-7BOR55EZ-pJDdOv19.js +5 -0
  82. package/dist/pieDiagram-ADFJNKIX-DDwfWBNn.js +161 -0
  83. package/dist/quadrantDiagram-AYHSOK5B-Ddpxuto0.js +1024 -0
  84. package/dist/radar-NHE76QYJ-jE07d-4Z.js +5 -0
  85. package/dist/requirementDiagram-UZGBJVZJ-B_TLWQ-N.js +852 -0
  86. package/dist/sankeyDiagram-TZEHDZUN-C1TWrUpG.js +810 -0
  87. package/dist/sequenceDiagram-WL72ISMW-B8pqb5YX.js +2518 -0
  88. package/dist/stateDiagram-FKZM4ZOC-DwNT1ej6.js +263 -0
  89. package/dist/stateDiagram-v2-4FDKWEC3-CZQUitzd.js +16 -0
  90. package/dist/style.css +1 -1
  91. package/dist/timeline-definition-IT6M3QCI-BGdS1wvf.js +799 -0
  92. package/dist/treemap-KMMF4GRG-B_eEU0Q4.js +5 -0
  93. package/dist/types/index.d.ts +36 -37
  94. package/dist/types/index.d.ts.map +1 -1
  95. package/dist/xychartDiagram-PRI3JC2R-DtwXQqN4.js +1340 -0
  96. package/package.json +3 -3
  97. package/src/components/ChatPanel.vue +75 -13
  98. package/src/components/common/SettingsPanel.vue +169 -0
  99. package/src/components/input/ChatInput.vue +49 -34
  100. package/src/components/input/DropdownSelector.vue +175 -18
  101. package/src/components/message/MessageBubble.vue +61 -40
  102. package/src/components/message/PartsRenderer.vue +106 -27
  103. package/src/components/message/parts/CollapsibleCard.vue +8 -3
  104. package/src/components/message/parts/ImagePart.vue +0 -1
  105. package/src/components/message/parts/SearchPart.vue +2 -0
  106. package/src/components/message/parts/TextPart.vue +753 -11
  107. package/src/components/message/parts/ToolCallPart.vue +315 -246
  108. package/src/components/message/parts/index.ts +5 -1
  109. package/src/components/message/parts/visual-predicate.ts +43 -0
  110. package/src/components/message/parts/visual-render.ts +19 -0
  111. package/src/components/message/parts/visual.ts +12 -0
  112. package/src/composables/useChat.ts +560 -92
  113. package/src/composables/useImageUpload.ts +115 -11
  114. package/src/index.ts +6 -8
  115. package/src/styles.css +124 -0
  116. package/src/types/index.ts +52 -44
  117. package/dist/components/message/blocks/CodeBlock.vue.d.ts +0 -11
  118. package/dist/components/message/blocks/CodeBlock.vue.d.ts.map +0 -1
  119. package/dist/components/message/blocks/TextBlock.vue.d.ts +0 -7
  120. package/dist/components/message/blocks/TextBlock.vue.d.ts.map +0 -1
  121. package/dist/components/message/blocks/index.d.ts +0 -6
  122. package/dist/components/message/blocks/index.d.ts.map +0 -1
  123. package/dist/components/message/parts/ToolResultPart.vue.d.ts +0 -14
  124. package/dist/components/message/parts/ToolResultPart.vue.d.ts.map +0 -1
  125. package/dist/components/message/tool-results/DefaultToolResult.vue.d.ts +0 -4
  126. package/dist/components/message/tool-results/DefaultToolResult.vue.d.ts.map +0 -1
  127. package/dist/components/message/tool-results/SearchResults.vue.d.ts +0 -4
  128. package/dist/components/message/tool-results/SearchResults.vue.d.ts.map +0 -1
  129. package/dist/components/message/tool-results/WeatherCard.vue.d.ts +0 -4
  130. package/dist/components/message/tool-results/WeatherCard.vue.d.ts.map +0 -1
  131. package/dist/components/message/tool-results/index.d.ts +0 -7
  132. package/dist/components/message/tool-results/index.d.ts.map +0 -1
  133. package/src/components/message/ContentRenderer.vue +0 -61
  134. package/src/components/message/ToolResultRenderer.vue +0 -27
  135. package/src/components/message/blocks/CodeBlock.vue +0 -113
  136. package/src/components/message/blocks/TextBlock.vue +0 -21
  137. package/src/components/message/blocks/index.ts +0 -6
  138. package/src/components/message/parts/ToolResultPart.vue +0 -78
  139. package/src/components/message/tool-results/DefaultToolResult.vue +0 -45
  140. package/src/components/message/tool-results/SearchResults.vue +0 -133
  141. package/src/components/message/tool-results/WeatherCard.vue +0 -139
  142. package/src/components/message/tool-results/index.ts +0 -7
@@ -24,6 +24,12 @@ interface UseImageUploadOptions {
24
24
  maxImages?: number;
25
25
  /** 单张图片最大大小(字节),默认 10MB */
26
26
  maxSize?: number;
27
+ /** 图片最大宽度,默认 4000px(避免超过 API 像素限制) */
28
+ maxWidth?: number;
29
+ /** 图片最大高度,默认 4000px(避免超过 API 像素限制) */
30
+ maxHeight?: number;
31
+ /** 压缩质量 0-1,默认 0.85 */
32
+ quality?: number;
27
33
  }
28
34
 
29
35
  /**
@@ -31,7 +37,13 @@ interface UseImageUploadOptions {
31
37
  * @param options 配置项
32
38
  */
33
39
  export function useImageUpload(options: UseImageUploadOptions = {}) {
34
- const { maxImages = 5, maxSize = 10 * 1024 * 1024 } = options;
40
+ const {
41
+ maxImages = 5,
42
+ maxSize = 10 * 1024 * 1024,
43
+ maxWidth = 4000,
44
+ maxHeight = 4000,
45
+ quality = 0.85
46
+ } = options;
35
47
 
36
48
  // 图片列表
37
49
  const images = ref<ImageItem[]>([]);
@@ -58,26 +70,88 @@ export function useImageUpload(options: UseImageUploadOptions = {}) {
58
70
  const hasImages = computed(() => images.value.length > 0);
59
71
 
60
72
  /**
61
- * 读取图片文件为 base64
73
+ * 压缩图片到指定尺寸
74
+ * @param file 原始图片文件
75
+ * @returns 压缩后的 ImageItem
62
76
  */
63
- function readImageFile(file: File): Promise<ImageItem> {
77
+ function compressImage(file: File): Promise<ImageItem> {
64
78
  return new Promise((resolve, reject) => {
79
+ console.log('[compressImage] 开始处理:', file.name, file.size, file.type);
80
+
81
+ // 使用 FileReader 读取文件为 data URL(避免 CSP 对 blob URL 的限制)
65
82
  const reader = new FileReader();
83
+
66
84
  reader.onload = () => {
67
85
  const dataUrl = reader.result as string;
68
- // dataUrl 格式: data:image/png;base64,xxxxx
69
- const base64 = dataUrl.split(',')[1];
70
- resolve({
71
- dataUrl,
72
- base64,
73
- mimeType: file.type,
74
- });
86
+ console.log('[compressImage] FileReader 完成, dataUrl 长度:', dataUrl.length);
87
+
88
+ const img = new Image();
89
+
90
+ img.onload = () => {
91
+ console.log('[compressImage] 原始尺寸:', img.width, 'x', img.height);
92
+ let { width, height } = img;
93
+
94
+ // 计算缩放比例
95
+ if (width > maxWidth || height > maxHeight) {
96
+ const ratio = Math.min(maxWidth / width, maxHeight / height);
97
+ width = Math.round(width * ratio);
98
+ height = Math.round(height * ratio);
99
+ console.log('[compressImage] 压缩后尺寸:', width, 'x', height);
100
+ } else {
101
+ console.log('[compressImage] 无需压缩');
102
+ }
103
+
104
+ // 创建 canvas 进行压缩
105
+ const canvas = document.createElement('canvas');
106
+ canvas.width = width;
107
+ canvas.height = height;
108
+
109
+ const ctx = canvas.getContext('2d');
110
+ if (!ctx) {
111
+ reject(new Error('无法创建 canvas context'));
112
+ return;
113
+ }
114
+
115
+ ctx.drawImage(img, 0, 0, width, height);
116
+
117
+ // 转换为 dataUrl(JPEG 格式以减小体积)
118
+ const mimeType = file.type === 'image/png' ? 'image/png' : 'image/jpeg';
119
+ const compressedDataUrl = canvas.toDataURL(mimeType, quality);
120
+ const base64 = compressedDataUrl.split(',')[1];
121
+
122
+ console.log('[compressImage] 压缩完成, base64 长度:', base64.length);
123
+
124
+ resolve({
125
+ dataUrl: compressedDataUrl,
126
+ base64,
127
+ mimeType,
128
+ });
129
+ };
130
+
131
+ img.onerror = (e) => {
132
+ console.error('[compressImage] Image 加载失败:', e);
133
+ reject(new Error('图片加载失败'));
134
+ };
135
+
136
+ img.src = dataUrl;
75
137
  };
76
- reader.onerror = reject;
138
+
139
+ reader.onerror = (e) => {
140
+ console.error('[compressImage] FileReader 失败:', e);
141
+ reject(new Error('读取文件失败'));
142
+ };
143
+
77
144
  reader.readAsDataURL(file);
78
145
  });
79
146
  }
80
147
 
148
+ /**
149
+ * 读取图片文件为 base64(带压缩)
150
+ */
151
+ function readImageFile(file: File): Promise<ImageItem> {
152
+ return compressImage(file);
153
+ }
154
+
81
155
  /**
82
156
  * 处理文件列表
83
157
  */
@@ -197,6 +271,35 @@ export function useImageUpload(options: UseImageUploadOptions = {}) {
197
271
  processFiles(files);
198
272
  }
199
273
 
274
+ /**
275
+ * 从 data URL 字符串数组初始化图片(用于历史消息编辑)
276
+ * @param data data URL 字符串数组
277
+ */
278
+ function initImages(data: string[]) {
279
+ const items: ImageItem[] = [];
280
+ for (const item of data) {
281
+ if (item.startsWith('data:')) {
282
+ // data URL 格式: data:image/png;base64,xxxxx
283
+ const matches = item.match(/^data:([^;]+);base64,(.+)$/);
284
+ if (matches) {
285
+ items.push({
286
+ dataUrl: item,
287
+ base64: matches[2],
288
+ mimeType: matches[1],
289
+ });
290
+ }
291
+ } else {
292
+ // 普通 URL,只能作为预览显示
293
+ items.push({
294
+ dataUrl: item,
295
+ base64: '',
296
+ mimeType: 'image/unknown',
297
+ });
298
+ }
299
+ }
300
+ images.value = items;
301
+ }
302
+
200
303
  return {
201
304
  // 状态
202
305
  images,
@@ -224,5 +327,6 @@ export function useImageUpload(options: UseImageUploadOptions = {}) {
224
327
  removeImage,
225
328
  clearImages,
226
329
  addImages,
330
+ initImages,
227
331
  };
228
332
  }
package/src/index.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * 新架构:使用 ContentPart 数组渲染消息内容
7
7
  * - 支持流式渲染
8
- * - 支持自定义工具结果 UI
8
+ * - 支持自定义 Part 类型渲染(如 weather, stock)
9
9
  * - 支持思考、搜索、工具调用等多种内容类型
10
10
  */
11
11
 
@@ -51,7 +51,6 @@ export type {
51
51
  ThinkingPart,
52
52
  SearchPart,
53
53
  ToolCallPart,
54
- ToolResultPart,
55
54
  ImagePart,
56
55
  ErrorPart,
57
56
  // 搜索结果
@@ -85,7 +84,7 @@ export {
85
84
  ThinkingPart as ThinkingPartComponent,
86
85
  SearchPart as SearchPartComponent,
87
86
  ToolCallPart as ToolCallPartComponent,
88
- ToolResultPart as ToolResultPartComponent,
87
+ ImagePart as ImagePartComponent,
89
88
  ErrorPart as ErrorPartComponent,
90
89
  } from './components/message/parts'
91
90
 
@@ -110,7 +109,6 @@ export { default as Toast } from './components/common/Toast.vue'
110
109
 
111
110
  // 从 ai-chat-shared 重新导出(用于自定义工具渲染器)
112
111
  export type {
113
- ToolRendererProps,
114
112
  WeatherData,
115
113
  SearchResultItem,
116
114
  } from '@huyooo/ai-chat-shared'
@@ -127,11 +125,11 @@ export type {
127
125
  * const adapter = createElectronAdapter()
128
126
  * <ChatPanel :adapter="adapter" :working-dir="'/path/to/dir'" />
129
127
  *
130
- * 3. 自定义工具渲染器:
128
+ * 3. 自定义 Part 渲染器(新架构):
131
129
  * import { markRaw } from 'vue'
132
- * import CustomWeatherCard from './CustomWeatherCard.vue'
133
- * const toolRenderers = { get_weather: markRaw(CustomWeatherCard) }
134
- * <ChatPanel :adapter="adapter" :tool-renderers="toolRenderers" />
130
+ * import WeatherCard from './WeatherCard.vue'
131
+ * const partRenderers = { weather: markRaw(WeatherCard) }
132
+ * <ChatPanel :adapter="adapter" :part-renderers="partRenderers" />
135
133
  *
136
134
  * 4. 使用 useChat 自定义 UI:
137
135
  * import { useChat } from '@huyooo/ai-chat-frontend-vue'
package/src/styles.css CHANGED
@@ -28,6 +28,7 @@
28
28
  --chat-muted-hover: #e6e5e0;
29
29
  --chat-border: #26251e1a;
30
30
  --chat-text: #26251eeb;
31
+ --chat-text-strong: #26251e;
31
32
  --chat-text-muted: #26251e99;
32
33
  /* 主色:light/dark 保持一致(蓝) */
33
34
  --chat-primary: #54a9ff;
@@ -44,6 +45,36 @@
44
45
  --chat-fab-bg: #ffffff;
45
46
  --chat-fab-bg-hover: var(--chat-input-bg);
46
47
  --chat-fab-shadow: 0 10px 24px rgba(0, 0, 0, 0.12);
48
+
49
+ /* 代码高亮 - GitHub Light 风格 */
50
+ --chat-hljs-keyword: #cf222e;
51
+ --chat-hljs-built-in: #953800;
52
+ --chat-hljs-type: #953800;
53
+ --chat-hljs-function: #8250df;
54
+ --chat-hljs-string: #0a3069;
55
+ --chat-hljs-number: #0550ae;
56
+ --chat-hljs-literal: #0550ae;
57
+ --chat-hljs-comment: #6e7781;
58
+ --chat-hljs-variable: #953800;
59
+ --chat-hljs-attr: #0550ae;
60
+ --chat-hljs-property: #116329;
61
+ --chat-hljs-operator: #cf222e;
62
+ --chat-hljs-punctuation: #24292f;
63
+ --chat-hljs-params: #24292f;
64
+ --chat-hljs-regexp: #116329;
65
+ --chat-hljs-selector: #116329;
66
+ --chat-hljs-tag: #116329;
67
+ --chat-hljs-name: #116329;
68
+ --chat-hljs-deletion: #82071e;
69
+ --chat-hljs-deletion-bg: rgba(255, 129, 130, 0.15);
70
+ --chat-hljs-addition: #116329;
71
+ --chat-hljs-addition-bg: rgba(46, 160, 67, 0.15);
72
+ --chat-hljs-meta: #6e7781;
73
+ --chat-hljs-link: #0a3069;
74
+ --chat-hljs-symbol: #0550ae;
75
+ --chat-hljs-subst: #24292f;
76
+ --chat-hljs-section: #0550ae;
77
+ --chat-hljs-bullet: #953800;
47
78
  }
48
79
 
49
80
  /* 默认跟随系统暗色 */
@@ -59,6 +90,7 @@
59
90
  --chat-muted-hover: #3c3c3c;
60
91
  --chat-border: #333;
61
92
  --chat-text: #ccc;
93
+ --chat-text-strong: #fff;
62
94
  --chat-text-muted: #888;
63
95
  /* 主色:light/dark 保持一致(蓝) */
64
96
  --chat-primary: #54a9ff;
@@ -74,6 +106,36 @@
74
106
  --chat-fab-bg: var(--chat-muted);
75
107
  --chat-fab-bg-hover: var(--chat-muted-hover);
76
108
  --chat-fab-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
109
+
110
+ /* 代码高亮 - GitHub Dark 风格 */
111
+ --chat-hljs-keyword: #ff7b72;
112
+ --chat-hljs-built-in: #ffa657;
113
+ --chat-hljs-type: #ffa657;
114
+ --chat-hljs-function: #d2a8ff;
115
+ --chat-hljs-string: #a5d6ff;
116
+ --chat-hljs-number: #79c0ff;
117
+ --chat-hljs-literal: #79c0ff;
118
+ --chat-hljs-comment: #8b949e;
119
+ --chat-hljs-variable: #ffa657;
120
+ --chat-hljs-attr: #79c0ff;
121
+ --chat-hljs-property: #7ee787;
122
+ --chat-hljs-operator: #ff7b72;
123
+ --chat-hljs-punctuation: #e6edf3;
124
+ --chat-hljs-params: #e6edf3;
125
+ --chat-hljs-regexp: #7ee787;
126
+ --chat-hljs-selector: #7ee787;
127
+ --chat-hljs-tag: #7ee787;
128
+ --chat-hljs-name: #7ee787;
129
+ --chat-hljs-deletion: #ffa198;
130
+ --chat-hljs-deletion-bg: rgba(248, 81, 73, 0.15);
131
+ --chat-hljs-addition: #7ee787;
132
+ --chat-hljs-addition-bg: rgba(46, 160, 67, 0.15);
133
+ --chat-hljs-meta: #8b949e;
134
+ --chat-hljs-link: #a5d6ff;
135
+ --chat-hljs-symbol: #79c0ff;
136
+ --chat-hljs-subst: #e6edf3;
137
+ --chat-hljs-section: #79c0ff;
138
+ --chat-hljs-bullet: #ffa657;
77
139
  }
78
140
  }
79
141
 
@@ -89,6 +151,7 @@
89
151
  --chat-muted-hover: #e6e5e0;
90
152
  --chat-border: #26251e1a;
91
153
  --chat-text: #26251eeb;
154
+ --chat-text-strong: #26251e;
92
155
  --chat-text-muted: #26251e99;
93
156
  --chat-primary: #54a9ff;
94
157
  --chat-primary-hover: #2f90ff;
@@ -102,6 +165,36 @@
102
165
  --chat-fab-bg: #ffffff;
103
166
  --chat-fab-bg-hover: var(--chat-input-bg);
104
167
  --chat-fab-shadow: 0 10px 24px rgba(0, 0, 0, 0.12);
168
+
169
+ /* 代码高亮 - GitHub Light 风格 */
170
+ --chat-hljs-keyword: #cf222e;
171
+ --chat-hljs-built-in: #953800;
172
+ --chat-hljs-type: #953800;
173
+ --chat-hljs-function: #8250df;
174
+ --chat-hljs-string: #0a3069;
175
+ --chat-hljs-number: #0550ae;
176
+ --chat-hljs-literal: #0550ae;
177
+ --chat-hljs-comment: #6e7781;
178
+ --chat-hljs-variable: #953800;
179
+ --chat-hljs-attr: #0550ae;
180
+ --chat-hljs-property: #116329;
181
+ --chat-hljs-operator: #cf222e;
182
+ --chat-hljs-punctuation: #24292f;
183
+ --chat-hljs-params: #24292f;
184
+ --chat-hljs-regexp: #116329;
185
+ --chat-hljs-selector: #116329;
186
+ --chat-hljs-tag: #116329;
187
+ --chat-hljs-name: #116329;
188
+ --chat-hljs-deletion: #82071e;
189
+ --chat-hljs-deletion-bg: rgba(255, 129, 130, 0.15);
190
+ --chat-hljs-addition: #116329;
191
+ --chat-hljs-addition-bg: rgba(46, 160, 67, 0.15);
192
+ --chat-hljs-meta: #6e7781;
193
+ --chat-hljs-link: #0a3069;
194
+ --chat-hljs-symbol: #0550ae;
195
+ --chat-hljs-subst: #24292f;
196
+ --chat-hljs-section: #0550ae;
197
+ --chat-hljs-bullet: #953800;
105
198
  }
106
199
 
107
200
  :root[data-theme="dark"] {
@@ -115,6 +208,7 @@
115
208
  --chat-muted-hover: #3c3c3c;
116
209
  --chat-border: #333;
117
210
  --chat-text: #ccc;
211
+ --chat-text-strong: #fff;
118
212
  --chat-text-muted: #888;
119
213
  /* 主色:light/dark 保持一致(蓝) */
120
214
  --chat-primary: #54a9ff;
@@ -129,6 +223,36 @@
129
223
  --chat-fab-bg: var(--chat-muted);
130
224
  --chat-fab-bg-hover: var(--chat-muted-hover);
131
225
  --chat-fab-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
226
+
227
+ /* 代码高亮 - GitHub Dark 风格 */
228
+ --chat-hljs-keyword: #ff7b72;
229
+ --chat-hljs-built-in: #ffa657;
230
+ --chat-hljs-type: #ffa657;
231
+ --chat-hljs-function: #d2a8ff;
232
+ --chat-hljs-string: #a5d6ff;
233
+ --chat-hljs-number: #79c0ff;
234
+ --chat-hljs-literal: #79c0ff;
235
+ --chat-hljs-comment: #8b949e;
236
+ --chat-hljs-variable: #ffa657;
237
+ --chat-hljs-attr: #79c0ff;
238
+ --chat-hljs-property: #7ee787;
239
+ --chat-hljs-operator: #ff7b72;
240
+ --chat-hljs-punctuation: #e6edf3;
241
+ --chat-hljs-params: #e6edf3;
242
+ --chat-hljs-regexp: #7ee787;
243
+ --chat-hljs-selector: #7ee787;
244
+ --chat-hljs-tag: #7ee787;
245
+ --chat-hljs-name: #7ee787;
246
+ --chat-hljs-deletion: #ffa198;
247
+ --chat-hljs-deletion-bg: rgba(248, 81, 73, 0.15);
248
+ --chat-hljs-addition: #7ee787;
249
+ --chat-hljs-addition-bg: rgba(46, 160, 67, 0.15);
250
+ --chat-hljs-meta: #8b949e;
251
+ --chat-hljs-link: #a5d6ff;
252
+ --chat-hljs-symbol: #79c0ff;
253
+ --chat-hljs-subst: #e6edf3;
254
+ --chat-hljs-section: #79c0ff;
255
+ --chat-hljs-bullet: #ffa657;
132
256
  }
133
257
 
134
258
  /* 统一滚动条样式 - 任何需要自定义滚动条的元素添加此类 */
@@ -24,6 +24,12 @@ export type ModelOption = ModelOptionType
24
24
  export type ProviderType = ProviderTypeType
25
25
 
26
26
  // ==================== Content Part 类型 ====================
27
+ //
28
+ // 架构说明:
29
+ // 1. 内置类型:text, code, thinking, search, tool_call, image, error
30
+ // 2. 扩展类型:weather, stock 等业务类型(通过 partRenderers 注册渲染器)
31
+ // 3. 工具执行完成后,如果定义了 resultType,直接生成对应类型的 Part(如 weather)
32
+ // 4. 前端通过 partRenderers 统一渲染所有 Part 类型
27
33
 
28
34
  /** 搜索结果 */
29
35
  export interface SearchResult {
@@ -38,6 +44,13 @@ export interface TextPart {
38
44
  text: string
39
45
  }
40
46
 
47
+ /** 代码块 Part(从 text 中解析,或直接返回)*/
48
+ export interface CodePart {
49
+ type: 'code'
50
+ content: string
51
+ language?: string
52
+ }
53
+
41
54
  /** 思考过程 Part */
42
55
  export interface ThinkingPart {
43
56
  type: 'thinking'
@@ -54,24 +67,19 @@ export interface SearchPart {
54
67
  status: 'running' | 'done'
55
68
  }
56
69
 
57
- /** 工具调用 Part */
70
+ /** 工具调用 Part(仅展示执行过程,结果由具体类型 Part 渲染)*/
58
71
  export interface ToolCallPart {
59
72
  type: 'tool_call'
60
73
  id: string
61
74
  name: string
62
75
  args?: Record<string, unknown>
63
- status: 'pending' | 'running' | 'done' | 'error' | 'cancelled' | 'skipped' // pending: 等待用户批准, cancelled: 已取消, skipped: 已跳过
64
- result: unknown | null // 执行结果,null 表示尚未执行或没有结果
65
- }
66
-
67
- /** 工具结果 Part - 用于自定义 UI 渲染(备用,主要使用 ToolCallPart) */
68
- export interface ToolResultPart {
69
- type: 'tool_result'
70
- id: string
71
- name: string
72
- args?: Record<string, unknown>
73
- result: unknown // 解析后的结构化数据
74
- status: 'done' | 'error' | 'cancelled' | 'skipped'
76
+ status: 'pending' | 'running' | 'done' | 'error' | 'cancelled' | 'skipped'
77
+ // 注意:不再有 result 字段,结果由工具的 resultType 指定的 Part 类型渲染
78
+ /** 工具执行输出(用于 execute_command 等需要展示 stdout/stderr 的工具) */
79
+ output?: {
80
+ stdout?: string
81
+ stderr?: string
82
+ }
75
83
  }
76
84
 
77
85
  /** 图片 Part */
@@ -88,18 +96,44 @@ export interface ErrorPart {
88
96
  retryable?: boolean
89
97
  }
90
98
 
91
- /** 内容 Part 联合类型 */
92
- export type ContentPart =
99
+ // ==================== 扩展 Part 类型(业务类型)====================
100
+
101
+ /** 天气 Part(由 get_weather 工具生成)*/
102
+ export interface WeatherPart {
103
+ type: 'weather'
104
+ city: string
105
+ temperature: number
106
+ condition: string
107
+ humidity: number
108
+ wind: string
109
+ reportTime?: string
110
+ province?: string
111
+ }
112
+
113
+ /** 自定义 Part 基础接口(用于扩展)*/
114
+ export interface CustomPart {
115
+ type: string
116
+ [key: string]: unknown
117
+ }
118
+
119
+ /** 内置 Part 联合类型 */
120
+ export type BuiltinPart =
93
121
  | TextPart
122
+ | CodePart
94
123
  | ThinkingPart
95
124
  | SearchPart
96
125
  | ToolCallPart
97
- | ToolResultPart
98
126
  | ImagePart
99
127
  | ErrorPart
100
128
 
129
+ /** 内容 Part 联合类型(包含内置和扩展类型)*/
130
+ export type ContentPart = BuiltinPart | WeatherPart | CustomPart
131
+
132
+ /** 内置 Part 类型字符串 */
133
+ export type BuiltinPartType = BuiltinPart['type']
134
+
101
135
  /** 内容 Part 类型字符串 */
102
- export type ContentPartType = ContentPart['type']
136
+ export type ContentPartType = string
103
137
 
104
138
  /** 步骤折叠模式 */
105
139
  export type StepsExpandedType = 'open' | 'close' | 'auto'
@@ -138,7 +172,7 @@ export interface ChatMessage {
138
172
  /** 是否已复制 */
139
173
  copied?: boolean
140
174
  /** 消息时间戳 */
141
- timestamp?: Date
175
+ timestamp?: number
142
176
  /** 错误详情(如果有错误) */
143
177
  error?: ErrorDetails
144
178
  /** 是否被用户中止 */
@@ -161,30 +195,4 @@ export interface ChatInputOptions {
161
195
  thinkingEnabled: boolean
162
196
  }
163
197
 
164
- // ==================== 兼容旧类型(逐步废弃)====================
165
-
166
- /** @deprecated 使用 ContentPart 替代 */
167
- export type ExecutionStepType = 'thinking' | 'search' | 'tool_call' | 'error'
168
-
169
- /** @deprecated 使用 ContentPart 替代 */
170
- export type ExecutionStepStatus = 'running' | 'completed' | 'error'
171
-
172
- /** @deprecated 使用 ContentPart 替代 */
173
- export type ExecutionStep = {
174
- id: string
175
- type: ExecutionStepType
176
- status: ExecutionStepStatus
177
- startedAt?: number
178
- completedAt?: number
179
- duration?: number
180
- content?: string
181
- query?: string
182
- results?: SearchResult[]
183
- callId?: string
184
- name?: string
185
- args?: Record<string, unknown>
186
- result?: string
187
- message?: string
188
- category?: string
189
- }
190
198
 
@@ -1,11 +0,0 @@
1
- import { CodeBlock } from '@huyooo/ai-chat-shared';
2
- type __VLS_Props = {
3
- block: CodeBlock;
4
- };
5
- declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
6
- copy: (code: string) => any;
7
- }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
8
- onCopy?: ((code: string) => any) | undefined;
9
- }>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
10
- export default _default;
11
- //# sourceMappingURL=CodeBlock.vue.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"CodeBlock.vue.d.ts","sourceRoot":"","sources":["../../../../src/components/message/blocks/CodeBlock.vue"],"names":[],"mappings":"AAqHA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAGvD,KAAK,WAAW,GAAG;IACjB,KAAK,EAAE,SAAS,CAAA;CACjB,CAAC;;;;;;AAwHF,wBAQG"}
@@ -1,7 +0,0 @@
1
- import { TextBlock } from '@huyooo/ai-chat-shared';
2
- type __VLS_Props = {
3
- block: TextBlock;
4
- };
5
- declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
6
- export default _default;
7
- //# sourceMappingURL=TextBlock.vue.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"TextBlock.vue.d.ts","sourceRoot":"","sources":["../../../../src/components/message/blocks/TextBlock.vue"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAEvD,KAAK,WAAW,GAAG;IACjB,KAAK,EAAE,SAAS,CAAA;CACjB,CAAC;;AA4CF,wBAOG"}
@@ -1,6 +0,0 @@
1
- /**
2
- * 内容块组件导出
3
- */
4
- export { default as TextBlock } from './TextBlock.vue';
5
- export { default as CodeBlock } from './CodeBlock.vue';
6
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/blocks/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,iBAAiB,CAAA;AACtD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,iBAAiB,CAAA"}
@@ -1,14 +0,0 @@
1
- import { StepsExpandedType } from '../../../types';
2
- type __VLS_Props = {
3
- id: string;
4
- name: string;
5
- args?: Record<string, unknown>;
6
- result: unknown;
7
- status: 'done' | 'error' | 'cancelled' | 'skipped';
8
- expandedType?: StepsExpandedType;
9
- };
10
- declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
11
- expandedType: StepsExpandedType;
12
- }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
13
- export default _default;
14
- //# sourceMappingURL=ToolResultPart.vue.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ToolResultPart.vue.d.ts","sourceRoot":"","sources":["../../../../src/components/message/parts/ToolResultPart.vue"],"names":[],"mappings":"AAwBA;AAkFA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAEvD,KAAK,WAAW,GAAG;IACjB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,MAAM,EAAE,OAAO,CAAA;IACf,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,WAAW,GAAG,SAAS,CAAA;IAClD,YAAY,CAAC,EAAE,iBAAiB,CAAA;CACjC,CAAC;;kBADe,iBAAiB;;AAsHlC,wBAQG"}
@@ -1,4 +0,0 @@
1
- import { ToolRendererProps } from '@huyooo/ai-chat-shared';
2
- declare const _default: import('vue').DefineComponent<ToolRendererProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<ToolRendererProps> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
3
- export default _default;
4
- //# sourceMappingURL=DefaultToolResult.vue.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"DefaultToolResult.vue.d.ts","sourceRoot":"","sources":["../../../../src/components/message/tool-results/DefaultToolResult.vue"],"names":[],"mappings":"AAgDA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;;AA8D/D,wBAOG"}
@@ -1,4 +0,0 @@
1
- import { ToolRendererProps } from '@huyooo/ai-chat-shared';
2
- declare const _default: import('vue').DefineComponent<ToolRendererProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<ToolRendererProps> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
3
- export default _default;
4
- //# sourceMappingURL=SearchResults.vue.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SearchResults.vue.d.ts","sourceRoot":"","sources":["../../../../src/components/message/tool-results/SearchResults.vue"],"names":[],"mappings":"AAyIA,OAAO,KAAK,EAAE,iBAAiB,EAAoB,MAAM,wBAAwB,CAAA;;AAwIjF,wBAOG"}
@@ -1,4 +0,0 @@
1
- import { ToolRendererProps } from '@huyooo/ai-chat-shared';
2
- declare const _default: import('vue').DefineComponent<ToolRendererProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<ToolRendererProps> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
3
- export default _default;
4
- //# sourceMappingURL=WeatherCard.vue.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"WeatherCard.vue.d.ts","sourceRoot":"","sources":["../../../../src/components/message/tool-results/WeatherCard.vue"],"names":[],"mappings":"AA+IA,OAAO,KAAK,EAAE,iBAAiB,EAAe,MAAM,wBAAwB,CAAA;;AA2K5E,wBAOG"}
@@ -1,7 +0,0 @@
1
- /**
2
- * 工具结果渲染组件导出
3
- */
4
- export { default as DefaultToolResult } from './DefaultToolResult.vue';
5
- export { default as WeatherCard } from './WeatherCard.vue';
6
- export { default as SearchResults } from './SearchResults.vue';
7
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/tool-results/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AACtE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC1D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA"}