@tbox.cn/app-template-agent 0.7.5 → 0.8.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.
Files changed (152) hide show
  1. package/.gitignore +5 -0
  2. package/.tbox/app.json +9 -1
  3. package/AGENTS.md +12 -4
  4. package/ENV.md +102 -17
  5. package/README.md +36 -34
  6. package/apps/client/VISUAL_RESTORE.md +11 -11
  7. package/apps/client/index.html +3 -1
  8. package/apps/client/package.json +9 -8
  9. package/apps/client/src/business/App.tsx +4 -4
  10. package/apps/client/src/business/README.md +5 -11
  11. package/apps/client/src/cards/registry.ts +23 -8
  12. package/apps/client/src/main.tsx +36 -2
  13. package/apps/client/src/modules.ts +7 -4
  14. package/apps/client/src/pages/index/index.tsx +18 -162
  15. package/apps/client/src/platform/AgentChatApp.tsx +3 -3
  16. package/apps/client/src/platform/agent-runtime.ts +5 -0
  17. package/apps/client/src/store/chat.ts +47 -185
  18. package/apps/client/src/test/setup.ts +7 -0
  19. package/apps/client/vite.config.ts +16 -1
  20. package/apps/server/package.json +0 -1
  21. package/apps/server/src/app-tools.ts +2 -1
  22. package/apps/server/src/app.ts +178 -110
  23. package/apps/server/src/config/integration.ts +2 -6
  24. package/apps/server/src/dev.ts +0 -1
  25. package/apps/server/src/intent-routes.ts +3 -3
  26. package/apps/server/src/modules.ts +4 -4
  27. package/apps/server/src/pre-tools.ts +4 -3
  28. package/apps/server/tests/agent-prompt.test.ts +81 -0
  29. package/apps/server/tests/cards-registry.test.ts +1 -3
  30. package/apps/server/tests/deployment-loader.test.ts +35 -56
  31. package/apps/server/tests/tool-runtime.test.ts +25 -61
  32. package/package.json +5 -4
  33. package/packages/agent-ui-kit/LICENSE +21 -0
  34. package/packages/agent-ui-kit/README.md +17 -0
  35. package/packages/agent-ui-kit/package.json +58 -0
  36. package/packages/agent-ui-kit/src/ChatPage.less +62 -0
  37. package/packages/agent-ui-kit/src/ChatPage.tsx +305 -0
  38. package/packages/agent-ui-kit/src/chat-config.test.ts +82 -0
  39. package/packages/agent-ui-kit/src/chat-config.ts +62 -0
  40. package/{apps/client → packages/agent-ui-kit}/src/components/ActionBar/DebugButton.tsx +2 -2
  41. package/{apps/client → packages/agent-ui-kit}/src/components/ActionBar/index.less +19 -16
  42. package/{apps/client → packages/agent-ui-kit}/src/components/ActionBar/index.tsx +10 -13
  43. package/packages/agent-ui-kit/src/components/CardRenderer.tsx +36 -0
  44. package/packages/agent-ui-kit/src/components/ChatHeader/index.less +148 -0
  45. package/packages/agent-ui-kit/src/components/ChatHeader/index.test.tsx +74 -0
  46. package/{apps/client → packages/agent-ui-kit}/src/components/ChatHeader/index.tsx +27 -15
  47. package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/index.less +47 -39
  48. package/packages/agent-ui-kit/src/components/ChatInput/index.test.tsx +156 -0
  49. package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/index.tsx +13 -4
  50. package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/voice/useVoicePress.ts +2 -2
  51. package/packages/agent-ui-kit/src/components/HistoryEntry/index.less +49 -0
  52. package/packages/agent-ui-kit/src/components/HistoryEntry/index.tsx +42 -0
  53. package/{apps/client → packages/agent-ui-kit}/src/components/MediaList/index.less +3 -3
  54. package/{apps/client → packages/agent-ui-kit}/src/components/MediaList/index.tsx +6 -6
  55. package/{apps/client → packages/agent-ui-kit}/src/components/MessageBubble/index.less +25 -20
  56. package/packages/agent-ui-kit/src/components/MessageBubble/index.test.tsx +112 -0
  57. package/{apps/client → packages/agent-ui-kit}/src/components/MessageBubble/index.tsx +22 -15
  58. package/{apps/client → packages/agent-ui-kit}/src/components/PullDownIndicator/index.less +2 -2
  59. package/{apps/client → packages/agent-ui-kit}/src/components/PullDownIndicator/index.tsx +2 -2
  60. package/packages/agent-ui-kit/src/components/QuickCommands/index.less +63 -0
  61. package/packages/agent-ui-kit/src/components/QuickCommands/index.test.tsx +99 -0
  62. package/packages/agent-ui-kit/src/components/QuickCommands/index.tsx +52 -0
  63. package/{apps/client → packages/agent-ui-kit}/src/components/ScrollToBottomButton/index.less +1 -1
  64. package/packages/agent-ui-kit/src/components/ToolCallBlock/detail.test.ts +346 -0
  65. package/packages/agent-ui-kit/src/components/ToolCallBlock/detail.ts +204 -0
  66. package/packages/agent-ui-kit/src/components/ToolCallBlock/index.less +312 -0
  67. package/packages/agent-ui-kit/src/components/ToolCallBlock/index.test.tsx +261 -0
  68. package/packages/agent-ui-kit/src/components/ToolCallBlock/index.tsx +212 -0
  69. package/packages/agent-ui-kit/src/components/ToolCallBlock/titles.ts +102 -0
  70. package/packages/agent-ui-kit/src/hooks/use-drag-gesture.test.ts +66 -0
  71. package/packages/agent-ui-kit/src/hooks/use-drag-gesture.ts +129 -0
  72. package/packages/agent-ui-kit/src/hooks/use-pull-down-history.ts +241 -0
  73. package/packages/agent-ui-kit/src/hooks/use-pull-down-wheel-native.test.ts +64 -0
  74. package/{apps/client → packages/agent-ui-kit}/src/hooks/useMediaUpload.ts +3 -3
  75. package/{apps/client → packages/agent-ui-kit}/src/hooks/useTTSAutoPlay.ts +7 -5
  76. package/packages/agent-ui-kit/src/index.ts +69 -0
  77. package/packages/agent-ui-kit/src/store-context.tsx +92 -0
  78. package/packages/agent-ui-kit/src/test/setup.ts +7 -0
  79. package/packages/agent-ui-kit/src/theme/contract.json +72 -0
  80. package/packages/agent-ui-kit/src/theme/global.less +99 -0
  81. package/packages/agent-ui-kit/src/theme/motion.less +19 -0
  82. package/packages/agent-ui-kit/src/theme/theme-contract.test.ts +69 -0
  83. package/packages/agent-ui-kit/src/theme/theme.ts +17 -0
  84. package/packages/agent-ui-kit/src/theme/tokens.less +79 -0
  85. package/packages/agent-ui-kit/src/utils/haptics.test.ts +51 -0
  86. package/packages/agent-ui-kit/src/utils/haptics.ts +44 -0
  87. package/packages/agent-ui-kit/src/utils/pull-down-machine.test.ts +188 -0
  88. package/packages/agent-ui-kit/src/utils/pull-down-machine.ts +157 -0
  89. package/packages/agent-ui-kit/src/utils/tool-call-display.test.ts +240 -0
  90. package/packages/agent-ui-kit/src/utils/tool-call-display.ts +146 -0
  91. package/packages/agent-ui-kit/src/vite-env.d.ts +1 -0
  92. package/packages/agent-ui-kit/tsconfig.json +8 -0
  93. package/packages/agent-ui-kit/vitest.config.ts +7 -0
  94. package/pnpm-lock.template.yaml +177 -83
  95. package/pnpm-workspace.template.yaml +12 -4
  96. package/profiles/mall/config/agent.jsonc +33 -0
  97. package/profiles/mall/config/chat.json +21 -0
  98. package/profiles/mall/config/knowledge.json +5 -0
  99. package/profiles/mall/config/llm_provider.json +5 -0
  100. package/profiles/mall/config/plugins.json +3 -0
  101. package/profiles/mall.json +16 -0
  102. package/scripts/check-workspace.mjs +3 -2
  103. package/apps/client/assets/copy@2x.png +0 -0
  104. package/apps/client/assets/debug.png +0 -0
  105. package/apps/client/assets/dislike@2x.png +0 -0
  106. package/apps/client/assets/like@2x.png +0 -0
  107. package/apps/client/assets/speaker@2x.png +0 -0
  108. package/apps/client/src/cards/CardRenderer.tsx +0 -20
  109. package/apps/client/src/components/ChatHeader/index.less +0 -104
  110. package/apps/client/src/components/ErrorBoundary.tsx +0 -2
  111. package/apps/client/src/components/QuickCommands/index.less +0 -48
  112. package/apps/client/src/components/QuickCommands/index.tsx +0 -29
  113. package/apps/client/src/components/ToolCallBlock/index.less +0 -131
  114. package/apps/client/src/components/ToolCallBlock/index.tsx +0 -125
  115. package/apps/client/src/examples/tourism/TourismLanding.tsx +0 -96
  116. package/apps/client/src/examples/tourism/index.less +0 -192
  117. package/apps/client/src/pages/index/index.less +0 -43
  118. package/apps/client/src/store/chat-config.ts +0 -40
  119. package/apps/client/src/styles/global.less +0 -60
  120. package/apps/client/src/styles/tokens.less +0 -60
  121. package/apps/server/src/config/deployment.ts +0 -47
  122. package/packages/module-weather/package.json +0 -36
  123. package/packages/module-weather/skills/weather-guide/SKILL.md +0 -22
  124. package/packages/module-weather/skills/weather-guide/references/dressing-tips.md +0 -5
  125. package/packages/module-weather/src/client/cards/weather/index.less +0 -22
  126. package/packages/module-weather/src/client/cards/weather/index.tsx +0 -14
  127. package/packages/module-weather/src/client/index.ts +0 -19
  128. package/packages/module-weather/src/index.ts +0 -2
  129. package/packages/module-weather/src/server/cards/weather/meta.ts +0 -27
  130. package/packages/module-weather/src/server/index.ts +0 -45
  131. package/packages/module-weather/src/server/pre-tools.ts +0 -20
  132. package/packages/module-weather/src/server/tool.ts +0 -29
  133. package/packages/module-weather/tbox.module.json +0 -27
  134. package/packages/module-weather/tsconfig.json +0 -14
  135. /package/{apps/client → packages/agent-ui-kit}/assets/keyboard-circle.svg +0 -0
  136. /package/{apps/client → packages/agent-ui-kit}/assets/more-circle.svg +0 -0
  137. /package/{apps/client → packages/agent-ui-kit}/assets/new-chat.svg +0 -0
  138. /package/{apps/client → packages/agent-ui-kit}/assets/quick-command-star.svg +0 -0
  139. /package/{apps/client → packages/agent-ui-kit}/assets/send-gradient.svg +0 -0
  140. /package/{apps/client → packages/agent-ui-kit}/assets/stop@2x.png +0 -0
  141. /package/{apps/client → packages/agent-ui-kit}/assets/voice-circle.svg +0 -0
  142. /package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/voice/VoicePressField.tsx +0 -0
  143. /package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/voice/VoiceRecordingOverlay.tsx +0 -0
  144. /package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/voice/machine.ts +0 -0
  145. /package/{apps/client → packages/agent-ui-kit}/src/components/ReasoningBlock/index.less +0 -0
  146. /package/{apps/client → packages/agent-ui-kit}/src/components/ReasoningBlock/index.tsx +0 -0
  147. /package/{apps/client → packages/agent-ui-kit}/src/components/ScrollToBottomButton/index.tsx +0 -0
  148. /package/{apps/client/src/types/chat.ts → packages/agent-ui-kit/src/types.ts} +0 -0
  149. /package/{apps/client → packages/agent-ui-kit}/src/utils/copyText.ts +0 -0
  150. /package/{apps/client → packages/agent-ui-kit}/src/utils/imagePreview.ts +0 -0
  151. /package/{apps/client → packages/agent-ui-kit}/src/utils/session-error.ts +0 -0
  152. /package/{apps/client → packages/agent-ui-kit}/src/utils/toast.ts +0 -0
