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,298 @@
1
+ "use client";
2
+
3
+ import type { HTMLAttributes, ReactNode } from "react";
4
+
5
+ import {
6
+ Collapsible,
7
+ CollapsibleContent,
8
+ CollapsibleTrigger,
9
+ } from "@/components/ui/collapsible";
10
+ import { cn } from "@/lib/utils";
11
+ import {
12
+ ChevronRightIcon,
13
+ FileIcon,
14
+ FolderIcon,
15
+ FolderOpenIcon,
16
+ } from "lucide-react";
17
+ import {
18
+ createContext,
19
+ useCallback,
20
+ useContext,
21
+ useMemo,
22
+ useState,
23
+ } from "react";
24
+
25
+ interface FileTreeContextType {
26
+ expandedPaths: Set<string>;
27
+ togglePath: (path: string) => void;
28
+ selectedPath?: string;
29
+ onSelect?: (path: string) => void;
30
+ }
31
+
32
+ // Default noop for context default value
33
+ // oxlint-disable-next-line eslint(no-empty-function)
34
+ const noop = () => {};
35
+
36
+ const FileTreeContext = createContext<FileTreeContextType>({
37
+ // oxlint-disable-next-line eslint-plugin-unicorn(no-new-builtin)
38
+ expandedPaths: new Set(),
39
+ togglePath: noop,
40
+ });
41
+
42
+ export type FileTreeProps = HTMLAttributes<HTMLDivElement> & {
43
+ expanded?: Set<string>;
44
+ defaultExpanded?: Set<string>;
45
+ selectedPath?: string;
46
+ onSelect?: (path: string) => void;
47
+ onExpandedChange?: (expanded: Set<string>) => void;
48
+ };
49
+
50
+ export const FileTree = ({
51
+ expanded: controlledExpanded,
52
+ defaultExpanded = new Set(),
53
+ selectedPath,
54
+ onSelect,
55
+ onExpandedChange,
56
+ className,
57
+ children,
58
+ ...props
59
+ }: FileTreeProps) => {
60
+ const [internalExpanded, setInternalExpanded] = useState(defaultExpanded);
61
+ const expandedPaths = controlledExpanded ?? internalExpanded;
62
+
63
+ const togglePath = useCallback(
64
+ (path: string) => {
65
+ const newExpanded = new Set(expandedPaths);
66
+ if (newExpanded.has(path)) {
67
+ newExpanded.delete(path);
68
+ } else {
69
+ newExpanded.add(path);
70
+ }
71
+ setInternalExpanded(newExpanded);
72
+ onExpandedChange?.(newExpanded);
73
+ },
74
+ [expandedPaths, onExpandedChange]
75
+ );
76
+
77
+ const contextValue = useMemo(
78
+ () => ({ expandedPaths, onSelect, selectedPath, togglePath }),
79
+ [expandedPaths, onSelect, selectedPath, togglePath]
80
+ );
81
+
82
+ return (
83
+ <FileTreeContext.Provider value={contextValue}>
84
+ <div
85
+ className={cn(
86
+ "rounded-lg border bg-background font-mono text-sm",
87
+ className
88
+ )}
89
+ role="tree"
90
+ {...props}
91
+ >
92
+ <div className="p-2">{children}</div>
93
+ </div>
94
+ </FileTreeContext.Provider>
95
+ );
96
+ };
97
+
98
+ interface FileTreeFolderContextType {
99
+ path: string;
100
+ name: string;
101
+ isExpanded: boolean;
102
+ }
103
+
104
+ const FileTreeFolderContext = createContext<FileTreeFolderContextType>({
105
+ isExpanded: false,
106
+ name: "",
107
+ path: "",
108
+ });
109
+
110
+ export type FileTreeFolderProps = HTMLAttributes<HTMLDivElement> & {
111
+ path: string;
112
+ name: string;
113
+ };
114
+
115
+ export const FileTreeFolder = ({
116
+ path,
117
+ name,
118
+ className,
119
+ children,
120
+ ...props
121
+ }: FileTreeFolderProps) => {
122
+ const { expandedPaths, togglePath, selectedPath, onSelect } =
123
+ useContext(FileTreeContext);
124
+ const isExpanded = expandedPaths.has(path);
125
+ const isSelected = selectedPath === path;
126
+
127
+ const handleOpenChange = useCallback(() => {
128
+ togglePath(path);
129
+ }, [togglePath, path]);
130
+
131
+ const handleSelect = useCallback(() => {
132
+ onSelect?.(path);
133
+ }, [onSelect, path]);
134
+
135
+ const folderContextValue = useMemo(
136
+ () => ({ isExpanded, name, path }),
137
+ [isExpanded, name, path]
138
+ );
139
+
140
+ return (
141
+ <FileTreeFolderContext.Provider value={folderContextValue}>
142
+ <Collapsible onOpenChange={handleOpenChange} open={isExpanded}>
143
+ <div
144
+ className={cn("", className)}
145
+ role="treeitem"
146
+ tabIndex={0}
147
+ {...props}
148
+ >
149
+ <CollapsibleTrigger asChild>
150
+ <button
151
+ className={cn(
152
+ "flex w-full items-center gap-1 rounded px-2 py-1 text-left transition-colors hover:bg-muted/50",
153
+ isSelected && "bg-muted"
154
+ )}
155
+ onClick={handleSelect}
156
+ type="button"
157
+ >
158
+ <ChevronRightIcon
159
+ className={cn(
160
+ "size-4 shrink-0 text-muted-foreground transition-transform",
161
+ isExpanded && "rotate-90"
162
+ )}
163
+ />
164
+ <FileTreeIcon>
165
+ {isExpanded ? (
166
+ <FolderOpenIcon className="size-4 text-blue-500" />
167
+ ) : (
168
+ <FolderIcon className="size-4 text-blue-500" />
169
+ )}
170
+ </FileTreeIcon>
171
+ <FileTreeName>{name}</FileTreeName>
172
+ </button>
173
+ </CollapsibleTrigger>
174
+ <CollapsibleContent>
175
+ <div className="ml-4 border-l pl-2">{children}</div>
176
+ </CollapsibleContent>
177
+ </div>
178
+ </Collapsible>
179
+ </FileTreeFolderContext.Provider>
180
+ );
181
+ };
182
+
183
+ interface FileTreeFileContextType {
184
+ path: string;
185
+ name: string;
186
+ }
187
+
188
+ const FileTreeFileContext = createContext<FileTreeFileContextType>({
189
+ name: "",
190
+ path: "",
191
+ });
192
+
193
+ export type FileTreeFileProps = HTMLAttributes<HTMLDivElement> & {
194
+ path: string;
195
+ name: string;
196
+ icon?: ReactNode;
197
+ };
198
+
199
+ export const FileTreeFile = ({
200
+ path,
201
+ name,
202
+ icon,
203
+ className,
204
+ children,
205
+ ...props
206
+ }: FileTreeFileProps) => {
207
+ const { selectedPath, onSelect } = useContext(FileTreeContext);
208
+ const isSelected = selectedPath === path;
209
+
210
+ const handleClick = useCallback(() => {
211
+ onSelect?.(path);
212
+ }, [onSelect, path]);
213
+
214
+ const handleKeyDown = useCallback(
215
+ (e: React.KeyboardEvent) => {
216
+ if (e.key === "Enter" || e.key === " ") {
217
+ onSelect?.(path);
218
+ }
219
+ },
220
+ [onSelect, path]
221
+ );
222
+
223
+ const fileContextValue = useMemo(() => ({ name, path }), [name, path]);
224
+
225
+ return (
226
+ <FileTreeFileContext.Provider value={fileContextValue}>
227
+ <div
228
+ className={cn(
229
+ "flex cursor-pointer items-center gap-1 rounded px-2 py-1 transition-colors hover:bg-muted/50",
230
+ isSelected && "bg-muted",
231
+ className
232
+ )}
233
+ onClick={handleClick}
234
+ onKeyDown={handleKeyDown}
235
+ role="treeitem"
236
+ tabIndex={0}
237
+ {...props}
238
+ >
239
+ {children ?? (
240
+ <>
241
+ {/* Spacer for alignment */}
242
+ <span className="size-4" />
243
+ <FileTreeIcon>
244
+ {icon ?? <FileIcon className="size-4 text-muted-foreground" />}
245
+ </FileTreeIcon>
246
+ <FileTreeName>{name}</FileTreeName>
247
+ </>
248
+ )}
249
+ </div>
250
+ </FileTreeFileContext.Provider>
251
+ );
252
+ };
253
+
254
+ export type FileTreeIconProps = HTMLAttributes<HTMLSpanElement>;
255
+
256
+ export const FileTreeIcon = ({
257
+ className,
258
+ children,
259
+ ...props
260
+ }: FileTreeIconProps) => (
261
+ <span className={cn("shrink-0", className)} {...props}>
262
+ {children}
263
+ </span>
264
+ );
265
+
266
+ export type FileTreeNameProps = HTMLAttributes<HTMLSpanElement>;
267
+
268
+ export const FileTreeName = ({
269
+ className,
270
+ children,
271
+ ...props
272
+ }: FileTreeNameProps) => (
273
+ <span className={cn("truncate", className)} {...props}>
274
+ {children}
275
+ </span>
276
+ );
277
+
278
+ export type FileTreeActionsProps = HTMLAttributes<HTMLDivElement>;
279
+
280
+ const stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation();
281
+
282
+ export const FileTreeActions = ({
283
+ className,
284
+ children,
285
+ ...props
286
+ }: FileTreeActionsProps) => (
287
+ // biome-ignore lint/a11y/noNoninteractiveElementInteractions: stopPropagation required for nested interactions
288
+ // biome-ignore lint/a11y/useSemanticElements: fieldset doesn't fit this UI pattern
289
+ <div
290
+ className={cn("ml-auto flex items-center gap-1", className)}
291
+ onClick={stopPropagation}
292
+ onKeyDown={stopPropagation}
293
+ role="group"
294
+ {...props}
295
+ >
296
+ {children}
297
+ </div>
298
+ );
@@ -0,0 +1,25 @@
1
+ import type { Experimental_GeneratedImage } from "ai";
2
+
3
+ import { cn } from "@/lib/utils";
4
+
5
+ export type ImageProps = Experimental_GeneratedImage & {
6
+ className?: string;
7
+ alt?: string;
8
+ };
9
+
10
+ export const Image = ({
11
+ base64,
12
+ uint8Array: _uint8Array,
13
+ mediaType,
14
+ ...props
15
+ }: ImageProps) => (
16
+ <img
17
+ {...props}
18
+ alt={props.alt}
19
+ className={cn(
20
+ "h-auto max-w-full overflow-hidden rounded-md",
21
+ props.className
22
+ )}
23
+ src={`data:${mediaType};base64,${base64}`}
24
+ />
25
+ );
@@ -0,0 +1,294 @@
1
+ "use client";
2
+
3
+ import type { CarouselApi } from "@/components/ui/carousel";
4
+ import type { ComponentProps } from "react";
5
+
6
+ import { Badge } from "@/components/ui/badge";
7
+ import {
8
+ Carousel,
9
+ CarouselContent,
10
+ CarouselItem,
11
+ } from "@/components/ui/carousel";
12
+ import {
13
+ HoverCard,
14
+ HoverCardContent,
15
+ HoverCardTrigger,
16
+ } from "@/components/ui/hover-card";
17
+ import { cn } from "@/lib/utils";
18
+ import { ArrowLeftIcon, ArrowRightIcon } from "lucide-react";
19
+ import {
20
+ createContext,
21
+ useCallback,
22
+ useContext,
23
+ useEffect,
24
+ useState,
25
+ } from "react";
26
+
27
+ export type InlineCitationProps = ComponentProps<"span">;
28
+
29
+ export const InlineCitation = ({
30
+ className,
31
+ ...props
32
+ }: InlineCitationProps) => (
33
+ <span
34
+ className={cn("group inline items-center gap-1", className)}
35
+ {...props}
36
+ />
37
+ );
38
+
39
+ export type InlineCitationTextProps = ComponentProps<"span">;
40
+
41
+ export const InlineCitationText = ({
42
+ className,
43
+ ...props
44
+ }: InlineCitationTextProps) => (
45
+ <span
46
+ className={cn("transition-colors group-hover:bg-accent", className)}
47
+ {...props}
48
+ />
49
+ );
50
+
51
+ export type InlineCitationCardProps = ComponentProps<typeof HoverCard>;
52
+
53
+ export const InlineCitationCard = (props: InlineCitationCardProps) => (
54
+ <HoverCard closeDelay={0} openDelay={0} {...props} />
55
+ );
56
+
57
+ export type InlineCitationCardTriggerProps = ComponentProps<typeof Badge> & {
58
+ sources: string[];
59
+ };
60
+
61
+ export const InlineCitationCardTrigger = ({
62
+ sources,
63
+ className,
64
+ ...props
65
+ }: InlineCitationCardTriggerProps) => (
66
+ <HoverCardTrigger asChild>
67
+ <Badge
68
+ className={cn("ml-1 rounded-full", className)}
69
+ variant="secondary"
70
+ {...props}
71
+ >
72
+ {sources[0] ? (
73
+ <>
74
+ {new URL(sources[0]).hostname}{" "}
75
+ {sources.length > 1 && `+${sources.length - 1}`}
76
+ </>
77
+ ) : (
78
+ "unknown"
79
+ )}
80
+ </Badge>
81
+ </HoverCardTrigger>
82
+ );
83
+
84
+ export type InlineCitationCardBodyProps = ComponentProps<"div">;
85
+
86
+ export const InlineCitationCardBody = ({
87
+ className,
88
+ ...props
89
+ }: InlineCitationCardBodyProps) => (
90
+ <HoverCardContent className={cn("relative w-80 p-0", className)} {...props} />
91
+ );
92
+
93
+ const CarouselApiContext = createContext<CarouselApi | undefined>(undefined);
94
+
95
+ const useCarouselApi = () => {
96
+ const context = useContext(CarouselApiContext);
97
+ return context;
98
+ };
99
+
100
+ export type InlineCitationCarouselProps = ComponentProps<typeof Carousel>;
101
+
102
+ export const InlineCitationCarousel = ({
103
+ className,
104
+ children,
105
+ ...props
106
+ }: InlineCitationCarouselProps) => {
107
+ const [api, setApi] = useState<CarouselApi>();
108
+
109
+ return (
110
+ <CarouselApiContext.Provider value={api}>
111
+ <Carousel className={cn("w-full", className)} setApi={setApi} {...props}>
112
+ {children}
113
+ </Carousel>
114
+ </CarouselApiContext.Provider>
115
+ );
116
+ };
117
+
118
+ export type InlineCitationCarouselContentProps = ComponentProps<"div">;
119
+
120
+ export const InlineCitationCarouselContent = (
121
+ props: InlineCitationCarouselContentProps
122
+ ) => <CarouselContent {...props} />;
123
+
124
+ export type InlineCitationCarouselItemProps = ComponentProps<"div">;
125
+
126
+ export const InlineCitationCarouselItem = ({
127
+ className,
128
+ ...props
129
+ }: InlineCitationCarouselItemProps) => (
130
+ <CarouselItem
131
+ className={cn("w-full space-y-2 p-4 pl-8", className)}
132
+ {...props}
133
+ />
134
+ );
135
+
136
+ export type InlineCitationCarouselHeaderProps = ComponentProps<"div">;
137
+
138
+ export const InlineCitationCarouselHeader = ({
139
+ className,
140
+ ...props
141
+ }: InlineCitationCarouselHeaderProps) => (
142
+ <div
143
+ className={cn(
144
+ "flex items-center justify-between gap-2 rounded-t-md bg-secondary p-2",
145
+ className
146
+ )}
147
+ {...props}
148
+ />
149
+ );
150
+
151
+ export type InlineCitationCarouselIndexProps = ComponentProps<"div">;
152
+
153
+ export const InlineCitationCarouselIndex = ({
154
+ children,
155
+ className,
156
+ ...props
157
+ }: InlineCitationCarouselIndexProps) => {
158
+ const api = useCarouselApi();
159
+ const [current, setCurrent] = useState(0);
160
+ const [count, setCount] = useState(0);
161
+
162
+ useEffect(() => {
163
+ if (!api) {
164
+ return;
165
+ }
166
+
167
+ setCount(api.scrollSnapList().length);
168
+ setCurrent(api.selectedScrollSnap() + 1);
169
+
170
+ const handleSelect = () => {
171
+ setCurrent(api.selectedScrollSnap() + 1);
172
+ };
173
+
174
+ api.on("select", handleSelect);
175
+
176
+ return () => {
177
+ api.off("select", handleSelect);
178
+ };
179
+ }, [api]);
180
+
181
+ return (
182
+ <div
183
+ className={cn(
184
+ "flex flex-1 items-center justify-end px-3 py-1 text-muted-foreground text-xs",
185
+ className
186
+ )}
187
+ {...props}
188
+ >
189
+ {children ?? `${current}/${count}`}
190
+ </div>
191
+ );
192
+ };
193
+
194
+ export type InlineCitationCarouselPrevProps = ComponentProps<"button">;
195
+
196
+ export const InlineCitationCarouselPrev = ({
197
+ className,
198
+ ...props
199
+ }: InlineCitationCarouselPrevProps) => {
200
+ const api = useCarouselApi();
201
+
202
+ const handleClick = useCallback(() => {
203
+ if (api) {
204
+ api.scrollPrev();
205
+ }
206
+ }, [api]);
207
+
208
+ return (
209
+ <button
210
+ aria-label="Previous"
211
+ className={cn("shrink-0", className)}
212
+ onClick={handleClick}
213
+ type="button"
214
+ {...props}
215
+ >
216
+ <ArrowLeftIcon className="size-4 text-muted-foreground" />
217
+ </button>
218
+ );
219
+ };
220
+
221
+ export type InlineCitationCarouselNextProps = ComponentProps<"button">;
222
+
223
+ export const InlineCitationCarouselNext = ({
224
+ className,
225
+ ...props
226
+ }: InlineCitationCarouselNextProps) => {
227
+ const api = useCarouselApi();
228
+
229
+ const handleClick = useCallback(() => {
230
+ if (api) {
231
+ api.scrollNext();
232
+ }
233
+ }, [api]);
234
+
235
+ return (
236
+ <button
237
+ aria-label="Next"
238
+ className={cn("shrink-0", className)}
239
+ onClick={handleClick}
240
+ type="button"
241
+ {...props}
242
+ >
243
+ <ArrowRightIcon className="size-4 text-muted-foreground" />
244
+ </button>
245
+ );
246
+ };
247
+
248
+ export type InlineCitationSourceProps = ComponentProps<"div"> & {
249
+ title?: string;
250
+ url?: string;
251
+ description?: string;
252
+ };
253
+
254
+ export const InlineCitationSource = ({
255
+ title,
256
+ url,
257
+ description,
258
+ className,
259
+ children,
260
+ ...props
261
+ }: InlineCitationSourceProps) => (
262
+ <div className={cn("space-y-1", className)} {...props}>
263
+ {title && (
264
+ <h4 className="truncate font-medium text-sm leading-tight">{title}</h4>
265
+ )}
266
+ {url && (
267
+ <p className="truncate break-all text-muted-foreground text-xs">{url}</p>
268
+ )}
269
+ {description && (
270
+ <p className="line-clamp-3 text-muted-foreground text-sm leading-relaxed">
271
+ {description}
272
+ </p>
273
+ )}
274
+ {children}
275
+ </div>
276
+ );
277
+
278
+ export type InlineCitationQuoteProps = ComponentProps<"blockquote">;
279
+
280
+ export const InlineCitationQuote = ({
281
+ children,
282
+ className,
283
+ ...props
284
+ }: InlineCitationQuoteProps) => (
285
+ <blockquote
286
+ className={cn(
287
+ "border-muted border-l-2 pl-3 text-muted-foreground text-sm italic",
288
+ className
289
+ )}
290
+ {...props}
291
+ >
292
+ {children}
293
+ </blockquote>
294
+ );