@gmickel/gno 0.7.0 → 0.8.1

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 (209) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +90 -50
  3. package/THIRD_PARTY_NOTICES.md +22 -0
  4. package/assets/screenshots/webui-ask-answer.png +0 -0
  5. package/assets/screenshots/webui-collections.png +0 -0
  6. package/assets/screenshots/webui-editor.png +0 -0
  7. package/assets/screenshots/webui-home.png +0 -0
  8. package/assets/skill/SKILL.md +12 -12
  9. package/assets/skill/cli-reference.md +59 -57
  10. package/assets/skill/examples.md +8 -7
  11. package/assets/skill/mcp-reference.md +8 -4
  12. package/package.json +31 -24
  13. package/src/app/constants.ts +43 -42
  14. package/src/cli/colors.ts +1 -1
  15. package/src/cli/commands/ask.ts +44 -43
  16. package/src/cli/commands/cleanup.ts +9 -8
  17. package/src/cli/commands/collection/add.ts +12 -12
  18. package/src/cli/commands/collection/index.ts +4 -4
  19. package/src/cli/commands/collection/list.ts +26 -25
  20. package/src/cli/commands/collection/remove.ts +10 -10
  21. package/src/cli/commands/collection/rename.ts +10 -10
  22. package/src/cli/commands/context/add.ts +1 -1
  23. package/src/cli/commands/context/check.ts +17 -17
  24. package/src/cli/commands/context/index.ts +4 -4
  25. package/src/cli/commands/context/list.ts +11 -11
  26. package/src/cli/commands/context/rm.ts +1 -1
  27. package/src/cli/commands/doctor.ts +86 -84
  28. package/src/cli/commands/embed.ts +30 -28
  29. package/src/cli/commands/get.ts +27 -26
  30. package/src/cli/commands/index-cmd.ts +9 -9
  31. package/src/cli/commands/index.ts +16 -16
  32. package/src/cli/commands/init.ts +13 -12
  33. package/src/cli/commands/ls.ts +20 -19
  34. package/src/cli/commands/mcp/config.ts +30 -28
  35. package/src/cli/commands/mcp/index.ts +4 -4
  36. package/src/cli/commands/mcp/install.ts +17 -17
  37. package/src/cli/commands/mcp/paths.ts +133 -133
  38. package/src/cli/commands/mcp/status.ts +21 -21
  39. package/src/cli/commands/mcp/uninstall.ts +13 -13
  40. package/src/cli/commands/mcp.ts +2 -2
  41. package/src/cli/commands/models/clear.ts +12 -11
  42. package/src/cli/commands/models/index.ts +5 -5
  43. package/src/cli/commands/models/list.ts +31 -30
  44. package/src/cli/commands/models/path.ts +1 -1
  45. package/src/cli/commands/models/pull.ts +19 -18
  46. package/src/cli/commands/models/use.ts +4 -4
  47. package/src/cli/commands/multi-get.ts +38 -36
  48. package/src/cli/commands/query.ts +21 -20
  49. package/src/cli/commands/ref-parser.ts +10 -10
  50. package/src/cli/commands/reset.ts +40 -39
  51. package/src/cli/commands/search.ts +14 -13
  52. package/src/cli/commands/serve.ts +4 -4
  53. package/src/cli/commands/shared.ts +11 -10
  54. package/src/cli/commands/skill/index.ts +5 -5
  55. package/src/cli/commands/skill/install.ts +18 -17
  56. package/src/cli/commands/skill/paths-cmd.ts +11 -10
  57. package/src/cli/commands/skill/paths.ts +23 -23
  58. package/src/cli/commands/skill/show.ts +13 -12
  59. package/src/cli/commands/skill/uninstall.ts +16 -15
  60. package/src/cli/commands/status.ts +25 -24
  61. package/src/cli/commands/update.ts +3 -3
  62. package/src/cli/commands/vsearch.ts +17 -16
  63. package/src/cli/context.ts +5 -5
  64. package/src/cli/errors.ts +3 -3
  65. package/src/cli/format/search-results.ts +37 -37
  66. package/src/cli/options.ts +43 -43
  67. package/src/cli/program.ts +455 -459
  68. package/src/cli/progress.ts +1 -1
  69. package/src/cli/run.ts +24 -23
  70. package/src/collection/add.ts +9 -8
  71. package/src/collection/index.ts +3 -3
  72. package/src/collection/remove.ts +7 -6
  73. package/src/collection/types.ts +6 -6
  74. package/src/config/defaults.ts +1 -1
  75. package/src/config/index.ts +5 -5
  76. package/src/config/loader.ts +19 -18
  77. package/src/config/paths.ts +9 -8
  78. package/src/config/saver.ts +14 -13
  79. package/src/config/types.ts +53 -52
  80. package/src/converters/adapters/markitdownTs/adapter.ts +21 -19
  81. package/src/converters/adapters/officeparser/adapter.ts +18 -16
  82. package/src/converters/canonicalize.ts +12 -12
  83. package/src/converters/errors.ts +26 -22
  84. package/src/converters/index.ts +8 -8
  85. package/src/converters/mime.ts +25 -25
  86. package/src/converters/native/markdown.ts +10 -9
  87. package/src/converters/native/plaintext.ts +8 -7
  88. package/src/converters/path.ts +2 -2
  89. package/src/converters/pipeline.ts +11 -10
  90. package/src/converters/registry.ts +8 -8
  91. package/src/converters/types.ts +14 -14
  92. package/src/converters/versions.ts +4 -4
  93. package/src/index.ts +4 -4
  94. package/src/ingestion/chunker.ts +10 -9
  95. package/src/ingestion/index.ts +6 -6
  96. package/src/ingestion/language.ts +62 -62
  97. package/src/ingestion/sync.ts +50 -49
  98. package/src/ingestion/types.ts +10 -10
  99. package/src/ingestion/walker.ts +14 -13
  100. package/src/llm/cache.ts +51 -49
  101. package/src/llm/errors.ts +40 -36
  102. package/src/llm/index.ts +9 -9
  103. package/src/llm/lockfile.ts +6 -6
  104. package/src/llm/nodeLlamaCpp/adapter.ts +13 -12
  105. package/src/llm/nodeLlamaCpp/embedding.ts +9 -8
  106. package/src/llm/nodeLlamaCpp/generation.ts +7 -6
  107. package/src/llm/nodeLlamaCpp/lifecycle.ts +11 -10
  108. package/src/llm/nodeLlamaCpp/rerank.ts +6 -5
  109. package/src/llm/policy.ts +5 -5
  110. package/src/llm/registry.ts +6 -5
  111. package/src/llm/types.ts +2 -2
  112. package/src/mcp/resources/index.ts +15 -13
  113. package/src/mcp/server.ts +25 -23
  114. package/src/mcp/tools/get.ts +25 -23
  115. package/src/mcp/tools/index.ts +32 -29
  116. package/src/mcp/tools/multi-get.ts +34 -32
  117. package/src/mcp/tools/query.ts +29 -27
  118. package/src/mcp/tools/search.ts +14 -12
  119. package/src/mcp/tools/status.ts +12 -11
  120. package/src/mcp/tools/vsearch.ts +26 -24
  121. package/src/pipeline/answer.ts +9 -9
  122. package/src/pipeline/chunk-lookup.ts +1 -1
  123. package/src/pipeline/contextual.ts +4 -4
  124. package/src/pipeline/expansion.ts +23 -21
  125. package/src/pipeline/explain.ts +21 -21
  126. package/src/pipeline/fusion.ts +9 -9
  127. package/src/pipeline/hybrid.ts +41 -42
  128. package/src/pipeline/index.ts +10 -10
  129. package/src/pipeline/query-language.ts +39 -39
  130. package/src/pipeline/rerank.ts +8 -7
  131. package/src/pipeline/search.ts +22 -22
  132. package/src/pipeline/types.ts +8 -8
  133. package/src/pipeline/vsearch.ts +21 -24
  134. package/src/serve/CLAUDE.md +21 -15
  135. package/src/serve/config-sync.ts +9 -8
  136. package/src/serve/context.ts +19 -18
  137. package/src/serve/index.ts +1 -1
  138. package/src/serve/jobs.ts +7 -7
  139. package/src/serve/public/app.tsx +79 -25
  140. package/src/serve/public/components/AddCollectionDialog.tsx +382 -0
  141. package/src/serve/public/components/CaptureButton.tsx +60 -0
  142. package/src/serve/public/components/CaptureModal.tsx +365 -0
  143. package/src/serve/public/components/IndexingProgress.tsx +333 -0
  144. package/src/serve/public/components/ShortcutHelpModal.tsx +106 -0
  145. package/src/serve/public/components/ai-elements/code-block.tsx +42 -32
  146. package/src/serve/public/components/ai-elements/conversation.tsx +16 -14
  147. package/src/serve/public/components/ai-elements/inline-citation.tsx +33 -32
  148. package/src/serve/public/components/ai-elements/loader.tsx +5 -4
  149. package/src/serve/public/components/ai-elements/message.tsx +39 -37
  150. package/src/serve/public/components/ai-elements/prompt-input.tsx +97 -95
  151. package/src/serve/public/components/ai-elements/sources.tsx +12 -10
  152. package/src/serve/public/components/ai-elements/suggestion.tsx +10 -9
  153. package/src/serve/public/components/editor/CodeMirrorEditor.tsx +142 -0
  154. package/src/serve/public/components/editor/MarkdownPreview.tsx +311 -0
  155. package/src/serve/public/components/editor/index.ts +6 -0
  156. package/src/serve/public/components/preset-selector.tsx +29 -28
  157. package/src/serve/public/components/ui/badge.tsx +13 -12
  158. package/src/serve/public/components/ui/button-group.tsx +13 -12
  159. package/src/serve/public/components/ui/button.tsx +23 -22
  160. package/src/serve/public/components/ui/card.tsx +16 -16
  161. package/src/serve/public/components/ui/carousel.tsx +36 -35
  162. package/src/serve/public/components/ui/collapsible.tsx +1 -1
  163. package/src/serve/public/components/ui/command.tsx +17 -15
  164. package/src/serve/public/components/ui/dialog.tsx +13 -12
  165. package/src/serve/public/components/ui/dropdown-menu.tsx +13 -12
  166. package/src/serve/public/components/ui/hover-card.tsx +6 -5
  167. package/src/serve/public/components/ui/input-group.tsx +45 -43
  168. package/src/serve/public/components/ui/input.tsx +6 -6
  169. package/src/serve/public/components/ui/progress.tsx +5 -4
  170. package/src/serve/public/components/ui/scroll-area.tsx +11 -10
  171. package/src/serve/public/components/ui/select.tsx +19 -18
  172. package/src/serve/public/components/ui/separator.tsx +6 -5
  173. package/src/serve/public/components/ui/table.tsx +18 -18
  174. package/src/serve/public/components/ui/textarea.tsx +4 -4
  175. package/src/serve/public/components/ui/tooltip.tsx +5 -4
  176. package/src/serve/public/globals.css +27 -4
  177. package/src/serve/public/hooks/use-api.ts +8 -8
  178. package/src/serve/public/hooks/useCaptureModal.tsx +83 -0
  179. package/src/serve/public/hooks/useKeyboardShortcuts.ts +85 -0
  180. package/src/serve/public/index.html +4 -4
  181. package/src/serve/public/lib/utils.ts +6 -0
  182. package/src/serve/public/pages/Ask.tsx +27 -26
  183. package/src/serve/public/pages/Browse.tsx +28 -27
  184. package/src/serve/public/pages/Collections.tsx +439 -0
  185. package/src/serve/public/pages/Dashboard.tsx +166 -40
  186. package/src/serve/public/pages/DocView.tsx +258 -73
  187. package/src/serve/public/pages/DocumentEditor.tsx +510 -0
  188. package/src/serve/public/pages/Search.tsx +80 -58
  189. package/src/serve/routes/api.ts +272 -155
  190. package/src/serve/security.ts +4 -4
  191. package/src/serve/server.ts +66 -48
  192. package/src/store/index.ts +5 -5
  193. package/src/store/migrations/001-initial.ts +24 -23
  194. package/src/store/migrations/002-documents-fts.ts +7 -6
  195. package/src/store/migrations/index.ts +4 -4
  196. package/src/store/migrations/runner.ts +17 -15
  197. package/src/store/sqlite/adapter.ts +123 -121
  198. package/src/store/sqlite/fts5-snowball.ts +24 -23
  199. package/src/store/sqlite/index.ts +1 -1
  200. package/src/store/sqlite/setup.ts +12 -12
  201. package/src/store/sqlite/types.ts +4 -4
  202. package/src/store/types.ts +19 -19
  203. package/src/store/vector/index.ts +3 -3
  204. package/src/store/vector/sqlite-vec.ts +23 -20
  205. package/src/store/vector/stats.ts +10 -8
  206. package/src/store/vector/types.ts +2 -2
  207. package/vendor/fts5-snowball/README.md +6 -6
  208. package/assets/screenshots/webui-ask-answer.jpg +0 -0
  209. package/assets/screenshots/webui-home.jpg +0 -0