@@ -0,0 +1,5 @@
1
+ {
2
+ "modelName": "glm-5.1",
3
+ "thinking": false,
4
+ "parallelToolCalls": true
5
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "plugins": []
3
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "$comment": "mall 画像(单一模板统一化 W4):create --profile mall 执行器(W5 接线)按 modules 序列加装 + configOverlay 叠加(--config-dir 通道)。provider 轴独立(缺省 mock-only 演示态;vendor 档由部署者后续 add)。",
3
+ "name": "mall",
4
+ "description": "商圈 AI Agent:家页形态(home 槽)+ 域装配 + 六业务域(导购/会员/停车/营销/活动/客服)",
5
+ "modules": [
6
+ "@tbox.cn/app-module-mall-core",
7
+ "@tbox.cn/app-module-mall-home",
8
+ "@tbox.cn/app-module-shopping-guide",
9
+ "@tbox.cn/app-module-member",
10
+ "@tbox.cn/app-module-parking",
11
+ "@tbox.cn/app-module-promotion",
12
+ "@tbox.cn/app-module-alipay-activity",
13
+ "@tbox.cn/app-module-customer-service"
14
+ ],
15
+ "configOverlay": "profiles/mall/config"
16
+ }
@@ -100,9 +100,10 @@ function printFailure(findings) {
100
100
  process.exitCode = 1;
101
101
  }
