work-agent 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. package/README.md +234 -0
  2. package/app/(admin)/approvals/page.tsx +16 -0
  3. package/app/(admin)/audit/page.tsx +18 -0
  4. package/app/(admin)/layout.tsx +47 -0
  5. package/app/(admin)/scheduled-tasks/page.tsx +17 -0
  6. package/app/(admin)/settings/page.tsx +46 -0
  7. package/app/(admin)/skills/[name]/page.tsx +378 -0
  8. package/app/(admin)/skills/page.tsx +406 -0
  9. package/app/(admin)/statistics/page.tsx +416 -0
  10. package/app/(admin)/tickets/[id]/page.tsx +348 -0
  11. package/app/(admin)/tickets/new/page.tsx +309 -0
  12. package/app/(admin)/tickets/page.tsx +27 -0
  13. package/app/api/audit/route.ts +30 -0
  14. package/app/api/auth/feishu/callback/route.ts +72 -0
  15. package/app/api/auth/feishu/login/route.ts +17 -0
  16. package/app/api/auth/feishu/sso/route.ts +78 -0
  17. package/app/api/auth/login/route.ts +85 -0
  18. package/app/api/auth/oauth/route.ts +168 -0
  19. package/app/api/config/providers/route.ts +105 -0
  20. package/app/api/config/route.ts +115 -0
  21. package/app/api/config/status/route.ts +56 -0
  22. package/app/api/config/test/route.ts +212 -0
  23. package/app/api/documents/[id]/route.ts +88 -0
  24. package/app/api/documents/route.ts +53 -0
  25. package/app/api/health/route.ts +32 -0
  26. package/app/api/knowledge/[id]/route.ts +152 -0
  27. package/app/api/knowledge/from-session/route.ts +27 -0
  28. package/app/api/knowledge/route.ts +100 -0
  29. package/app/api/market/knowledge/[id]/route.ts +92 -0
  30. package/app/api/market/knowledge/route.ts +130 -0
  31. package/app/api/marketplace/skills/[id]/approve/route.ts +68 -0
  32. package/app/api/marketplace/skills/[id]/certify/route.ts +54 -0
  33. package/app/api/marketplace/skills/[id]/install/route.ts +180 -0
  34. package/app/api/marketplace/skills/[id]/promote-to-system/route.ts +219 -0
  35. package/app/api/marketplace/skills/[id]/rate/route.ts +90 -0
  36. package/app/api/marketplace/skills/[id]/ratings/route.ts +55 -0
  37. package/app/api/marketplace/skills/[id]/reject/route.ts +68 -0
  38. package/app/api/marketplace/skills/[id]/route.ts +177 -0
  39. package/app/api/marketplace/skills/route.ts +235 -0
  40. package/app/api/memory/route.ts +40 -0
  41. package/app/api/my/files/[id]/route.ts +52 -0
  42. package/app/api/my/files/route.ts +230 -0
  43. package/app/api/my/knowledge/route.ts +36 -0
  44. package/app/api/pi-chat/route.ts +443 -0
  45. package/app/api/recommend/route.ts +38 -0
  46. package/app/api/scheduled-tasks/[id]/execute/route.ts +132 -0
  47. package/app/api/scheduled-tasks/[id]/route.ts +165 -0
  48. package/app/api/scheduled-tasks/[id]/toggle/route.ts +53 -0
  49. package/app/api/scheduled-tasks/route.ts +101 -0
  50. package/app/api/sessions/[id]/messages/route.ts +212 -0
  51. package/app/api/sessions/route.ts +101 -0
  52. package/app/api/share/file/[id]/route.ts +37 -0
  53. package/app/api/skills/[name]/execute/route.ts +121 -0
  54. package/app/api/skills/[name]/route.ts +167 -0
  55. package/app/api/skills/create/route.ts +65 -0
  56. package/app/api/skills/generate/route.ts +405 -0
  57. package/app/api/skills/installed/route.ts +151 -0
  58. package/app/api/skills/route.ts +174 -0
  59. package/app/api/skills/translate/route.ts +40 -0
  60. package/app/api/skills/user/[name]/route.ts +159 -0
  61. package/app/api/skills/user/route.ts +90 -0
  62. package/app/api/statistics/route.ts +94 -0
  63. package/app/api/task-executions/[id]/route.ts +34 -0
  64. package/app/api/task-executions/route.ts +29 -0
  65. package/app/api/tickets/[id]/approve/route.ts +129 -0
  66. package/app/api/tickets/[id]/execute/route.ts +201 -0
  67. package/app/api/tickets/[id]/route.ts +127 -0
  68. package/app/api/tickets/route.ts +103 -0
  69. package/app/api/user/skills/route.ts +175 -0
  70. package/app/api/users/route.ts +80 -0
  71. package/app/chat/page.tsx +5 -0
  72. package/app/globals.css +84 -0
  73. package/app/h5/layout.tsx +5 -0
  74. package/app/h5/mobile-approvals-page.tsx +167 -0
  75. package/app/h5/mobile-chat-page.tsx +951 -0
  76. package/app/h5/mobile-profile-page.tsx +147 -0
  77. package/app/h5/mobile-tickets-page.tsx +121 -0
  78. package/app/h5/page.tsx +23 -0
  79. package/app/h5/ticket-action-buttons.tsx +80 -0
  80. package/app/layout.tsx +26 -0
  81. package/app/login/page.tsx +318 -0
  82. package/app/market/knowledge/[id]/page.tsx +77 -0
  83. package/app/market/knowledge/page.tsx +358 -0
  84. package/app/market/layout.tsx +29 -0
  85. package/app/market/page.tsx +18 -0
  86. package/app/market/skills/page.tsx +397 -0
  87. package/app/my/files/page.tsx +511 -0
  88. package/app/my/knowledge/[id]/page.tsx +271 -0
  89. package/app/my/knowledge/new/page.tsx +234 -0
  90. package/app/my/knowledge/page.tsx +248 -0
  91. package/app/my/layout.tsx +32 -0
  92. package/app/my/memory/page.tsx +164 -0
  93. package/app/my/page.tsx +18 -0
  94. package/app/my/scheduled-tasks/[id]/edit/page.tsx +290 -0
  95. package/app/my/scheduled-tasks/[id]/executions/page.tsx +275 -0
  96. package/app/my/scheduled-tasks/[id]/page.tsx +284 -0
  97. package/app/my/scheduled-tasks/new/page.tsx +230 -0
  98. package/app/my/scheduled-tasks/page.tsx +27 -0
  99. package/app/my/skills/[name]/page.tsx +320 -0
  100. package/app/my/skills/new/page.tsx +394 -0
  101. package/app/my/skills/page.tsx +303 -0
  102. package/app/page.tsx +2288 -0
  103. package/app/share/[sessionId]/page.tsx +226 -0
  104. package/app/share/file/[id]/page.tsx +140 -0
  105. package/bin/README.md +63 -0
  106. package/bin/generate-api-system +300 -0
  107. package/bin/postinstall.js +95 -0
  108. package/bin/work-agent.js +173 -0
  109. package/components/ai-elements/agent.tsx +142 -0
  110. package/components/ai-elements/artifact.tsx +149 -0
  111. package/components/ai-elements/attachments.tsx +427 -0
  112. package/components/ai-elements/audio-player.tsx +232 -0
  113. package/components/ai-elements/canvas.tsx +26 -0
  114. package/components/ai-elements/chain-of-thought.tsx +223 -0
  115. package/components/ai-elements/checkpoint.tsx +72 -0
  116. package/components/ai-elements/code-block.tsx +555 -0
  117. package/components/ai-elements/commit.tsx +449 -0
  118. package/components/ai-elements/confirmation.tsx +173 -0
  119. package/components/ai-elements/connection.tsx +28 -0
  120. package/components/ai-elements/context.tsx +410 -0
  121. package/components/ai-elements/controls.tsx +19 -0
  122. package/components/ai-elements/conversation.tsx +167 -0
  123. package/components/ai-elements/edge.tsx +144 -0
  124. package/components/ai-elements/environment-variables.tsx +325 -0
  125. package/components/ai-elements/file-tree.tsx +298 -0
  126. package/components/ai-elements/image.tsx +25 -0
  127. package/components/ai-elements/inline-citation.tsx +294 -0
  128. package/components/ai-elements/jsx-preview.tsx +250 -0
  129. package/components/ai-elements/message.tsx +367 -0
  130. package/components/ai-elements/mic-selector.tsx +372 -0
  131. package/components/ai-elements/model-selector.tsx +214 -0
  132. package/components/ai-elements/node.tsx +72 -0
  133. package/components/ai-elements/open-in-chat.tsx +367 -0
  134. package/components/ai-elements/package-info.tsx +235 -0
  135. package/components/ai-elements/panel.tsx +16 -0
  136. package/components/ai-elements/persona.tsx +280 -0
  137. package/components/ai-elements/plan.tsx +144 -0
  138. package/components/ai-elements/prompt-input.tsx +1341 -0
  139. package/components/ai-elements/queue.tsx +275 -0
  140. package/components/ai-elements/reasoning.tsx +355 -0
  141. package/components/ai-elements/sandbox.tsx +133 -0
  142. package/components/ai-elements/schema-display.tsx +473 -0
  143. package/components/ai-elements/shimmer.tsx +78 -0
  144. package/components/ai-elements/snippet.tsx +141 -0
  145. package/components/ai-elements/sources.tsx +78 -0
  146. package/components/ai-elements/speech-input.tsx +324 -0
  147. package/components/ai-elements/stack-trace.tsx +531 -0
  148. package/components/ai-elements/suggestion.tsx +58 -0
  149. package/components/ai-elements/task.tsx +88 -0
  150. package/components/ai-elements/terminal.tsx +277 -0
  151. package/components/ai-elements/test-results.tsx +497 -0
  152. package/components/ai-elements/tool.tsx +174 -0
  153. package/components/ai-elements/toolbar.tsx +17 -0
  154. package/components/ai-elements/transcription.tsx +126 -0
  155. package/components/ai-elements/voice-selector.tsx +525 -0
  156. package/components/ai-elements/web-preview.tsx +282 -0
  157. package/components/audit-log-list.tsx +114 -0
  158. package/components/chat/EmptyPreviewState.tsx +12 -0
  159. package/components/chat/KnowledgePickerDialog.tsx +464 -0
  160. package/components/chat/KnowledgePreview.tsx +70 -0
  161. package/components/chat/KnowledgePreviewPanel.tsx +86 -0
  162. package/components/chat/MentionInput.tsx +309 -0
  163. package/components/chat/OrganizeDialog.tsx +258 -0
  164. package/components/chat/RecommendationBanner.tsx +94 -0
  165. package/components/chat/SaveToKnowledgeDialog.tsx +193 -0
  166. package/components/chat/SkillSelector.tsx +305 -0
  167. package/components/chat/SkillSwitcher.tsx +163 -0
  168. package/components/client-layout.tsx +15 -0
  169. package/components/knowledge/KnowledgeMetadataPanel.tsx +293 -0
  170. package/components/layout-wrapper.tsx +18 -0
  171. package/components/mobile-layout.tsx +62 -0
  172. package/components/scheduled-task-list.tsx +356 -0
  173. package/components/setup-guide.tsx +484 -0
  174. package/components/sub-nav.tsx +54 -0
  175. package/components/ticket-detail-content.tsx +383 -0
  176. package/components/ticket-list.tsx +366 -0
  177. package/components/top-nav.tsx +132 -0
  178. package/components/ui/accordion.tsx +58 -0
  179. package/components/ui/alert.tsx +59 -0
  180. package/components/ui/avatar.tsx +50 -0
  181. package/components/ui/badge.tsx +36 -0
  182. package/components/ui/button-group.tsx +83 -0
  183. package/components/ui/button.tsx +57 -0
  184. package/components/ui/card.tsx +91 -0
  185. package/components/ui/carousel.tsx +262 -0
  186. package/components/ui/collapsible.tsx +11 -0
  187. package/components/ui/command.tsx +153 -0
  188. package/components/ui/dialog.tsx +122 -0
  189. package/components/ui/dropdown-menu.tsx +200 -0
  190. package/components/ui/hover-card.tsx +29 -0
  191. package/components/ui/input-group.tsx +170 -0
  192. package/components/ui/input.tsx +22 -0
  193. package/components/ui/label.tsx +26 -0
  194. package/components/ui/popover.tsx +31 -0
  195. package/components/ui/progress.tsx +28 -0
  196. package/components/ui/scroll-area.tsx +48 -0
  197. package/components/ui/select.tsx +174 -0
  198. package/components/ui/separator.tsx +31 -0
  199. package/components/ui/spinner.tsx +16 -0
  200. package/components/ui/switch.tsx +29 -0
  201. package/components/ui/table.tsx +120 -0
  202. package/components/ui/tabs.tsx +55 -0
  203. package/components/ui/textarea.tsx +22 -0
  204. package/components/ui/tooltip.tsx +30 -0
  205. package/components/welcome-guide.tsx +182 -0
  206. package/components.json +24 -0
  207. package/lib/command-parser.ts +331 -0
  208. package/lib/dangerous-commands.ts +672 -0
  209. package/lib/db.ts +2250 -0
  210. package/lib/feishu-auth.ts +135 -0
  211. package/lib/file-storage.ts +306 -0
  212. package/lib/file-tool.ts +583 -0
  213. package/lib/knowledge-tool.ts +152 -0
  214. package/lib/knowledge-types.ts +66 -0
  215. package/lib/market-client.ts +313 -0
  216. package/lib/market-db.ts +736 -0
  217. package/lib/market-types.ts +51 -0
  218. package/lib/memory-tool.ts +211 -0
  219. package/lib/memory.ts +197 -0
  220. package/lib/pi-config.ts +436 -0
  221. package/lib/pi-session.ts +799 -0
  222. package/lib/pinyin.ts +13 -0
  223. package/lib/recommendation.ts +227 -0
  224. package/lib/risk-estimator.ts +350 -0
  225. package/lib/scheduled-task-tool.ts +184 -0
  226. package/lib/scheduler-init.ts +43 -0
  227. package/lib/scheduler.ts +416 -0
  228. package/lib/secure-bash-tool.ts +413 -0
  229. package/lib/skill-engine.ts +396 -0
  230. package/lib/skill-generator.ts +269 -0
  231. package/lib/skill-loader.ts +234 -0
  232. package/lib/skill-tool.ts +188 -0
  233. package/lib/skill-types.ts +82 -0
  234. package/lib/skills-init.ts +58 -0
  235. package/lib/ticket-tool.ts +246 -0
  236. package/lib/user-skill-types.ts +30 -0
  237. package/lib/user-skills.ts +362 -0
  238. package/lib/utils.ts +6 -0
  239. package/lib/workflow.ts +154 -0
  240. package/lib/zip-tool.ts +191 -0
  241. package/next.config.js +8 -0
  242. package/package.json +106 -0
  243. package/public/.gitkeep +1 -0
  244. package/public/icon.svg +1 -0
  245. package/tsconfig.json +42 -0