@@ -1,4 +1,5 @@
1
- import type { ChatStatus, FileUIPart } from 'ai';
1
+ import type { ChatStatus, FileUIPart } from "ai";
2
+
2
3
  import {
3
4
  CornerDownLeftIcon,
4
5
  ImageIcon,
@@ -8,8 +9,8 @@ import {
8
9
  PlusIcon,
9
10
  SquareIcon,
10
11
  XIcon,
11
- } from 'lucide-react';
12
- import { nanoid } from 'nanoid';
12
+ } from "lucide-react";
13
+ import { nanoid } from "nanoid";
13
14
  import {
14
15
  type ChangeEvent,
15
16
  type ChangeEventHandler,
@@ -31,9 +32,10 @@ import {
31
32
  useMemo,
32
33
  useRef,
33
34
  useState,
34
- } from 'react';
35
- import { cn } from '../../lib/utils';
36
- import { Button } from '../ui/button';
35
+ } from "react";
36
+
37
+ import { cn } from "../../lib/utils";
38
+ import { Button } from "../ui/button";
37
39
  import {
38
40
  Command,
39
41
  CommandEmpty,
@@ -42,31 +44,31 @@ import {
42
44
  CommandItem,
43
45
  CommandList,
44
46
  CommandSeparator,
45
- } from '../ui/command';
47
+ } from "../ui/command";
46
48
  import {
47
49
  DropdownMenu,
48
50
  DropdownMenuContent,
49
51
  DropdownMenuItem,
50
52
  DropdownMenuTrigger,
51
- } from '../ui/dropdown-menu';
53
+ } from "../ui/dropdown-menu";
52
54
  import {
53
55
  HoverCard,
54
56
  HoverCardContent,
55
57
  HoverCardTrigger,
56
- } from '../ui/hover-card';
58
+ } from "../ui/hover-card";
57
59
  import {
58
60
  InputGroup,
59
61
  InputGroupAddon,
60
62
  InputGroupButton,
61
63
  InputGroupTextarea,
62
- } from '../ui/input-group';
64
+ } from "../ui/input-group";
63
65
  import {
64
66
  Select,
65
67
  SelectContent,
66
68
  SelectItem,
67
69
  SelectTrigger,
68
70
  SelectValue,
69
- } from '../ui/select';
71
+ } from "../ui/select";
70
72
 