102
102
 
103
- /** 豁免:SDK/契约包无 tbox.module.json(002 §2.3 / 003 §4.1) */
103
+ /** 豁免:SDK/契约/UI-Kit 包无 tbox.module.json(002 §2.3 / 003 §4.1;kit 为壳 UI 源码包,
104
+ * 单一模板统一化 W2-5——无模块贡献面,非业务模块) */
104
105
  function isExemptPackage(dirName) {
105
- return dirName === 'app-sdk' || dirName === 'shared' || dirName.startsWith('contracts');
106
+ return dirName === 'app-sdk' || dirName === 'shared' || dirName.startsWith('contracts') || dirName === 'agent-ui-kit';
106
107
  }
107
108
 
108
109
  /** 校验 packages/* 每个业务包必须有 tbox.module.json(002 §2.3 / 003 §4.1) */
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,20 +0,0 @@
1
- /**
2
- * 卡片渲染入口 — 注入本地 registry 后委托 SDK 的 CardRenderer。
3
- * 011 B3:动作按钮 → sendUiAction(token 来自 payload.actionTokens)。
4
- * P1:onAction 升级为完整 UiAction(CardRenderer 按 kind 分发后回传,桥一行透传)。
5
- */
6
- import { CardRenderer as SdkCardRenderer } from '@tbox.cn/app-sdk/client';
7
- import type { TboxCardPayload, UiAction } from '@tbox.cn/app-contracts';
8
- import { CARD_REGISTRY } from './registry';
9
- import { useChatStore } from '@/store/chat';
10
-
11
- export function CardRenderer({ card }: { card: TboxCardPayload }) {
12
- const sendUiAction = useChatStore((s) => s.sendUiAction);
13
- return (
14
- <SdkCardRenderer
15
- card={card}
16
- registry={CARD_REGISTRY}
17
- onAction={(action: UiAction) => sendUiAction(card.id, action)}
18
- />
19
- );
20
- }
@@ -1,104 +0,0 @@
1
- /* 顶部居中浮动控件:毛玻璃按钮组 + 下方小手柄;折叠时只剩手柄。对齐原模板。 */
2
- .chat-header {
3
- position: absolute;
4
- top: 4px;
5
- left: 0;
6
- right: 0;
7
- z-index: 100;
8
- display: flex;
9
- flex-direction: column;
10
- align-items: center;
11
- padding: 4px 16px 0;
12
- pointer-events: none;
13
-
14
- &__group {
15
- pointer-events: auto;
16
- display: flex;
17
- align-items: center;
18
- justify-content: center;
19
- gap: 8px;
20
- margin-bottom: 8px;
21
- padding: 12px 16px;
22
- background: rgba(255, 255, 255, 0.4);
23
- backdrop-filter: blur(10px);
24
- border-radius: 31px;
25
- max-height: 100px;
26
- overflow: hidden;
27
- opacity: 1;
28
- transform: translateY(0) scale(1);
29
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
30
-
31
- &--collapsed {
32
- opacity: 0;
33
- transform: translateY(-20px) scale(0.95);
34
- max-height: 0;
35
- margin-bottom: 0;
36
- padding-top: 0;
37
- padding-bottom: 0;
38
- pointer-events: none;
39
- }
40
- }
41
-
42
- &__btn {
43
- display: flex;
44
- align-items: center;
45
- gap: 8px;
46
- appearance: none;
47
- border: none;
48
- padding: 12px 24px;
49
- background: #fff;
50
- border-radius: 92px;
51
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
52
- transition: all 0.2s ease;
53
-
54
- &:active {
55
- transform: scale(0.98);
56
- background: #f5f5f5;
57
- }
58
- }
59
-
60
- &__btn-icon {
61
- width: 32px;
62
- height: 32px;
63
- flex-shrink: 0;
64
- }
65
-
66
- &__btn-text {
67
- font-size: 24px;
68
- color: #4b6282;
69
- font-weight: 500;
70
- white-space: nowrap;
71
- line-height: 1;
72
- }
73
-
74
- &__toggle {
75
- pointer-events: auto;
76
- appearance: none;
77
- border: none;
78
- display: flex;
79
- align-items: center;
80
- justify-content: center;
81
- width: 110px;
82
- height: 30px;
83
- background: rgba(255, 255, 255, 0.8);
84
- border-radius: 14px;
85
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
86
- transition: all 0.2s ease;
87
-
88
- &:active {
89
- transform: scale(0.98);
90
- background: #f5f5f5;
91
- }
92
- }
93
-
94
- &__arrow {
95
- display: flex;
96
- align-items: center;
97
- justify-content: center;
98
- transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
99
-
100
- &--up {
101
- transform: rotate(180deg);
102
- }
103
- }
104
- }
@@ -1,2 +0,0 @@
1
- // Re-export from app-sdk
2
- export { ErrorBoundary } from '@tbox.cn/app-sdk/client';
@@ -1,48 +0,0 @@
1
- .quick-commands {
2
- position: relative;
3
- z-index: 1;
4
- width: 100%;
5
- max-width: var(--tbox-content-max-width);
6
- margin: 0 auto;
7
- box-sizing: border-box;
8
- display: flex;
9
- gap: 16px;
10
- overflow-x: auto;
11
- padding: 0 24px 12px;
12
- -ms-overflow-style: none;
13
- scrollbar-width: none;
14
-
15
- &::-webkit-scrollbar {
16
- display: none;
17
- }
18
-
19
- &__item {
20
- flex-shrink: 0;
21
- display: inline-flex;
22
- align-items: center;
23
- gap: 8px;
24
- height: 61px;
25
- padding: 0 24px 0 16px;
26
- appearance: none;
27
- border: none;
28
- background: #fff;
29
- border-radius: 31px;
30
- box-shadow: 0 2px 12px rgba(110, 139, 180, 0.12);
31
- }
32
-
33
- &__icon {
34
- width: 32px;
35
- height: 32px;
36
- object-fit: contain;
37
- }
38
-
39
- &__icon-svg {
40
- color: var(--tbox-primary);
41
- }
42
-
43
- &__label {
44
- font-size: 26px;
45
- color: rgba(31, 59, 99, 0.85);
46
- white-space: nowrap;
47
- }
48
- }
@@ -1,29 +0,0 @@
1
- // ⚠️ 平台标准组件:输入框上方的「快捷指令」。视觉还原时不改本组件、不另画。
2
- // 改内容只改 client/config/chat.json 的 quickCommands 数组({ label, query, icon? })。
3
- // 011 I2:服务端 HelloAck.chatConfig.quickCommands 优先(单一真源),本地 chat.json 为 fallback。
4
- // label 只写纯文字;前面的星标由本组件渲染(icon 字段或默认 ✦),不要把 ✦/emoji 写进 label。
5
- import { useChatConfigStore } from '@/store/chat-config';
6
- import { useChatStore } from '@/store/chat';
7
- import starIcon from '../../../assets/quick-command-star.svg';
8
- import './index.less';
9
-
10
- export function QuickCommands({ onCommand }: { onCommand: (query: string) => void }) {
11
- const staticCommands = useChatConfigStore((s) => s.quickCommands);
12
- const serverCommands = useChatStore((s) => s.helloAck?.chatConfig.quickCommands);
13
- // 服务端下发优先;未收到 HELLO_ACK(非 mall 部署)回落本地静态
14
- const commands = (serverCommands && serverCommands.length > 0
15
- ? serverCommands.map((c) => ({ label: c.label, query: c.prompt }))
16
- : staticCommands) as { label: string; query: string }[];
17
- if (!commands.length) return null;
18
-
19
- return (
20
- <div className="quick-commands">
21
- {commands.map((cmd) => (
22
- <button key={cmd.label} className="quick-commands__item" onClick={() => onCommand(cmd.query)}>
23
- <img className="quick-commands__icon" src={starIcon} alt="" />
24
- <span className="quick-commands__label">{cmd.label}</span>
25
- </button>
26
- ))}
27
- </div>
28
- );
29
- }
@@ -1,131 +0,0 @@
1
- .tool-calls {
2
- display: flex;
3
- flex-direction: column;
4
- gap: 8px;
5
- margin: 0 0 12px;
6
- }
7
-
8
- .tool-call {
9
- border-radius: 12px;
10
- overflow: hidden;
11
-
12
- &--calling {
13
- background: rgba(16, 163, 127, 0.08);
14
- }
15
- &--done {
16
- background: rgba(0, 0, 0, 0.03);
17
- }
18
- &--error {
19
- background: rgba(239, 68, 68, 0.08);
20
- }
21
-
22
- &__header {
23
- display: flex;
24
- align-items: center;
25
- justify-content: space-between;
26
- padding: 12px 16px;
27
- }
28
-
29
- &__left {
30
- display: flex;
31
- align-items: center;
32
- gap: 8px;
33
- min-width: 0;
34
- }
35
-
36
- &__spinner {
37
- width: 14px;
38
- height: 14px;
39
- border: 2px solid rgba(16, 163, 127, 0.3);
40
- border-top-color: #10a37f;
41
- border-radius: 50%;
42
- flex-shrink: 0;
43
- animation: tool-spin 0.8s linear infinite;
44
- }
45
-
46
- // 010:前置工具「自动」徽标(与模型主动调用区分)
47
- &__badge {
48
- font-size: 18px;
49
- line-height: 1;
50
- padding: 3px 8px;
51
- border-radius: 8px;
52
- background: rgba(93, 126, 198, 0.12);
53
- color: #5f7ec6;
54
- flex-shrink: 0;
55
- }
56
-
57
- &__name {
58
- font-size: 22px;
59
- line-height: 1.4;
60
- color: #4a5568;
61
- font-weight: 500;
62
- overflow: hidden;
63
- text-overflow: ellipsis;
64
- white-space: nowrap;
65
- }
66
- &--calling &__name {
67
- color: #10a37f;
68
- }
69
- &--error &__name {
70
- color: #ef4444;
71
- }
72
-
73
- &__toggle {
74
- font-size: 20px;
75
- color: #8b95b0;
76
- flex-shrink: 0;
77
- margin-left: 8px;
78
- }
79
-
80
- &__result {
81
- padding: 0 16px 12px;
82
- }
83
-
84
- &__result-text {
85
- font-size: 20px;
86
- color: #6b7a99;
87
- line-height: 1.6;
88
- font-family: Menlo, Monaco, Consolas, monospace;
89
- white-space: pre-wrap;
90
- word-break: break-all;
91
-
92
- &--error {
93
- color: #dc2626;
94
- }
95
- }
96
-
97
- // 018 dev 视图输入/输出双栏(紧凑单行 + 头尾保留)
98
- &__io {
99
- display: flex;
100
- flex-direction: column;
101
- gap: 4px;
102
-
103
- & + & {
104
- margin-top: 12px;
105
- }
106
- }
107
-
108
- &__io-label {
109
- font-size: 18px;
110
- color: #8a97ad;
111
- }
112
-
113
- &__io-text {
114
- font-size: 20px;
115
- color: #6b7a99;
116
- line-height: 1.6;
117
- font-family: Menlo, Monaco, Consolas, monospace;
118
- white-space: pre-wrap;
119
- word-break: break-all;
120
-
121
- &--error {
122
- color: #dc2626;
123
- }
124
- }
125
- }
126
-
127
- @keyframes tool-spin {
128
- to {
129
- transform: rotate(360deg);
130
- }
131
- }
@@ -1,125 +0,0 @@
1
- import { useState } from 'react';
2
- import { Check, X } from '@phosphor-icons/react';
3
- import { parseMemoryToolCallArgs, parsePreToolToolCallArgs } from '@tbox.cn/app-contracts';
4
- import type { ToolCallInfo } from '@/types/chat';
5
- import { describeToolCall, formatCompactToolIo } from '@tbox.cn/app-sdk/client';
6
- import './index.less';
7
-
8
- /**
9
- * 工具调用状态卡片(调用中 spinner / 完成 / 失败)。
10
- * 010:展示语义判定下沉 SDK describeToolCall(skill/preTool/normal/memory 四分支),
11
- * 本组件退化为纯渲染(徽标/颜色/展开交互)。
12
- * 018 tool-debug:debug 门控在渲染层——prod 恒无出入参展开入口(I4);
13
- * dev 详情走 formatCompactToolIo(紧凑单行 + 头尾保留),输入输出双栏。
14
- */
15
- export default function ToolCallBlock({
16
- toolCalls,
17
- debug = false,
18
- }: {
19
- toolCalls: ToolCallInfo[];
20
- /** 018 tool-debug:dev 视图(出入参展开 + 紧凑格式);prod 缺省 false */
21
- debug?: boolean;
22
- }) {
23
- const [expandedId, setExpandedId] = useState<string | null>(null);
24
-
25
- return (
26
- <div className="tool-calls">
27
- {toolCalls.map((tc) => {
28
- const display = describeToolCall(tc);
29
- const isCalling = tc.status === 'calling';
30
- const isError =
31
- tc.status === 'error' ||
32
- (display.kind === 'preTool' && display.status === 'error') ||
33
- (display.kind === 'memory' && display.status === 'degraded');
34
- const isDone = !isCalling && !isError;
35
- const isExpanded = expandedId === tc.id;
36
- // 018 I4:prod 恒无展开入口;dev 按 display.hasDetail
37
- const hasDetail = debug && display.hasDetail;
38
- const stateClass = isError ? 'tool-call--error' : isCalling ? 'tool-call--calling' : 'tool-call--done';
39
-
40
- const displayName = (() => {
41
- if (display.kind === 'skill') {
42
- return isCalling ? `加载技能 ${display.label}` : `已加载技能 ${display.label}`;
43
- }
44
- if (display.kind === 'preTool') {
45
- if (display.status === 'calling') return `自动查询 ${display.label}`;
46
- if (display.status === 'error') return `自动查询失败 ${display.label}`;
47
- return `已自动获取 ${display.label}`;
48
- }
49
- if (display.kind === 'memory') {
50
- return display.label;
51
- }
52
- return isCalling
53
- ? `调用 ${display.label}`
54
- : isDone
55
- ? `${display.label} 已完成`
56
- : `${display.label} 调用失败`;
57
- })();
58
-
59
- // 详情文本(018:dev 态才计算——prod 无展开无渲染需求)
60
- const detailText = (() => {
61
- if (!hasDetail) return '';
62
- if (display.kind === 'preTool') {
63
- return (display.status === 'error' ? display.errorText : display.resultText) ?? '';
64
- }
65
- if (display.kind === 'memory') {
66
- return display.detailText ?? '';
67
- }
68
- return isError ? (tc.error ?? '') : (tc.result ?? '');
69
- })();
70
- // dev 输入侧:normal 用 tc.args;preTool 解包 toolArgs(对齐 mall 视图模型);memory 取 query
71
- const inputText = (() => {
72
- if (!hasDetail) return '';
73
- if (display.kind === 'normal') return tc.args ?? '';
74
- if (display.kind === 'preTool') {
75
- return JSON.stringify(parsePreToolToolCallArgs(tc.args)?.toolArgs ?? {});
76
- }
77
- if (display.kind === 'memory') {
78
- return parseMemoryToolCallArgs(tc.args)?.query ?? '';
79
- }
80
- return '';
81
- })();
82
-
83
- return (
84
- <div key={tc.id} className={`tool-call ${stateClass}`}>
85
- <div
86
- className="tool-call__header"
87
- onClick={() => hasDetail && setExpandedId(isExpanded ? null : tc.id)}
88
- >
89
- <div className="tool-call__left">
90
- {isCalling && <div className="tool-call__spinner" />}
91
- {isDone && <Check size={16} color="#10a37f" strokeWidth={2.5} />}
92
- {isError && <X size={16} color="#ef4444" strokeWidth={2.5} />}
93
- <span className="tool-call__name">{displayName}</span>
94
- {display.kind === 'preTool' && (
95
- <span className="tool-call__badge">自动</span>
96
- )}
97
- </div>
98
- {hasDetail && (
99
- <span className={`tool-call__toggle${isExpanded ? ' tool-call__toggle--up' : ''}`}>
100
- {isExpanded ? '收起' : '详情'}
101
- </span>
102
- )}
103
- </div>
104
- {isExpanded && hasDetail && (
105
- <div className="tool-call__result">
106
- {inputText && (
107
- <div className="tool-call__io">
108
- <span className="tool-call__io-label">输入</span>
109
- <span className="tool-call__io-text">{formatCompactToolIo(inputText)}</span>
110
- </div>
111
- )}
112
- <div className="tool-call__io">
113
- <span className="tool-call__io-label">输出</span>
114
- <span className={`tool-call__io-text${isError ? ' tool-call__io-text--error' : ''}`}>
115
- {formatCompactToolIo(detailText) || '(无)'}
116
- </span>
117
- </div>
118
- </div>
119
- )}
120
- </div>
121
- );
122
- })}
123
- </div>
124
- );
125
- }
@@ -1,96 +0,0 @@
1
- import { useState } from 'react';
2
- import { ArrowsClockwise, CaretRight } from '@phosphor-icons/react';
3
- import type { ChatLandingProps } from '@/pages/index/index';
4
- import './index.less';
5
-
6
- const CAROUSEL_ITEMS = [
7
- { title: '预约购票', desc: '景区门票多种选择', img: 'https://mdn.alipayobjects.com/tcmarket/afts/img/HmxURLvK52sAAAAARYAAAAgAwxLNAQBr/original', prompt: '我想预约购票' },
8
- { title: '智能导游', desc: '随心游无目的漫游', img: 'https://mdn.alipayobjects.com/tcmarket/afts/img/DbHcTK8C-JIAAAAARMAAAAgAwxLNAQBr/original', prompt: '请为我智能导游' },
9
- { title: '景点介绍', desc: '智能推荐景点讲解', img: 'https://mdn.alipayobjects.com/tcmarket/afts/img/CKZLRLnGC9sAAAAARVAAAAgAwxLNAQBr/original', prompt: '介绍一下景区的景点' },
10
- ];
11
-
12
- const SERVICE_ITEMS = [
13
- { img: 'https://mdn.alipayobjects.com/tcmarket/afts/img/pBvTRZ2lWokAAAAAQHAAAAgAwxLNAQBr/original', title: '杭城美食', desc: '千家商户 7 折起', prompt: '推荐一些杭州特色美食' },
14
- { img: 'https://mdn.alipayobjects.com/tcmarket/afts/img/OqxGT6_0RCYAAAAAQGAAAAgAwxLNAQBr/original', title: '酒店预定', desc: '万家酒店 5 折起', prompt: '帮我查找附近的酒店' },
15
- { img: 'https://mdn.alipayobjects.com/tcmarket/afts/img/HYeNTLrh83AAAAAAQFAAAAgAwxLNAQBr/original', title: '天气查询', desc: '实时天气速览', prompt: '今天天气怎么样?' },
16
- { img: 'https://mdn.alipayobjects.com/tcmarket/afts/img/atCHR6ykFnEAAAAAQHAAAAgAwxLNAQBr/original', title: '路线推荐', desc: '智能规划行程', prompt: '推荐一条游览路线' },
17
- ];
18
-
19
- const ALL_SUGGESTIONS = [
20
- { title: '热门景区推荐', prompt: '有哪些热门景区推荐?' },
21
- { title: '门票优惠政策', prompt: '有哪些门票优惠政策?' },
22
- { title: '景区开放时间', prompt: '景区的开放时间是什么时候?' },
23
- { title: '项目推荐', prompt: '有什么推荐的游玩项目?' },
24
- { title: '交通指南', prompt: '怎么到达景区?' },
25
- { title: '住宿推荐', prompt: '附近有哪些住宿推荐?' },
26
- ];
27
-
28
- const pickRandom = <T,>(items: T[], count: number): T[] =>
29
- [...items].sort(() => Math.random() - 0.5).slice(0, count);
30
-
31
- /** @tbox-template-example tourism */
32
- export default function TourismLanding({ onSend }: ChatLandingProps) {
33
- const [suggestions, setSuggestions] = useState(() => pickRandom(ALL_SUGGESTIONS, 4));
34
-
35
- return (
36
- <div className="tourism-example">
37
- <div className="tourism-example__welcome">
38
- <div className="tourism-example__welcome-title">欢迎来到文旅景区</div>
39
- <div className="tourism-example__welcome-desc">
40
- 我是您的通用文旅景区 AI 智能体,适用于山水类单景区、城市类多景区的业务场景。
41
- </div>
42
- </div>
43
-
44
- <section className="tourism-example__section tourism-example__carousel-section">
45
- <h2 className="tourism-example__section-title">游玩服务</h2>
46
- <div className="tourism-example__carousel">
47
- {CAROUSEL_ITEMS.map((item) => (
48
- <button key={item.title} className="tourism-example__carousel-item" onClick={() => onSend(item.prompt)}>
49
- <img className="tourism-example__carousel-image" src={item.img} alt={item.title} />
50
- <span className="tourism-example__carousel-overlay">
51
- <span className="tourism-example__carousel-title">{item.title}</span>
52
- <span className="tourism-example__carousel-desc">{item.desc}</span>
53
- </span>
54
- </button>
55
- ))}
56
- </div>
57
- </section>
58
-
59
- <section className="tourism-example__section">
60
- <h2 className="tourism-example__section-title">配套服务</h2>
61
- <div className="tourism-example__service-grid">
62
- {SERVICE_ITEMS.map((item) => (
63
- <button key={item.title} className="tourism-example__service-item" onClick={() => onSend(item.prompt)}>
64
- <img className="tourism-example__service-icon" src={item.img} alt={item.title} />
65
- <span className="tourism-example__service-content">
66
- <span className="tourism-example__service-title-row">
67
- <span className="tourism-example__service-title">{item.title}</span>
68
- <CaretRight size={20} color="#c0c4cc" />
69
- </span>
70
- <span className="tourism-example__service-desc">{item.desc}</span>
71
- </span>
72
- </button>
73
- ))}
74
- </div>
75
- </section>
76
-
77
- <section className="tourism-example__section">
78
- <div className="tourism-example__suggestion-header">
79
- <h2 className="tourism-example__section-title">猜你想问</h2>
80
- <button className="tourism-example__refresh" onClick={() => setSuggestions(pickRandom(ALL_SUGGESTIONS, 4))}>
81
- <ArrowsClockwise size={12} />
82
- <span>换一换</span>
83
- </button>
84
- </div>
85
- <div className="tourism-example__suggestion-grid">
86
- {suggestions.map((item) => (
87
- <button key={item.prompt} className="tourism-example__suggestion" onClick={() => onSend(item.prompt)}>
88
- <span>{item.title}</span>
89
- <CaretRight size={20} color="#c0c4cc" />
90
- </button>
91
- ))}
92
- </div>
93
- </section>
94
- </div>
95
- );
96
- }