@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.
- package/LICENSE +21 -0
- package/README.md +90 -50
- package/THIRD_PARTY_NOTICES.md +22 -0
- package/assets/screenshots/webui-ask-answer.png +0 -0
- package/assets/screenshots/webui-collections.png +0 -0
- package/assets/screenshots/webui-editor.png +0 -0
- package/assets/screenshots/webui-home.png +0 -0
- package/assets/skill/SKILL.md +12 -12
- package/assets/skill/cli-reference.md +59 -57
- package/assets/skill/examples.md +8 -7
- package/assets/skill/mcp-reference.md +8 -4
- package/package.json +31 -24
- package/src/app/constants.ts +43 -42
- package/src/cli/colors.ts +1 -1
- package/src/cli/commands/ask.ts +44 -43
- package/src/cli/commands/cleanup.ts +9 -8
- package/src/cli/commands/collection/add.ts +12 -12
- package/src/cli/commands/collection/index.ts +4 -4
- package/src/cli/commands/collection/list.ts +26 -25
- package/src/cli/commands/collection/remove.ts +10 -10
- package/src/cli/commands/collection/rename.ts +10 -10
- package/src/cli/commands/context/add.ts +1 -1
- package/src/cli/commands/context/check.ts +17 -17
- package/src/cli/commands/context/index.ts +4 -4
- package/src/cli/commands/context/list.ts +11 -11
- package/src/cli/commands/context/rm.ts +1 -1
- package/src/cli/commands/doctor.ts +86 -84
- package/src/cli/commands/embed.ts +30 -28
- package/src/cli/commands/get.ts +27 -26
- package/src/cli/commands/index-cmd.ts +9 -9
- package/src/cli/commands/index.ts +16 -16
- package/src/cli/commands/init.ts +13 -12
- package/src/cli/commands/ls.ts +20 -19
- package/src/cli/commands/mcp/config.ts +30 -28
- package/src/cli/commands/mcp/index.ts +4 -4
- package/src/cli/commands/mcp/install.ts +17 -17
- package/src/cli/commands/mcp/paths.ts +133 -133
- package/src/cli/commands/mcp/status.ts +21 -21
- package/src/cli/commands/mcp/uninstall.ts +13 -13
- package/src/cli/commands/mcp.ts +2 -2
- package/src/cli/commands/models/clear.ts +12 -11
- package/src/cli/commands/models/index.ts +5 -5
- package/src/cli/commands/models/list.ts +31 -30
- package/src/cli/commands/models/path.ts +1 -1
- package/src/cli/commands/models/pull.ts +19 -18
- package/src/cli/commands/models/use.ts +4 -4
- package/src/cli/commands/multi-get.ts +38 -36
- package/src/cli/commands/query.ts +21 -20
- package/src/cli/commands/ref-parser.ts +10 -10
- package/src/cli/commands/reset.ts +40 -39
- package/src/cli/commands/search.ts +14 -13
- package/src/cli/commands/serve.ts +4 -4
- package/src/cli/commands/shared.ts +11 -10
- package/src/cli/commands/skill/index.ts +5 -5
- package/src/cli/commands/skill/install.ts +18 -17
- package/src/cli/commands/skill/paths-cmd.ts +11 -10
- package/src/cli/commands/skill/paths.ts +23 -23
- package/src/cli/commands/skill/show.ts +13 -12
- package/src/cli/commands/skill/uninstall.ts +16 -15
- package/src/cli/commands/status.ts +25 -24
- package/src/cli/commands/update.ts +3 -3
- package/src/cli/commands/vsearch.ts +17 -16
- package/src/cli/context.ts +5 -5
- package/src/cli/errors.ts +3 -3
- package/src/cli/format/search-results.ts +37 -37
- package/src/cli/options.ts +43 -43
- package/src/cli/program.ts +455 -459
- package/src/cli/progress.ts +1 -1
- package/src/cli/run.ts +24 -23
- package/src/collection/add.ts +9 -8
- package/src/collection/index.ts +3 -3
- package/src/collection/remove.ts +7 -6
- package/src/collection/types.ts +6 -6
- package/src/config/defaults.ts +1 -1
- package/src/config/index.ts +5 -5
- package/src/config/loader.ts +19 -18
- package/src/config/paths.ts +9 -8
- package/src/config/saver.ts +14 -13
- package/src/config/types.ts +53 -52
- package/src/converters/adapters/markitdownTs/adapter.ts +21 -19
- package/src/converters/adapters/officeparser/adapter.ts +18 -16
- package/src/converters/canonicalize.ts +12 -12
- package/src/converters/errors.ts +26 -22
- package/src/converters/index.ts +8 -8
- package/src/converters/mime.ts +25 -25
- package/src/converters/native/markdown.ts +10 -9
- package/src/converters/native/plaintext.ts +8 -7
- package/src/converters/path.ts +2 -2
- package/src/converters/pipeline.ts +11 -10
- package/src/converters/registry.ts +8 -8
- package/src/converters/types.ts +14 -14
- package/src/converters/versions.ts +4 -4
- package/src/index.ts +4 -4
- package/src/ingestion/chunker.ts +10 -9
- package/src/ingestion/index.ts +6 -6
- package/src/ingestion/language.ts +62 -62
- package/src/ingestion/sync.ts +50 -49
- package/src/ingestion/types.ts +10 -10
- package/src/ingestion/walker.ts +14 -13
- package/src/llm/cache.ts +51 -49
- package/src/llm/errors.ts +40 -36
- package/src/llm/index.ts +9 -9
- package/src/llm/lockfile.ts +6 -6
- package/src/llm/nodeLlamaCpp/adapter.ts +13 -12
- package/src/llm/nodeLlamaCpp/embedding.ts +9 -8
- package/src/llm/nodeLlamaCpp/generation.ts +7 -6
- package/src/llm/nodeLlamaCpp/lifecycle.ts +11 -10
- package/src/llm/nodeLlamaCpp/rerank.ts +6 -5
- package/src/llm/policy.ts +5 -5
- package/src/llm/registry.ts +6 -5
- package/src/llm/types.ts +2 -2
- package/src/mcp/resources/index.ts +15 -13
- package/src/mcp/server.ts +25 -23
- package/src/mcp/tools/get.ts +25 -23
- package/src/mcp/tools/index.ts +32 -29
- package/src/mcp/tools/multi-get.ts +34 -32
- package/src/mcp/tools/query.ts +29 -27
- package/src/mcp/tools/search.ts +14 -12
- package/src/mcp/tools/status.ts +12 -11
- package/src/mcp/tools/vsearch.ts +26 -24
- package/src/pipeline/answer.ts +9 -9
- package/src/pipeline/chunk-lookup.ts +1 -1
- package/src/pipeline/contextual.ts +4 -4
- package/src/pipeline/expansion.ts +23 -21
- package/src/pipeline/explain.ts +21 -21
- package/src/pipeline/fusion.ts +9 -9
- package/src/pipeline/hybrid.ts +41 -42
- package/src/pipeline/index.ts +10 -10
- package/src/pipeline/query-language.ts +39 -39
- package/src/pipeline/rerank.ts +8 -7
- package/src/pipeline/search.ts +22 -22
- package/src/pipeline/types.ts +8 -8
- package/src/pipeline/vsearch.ts +21 -24
- package/src/serve/CLAUDE.md +21 -15
- package/src/serve/config-sync.ts +9 -8
- package/src/serve/context.ts +19 -18
- package/src/serve/index.ts +1 -1
- package/src/serve/jobs.ts +7 -7
- package/src/serve/public/app.tsx +79 -25
- package/src/serve/public/components/AddCollectionDialog.tsx +382 -0
- package/src/serve/public/components/CaptureButton.tsx +60 -0
- package/src/serve/public/components/CaptureModal.tsx +365 -0
- package/src/serve/public/components/IndexingProgress.tsx +333 -0
- package/src/serve/public/components/ShortcutHelpModal.tsx +106 -0
- package/src/serve/public/components/ai-elements/code-block.tsx +42 -32
- package/src/serve/public/components/ai-elements/conversation.tsx +16 -14
- package/src/serve/public/components/ai-elements/inline-citation.tsx +33 -32
- package/src/serve/public/components/ai-elements/loader.tsx +5 -4
- package/src/serve/public/components/ai-elements/message.tsx +39 -37
- package/src/serve/public/components/ai-elements/prompt-input.tsx +97 -95
- package/src/serve/public/components/ai-elements/sources.tsx +12 -10
- package/src/serve/public/components/ai-elements/suggestion.tsx +10 -9
- package/src/serve/public/components/editor/CodeMirrorEditor.tsx +142 -0
- package/src/serve/public/components/editor/MarkdownPreview.tsx +311 -0
- package/src/serve/public/components/editor/index.ts +6 -0
- package/src/serve/public/components/preset-selector.tsx +29 -28
- package/src/serve/public/components/ui/badge.tsx +13 -12
- package/src/serve/public/components/ui/button-group.tsx +13 -12
- package/src/serve/public/components/ui/button.tsx +23 -22
- package/src/serve/public/components/ui/card.tsx +16 -16
- package/src/serve/public/components/ui/carousel.tsx +36 -35
- package/src/serve/public/components/ui/collapsible.tsx +1 -1
- package/src/serve/public/components/ui/command.tsx +17 -15
- package/src/serve/public/components/ui/dialog.tsx +13 -12
- package/src/serve/public/components/ui/dropdown-menu.tsx +13 -12
- package/src/serve/public/components/ui/hover-card.tsx +6 -5
- package/src/serve/public/components/ui/input-group.tsx +45 -43
- package/src/serve/public/components/ui/input.tsx +6 -6
- package/src/serve/public/components/ui/progress.tsx +5 -4
- package/src/serve/public/components/ui/scroll-area.tsx +11 -10
- package/src/serve/public/components/ui/select.tsx +19 -18
- package/src/serve/public/components/ui/separator.tsx +6 -5
- package/src/serve/public/components/ui/table.tsx +18 -18
- package/src/serve/public/components/ui/textarea.tsx +4 -4
- package/src/serve/public/components/ui/tooltip.tsx +5 -4
- package/src/serve/public/globals.css +27 -4
- package/src/serve/public/hooks/use-api.ts +8 -8
- package/src/serve/public/hooks/useCaptureModal.tsx +83 -0
- package/src/serve/public/hooks/useKeyboardShortcuts.ts +85 -0
- package/src/serve/public/index.html +4 -4
- package/src/serve/public/lib/utils.ts +6 -0
- package/src/serve/public/pages/Ask.tsx +27 -26
- package/src/serve/public/pages/Browse.tsx +28 -27
- package/src/serve/public/pages/Collections.tsx +439 -0
- package/src/serve/public/pages/Dashboard.tsx +166 -40
- package/src/serve/public/pages/DocView.tsx +258 -73
- package/src/serve/public/pages/DocumentEditor.tsx +510 -0
- package/src/serve/public/pages/Search.tsx +80 -58
- package/src/serve/routes/api.ts +272 -155
- package/src/serve/security.ts +4 -4
- package/src/serve/server.ts +66 -48
- package/src/store/index.ts +5 -5
- package/src/store/migrations/001-initial.ts +24 -23
- package/src/store/migrations/002-documents-fts.ts +7 -6
- package/src/store/migrations/index.ts +4 -4
- package/src/store/migrations/runner.ts +17 -15
- package/src/store/sqlite/adapter.ts +123 -121
- package/src/store/sqlite/fts5-snowball.ts +24 -23
- package/src/store/sqlite/index.ts +1 -1
- package/src/store/sqlite/setup.ts +12 -12
- package/src/store/sqlite/types.ts +4 -4
- package/src/store/types.ts +19 -19
- package/src/store/vector/index.ts +3 -3
- package/src/store/vector/sqlite-vec.ts +23 -20
- package/src/store/vector/stats.ts +10 -8
- package/src/store/vector/types.ts +2 -2
- package/vendor/fts5-snowball/README.md +6 -6
- package/assets/screenshots/webui-ask-answer.jpg +0 -0
- package/assets/screenshots/webui-home.jpg +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { ChatStatus, FileUIPart } from
|
|
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
|
|
12
|
-
import { nanoid } from
|
|
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
|
|
35
|
-
|
|
36
|
-
import {
|
|
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
|
|
47
|
+
} from "../ui/command";
|
|
46
48
|
import {
|
|
47
49
|
DropdownMenu,
|
|
48
50
|
DropdownMenuContent,
|
|
49
51
|
DropdownMenuItem,
|
|
50
52
|
DropdownMenuTrigger,
|
|
51
|
-
} from
|
|
53
|
+
} from "../ui/dropdown-menu";
|
|
52
54
|
import {
|
|
53
55
|
HoverCard,
|
|
54
56
|
HoverCardContent,
|
|
55
57
|
HoverCardTrigger,
|
|
56
|
-
} from
|
|
58
|
+
} from "../ui/hover-card";
|
|
57
59
|
import {
|
|
58
60
|
InputGroup,
|
|
59
61
|
InputGroupAddon,
|
|
60
62
|
InputGroupButton,
|
|
61
63
|
InputGroupTextarea,
|
|
62
|
-
} from
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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(
|
|
293
|
-
const isImage = mediaType ===
|
|
294
|
+
data.mediaType?.startsWith("image/") && data.url ? "image" : "file";
|
|
295
|
+
const isImage = mediaType === "image";
|
|
294
296
|
|
|
295
|
-
const attachmentLabel = filename || (isImage ?
|
|
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
|
-
|
|
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 ||
|
|
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 ||
|
|
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 ?
|
|
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
|
-
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
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:
|
|
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:
|
|
518
|
-
message:
|
|
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:
|
|
528
|
-
message:
|
|
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 ===
|
|
537
|
+
typeof maxFiles === "number"
|
|
536
538
|
? Math.max(0, maxFiles - prev.length)
|
|
537
539
|
: undefined;
|
|
538
540
|
const capped =
|
|
539
|
-
typeof capacity ===
|
|
540
|
-
if (typeof capacity ===
|
|
541
|
+
typeof capacity === "number" ? sized.slice(0, capacity) : sized;
|
|
542
|
+
if (typeof capacity === "number" && sized.length > capacity) {
|
|
541
543
|
onError?.({
|
|
542
|
-
code:
|
|
543
|
-
message:
|
|
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:
|
|
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(
|
|
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(
|
|
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(
|
|
634
|
-
form.addEventListener(
|
|
635
|
+
form.addEventListener("dragover", onDragOver);
|
|
636
|
+
form.addEventListener("drop", onDrop);
|
|
635
637
|
return () => {
|
|
636
|
-
form.removeEventListener(
|
|
637
|
-
form.removeEventListener(
|
|
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(
|
|
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(
|
|
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(
|
|
660
|
-
document.addEventListener(
|
|
661
|
+
document.addEventListener("dragover", onDragOver);
|
|
662
|
+
document.addEventListener("drop", onDrop);
|
|
661
663
|
return () => {
|
|
662
|
-
document.removeEventListener(
|
|
663
|
-
document.removeEventListener(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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 =
|
|
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 ?
|
|
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 =
|
|
1036
|
-
size =
|
|
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 ===
|
|
1045
|
+
if (status === "submitted") {
|
|
1044
1046
|
Icon = <Loader2Icon className="size-4 animate-spin" />;
|
|
1045
|
-
} else if (status ===
|
|
1047
|
+
} else if (status === "streaming") {
|
|
1046
1048
|
Icon = <SquareIcon className="size-4" />;
|
|
1047
|
-
} else if (status ===
|
|
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 !==
|
|
1141
|
-
(
|
|
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 =
|
|
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 ?
|
|
1175
|
+
currentValue + (currentValue ? " " : "") + finalTranscript;
|
|
1174
1176
|
|
|
1175
1177
|
textarea.value = newValue;
|
|
1176
|
-
textarea.dispatchEvent(new Event(
|
|
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(
|
|
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
|
-
|
|
1213
|
-
isListening &&
|
|
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
|
-
|
|
1242
|
-
|
|
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 =
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
1355
|
+
"flex items-center gap-2 px-3 py-2 text-xs hover:bg-accent",
|
|
1354
1356
|
className
|
|
1355
1357
|
)}
|
|
1356
1358
|
{...props}
|