@@ -0,0 +1,232 @@
1
+ "use client";
2
+
3
+ import type { Experimental_SpeechResult as SpeechResult } from "ai";
4
+ import type { ComponentProps, CSSProperties } from "react";
5
+
6
+ import { Button } from "@/components/ui/button";
7
+ import {
8
+ ButtonGroup,
9
+ ButtonGroupText,
10
+ } from "@/components/ui/button-group";
11
+ import { cn } from "@/lib/utils";
12
+ import {
13
+ MediaControlBar,
14
+ MediaController,
15
+ MediaDurationDisplay,
16
+ MediaMuteButton,
17
+ MediaPlayButton,
18
+ MediaSeekBackwardButton,
19
+ MediaSeekForwardButton,
20
+ MediaTimeDisplay,
21
+ MediaTimeRange,
22
+ MediaVolumeRange,
23
+ } from "media-chrome/react";
24
+
25
+ export type AudioPlayerProps = Omit<
26
+ ComponentProps<typeof MediaController>,
27
+ "audio"
28
+ >;
29
+
30
+ export const AudioPlayer = ({
31
+ children,
32
+ style,
33
+ ...props
34
+ }: AudioPlayerProps) => (
35
+ <MediaController
36
+ audio
37
+ data-slot="audio-player"
38
+ style={
39
+ {
40
+ "--media-background-color": "transparent",
41
+ "--media-button-icon-height": "1rem",
42
+ "--media-button-icon-width": "1rem",
43
+ "--media-control-background": "transparent",
44
+ "--media-control-hover-background": "var(--color-accent)",
45
+ "--media-control-padding": "0",
46
+ "--media-font": "var(--font-sans)",
47
+ "--media-font-size": "10px",
48
+ "--media-icon-color": "currentColor",
49
+ "--media-preview-time-background": "var(--color-background)",
50
+ "--media-preview-time-border-radius": "var(--radius-md)",
51
+ "--media-preview-time-text-shadow": "none",
52
+ "--media-primary-color": "var(--color-primary)",
53
+ "--media-range-bar-color": "var(--color-primary)",
54
+ "--media-range-track-background": "var(--color-secondary)",
55
+ "--media-secondary-color": "var(--color-secondary)",
56
+ "--media-text-color": "var(--color-foreground)",
57
+ "--media-tooltip-arrow-display": "none",
58
+ "--media-tooltip-background": "var(--color-background)",
59
+ "--media-tooltip-border-radius": "var(--radius-md)",
60
+ ...style,
61
+ } as CSSProperties
62
+ }
63
+ {...props}
64
+ >
65
+ {children}
66
+ </MediaController>
67
+ );
68
+
69
+ export type AudioPlayerElementProps = Omit<ComponentProps<"audio">, "src"> &
70
+ (
71
+ | {
72
+ data: SpeechResult["audio"];
73
+ }
74
+ | {
75
+ src: string;
76
+ }
77
+ );
78
+
79
+ export const AudioPlayerElement = ({ ...props }: AudioPlayerElementProps) => (
80
+ // oxlint-disable-next-line eslint-plugin-jsx-a11y(media-has-caption) -- audio player captions are provided by consumer
81
+ <audio
82
+ data-slot="audio-player-element"
83
+ slot="media"
84
+ src={
85
+ "src" in props
86
+ ? props.src
87
+ : `data:${props.data.mediaType};base64,${props.data.base64}`
88
+ }
89
+ {...props}
90
+ />
91
+ );
92
+
93
+ export type AudioPlayerControlBarProps = ComponentProps<typeof MediaControlBar>;
94
+
95
+ export const AudioPlayerControlBar = ({
96
+ children,
97
+ ...props
98
+ }: AudioPlayerControlBarProps) => (
99
+ <MediaControlBar data-slot="audio-player-control-bar" {...props}>
100
+ <ButtonGroup orientation="horizontal">{children}</ButtonGroup>
101
+ </MediaControlBar>
102
+ );
103
+
104
+ export type AudioPlayerPlayButtonProps = ComponentProps<typeof MediaPlayButton>;
105
+
106
+ export const AudioPlayerPlayButton = ({
107
+ className,
108
+ ...props
109
+ }: AudioPlayerPlayButtonProps) => (
110
+ <Button asChild size="icon-sm" variant="outline">
111
+ <MediaPlayButton
112
+ className={cn("bg-transparent", className)}
113
+ data-slot="audio-player-play-button"
114
+ {...props}
115
+ />
116
+ </Button>
117
+ );
118
+
119
+ export type AudioPlayerSeekBackwardButtonProps = ComponentProps<
120
+ typeof MediaSeekBackwardButton
121
+ >;
122
+
123
+ export const AudioPlayerSeekBackwardButton = ({
124
+ seekOffset = 10,
125
+ ...props
126
+ }: AudioPlayerSeekBackwardButtonProps) => (
127
+ <Button asChild size="icon-sm" variant="outline">
128
+ <MediaSeekBackwardButton
129
+ data-slot="audio-player-seek-backward-button"
130
+ seekOffset={seekOffset}
131
+ {...props}
132
+ />
133
+ </Button>
134
+ );
135
+
136
+ export type AudioPlayerSeekForwardButtonProps = ComponentProps<
137
+ typeof MediaSeekForwardButton
138
+ >;
139
+
140
+ export const AudioPlayerSeekForwardButton = ({
141
+ seekOffset = 10,
142
+ ...props
143
+ }: AudioPlayerSeekForwardButtonProps) => (
144
+ <Button asChild size="icon-sm" variant="outline">
145
+ <MediaSeekForwardButton
146
+ data-slot="audio-player-seek-forward-button"
147
+ seekOffset={seekOffset}
148
+ {...props}
149
+ />
150
+ </Button>
151
+ );
152
+
153
+ export type AudioPlayerTimeDisplayProps = ComponentProps<
154
+ typeof MediaTimeDisplay
155
+ >;
156
+
157
+ export const AudioPlayerTimeDisplay = ({
158
+ className,
159
+ ...props
160
+ }: AudioPlayerTimeDisplayProps) => (
161
+ <ButtonGroupText asChild className="bg-transparent">
162
+ <MediaTimeDisplay
163
+ className={cn("tabular-nums", className)}
164
+ data-slot="audio-player-time-display"
165
+ {...props}
166
+ />
167
+ </ButtonGroupText>
168
+ );
169
+
170
+ export type AudioPlayerTimeRangeProps = ComponentProps<typeof MediaTimeRange>;
171
+
172
+ export const AudioPlayerTimeRange = ({
173
+ className,
174
+ ...props
175
+ }: AudioPlayerTimeRangeProps) => (
176
+ <ButtonGroupText asChild className="bg-transparent">
177
+ <MediaTimeRange
178
+ className={cn("", className)}
179
+ data-slot="audio-player-time-range"
180
+ {...props}
181
+ />
182
+ </ButtonGroupText>
183
+ );
184
+
185
+ export type AudioPlayerDurationDisplayProps = ComponentProps<
186
+ typeof MediaDurationDisplay
187
+ >;
188
+
189
+ export const AudioPlayerDurationDisplay = ({
190
+ className,
191
+ ...props
192
+ }: AudioPlayerDurationDisplayProps) => (
193
+ <ButtonGroupText asChild className="bg-transparent">
194
+ <MediaDurationDisplay
195
+ className={cn("tabular-nums", className)}
196
+ data-slot="audio-player-duration-display"
197
+ {...props}
198
+ />
199
+ </ButtonGroupText>
200
+ );
201
+
202
+ export type AudioPlayerMuteButtonProps = ComponentProps<typeof MediaMuteButton>;
203
+
204
+ export const AudioPlayerMuteButton = ({
205
+ className,
206
+ ...props
207
+ }: AudioPlayerMuteButtonProps) => (
208
+ <ButtonGroupText asChild className="bg-transparent">
209
+ <MediaMuteButton
210
+ className={cn("", className)}
211
+ data-slot="audio-player-mute-button"
212
+ {...props}
213
+ />
214
+ </ButtonGroupText>
215
+ );
216
+
217
+ export type AudioPlayerVolumeRangeProps = ComponentProps<
218
+ typeof MediaVolumeRange
219
+ >;
220
+
221
+ export const AudioPlayerVolumeRange = ({
222
+ className,
223
+ ...props
224
+ }: AudioPlayerVolumeRangeProps) => (
225
+ <ButtonGroupText asChild className="bg-transparent">
226
+ <MediaVolumeRange
227
+ className={cn("", className)}
228
+ data-slot="audio-player-volume-range"
229
+ {...props}
230
+ />
231
+ </ButtonGroupText>
232
+ );
@@ -0,0 +1,26 @@
1
+ import type { ReactFlowProps } from "@xyflow/react";
2
+ import type { ReactNode } from "react";
3
+
4
+ import { Background, ReactFlow } from "@xyflow/react";
5
+ import "@xyflow/react/dist/style.css";
6
+
7
+ type CanvasProps = ReactFlowProps & {
8
+ children?: ReactNode;
9
+ };
10
+
11
+ const deleteKeyCode = ["Backspace", "Delete"];
12
+
13
+ export const Canvas = ({ children, ...props }: CanvasProps) => (
14
+ <ReactFlow
15
+ deleteKeyCode={deleteKeyCode}
16
+ fitView
17
+ panOnDrag={false}
18
+ panOnScroll
19
+ selectionOnDrag={true}
20
+ zoomOnDoubleClick={false}
21
+ {...props}
22
+ >
23
+ <Background bgColor="var(--sidebar)" />
24
+ {children}
25
+ </ReactFlow>
26
+ );
@@ -0,0 +1,223 @@
1
+ "use client";
2
+
3
+ import type { LucideIcon } from "lucide-react";
4
+ import type { ComponentProps, ReactNode } from "react";
5
+
6
+ import { useControllableState } from "@radix-ui/react-use-controllable-state";
7
+ import { Badge } from "@/components/ui/badge";
8
+ import {
9
+ Collapsible,
10
+ CollapsibleContent,
11
+ CollapsibleTrigger,
12
+ } from "@/components/ui/collapsible";
13
+ import { cn } from "@/lib/utils";
14
+ import { BrainIcon, ChevronDownIcon, DotIcon } from "lucide-react";
15
+ import { createContext, memo, useContext, useMemo } from "react";
16
+
17
+ interface ChainOfThoughtContextValue {
18
+ isOpen: boolean;
19
+ setIsOpen: (open: boolean) => void;
20
+ }
21
+
22
+ const ChainOfThoughtContext = createContext<ChainOfThoughtContextValue | null>(
23
+ null
24
+ );
25
+
26
+ const useChainOfThought = () => {
27
+ const context = useContext(ChainOfThoughtContext);
28
+ if (!context) {
29
+ throw new Error(
30
+ "ChainOfThought components must be used within ChainOfThought"
31
+ );
32
+ }
33
+ return context;
34
+ };
35
+
36
+ export type ChainOfThoughtProps = ComponentProps<"div"> & {
37
+ open?: boolean;
38
+ defaultOpen?: boolean;
39
+ onOpenChange?: (open: boolean) => void;
40
+ };
41
+
42
+ export const ChainOfThought = memo(
43
+ ({
44
+ className,
45
+ open,
46
+ defaultOpen = false,
47
+ onOpenChange,
48
+ children,
49
+ ...props
50
+ }: ChainOfThoughtProps) => {
51
+ const [isOpen, setIsOpen] = useControllableState({
52
+ defaultProp: defaultOpen,
53
+ onChange: onOpenChange,
54
+ prop: open,
55
+ });
56
+
57
+ const chainOfThoughtContext = useMemo(
58
+ () => ({ isOpen, setIsOpen }),
59
+ [isOpen, setIsOpen]
60
+ );
61
+
62
+ return (
63
+ <ChainOfThoughtContext.Provider value={chainOfThoughtContext}>
64
+ <div className={cn("not-prose w-full space-y-4", className)} {...props}>
65
+ {children}
66
+ </div>
67
+ </ChainOfThoughtContext.Provider>
68
+ );
69
+ }
70
+ );
71
+
72
+ export type ChainOfThoughtHeaderProps = ComponentProps<
73
+ typeof CollapsibleTrigger
74
+ >;
75
+
76
+ export const ChainOfThoughtHeader = memo(
77
+ ({ className, children, ...props }: ChainOfThoughtHeaderProps) => {
78
+ const { isOpen, setIsOpen } = useChainOfThought();
79
+
80
+ return (
81
+ <Collapsible onOpenChange={setIsOpen} open={isOpen}>
82
+ <CollapsibleTrigger
83
+ className={cn(
84
+ "flex w-full items-center gap-2 text-muted-foreground text-sm transition-colors hover:text-foreground",
85
+ className
86
+ )}
87
+ {...props}
88
+ >
89
+ <BrainIcon className="size-4" />
90
+ <span className="flex-1 text-left">
91
+ {children ?? "Chain of Thought"}
92
+ </span>
93
+ <ChevronDownIcon
94
+ className={cn(
95
+ "size-4 transition-transform",
96
+ isOpen ? "rotate-180" : "rotate-0"
97
+ )}
98
+ />
99
+ </CollapsibleTrigger>
100
+ </Collapsible>
101
+ );
102
+ }
103
+ );
104
+
105
+ export type ChainOfThoughtStepProps = ComponentProps<"div"> & {
106
+ icon?: LucideIcon;
107
+ label: ReactNode;
108
+ description?: ReactNode;
109
+ status?: "complete" | "active" | "pending";
110
+ };
111
+
112
+ const stepStatusStyles = {
113
+ active: "text-foreground",
114
+ complete: "text-muted-foreground",
115
+ pending: "text-muted-foreground/50",
116
+ };
117
+
118
+ export const ChainOfThoughtStep = memo(
119
+ ({
120
+ className,
121
+ icon: Icon = DotIcon,
122
+ label,
123
+ description,
124
+ status = "complete",
125
+ children,
126
+ ...props
127
+ }: ChainOfThoughtStepProps) => (
128
+ <div
129
+ className={cn(
130
+ "flex gap-2 text-sm",
131
+ stepStatusStyles[status],
132
+ "fade-in-0 slide-in-from-top-2 animate-in",
133
+ className
134
+ )}
135
+ {...props}
136
+ >
137
+ <div className="relative mt-0.5">
138
+ <Icon className="size-4" />
139
+ <div className="absolute top-7 bottom-0 left-1/2 -mx-px w-px bg-border" />
140
+ </div>
141
+ <div className="flex-1 space-y-2 overflow-hidden">
142
+ <div>{label}</div>
143
+ {description && (
144
+ <div className="text-muted-foreground text-xs">{description}</div>
145
+ )}
146
+ {children}
147
+ </div>
148
+ </div>
149
+ )
150
+ );
151
+
152
+ export type ChainOfThoughtSearchResultsProps = ComponentProps<"div">;
153
+
154
+ export const ChainOfThoughtSearchResults = memo(
155
+ ({ className, ...props }: ChainOfThoughtSearchResultsProps) => (
156
+ <div
157
+ className={cn("flex flex-wrap items-center gap-2", className)}
158
+ {...props}
159
+ />
160
+ )
161
+ );
162
+
163
+ export type ChainOfThoughtSearchResultProps = ComponentProps<typeof Badge>;
164
+
165
+ export const ChainOfThoughtSearchResult = memo(
166
+ ({ className, children, ...props }: ChainOfThoughtSearchResultProps) => (
167
+ <Badge
168
+ className={cn("gap-1 px-2 py-0.5 font-normal text-xs", className)}
169
+ variant="secondary"
170
+ {...props}
171
+ >
172
+ {children}
173
+ </Badge>
174
+ )
175
+ );
176
+
177
+ export type ChainOfThoughtContentProps = ComponentProps<
178
+ typeof CollapsibleContent
179
+ >;
180
+
181
+ export const ChainOfThoughtContent = memo(
182
+ ({ className, children, ...props }: ChainOfThoughtContentProps) => {
183
+ const { isOpen } = useChainOfThought();
184
+
185
+ return (
186
+ <Collapsible open={isOpen}>
187
+ <CollapsibleContent
188
+ className={cn(
189
+ "mt-2 space-y-3",
190
+ "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
191
+ className
192
+ )}
193
+ {...props}
194
+ >
195
+ {children}
196
+ </CollapsibleContent>
197
+ </Collapsible>
198
+ );
199
+ }
200
+ );
201
+
202
+ export type ChainOfThoughtImageProps = ComponentProps<"div"> & {
203
+ caption?: string;
204
+ };
205
+
206
+ export const ChainOfThoughtImage = memo(
207
+ ({ className, children, caption, ...props }: ChainOfThoughtImageProps) => (
208
+ <div className={cn("mt-2 space-y-2", className)} {...props}>
209
+ <div className="relative flex max-h-[22rem] items-center justify-center overflow-hidden rounded-lg bg-muted p-3">
210
+ {children}
211
+ </div>
212
+ {caption && <p className="text-muted-foreground text-xs">{caption}</p>}
213
+ </div>
214
+ )
215
+ );
216
+
217
+ ChainOfThought.displayName = "ChainOfThought";
218
+ ChainOfThoughtHeader.displayName = "ChainOfThoughtHeader";
219
+ ChainOfThoughtStep.displayName = "ChainOfThoughtStep";
220
+ ChainOfThoughtSearchResults.displayName = "ChainOfThoughtSearchResults";
221
+ ChainOfThoughtSearchResult.displayName = "ChainOfThoughtSearchResult";
222
+ ChainOfThoughtContent.displayName = "ChainOfThoughtContent";
223
+ ChainOfThoughtImage.displayName = "ChainOfThoughtImage";
@@ -0,0 +1,72 @@
1
+ "use client";
2
+
3
+ import type { LucideProps } from "lucide-react";
4
+ import type { ComponentProps, HTMLAttributes } from "react";
5
+
6
+ import { Button } from "@/components/ui/button";
7
+ import { Separator } from "@/components/ui/separator";
8
+ import {
9
+ Tooltip,
10
+ TooltipContent,
11
+ TooltipTrigger,
12
+ } from "@/components/ui/tooltip";
13
+ import { cn } from "@/lib/utils";
14
+ import { BookmarkIcon } from "lucide-react";
15
+
16
+ export type CheckpointProps = HTMLAttributes<HTMLDivElement>;
17
+
18
+ export const Checkpoint = ({
19
+ className,
20
+ children,
21
+ ...props
22
+ }: CheckpointProps) => (
23
+ <div
24
+ className={cn(
25
+ "flex items-center gap-0.5 overflow-hidden text-muted-foreground",
26
+ className
27
+ )}
28
+ {...props}
29
+ >
30
+ {children}
31
+ <Separator />
32
+ </div>
33
+ );
34
+
35
+ export type CheckpointIconProps = LucideProps;
36
+
37
+ export const CheckpointIcon = ({
38
+ className,
39
+ children,
40
+ ...props
41
+ }: CheckpointIconProps) =>
42
+ children ?? (
43
+ <BookmarkIcon className={cn("size-4 shrink-0", className)} {...props} />
44
+ );
45
+
46
+ export type CheckpointTriggerProps = ComponentProps<typeof Button> & {
47
+ tooltip?: string;
48
+ };
49
+
50
+ export const CheckpointTrigger = ({
51
+ children,
52
+ variant = "ghost",
53
+ size = "sm",
54
+ tooltip,
55
+ ...props
56
+ }: CheckpointTriggerProps) =>
57
+ tooltip ? (
58
+ <Tooltip>
59
+ <TooltipTrigger asChild>
60
+ <Button size={size} type="button" variant={variant} {...props}>
61
+ {children}
62
+ </Button>
63
+ </TooltipTrigger>
64
+ <TooltipContent align="start" side="bottom">
65
+ {tooltip}
66
+ </TooltipContent>
67
+ </Tooltip>
68
+ ) : (
69
+ <Button size={size} type="button" variant={variant} {...props}>
70
+ {children}
71
+ </Button>
72
+ );