71
73
  // ============================================================================
72
74
  // Provider Context & Types
@@ -108,7 +110,7 @@ export const usePromptInputController = () => {
108
110
  const ctx = useContext(PromptInputController);
109
111
  if (!ctx) {
110
112
  throw new Error(
111
- 'Wrap your component inside <PromptInputProvider> to use usePromptInputController().'
113
+ "Wrap your component inside <PromptInputProvider> to use usePromptInputController()."
112
114
  );
113
115
  }
114
116
  return ctx;
@@ -122,7 +124,7 @@ export const useProviderAttachments = () => {
122
124
  const ctx = useContext(ProviderAttachmentsContext);
123
125
  if (!ctx) {
124
126
  throw new Error(
125
- 'Wrap your component inside <PromptInputProvider> to use useProviderAttachments().'
127
+ "Wrap your component inside <PromptInputProvider> to use useProviderAttachments()."
126
128
  );
127
129
  }
128
130
  return ctx;
@@ -140,12 +142,12 @@ export type PromptInputProviderProps = PropsWithChildren<{
140
142
  * If you don't use it, PromptInput stays fully self-managed.
141
143
  */
142
144
  export function PromptInputProvider({
143
- initialInput: initialTextInput = '',
145
+ initialInput: initialTextInput = "",
144
146
  children,
145
147
  }: PromptInputProviderProps) {
146
148
  // ----- textInput state
147
149
  const [textInput, setTextInput] = useState(initialTextInput);
148
- const clearInput = useCallback(() => setTextInput(''), []);
150
+ const clearInput = useCallback(() => setTextInput(""), []);
149
151
 
150
152
  // ----- attachments state (global when wrapped)
151
153
  const [attachmentFiles, setAttachmentFiles] = useState<
@@ -164,7 +166,7 @@ export function PromptInputProvider({
164
166
  prev.concat(
165
167
  incoming.map((file) => ({
166
168
  id: nanoid(),
167
- type: 'file' as const,
169
+ type: "file" as const,
168
170
  url: URL.createObjectURL(file),
169
171
  mediaType: file.type,
170
172
  filename: file.name,
@@ -268,7 +270,7 @@ export const usePromptInputAttachments = () => {
268
270
  const context = provider ?? local;
269
271
  if (!context) {
270
272
  throw new Error(
271
- 'usePromptInputAttachments must be used within a PromptInput or PromptInputProvider'
273
+ "usePromptInputAttachments must be used within a PromptInput or PromptInputProvider"
272
274
  );
273
275
  }
274
276
  return context;
@@ -286,20 +288,20 @@ export function PromptInputAttachment({
286
288
  }: PromptInputAttachmentProps) {
287
289
  const attachments = usePromptInputAttachments();
288
290
 
289
- const filename = data.filename || '';
291
+ const filename = data.filename || "";
290
292
 
291
293
  const mediaType =
292
- data.mediaType?.startsWith('image/') && data.url ? 'image' : 'file';
293
- const isImage = mediaType === 'image';
294
+ data.mediaType?.startsWith("image/") && data.url ? "image" : "file";
295
+ const isImage = mediaType === "image";
294
296
 
295
- const attachmentLabel = filename || (isImage ? 'Image' : 'Attachment');
297
+ const attachmentLabel = filename || (isImage ? "Image" : "Attachment");
296
298
 
297
299
  return (
298
300
  <PromptInputHoverCard>
299
301
  <HoverCardTrigger asChild>
300
302
  <div
301
303
  className={cn(
302
- 'group relative flex h-8 cursor-pointer select-none items-center gap-1.5 rounded-md border border-border px-1.5 font-medium text-sm transition-all hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
304
+ "group relative flex h-8 cursor-pointer select-none items-center gap-1.5 rounded-md border border-border px-1.5 font-medium text-sm transition-all hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
303
305
  className
304
306
  )}
305
307
  key={data.id}
@@ -309,7 +311,7 @@ export function PromptInputAttachment({
309
311
  <div className="absolute inset-0 flex size-5 items-center justify-center overflow-hidden rounded bg-background transition-opacity group-hover:opacity-0">
310
312
  {isImage ? (
311
313
  <img
312
- alt={filename || 'attachment'}
314
+ alt={filename || "attachment"}
313
315
  className="size-5 object-cover"
314
316
  height={20}
315
317
  src={data.url}
@@ -344,7 +346,7 @@ export function PromptInputAttachment({
344
346
  {isImage && (
345
347
  <div className="flex max-h-96 w-96 items-center justify-center overflow-hidden rounded-md border">
346
348
  <img
347
- alt={filename || 'attachment preview'}
349
+ alt={filename || "attachment preview"}
348
350
  className="max-h-full max-w-full object-contain"
349
351
  height={384}
350
352
  src={data.url}
@@ -355,7 +357,7 @@ export function PromptInputAttachment({
355
357
  <div className="flex items-center gap-2.5">
356
358
  <div className="min-w-0 flex-1 space-y-1 px-0.5">
357
359
  <h4 className="truncate font-semibold text-sm leading-none">
358
- {filename || (isImage ? 'Image' : 'Attachment')}
360
+ {filename || (isImage ? "Image" : "Attachment")}
359
361
  </h4>
360
362
  {data.mediaType && (
361
363
  <p className="truncate font-mono text-muted-foreground text-xs">
@@ -372,7 +374,7 @@ export function PromptInputAttachment({
372
374
 
373
375
  export type PromptInputAttachmentsProps = Omit<
374
376
  HTMLAttributes<HTMLDivElement>,
375
- 'children'
377
+ "children"
376
378
  > & {
377
379
  children: (attachment: FileUIPart & { id: string }) => ReactNode;
378
380
  };
@@ -390,7 +392,7 @@ export function PromptInputAttachments({
390
392
 
391
393
  return (
392
394
  <div
393
- className={cn('flex w-full flex-wrap items-center gap-2 p-3', className)}
395
+ className={cn("flex w-full flex-wrap items-center gap-2 p-3", className)}
394
396
  {...props}
395
397
  >
396
398
  {attachments.files.map((file) => (
@@ -407,7 +409,7 @@ export type PromptInputActionAddAttachmentsProps = ComponentProps<
407
409
  };
408
410
 
409
411
  export const PromptInputActionAddAttachments = ({
410
- label = 'Add photos or files',
412
+ label = "Add photos or files",
411
413
  ...props
412
414
  }: PromptInputActionAddAttachmentsProps) => {
413
415
  const attachments = usePromptInputAttachments();
@@ -432,7 +434,7 @@ export interface PromptInputMessage {
432
434
 
433
435
  export type PromptInputProps = Omit<
434
436
  HTMLAttributes<HTMLFormElement>,
435
- 'onSubmit' | 'onError'
437
+ "onSubmit" | "onError"
436
438
  > & {
437
439
  accept?: string; // e.g., "image/*" or leave undefined for any
438
440
  multiple?: boolean;
@@ -444,7 +446,7 @@ export type PromptInputProps = Omit<
444
446
  maxFiles?: number;
445
447
  maxFileSize?: number; // bytes
446
448
  onError?: (err: {
447
- code: 'max_files' | 'max_file_size' | 'accept';
449
+ code: "max_files" | "max_file_size" | "accept";
448
450
  message: string;
449
451
  }) => void;
450
452
  onSubmit: (
@@ -488,17 +490,17 @@ export const PromptInput = ({
488
490
 
489
491
  const matchesAccept = useCallback(
490
492
  (f: File) => {
491
- if (!accept || accept.trim() === '') {
493
+ if (!accept || accept.trim() === "") {
492
494
  return true;
493
495
  }
494
496
 
495
497
  const patterns = accept
496
- .split(',')
498
+ .split(",")
497
499
  .map((s) => s.trim())
498
500
  .filter(Boolean);
499
501
 
500
502
  return patterns.some((pattern) => {
501
- if (pattern.endsWith('/*')) {
503
+ if (pattern.endsWith("/*")) {
502
504
  const prefix = pattern.slice(0, -1); // e.g: image/* -> image/
503
505
  return f.type.startsWith(prefix);
504
506
  }
@@ -514,8 +516,8 @@ export const PromptInput = ({
514
516
  const accepted = incoming.filter((f) => matchesAccept(f));
515
517
  if (incoming.length && accepted.length === 0) {
516
518
  onError?.({
517
- code: 'accept',
518
- message: 'No files match the accepted types.',
519
+ code: "accept",
520
+ message: "No files match the accepted types.",
519
521
  });
520
522
  return;
521
523
  }
@@ -524,30 +526,30 @@ export const PromptInput = ({
524
526
  const sized = accepted.filter(withinSize);
525
527
  if (accepted.length > 0 && sized.length === 0) {
526
528
  onError?.({
527
- code: 'max_file_size',
528
- message: 'All files exceed the maximum size.',
529
+ code: "max_file_size",
530
+ message: "All files exceed the maximum size.",
529
531
  });
530
532
  return;
531
533
  }
532
534
 
533
535
  setItems((prev) => {
534
536
  const capacity =
535
- typeof maxFiles === 'number'
537
+ typeof maxFiles === "number"
536
538
  ? Math.max(0, maxFiles - prev.length)
537
539
  : undefined;
538
540
  const capped =
539
- typeof capacity === 'number' ? sized.slice(0, capacity) : sized;
540
- if (typeof capacity === 'number' && sized.length > capacity) {
541
+ typeof capacity === "number" ? sized.slice(0, capacity) : sized;
542
+ if (typeof capacity === "number" && sized.length > capacity) {
541
543
  onError?.({
542
- code: 'max_files',
543
- message: 'Too many files. Some were not added.',
544
+ code: "max_files",
545
+ message: "Too many files. Some were not added.",
544
546
  });
545
547
  }
546
548
  const next: (FileUIPart & { id: string })[] = [];
547
549
  for (const file of capped) {
548
550
  next.push({
549
551
  id: nanoid(),
550
- type: 'file',
552
+ type: "file",
551
553
  url: URL.createObjectURL(file),
552
554
  mediaType: file.type,
553
555
  filename: file.name,
@@ -603,7 +605,7 @@ export const PromptInput = ({
603
605
  // The syncHiddenInput prop is no longer functional
604
606
  useEffect(() => {
605
607
  if (syncHiddenInput && inputRef.current && files.length === 0) {
606
- inputRef.current.value = '';
608
+ inputRef.current.value = "";
607
609
  }
608
610
  }, [files, syncHiddenInput]);
609
611
 
@@ -618,23 +620,23 @@ export const PromptInput = ({
618
620
  }
619
621
 
620
622
  const onDragOver = (e: DragEvent) => {
621
- if (e.dataTransfer?.types?.includes('Files')) {
623
+ if (e.dataTransfer?.types?.includes("Files")) {
622
624
  e.preventDefault();
623
625
  }
624
626
  };
625
627
  const onDrop = (e: DragEvent) => {
626
- if (e.dataTransfer?.types?.includes('Files')) {
628
+ if (e.dataTransfer?.types?.includes("Files")) {
627
629
  e.preventDefault();
628
630
  }
629
631
  if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {
630
632
  add(e.dataTransfer.files);
631
633
  }
632
634
  };
633
- form.addEventListener('dragover', onDragOver);
634
- form.addEventListener('drop', onDrop);
635
+ form.addEventListener("dragover", onDragOver);
636
+ form.addEventListener("drop", onDrop);
635
637
  return () => {
636
- form.removeEventListener('dragover', onDragOver);
637
- form.removeEventListener('drop', onDrop);
638
+ form.removeEventListener("dragover", onDragOver);
639
+ form.removeEventListener("drop", onDrop);
638
640
  };
639
641
  }, [add, globalDrop]);
640
642
 
@@ -644,23 +646,23 @@ export const PromptInput = ({
644
646
  }
645
647
 
646
648
  const onDragOver = (e: DragEvent) => {
647
- if (e.dataTransfer?.types?.includes('Files')) {
649
+ if (e.dataTransfer?.types?.includes("Files")) {
648
650
  e.preventDefault();
649
651
  }
650
652
  };
651
653
  const onDrop = (e: DragEvent) => {
652
- if (e.dataTransfer?.types?.includes('Files')) {
654
+ if (e.dataTransfer?.types?.includes("Files")) {
653
655
  e.preventDefault();
654
656
  }
655
657
  if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {
656
658
  add(e.dataTransfer.files);
657
659
  }
658
660
  };
659
- document.addEventListener('dragover', onDragOver);
660
- document.addEventListener('drop', onDrop);
661
+ document.addEventListener("dragover", onDragOver);
662
+ document.addEventListener("drop", onDrop);
661
663
  return () => {
662
- document.removeEventListener('dragover', onDragOver);
663
- document.removeEventListener('drop', onDrop);
664
+ document.removeEventListener("dragover", onDragOver);
665
+ document.removeEventListener("drop", onDrop);
664
666
  };
665
667
  }, [add, globalDrop]);
666
668
 
@@ -683,7 +685,7 @@ export const PromptInput = ({
683
685
  add(event.currentTarget.files);
684
686
  }
685
687
  // Reset input value to allow selecting files that were previously removed
686
- event.currentTarget.value = '';
688
+ event.currentTarget.value = "";
687
689
  };
688
690
 
689
691
  const convertBlobUrlToDataUrl = async (
@@ -723,7 +725,7 @@ export const PromptInput = ({
723
725
  ? controller.textInput.value
724
726
  : (() => {
725
727
  const formData = new FormData(form);
726
- return (formData.get('message') as string) || '';
728
+ return (formData.get("message") as string) || "";
727
729
  })();
728
730
 
729
731
  // Reset form immediately after capturing text to avoid race condition
@@ -735,7 +737,7 @@ export const PromptInput = ({
735
737
  // Convert blob URLs to data URLs asynchronously
736
738
  Promise.all(
737
739
  files.map(async ({ id, ...item }) => {
738
- if (item.url?.startsWith('blob:')) {
740
+ if (item.url?.startsWith("blob:")) {
739
741
  const dataUrl = await convertBlobUrlToDataUrl(item.url);
740
742
  // If conversion failed, keep the original blob URL
741
743
  return {
@@ -792,7 +794,7 @@ export const PromptInput = ({
792
794
  type="file"
793
795
  />
794
796
  <form
795
- className={cn('w-full', className)}
797
+ className={cn("w-full", className)}
796
798
  onSubmit={handleSubmit}
797
799
  ref={formRef}
798
800
  {...props}
@@ -817,7 +819,7 @@ export const PromptInputBody = ({
817
819
  className,
818
820
  ...props
819
821
  }: PromptInputBodyProps) => (
820
- <div className={cn('contents', className)} {...props} />
822
+ <div className={cn("contents", className)} {...props} />
821
823
  );
822
824
 
823
825
  export type PromptInputTextareaProps = ComponentProps<
@@ -827,7 +829,7 @@ export type PromptInputTextareaProps = ComponentProps<
827
829
  export const PromptInputTextarea = ({
828
830
  onChange,
829
831
  className,
830
- placeholder = 'What would you like to know?',
832
+ placeholder = "What would you like to know?",
831
833
  ...props
832
834
  }: PromptInputTextareaProps) => {
833
835
  const controller = useOptionalPromptInputController();
@@ -835,7 +837,7 @@ export const PromptInputTextarea = ({
835
837
  const [isComposing, setIsComposing] = useState(false);
836
838
 
837
839
  const handleKeyDown: KeyboardEventHandler<HTMLTextAreaElement> = (e) => {
838
- if (e.key === 'Enter') {
840
+ if (e.key === "Enter") {
839
841
  if (isComposing || e.nativeEvent.isComposing) {
840
842
  return;
841
843
  }
@@ -858,8 +860,8 @@ export const PromptInputTextarea = ({
858
860
 
859
861
  // Remove last attachment when Backspace is pressed and textarea is empty
860
862
  if (
861
- e.key === 'Backspace' &&
862
- e.currentTarget.value === '' &&
863
+ e.key === "Backspace" &&
864
+ e.currentTarget.value === "" &&
863
865
  attachments.files.length > 0
864
866
  ) {
865
867
  e.preventDefault();
@@ -880,7 +882,7 @@ export const PromptInputTextarea = ({
880
882
  const files: File[] = [];
881
883
 
882
884
  for (const item of items) {
883
- if (item.kind === 'file') {
885
+ if (item.kind === "file") {
884
886
  const file = item.getAsFile();
885
887
  if (file) {
886
888
  files.push(file);
@@ -908,7 +910,7 @@ export const PromptInputTextarea = ({
908
910
 
909
911
  return (
910
912
  <InputGroupTextarea
911
- className={cn('field-sizing-content max-h-48 min-h-16', className)}
913
+ className={cn("field-sizing-content max-h-48 min-h-16", className)}
912
914
  name="message"
913
915
  onCompositionEnd={() => setIsComposing(false)}
914
916
  onCompositionStart={() => setIsComposing(true)}
@@ -923,7 +925,7 @@ export const PromptInputTextarea = ({
923
925
 
924
926
  export type PromptInputHeaderProps = Omit<
925
927
  ComponentProps<typeof InputGroupAddon>,
926
- 'align'
928
+ "align"
927
929
  >;
928
930
 
929
931
  export const PromptInputHeader = ({
@@ -932,14 +934,14 @@ export const PromptInputHeader = ({
932
934
  }: PromptInputHeaderProps) => (
933
935
  <InputGroupAddon
934
936
  align="block-end"
935
- className={cn('order-first flex-wrap gap-1', className)}
937
+ className={cn("order-first flex-wrap gap-1", className)}
936
938
  {...props}
937
939
  />
938
940
  );
939
941
 
940
942
  export type PromptInputFooterProps = Omit<
941
943
  ComponentProps<typeof InputGroupAddon>,
942
- 'align'
944
+ "align"
943
945
  >;
944
946
 
945
947
  export const PromptInputFooter = ({
@@ -948,7 +950,7 @@ export const PromptInputFooter = ({
948
950
  }: PromptInputFooterProps) => (
949
951
  <InputGroupAddon
950
952
  align="block-end"
951
- className={cn('justify-between gap-1', className)}
953
+ className={cn("justify-between gap-1", className)}
952
954
  {...props}
953
955
  />
954
956
  );
@@ -959,19 +961,19 @@ export const PromptInputTools = ({
959
961
  className,
960
962
  ...props
961
963
  }: PromptInputToolsProps) => (
962
- <div className={cn('flex items-center gap-1', className)} {...props} />
964
+ <div className={cn("flex items-center gap-1", className)} {...props} />
963
965
  );
964
966
 
965
967
  export type PromptInputButtonProps = ComponentProps<typeof InputGroupButton>;
966
968
 
967
969
  export const PromptInputButton = ({
968
- variant = 'ghost',
970
+ variant = "ghost",
969
971
  className,
970
972
  size,
971
973
  ...props
972
974
  }: PromptInputButtonProps) => {
973
975
  const newSize =
974
- size ?? (Children.count(props.children) > 1 ? 'sm' : 'icon-sm');
976
+ size ?? (Children.count(props.children) > 1 ? "sm" : "icon-sm");
975
977
 
976
978
  return (
977
979
  <InputGroupButton
@@ -1032,19 +1034,19 @@ export type PromptInputSubmitProps = ComponentProps<typeof InputGroupButton> & {
1032
1034
 
1033
1035
  export const PromptInputSubmit = ({
1034
1036
  className,
1035
- variant = 'default',
1036
- size = 'icon-sm',
1037
+ variant = "default",
1038
+ size = "icon-sm",
1037
1039
  status,
1038
1040
  children,
1039
1041
  ...props
1040
1042
  }: PromptInputSubmitProps) => {
1041
1043
  let Icon = <CornerDownLeftIcon className="size-4" />;
1042
1044
 
1043
- if (status === 'submitted') {
1045
+ if (status === "submitted") {
1044
1046
  Icon = <Loader2Icon className="size-4 animate-spin" />;
1045
- } else if (status === 'streaming') {
1047
+ } else if (status === "streaming") {
1046
1048
  Icon = <SquareIcon className="size-4" />;
1047
- } else if (status === 'error') {
1049
+ } else if (status === "error") {
1048
1050
  Icon = <XIcon className="size-4" />;
1049
1051
  }
1050
1052
 
@@ -1137,8 +1139,8 @@ export const PromptInputSpeechButton = ({
1137
1139
 
1138
1140
  useEffect(() => {
1139
1141
  if (
1140
- typeof window !== 'undefined' &&
1141
- ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window)
1142
+ typeof window !== "undefined" &&
1143
+ ("SpeechRecognition" in window || "webkitSpeechRecognition" in window)
1142
1144
  ) {
1143
1145
  const SpeechRecognition =
1144
1146
  window.SpeechRecognition || window.webkitSpeechRecognition;
@@ -1146,7 +1148,7 @@ export const PromptInputSpeechButton = ({
1146
1148
 
1147
1149
  speechRecognition.continuous = true;
1148
1150
  speechRecognition.interimResults = true;
1149
- speechRecognition.lang = 'en-US';
1151
+ speechRecognition.lang = "en-US";
1150
1152
 
1151
1153
  speechRecognition.onstart = () => {
1152
1154
  setIsListening(true);
@@ -1157,12 +1159,12 @@ export const PromptInputSpeechButton = ({
1157
1159
  };
1158
1160
 
1159
1161
  speechRecognition.onresult = (event) => {
1160
- let finalTranscript = '';
1162
+ let finalTranscript = "";
1161
1163
 
1162
1164
  for (let i = event.resultIndex; i < event.results.length; i++) {
1163
1165
  const result = event.results[i];
1164
1166
  if (result.isFinal) {
1165
- finalTranscript += result[0]?.transcript ?? '';
1167
+ finalTranscript += result[0]?.transcript ?? "";
1166
1168
  }
1167
1169
  }
1168
1170
 
@@ -1170,16 +1172,16 @@ export const PromptInputSpeechButton = ({
1170
1172
  const textarea = textareaRef.current;
1171
1173
  const currentValue = textarea.value;
1172
1174
  const newValue =
1173
- currentValue + (currentValue ? ' ' : '') + finalTranscript;
1175
+ currentValue + (currentValue ? " " : "") + finalTranscript;
1174
1176
 
1175
1177
  textarea.value = newValue;
1176
- textarea.dispatchEvent(new Event('input', { bubbles: true }));
1178
+ textarea.dispatchEvent(new Event("input", { bubbles: true }));
1177
1179
  onTranscriptionChange?.(newValue);
1178
1180
  }
1179
1181
  };
1180
1182
 
1181
1183
  speechRecognition.onerror = (event) => {
1182
- console.error('Speech recognition error:', event.error);
1184
+ console.error("Speech recognition error:", event.error);
1183
1185
  setIsListening(false);
1184
1186
  };
1185
1187
 
@@ -1209,8 +1211,8 @@ export const PromptInputSpeechButton = ({
1209
1211
  return (
1210
1212
  <PromptInputButton
1211
1213
  className={cn(
1212
- 'relative transition-all duration-200',
1213
- isListening && 'animate-pulse bg-accent text-accent-foreground',
1214
+ "relative transition-all duration-200",
1215
+ isListening && "animate-pulse bg-accent text-accent-foreground",
1214
1216
  className
1215
1217
  )}
1216
1218
  disabled={!recognition}
@@ -1238,8 +1240,8 @@ export const PromptInputSelectTrigger = ({
1238
1240
  }: PromptInputSelectTriggerProps) => (
1239
1241
  <SelectTrigger
1240
1242
  className={cn(
1241
- 'border-none bg-transparent font-medium text-muted-foreground shadow-none transition-colors',
1242
- 'hover:bg-accent hover:text-foreground aria-expanded:bg-accent aria-expanded:text-foreground',
1243
+ "border-none bg-transparent font-medium text-muted-foreground shadow-none transition-colors",
1244
+ "hover:bg-accent hover:text-foreground aria-expanded:bg-accent aria-expanded:text-foreground",
1243
1245
  className
1244
1246
  )}
1245
1247
  {...props}
@@ -1298,7 +1300,7 @@ export type PromptInputHoverCardContentProps = ComponentProps<
1298
1300
  >;
1299
1301
 
1300
1302
  export const PromptInputHoverCardContent = ({
1301
- align = 'start',
1303
+ align = "start",
1302
1304
  ...props
1303
1305
  }: PromptInputHoverCardContentProps) => (
1304
1306
  <HoverCardContent align={align} {...props} />
@@ -1326,7 +1328,7 @@ export const PromptInputTabLabel = ({
1326
1328
  }: PromptInputTabLabelProps) => (
1327
1329
  <h3
1328
1330
  className={cn(
1329
- 'mb-2 px-3 font-medium text-muted-foreground text-xs',
1331
+ "mb-2 px-3 font-medium text-muted-foreground text-xs",
1330
1332
  className
1331
1333
  )}
1332
1334
  {...props}
@@ -1339,7 +1341,7 @@ export const PromptInputTabBody = ({
1339
1341
  className,
1340
1342
  ...props
1341
1343
  }: PromptInputTabBodyProps) => (
1342
- <div className={cn('space-y-1', className)} {...props} />
1344
+ <div className={cn("space-y-1", className)} {...props} />
1343
1345
  );
1344
1346
 
1345
1347
  export type PromptInputTabItemProps = HTMLAttributes<HTMLDivElement>;
@@ -1350,7 +1352,7 @@ export const PromptInputTabItem = ({
1350
1352
  }: PromptInputTabItemProps) => (
1351
1353
  <div
1352
1354
  className={cn(
1353
- 'flex items-center gap-2 px-3 py-2 text-xs hover:bg-accent',
1355
+ "flex items-center gap-2 px-3 py-2 text-xs hover:bg-accent",
1354
1356
  className
1355
1357
  )}
1356
1358
  {...props}