@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
@@ -1,61 +0,0 @@
1
- <template>
2
- <div v-if="blocks.length" class="content-renderer">
3
- <template v-for="block in blocks" :key="block.id">
4
- <!-- 自定义块渲染器 -->
5
- <component
6
- v-if="customRenderers[block.type]"
7
- :is="customRenderers[block.type]"
8
- :block="block"
9
- />
10
- <!-- 内置文本块 -->
11
- <TextBlock v-else-if="block.type === 'text'" :block="block" />
12
- <!-- 内置代码块 -->
13
- <CodeBlock v-else-if="block.type === 'code'" :block="block" @copy="handleCodeCopy" />
14
- </template>
15
- </div>
16
- </template>
17
-
18
- <script setup lang="ts">
19
- import { computed, inject, type Component } from 'vue'
20
- import { parseContent } from '@huyooo/ai-chat-shared'
21
- import type { ContentBlock } from '@huyooo/ai-chat-shared'
22
- import { TextBlock, CodeBlock } from './blocks'
23
-
24
- const props = defineProps<{
25
- /** 原始文本内容 */
26
- content: string
27
- /** 预解析的块列表(可选,用于流式更新) */
28
- blocks?: ContentBlock[]
29
- }>()
30
-
31
- const emit = defineEmits<{
32
- /** 代码复制事件 */
33
- 'code-copy': [code: string]
34
- }>()
35
-
36
- // 从上层注入的自定义块渲染器
37
- const customRenderers = inject<Record<string, Component>>('blockRenderers', {})
38
-
39
- // 解析后的内容块
40
- const blocks = computed(() => {
41
- // 优先使用预解析的块
42
- if (props.blocks?.length) {
43
- return props.blocks
44
- }
45
- // 否则解析原始内容
46
- return parseContent(props.content)
47
- })
48
-
49
- /** 代码复制处理 */
50
- function handleCodeCopy(code: string) {
51
- emit('code-copy', code)
52
- }
53
- </script>
54
-
55
- <style scoped>
56
- .content-renderer {
57
- display: flex;
58
- flex-direction: column;
59
- gap: 4px;
60
- }
61
- </style>
@@ -1,27 +0,0 @@
1
- <template>
2
- <component :is="resolvedComponent" v-bind="rendererProps" />
3
- </template>
4
-
5
- <script setup lang="ts">
6
- import { computed, inject, type Component } from 'vue'
7
- import type { ToolRendererProps } from '@huyooo/ai-chat-shared'
8
- import { DefaultToolResult } from './tool-results'
9
-
10
- const props = defineProps<ToolRendererProps>()
11
-
12
- // 从上层注入的自定义工具渲染器
13
- const customRenderers = inject<Record<string, Component>>('toolRenderers', {})
14
-
15
- // 解析使用哪个组件
16
- const resolvedComponent = computed(() =>
17
- customRenderers[props.toolName] || DefaultToolResult
18
- )
19
-
20
- // 传递给渲染器的 props
21
- const rendererProps = computed<ToolRendererProps>(() => ({
22
- toolName: props.toolName,
23
- toolArgs: props.toolArgs,
24
- toolResult: props.toolResult,
25
- status: props.status,
26
- }))
27
- </script>
@@ -1,113 +0,0 @@
1
- <template>
2
- <div class="code-block">
3
- <!-- 头部:语言 + 操作按钮 -->
4
- <div class="code-header">
5
- <span class="code-language">{{ languageDisplay }}</span>
6
- <div class="code-actions">
7
- <CopyButton :text="block.content" title="复制代码" @copy="emit('copy', $event)" />
8
- </div>
9
- </div>
10
- <!-- 代码内容 -->
11
- <pre class="code-content"><code v-html="highlightedCode"></code></pre>
12
- </div>
13
- </template>
14
-
15
- <script setup lang="ts">
16
- import { computed } from 'vue'
17
- import { highlightCode, getLanguageDisplayName } from '@huyooo/ai-chat-shared'
18
- import type { CodeBlock } from '@huyooo/ai-chat-shared'
19
- import CopyButton from '../../common/CopyButton.vue'
20
-
21
- const props = defineProps<{
22
- block: CodeBlock
23
- }>()
24
-
25
- const emit = defineEmits<{
26
- copy: [code: string]
27
- }>()
28
-
29
- // 语言显示名称
30
- const languageDisplay = computed(() =>
31
- getLanguageDisplayName(props.block.language)
32
- )
33
-
34
- // 高亮后的代码
35
- const highlightedCode = computed(() =>
36
- highlightCode(props.block.content, props.block.language)
37
- )
38
- </script>
39
-
40
- <style scoped>
41
- .code-block {
42
- background: var(--chat-code-bg, #1f2937);
43
- border-radius: 8px;
44
- overflow: hidden;
45
- margin: 8px 0;
46
- }
47
-
48
- .code-header {
49
- display: flex;
50
- align-items: center;
51
- justify-content: space-between;
52
- padding: 8px 12px;
53
- background: rgba(0, 0, 0, 0.2);
54
- border-bottom: 1px solid rgba(255, 255, 255, 0.1);
55
- }
56
-
57
- .code-language {
58
- font-size: 12px;
59
- color: var(--chat-text-muted, #888);
60
- font-family: 'SF Mono', Monaco, monospace;
61
- }
62
-
63
- .code-content {
64
- margin: 0;
65
- padding: 12px;
66
- overflow-x: auto;
67
- font-family: 'SF Mono', Monaco, Consolas, monospace;
68
- font-size: 13px;
69
- line-height: 1.5;
70
- color: var(--chat-code-text, #e5e7eb);
71
- }
72
-
73
- /* 统一滚动条样式 */
74
- .code-content::-webkit-scrollbar {
75
- width: 6px;
76
- height: 6px;
77
- }
78
-
79
- .code-content::-webkit-scrollbar-track {
80
- background: transparent;
81
- }
82
-
83
- .code-content::-webkit-scrollbar-thumb {
84
- background: var(--chat-scrollbar, rgba(255, 255, 255, 0.2));
85
- border-radius: 3px;
86
- }
87
-
88
- .code-content::-webkit-scrollbar-thumb:hover {
89
- background: var(--chat-scrollbar-hover, rgba(255, 255, 255, 0.3));
90
- }
91
-
92
- .code-content code {
93
- font-family: inherit;
94
- }
95
-
96
- /* highlight.js 主题覆盖 */
97
- .code-content :deep(.hljs-keyword) { color: #c678dd; }
98
- .code-content :deep(.hljs-string) { color: #98c379; }
99
- .code-content :deep(.hljs-number) { color: #d19a66; }
100
- .code-content :deep(.hljs-comment) { color: #5c6370; font-style: italic; }
101
- .code-content :deep(.hljs-function) { color: #61afef; }
102
- .code-content :deep(.hljs-class) { color: #e5c07b; }
103
- .code-content :deep(.hljs-variable) { color: #e06c75; }
104
- .code-content :deep(.hljs-operator) { color: #56b6c2; }
105
- .code-content :deep(.hljs-punctuation) { color: #abb2bf; }
106
- .code-content :deep(.hljs-property) { color: #e06c75; }
107
- .code-content :deep(.hljs-attr) { color: #d19a66; }
108
- .code-content :deep(.hljs-built_in) { color: #e5c07b; }
109
- .code-content :deep(.hljs-title) { color: #61afef; }
110
- .code-content :deep(.hljs-params) { color: #abb2bf; }
111
- .code-content :deep(.hljs-literal) { color: #56b6c2; }
112
- .code-content :deep(.hljs-type) { color: #e5c07b; }
113
- </style>
@@ -1,21 +0,0 @@
1
- <template>
2
- <div class="text-block">{{ block.content }}</div>
3
- </template>
4
-
5
- <script setup lang="ts">
6
- import type { TextBlock } from '@huyooo/ai-chat-shared'
7
-
8
- defineProps<{
9
- block: TextBlock
10
- }>()
11
- </script>
12
-
13
- <style scoped>
14
- .text-block {
15
- font-size: 14px;
16
- line-height: 1.7;
17
- color: var(--chat-text, #ccc);
18
- white-space: pre-wrap;
19
- word-break: break-word;
20
- }
21
- </style>
@@ -1,6 +0,0 @@
1
- /**
2
- * 内容块组件导出
3
- */
4
-
5
- export { default as TextBlock } from './TextBlock.vue'
6
- export { default as CodeBlock } from './CodeBlock.vue'
@@ -1,78 +0,0 @@
1
- <template>
2
- <div class="tool-result-part">
3
- <!-- 自定义渲染器 -->
4
- <component
5
- v-if="customRenderer"
6
- :is="customRenderer"
7
- :tool-name="name"
8
- :tool-args="args || {}"
9
- :tool-result="result"
10
- :status="status"
11
- />
12
- <!-- 默认渲染:可折叠的 JSON -->
13
- <CollapsibleCard
14
- v-else
15
- v-model="expanded"
16
- :icon="status === 'error' ? 'lucide:x-circle' : 'lucide:check-circle'"
17
- :icon-color="status === 'error' ? 'var(--chat-error, #ef4444)' : 'var(--chat-success, #22c55e)'"
18
- :title="name"
19
- title-color="var(--chat-text, #ccc)"
20
- >
21
- <pre class="result-content">{{ formattedResult }}</pre>
22
- </CollapsibleCard>
23
- </div>
24
- </template>
25
-
26
- <script setup lang="ts">
27
- import { ref, computed, inject, type Component } from 'vue'
28
- import CollapsibleCard from './CollapsibleCard.vue'
29
- import type { StepsExpandedType } from '../../../types'
30
-
31
- const props = withDefaults(defineProps<{
32
- id: string
33
- name: string
34
- args?: Record<string, unknown>
35
- result: unknown
36
- status: 'done' | 'error' | 'cancelled' | 'skipped'
37
- expandedType?: StepsExpandedType
38
- }>(), {
39
- expandedType: 'auto'
40
- })
41
-
42
- // 注入自定义渲染器配置
43
- const toolRenderers = inject<Record<string, Component>>('toolRenderers', {})
44
-
45
- // 获取自定义渲染器
46
- const customRenderer = computed(() => toolRenderers[props.name])
47
-
48
- // 根据模式计算初始状态(工具结果完成后默认折叠,除非 mode 是 open)
49
- function getInitialExpanded(): boolean {
50
- if (props.expandedType === 'open') return true
51
- return false
52
- }
53
-
54
- const expanded = ref(getInitialExpanded())
55
-
56
- // 格式化结果
57
- const formattedResult = computed(() => {
58
- if (typeof props.result === 'string') {
59
- return props.result
60
- }
61
- return JSON.stringify(props.result, null, 2)
62
- })
63
- </script>
64
-
65
- <style scoped>
66
- .tool-result-part {
67
- margin: 8px 0;
68
- }
69
-
70
- .result-content {
71
- margin: 0;
72
- font-size: 12px;
73
- font-family: "SF Mono", Monaco, "Cascadia Code", monospace;
74
- color: var(--chat-code-text, var(--chat-text-muted, #999));
75
- white-space: pre-wrap;
76
- word-break: break-word;
77
- }
78
- </style>
@@ -1,45 +0,0 @@
1
- <template>
2
- <div class="default-tool-result">
3
- <pre class="result-content">{{ formattedResult }}</pre>
4
- </div>
5
- </template>
6
-
7
- <script setup lang="ts">
8
- import { computed } from 'vue'
9
- import type { ToolRendererProps } from '@huyooo/ai-chat-shared'
10
-
11
- const props = defineProps<ToolRendererProps>()
12
-
13
- const formattedResult = computed(() => {
14
- if (typeof props.toolResult === 'string') {
15
- return props.toolResult
16
- }
17
- try {
18
- return JSON.stringify(props.toolResult, null, 2)
19
- } catch {
20
- return String(props.toolResult)
21
- }
22
- })
23
- </script>
24
-
25
- <style scoped>
26
- .default-tool-result {
27
- background: var(--chat-bg, #1e1e1e);
28
- border-radius: 6px;
29
- overflow: hidden;
30
- }
31
-
32
- .result-content {
33
- margin: 0;
34
- padding: 8px;
35
- font-size: 13px;
36
- line-height: 1.5;
37
- color: var(--chat-code-text, var(--chat-text, #999));
38
- font-family: 'SF Mono', Monaco, monospace;
39
- white-space: pre-wrap;
40
- word-break: break-word;
41
- overflow-x: auto;
42
- background: var(--chat-code-bg);
43
- border-top: 1px solid var(--chat-border);
44
- }
45
- </style>
@@ -1,133 +0,0 @@
1
- <template>
2
- <div class="search-results-card">
3
- <div class="search-header">
4
- <Icon icon="lucide:search" width="16" />
5
- <span>搜索结果</span>
6
- <span class="search-count">{{ results.length }} 条</span>
7
- </div>
8
- <div class="search-list">
9
- <a
10
- v-for="(item, index) in results"
11
- :key="index"
12
- :href="item.url"
13
- target="_blank"
14
- rel="noopener noreferrer"
15
- class="search-item"
16
- @click.prevent="openExternal(item.url)"
17
- >
18
- <div class="item-title">{{ item.title }}</div>
19
- <div v-if="item.snippet" class="item-snippet">{{ item.snippet }}</div>
20
- <div class="item-url">{{ getDomain(item.url) }}</div>
21
- </a>
22
- </div>
23
- </div>
24
- </template>
25
-
26
- <script setup lang="ts">
27
- import { computed } from 'vue'
28
- import { Icon } from '@iconify/vue'
29
- import type { ToolRendererProps, SearchResultItem } from '@huyooo/ai-chat-shared'
30
-
31
- const props = defineProps<ToolRendererProps>()
32
-
33
- const results = computed<SearchResultItem[]>(() => {
34
- const result = props.toolResult
35
- if (Array.isArray(result)) {
36
- return result as SearchResultItem[]
37
- }
38
- if (typeof result === 'object' && result !== null && 'results' in result) {
39
- return (result as { results: SearchResultItem[] }).results
40
- }
41
- return []
42
- })
43
-
44
- /** 获取域名 */
45
- function getDomain(url: string): string {
46
- try {
47
- return new URL(url).hostname
48
- } catch {
49
- return url
50
- }
51
- }
52
-
53
- /** 打开外部链接 */
54
- function openExternal(url: string) {
55
- const bridge = (window as { aiChatBridge?: { openExternal: (url: string) => Promise<void> } }).aiChatBridge
56
- if (bridge?.openExternal) {
57
- bridge.openExternal(url)
58
- } else {
59
- window.open(url, '_blank')
60
- }
61
- }
62
- </script>
63
-
64
- <style scoped>
65
- .search-results-card {
66
- background: var(--chat-muted, #2d2d2d);
67
- border-radius: 8px;
68
- overflow: hidden;
69
- }
70
-
71
- .search-header {
72
- display: flex;
73
- align-items: center;
74
- gap: 8px;
75
- padding: 10px 12px;
76
- background: rgba(0, 0, 0, 0.2);
77
- color: var(--chat-text-muted, #888);
78
- font-size: 13px;
79
- }
80
-
81
- .search-count {
82
- margin-left: auto;
83
- font-size: 12px;
84
- opacity: 0.7;
85
- }
86
-
87
- .search-list {
88
- display: flex;
89
- flex-direction: column;
90
- }
91
-
92
- .search-item {
93
- display: block;
94
- padding: 10px 12px;
95
- text-decoration: none;
96
- border-bottom: 1px solid rgba(255, 255, 255, 0.05);
97
- transition: background 0.15s;
98
- }
99
-
100
- .search-item:last-child {
101
- border-bottom: none;
102
- }
103
-
104
- .search-item:hover {
105
- background: rgba(255, 255, 255, 0.05);
106
- }
107
-
108
- .item-title {
109
- font-size: 14px;
110
- color: var(--chat-text, #ccc);
111
- font-weight: 500;
112
- margin-bottom: 4px;
113
- overflow: hidden;
114
- text-overflow: ellipsis;
115
- white-space: nowrap;
116
- }
117
-
118
- .item-snippet {
119
- font-size: 12px;
120
- color: var(--chat-text-muted, #888);
121
- line-height: 1.4;
122
- margin-bottom: 4px;
123
- display: -webkit-box;
124
- -webkit-line-clamp: 2;
125
- -webkit-box-orient: vertical;
126
- overflow: hidden;
127
- }
128
-
129
- .item-url {
130
- font-size: 11px;
131
- color: var(--chat-text-muted, #666);
132
- }
133
- </style>
@@ -1,139 +0,0 @@
1
- <template>
2
- <div class="weather-card">
3
- <div class="weather-header">
4
- <Icon icon="lucide:cloud-sun" width="24" class="weather-icon" />
5
- <div class="weather-location">{{ weather.city }}</div>
6
- </div>
7
- <div class="weather-main">
8
- <div class="weather-temp">{{ weather.temperature }}°</div>
9
- <div class="weather-condition">{{ weather.condition }}</div>
10
- </div>
11
- <div v-if="weather.humidity || weather.wind" class="weather-details">
12
- <div v-if="weather.humidity" class="weather-detail">
13
- <Icon icon="lucide:droplets" width="14" />
14
- <span>{{ weather.humidity }}%</span>
15
- </div>
16
- <div v-if="weather.wind" class="weather-detail">
17
- <Icon icon="lucide:wind" width="14" />
18
- <span>{{ weather.wind }}</span>
19
- </div>
20
- </div>
21
- <!-- 预报 -->
22
- <div v-if="weather.forecast?.length" class="weather-forecast">
23
- <div v-for="item in weather.forecast" :key="item.date" class="forecast-item">
24
- <div class="forecast-date">{{ item.date }}</div>
25
- <div class="forecast-temp">{{ item.low }}° / {{ item.high }}°</div>
26
- <div class="forecast-condition">{{ item.condition }}</div>
27
- </div>
28
- </div>
29
- </div>
30
- </template>
31
-
32
- <script setup lang="ts">
33
- import { computed } from 'vue'
34
- import { Icon } from '@iconify/vue'
35
- import type { ToolRendererProps, WeatherData } from '@huyooo/ai-chat-shared'
36
-
37
- const props = defineProps<ToolRendererProps>()
38
-
39
- const weather = computed<WeatherData>(() => {
40
- const result = props.toolResult
41
- if (typeof result === 'object' && result !== null) {
42
- return result as WeatherData
43
- }
44
- // 默认值
45
- return {
46
- city: '未知',
47
- temperature: 0,
48
- condition: '未知',
49
- }
50
- })
51
- </script>
52
-
53
- <style scoped>
54
- .weather-card {
55
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
56
- border-radius: 12px;
57
- padding: 16px;
58
- color: #fff;
59
- min-width: 200px;
60
- }
61
-
62
- .weather-header {
63
- display: flex;
64
- align-items: center;
65
- gap: 8px;
66
- margin-bottom: 12px;
67
- }
68
-
69
- .weather-icon {
70
- opacity: 0.9;
71
- }
72
-
73
- .weather-location {
74
- font-size: 14px;
75
- font-weight: 500;
76
- }
77
-
78
- .weather-main {
79
- margin-bottom: 12px;
80
- }
81
-
82
- .weather-temp {
83
- font-size: 48px;
84
- font-weight: 300;
85
- line-height: 1;
86
- }
87
-
88
- .weather-condition {
89
- font-size: 14px;
90
- opacity: 0.9;
91
- margin-top: 4px;
92
- }
93
-
94
- .weather-details {
95
- display: flex;
96
- gap: 16px;
97
- padding-top: 12px;
98
- border-top: 1px solid rgba(255, 255, 255, 0.2);
99
- }
100
-
101
- .weather-detail {
102
- display: flex;
103
- align-items: center;
104
- gap: 4px;
105
- font-size: 13px;
106
- opacity: 0.9;
107
- }
108
-
109
- .weather-forecast {
110
- margin-top: 12px;
111
- padding-top: 12px;
112
- border-top: 1px solid rgba(255, 255, 255, 0.2);
113
- display: flex;
114
- gap: 12px;
115
- overflow-x: auto;
116
- }
117
-
118
- .forecast-item {
119
- text-align: center;
120
- min-width: 60px;
121
- }
122
-
123
- .forecast-date {
124
- font-size: 12px;
125
- opacity: 0.8;
126
- margin-bottom: 4px;
127
- }
128
-
129
- .forecast-temp {
130
- font-size: 13px;
131
- font-weight: 500;
132
- }
133
-
134
- .forecast-condition {
135
- font-size: 11px;
136
- opacity: 0.8;
137
- margin-top: 2px;
138
- }
139
- </style>
@@ -1,7 +0,0 @@
1
- /**
2
- * 工具结果渲染组件导出
3
- */
4
-
5
- export { default as DefaultToolResult } from './DefaultToolResult.vue'
6
- export { default as WeatherCard } from './WeatherCard.vue'
7
- export { default as SearchResults } from './SearchResults.vue'