@k8o/arte-odyssey 10.6.4 → 10.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ai/_internal/collapsible.d.mts +14 -0
- package/dist/components/ai/_internal/collapsible.mjs +49 -0
- package/dist/components/ai/_internal/streaming-cursor.d.mts +6 -0
- package/dist/components/ai/_internal/streaming-cursor.mjs +8 -0
- package/dist/components/ai/conversation/conversation.d.mts +19 -0
- package/dist/components/ai/conversation/conversation.mjs +105 -0
- package/dist/components/ai/conversation/index.d.mts +2 -0
- package/dist/components/ai/conversation/index.mjs +2 -0
- package/dist/components/ai/index.d.mts +8 -0
- package/dist/components/ai/index.mjs +7 -0
- package/dist/components/ai/message/index.d.mts +2 -0
- package/dist/components/ai/message/index.mjs +2 -0
- package/dist/components/ai/message/message.d.mts +18 -0
- package/dist/components/ai/message/message.mjs +33 -0
- package/dist/components/ai/prompt-input/index.d.mts +2 -0
- package/dist/components/ai/prompt-input/index.mjs +2 -0
- package/dist/components/ai/prompt-input/prompt-input.d.mts +27 -0
- package/dist/components/ai/prompt-input/prompt-input.mjs +103 -0
- package/dist/components/ai/reasoning/index.d.mts +2 -0
- package/dist/components/ai/reasoning/index.mjs +2 -0
- package/dist/components/ai/reasoning/reasoning.d.mts +13 -0
- package/dist/components/ai/reasoning/reasoning.mjs +21 -0
- package/dist/components/ai/response/index.d.mts +2 -0
- package/dist/components/ai/response/index.mjs +2 -0
- package/dist/components/ai/response/response.d.mts +18 -0
- package/dist/components/ai/response/response.mjs +20 -0
- package/dist/components/ai/suggestion/index.d.mts +2 -0
- package/dist/components/ai/suggestion/index.mjs +2 -0
- package/dist/components/ai/suggestion/suggestion.d.mts +18 -0
- package/dist/components/ai/suggestion/suggestion.mjs +25 -0
- package/dist/components/ai/tool-invocation/index.d.mts +2 -0
- package/dist/components/ai/tool-invocation/index.mjs +2 -0
- package/dist/components/ai/tool-invocation/tool-invocation.d.mts +17 -0
- package/dist/components/ai/tool-invocation/tool-invocation.mjs +59 -0
- package/dist/components/ai/types.d.mts +5 -0
- package/dist/components/ai/types.mjs +1 -0
- package/dist/components/data-display/avatar/avatar.d.mts +3 -1
- package/dist/components/data-display/avatar/avatar.mjs +7 -3
- package/dist/components/form/checkbox-group/index.d.mts +3 -3
- package/dist/components/icons/color-scale.d.mts +7 -0
- package/dist/components/icons/color-scale.mjs +43 -0
- package/dist/components/icons/index.d.mts +3 -2
- package/dist/components/icons/index.mjs +3 -2
- package/dist/components/icons/lucide-imports.d.mts +2 -1
- package/dist/components/icons/lucide-imports.mjs +2 -1
- package/dist/components/icons/lucide.d.mts +2 -1
- package/dist/components/icons/lucide.mjs +6 -2
- package/dist/components/index.d.mts +3 -2
- package/dist/components/index.mjs +3 -2
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +3 -2
- package/dist/integrations/_shared/openui-defs.mjs +1 -1
- package/dist/integrations/_shared/schemas.d.mts +17 -17
- package/dist/integrations/ai-sdk/index.d.mts +3 -0
- package/dist/integrations/ai-sdk/index.mjs +2 -0
- package/dist/integrations/ai-sdk/map-parts.d.mts +28 -0
- package/dist/integrations/ai-sdk/map-parts.mjs +31 -0
- package/dist/integrations/json-render/catalog.d.mts +16 -16
- package/dist/integrations/json-render/registry.mjs +1 -1
- package/dist/integrations/openui/library.mjs +1 -1
- package/package.json +29 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { FC, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/components/ai/_internal/collapsible.d.ts
|
|
4
|
+
type Props = {
|
|
5
|
+
icon?: ReactNode;
|
|
6
|
+
label: ReactNode;
|
|
7
|
+
isOpen?: boolean;
|
|
8
|
+
defaultOpen?: boolean;
|
|
9
|
+
onChange?: (isOpen: boolean) => void;
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
};
|
|
12
|
+
declare const Collapsible: FC<Props>;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { Collapsible };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { FOCUS_RING_NO_BORDER } from "../../_internal/focus-ring.mjs";
|
|
3
|
+
import { cn } from "../../../helpers/cn.mjs";
|
|
4
|
+
import { useControllableState } from "../../../hooks/controllable-state/index.mjs";
|
|
5
|
+
import { ChevronIcon } from "../../icons/lucide.mjs";
|
|
6
|
+
import { useId } from "react";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
//#region src/components/ai/_internal/collapsible.tsx
|
|
9
|
+
const Collapsible = ({ icon, label, isOpen, defaultOpen = false, onChange, children }) => {
|
|
10
|
+
const [open, setOpen] = useControllableState({
|
|
11
|
+
value: isOpen,
|
|
12
|
+
defaultValue: defaultOpen,
|
|
13
|
+
onChange
|
|
14
|
+
});
|
|
15
|
+
const panelId = useId();
|
|
16
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
17
|
+
className: "border-border-mute bg-bg-subtle overflow-hidden rounded-xl border text-sm",
|
|
18
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
19
|
+
"aria-controls": panelId,
|
|
20
|
+
"aria-expanded": open,
|
|
21
|
+
className: cn("flex w-full items-center gap-2 px-3 py-2 text-left text-fg-mute transition-colors duration-150 ease-out hover:bg-bg-mute", FOCUS_RING_NO_BORDER),
|
|
22
|
+
onClick: () => {
|
|
23
|
+
setOpen(!open);
|
|
24
|
+
},
|
|
25
|
+
type: "button",
|
|
26
|
+
children: [
|
|
27
|
+
icon !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
28
|
+
className: "flex shrink-0 items-center",
|
|
29
|
+
children: icon
|
|
30
|
+
}),
|
|
31
|
+
/* @__PURE__ */ jsx("span", {
|
|
32
|
+
className: "min-w-0 flex-1 truncate",
|
|
33
|
+
children: label
|
|
34
|
+
}),
|
|
35
|
+
/* @__PURE__ */ jsx(ChevronIcon, {
|
|
36
|
+
direction: open ? "up" : "down",
|
|
37
|
+
size: "sm"
|
|
38
|
+
})
|
|
39
|
+
]
|
|
40
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
41
|
+
className: "px-3 pt-2 pb-3",
|
|
42
|
+
hidden: !open,
|
|
43
|
+
id: panelId,
|
|
44
|
+
children
|
|
45
|
+
})]
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
//#endregion
|
|
49
|
+
export { Collapsible };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
//#region src/components/ai/_internal/streaming-cursor.tsx
|
|
3
|
+
const StreamingCursor = () => /* @__PURE__ */ jsx("span", {
|
|
4
|
+
"aria-hidden": true,
|
|
5
|
+
className: "bg-fg-base ml-0.5 inline-block h-[1.1em] w-0.5 translate-y-[0.15em] rounded-xs motion-safe:animate-pulse"
|
|
6
|
+
});
|
|
7
|
+
//#endregion
|
|
8
|
+
export { StreamingCursor };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { FC, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/components/ai/conversation/conversation.d.ts
|
|
4
|
+
type MessagesProps = {
|
|
5
|
+
label?: string;
|
|
6
|
+
isStreaming?: boolean;
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
};
|
|
9
|
+
declare const Conversation: {
|
|
10
|
+
readonly Root: FC<{
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
}>;
|
|
13
|
+
readonly Messages: FC<MessagesProps>;
|
|
14
|
+
readonly ScrollButton: FC<{
|
|
15
|
+
label?: string;
|
|
16
|
+
}>;
|
|
17
|
+
};
|
|
18
|
+
//#endregion
|
|
19
|
+
export { Conversation };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { FOCUS_RING, FOCUS_RING_NO_BORDER } from "../../_internal/focus-ring.mjs";
|
|
3
|
+
import { cn } from "../../../helpers/cn.mjs";
|
|
4
|
+
import { createSafeContext } from "../../../helpers/create-safe-context.mjs";
|
|
5
|
+
import { useIntersectionObserver } from "../../../hooks/intersection-observer/use-intersection-observer.mjs";
|
|
6
|
+
import { useResize } from "../../../hooks/resize/index.mjs";
|
|
7
|
+
import { ChevronIcon } from "../../icons/lucide.mjs";
|
|
8
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
9
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
//#region src/components/ai/conversation/conversation.tsx
|
|
11
|
+
const prefersReducedMotion = () => window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
12
|
+
const [ConversationProvider, useConversationContext] = createSafeContext("Conversation.* must be used within <Conversation.Root>");
|
|
13
|
+
const Root = ({ children }) => {
|
|
14
|
+
const [viewport, setViewport] = useState(null);
|
|
15
|
+
const sentinelRef = useRef(null);
|
|
16
|
+
const contentRef = useRef(null);
|
|
17
|
+
const [isAtBottom, setIsAtBottom] = useState(true);
|
|
18
|
+
const isAtBottomRef = useRef(true);
|
|
19
|
+
const updateAtBottom = useCallback((next) => {
|
|
20
|
+
isAtBottomRef.current = next;
|
|
21
|
+
setIsAtBottom(next);
|
|
22
|
+
}, []);
|
|
23
|
+
const scrollToBottom = useCallback((behavior = "smooth") => {
|
|
24
|
+
if (!viewport) return;
|
|
25
|
+
viewport.scrollTo({
|
|
26
|
+
top: viewport.scrollHeight,
|
|
27
|
+
behavior: prefersReducedMotion() ? "instant" : behavior
|
|
28
|
+
});
|
|
29
|
+
}, [viewport]);
|
|
30
|
+
useIntersectionObserver(sentinelRef, useCallback((entry) => {
|
|
31
|
+
updateAtBottom(entry.isIntersecting);
|
|
32
|
+
}, [updateAtBottom]), {
|
|
33
|
+
root: viewport,
|
|
34
|
+
rootMargin: "0px 0px 24px 0px"
|
|
35
|
+
});
|
|
36
|
+
useResize(contentRef, useCallback(() => {
|
|
37
|
+
if (isAtBottomRef.current && viewport) viewport.scrollTo({
|
|
38
|
+
top: viewport.scrollHeight,
|
|
39
|
+
behavior: "instant"
|
|
40
|
+
});
|
|
41
|
+
}, [viewport]));
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (viewport) viewport.scrollTo({
|
|
44
|
+
top: viewport.scrollHeight,
|
|
45
|
+
behavior: "instant"
|
|
46
|
+
});
|
|
47
|
+
}, [viewport]);
|
|
48
|
+
return /* @__PURE__ */ jsx(ConversationProvider, {
|
|
49
|
+
value: useMemo(() => ({
|
|
50
|
+
isAtBottom,
|
|
51
|
+
scrollToBottom,
|
|
52
|
+
setViewport,
|
|
53
|
+
sentinelRef,
|
|
54
|
+
contentRef
|
|
55
|
+
}), [isAtBottom, scrollToBottom]),
|
|
56
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
57
|
+
className: "relative flex h-full min-h-0 flex-col",
|
|
58
|
+
children
|
|
59
|
+
})
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
const Messages = ({ label = "チャット", isStreaming = false, children }) => {
|
|
63
|
+
const { setViewport, sentinelRef, contentRef } = useConversationContext();
|
|
64
|
+
return /* @__PURE__ */ jsx("div", {
|
|
65
|
+
"aria-busy": isStreaming,
|
|
66
|
+
"aria-label": label,
|
|
67
|
+
"aria-live": "polite",
|
|
68
|
+
className: cn("min-h-0 flex-1 overflow-y-auto overscroll-contain", FOCUS_RING_NO_BORDER),
|
|
69
|
+
ref: setViewport,
|
|
70
|
+
role: "log",
|
|
71
|
+
tabIndex: 0,
|
|
72
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
73
|
+
className: "flex flex-col gap-6 p-4",
|
|
74
|
+
ref: contentRef,
|
|
75
|
+
children: [children, /* @__PURE__ */ jsx("div", {
|
|
76
|
+
"aria-hidden": true,
|
|
77
|
+
className: "h-px w-full shrink-0",
|
|
78
|
+
ref: sentinelRef
|
|
79
|
+
})]
|
|
80
|
+
})
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
const ScrollButton = ({ label = "最新のメッセージへ移動" }) => {
|
|
84
|
+
const { isAtBottom, scrollToBottom } = useConversationContext();
|
|
85
|
+
if (isAtBottom) return null;
|
|
86
|
+
return /* @__PURE__ */ jsx("button", {
|
|
87
|
+
"aria-label": label,
|
|
88
|
+
className: cn("absolute bottom-4 left-1/2 flex size-9 -translate-x-1/2 items-center justify-center rounded-full border border-border-base bg-bg-base text-fg-mute shadow-md transition-colors duration-150 ease-out hover:bg-bg-subtle", FOCUS_RING),
|
|
89
|
+
onClick: () => {
|
|
90
|
+
scrollToBottom("smooth");
|
|
91
|
+
},
|
|
92
|
+
type: "button",
|
|
93
|
+
children: /* @__PURE__ */ jsx(ChevronIcon, {
|
|
94
|
+
direction: "down",
|
|
95
|
+
size: "sm"
|
|
96
|
+
})
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
const Conversation = {
|
|
100
|
+
Root,
|
|
101
|
+
Messages,
|
|
102
|
+
ScrollButton
|
|
103
|
+
};
|
|
104
|
+
//#endregion
|
|
105
|
+
export { Conversation };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Conversation } from "./conversation/conversation.mjs";
|
|
2
|
+
import { Message } from "./message/message.mjs";
|
|
3
|
+
import { ChatStatus, ToolState } from "./types.mjs";
|
|
4
|
+
import { PromptInput } from "./prompt-input/prompt-input.mjs";
|
|
5
|
+
import { Reasoning } from "./reasoning/reasoning.mjs";
|
|
6
|
+
import { Suggestion } from "./suggestion/suggestion.mjs";
|
|
7
|
+
import { ToolInvocation } from "./tool-invocation/tool-invocation.mjs";
|
|
8
|
+
export { type ChatStatus, Conversation, Message, PromptInput, Reasoning, Suggestion, ToolInvocation, type ToolState };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Conversation } from "./conversation/conversation.mjs";
|
|
2
|
+
import { Message } from "./message/message.mjs";
|
|
3
|
+
import { PromptInput } from "./prompt-input/prompt-input.mjs";
|
|
4
|
+
import { Reasoning } from "./reasoning/reasoning.mjs";
|
|
5
|
+
import { Suggestion } from "./suggestion/suggestion.mjs";
|
|
6
|
+
import { ToolInvocation } from "./tool-invocation/tool-invocation.mjs";
|
|
7
|
+
export { Conversation, Message, PromptInput, Reasoning, Suggestion, ToolInvocation };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { FC, HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/components/ai/message/message.d.ts
|
|
4
|
+
type MessageRole = 'user' | 'assistant';
|
|
5
|
+
type RootProps = {
|
|
6
|
+
from: MessageRole;
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
} & Omit<HTMLAttributes<HTMLDivElement>, 'className' | 'style'>;
|
|
9
|
+
type ContentProps = {
|
|
10
|
+
isStreaming?: boolean;
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
} & Omit<HTMLAttributes<HTMLDivElement>, 'className' | 'style'>;
|
|
13
|
+
declare const Message: {
|
|
14
|
+
readonly Root: FC<RootProps>;
|
|
15
|
+
readonly Content: FC<ContentProps>;
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { Message };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../../../helpers/cn.mjs";
|
|
3
|
+
import { createSafeContext } from "../../../helpers/create-safe-context.mjs";
|
|
4
|
+
import { StreamingCursor } from "../_internal/streaming-cursor.mjs";
|
|
5
|
+
import { useMemo } from "react";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
//#region src/components/ai/message/message.tsx
|
|
8
|
+
const [MessageProvider, useMessageContext] = createSafeContext("Message.Content must be used within <Message.Root>");
|
|
9
|
+
const Root = ({ from, children, ...rest }) => {
|
|
10
|
+
return /* @__PURE__ */ jsx(MessageProvider, {
|
|
11
|
+
value: useMemo(() => ({ from }), [from]),
|
|
12
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
13
|
+
...rest,
|
|
14
|
+
className: cn("flex w-full items-start gap-3", from === "user" ? "flex-row-reverse" : "flex-row"),
|
|
15
|
+
"data-from": from,
|
|
16
|
+
children
|
|
17
|
+
})
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
const Content = ({ isStreaming = false, children, ...rest }) => {
|
|
21
|
+
const { from } = useMessageContext();
|
|
22
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
23
|
+
...rest,
|
|
24
|
+
className: cn("min-w-0 wrap-break-word whitespace-pre-wrap text-fg-base", from === "user" ? "w-fit max-w-[80%] rounded-2xl bg-bg-subtle px-4 py-2.5" : "flex-1 leading-relaxed"),
|
|
25
|
+
children: [children, isStreaming ? /* @__PURE__ */ jsx(StreamingCursor, {}) : null]
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
const Message = {
|
|
29
|
+
Root,
|
|
30
|
+
Content
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
33
|
+
export { Message };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ChatStatus } from "../types.mjs";
|
|
2
|
+
import { FC, ReactNode, TextareaHTMLAttributes } from "react";
|
|
3
|
+
|
|
4
|
+
//#region src/components/ai/prompt-input/prompt-input.d.ts
|
|
5
|
+
type RootProps = {
|
|
6
|
+
status?: ChatStatus;
|
|
7
|
+
value?: string;
|
|
8
|
+
defaultValue?: string;
|
|
9
|
+
onChange?: (value: string) => void;
|
|
10
|
+
onSubmit?: (message: string) => void;
|
|
11
|
+
onStop?: () => void;
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
};
|
|
14
|
+
type TextareaProps = {
|
|
15
|
+
placeholder?: string;
|
|
16
|
+
} & Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'value' | 'defaultValue' | 'onChange' | 'className' | 'style' | 'rows'>;
|
|
17
|
+
type SubmitProps = {
|
|
18
|
+
sendLabel?: string;
|
|
19
|
+
stopLabel?: string;
|
|
20
|
+
};
|
|
21
|
+
declare const PromptInput: {
|
|
22
|
+
readonly Root: FC<RootProps>;
|
|
23
|
+
readonly Textarea: FC<TextareaProps>;
|
|
24
|
+
readonly Submit: FC<SubmitProps>;
|
|
25
|
+
};
|
|
26
|
+
//#endregion
|
|
27
|
+
export { PromptInput };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { FOCUS_RING, FOCUS_RING_WITHIN } from "../../_internal/focus-ring.mjs";
|
|
3
|
+
import { cn } from "../../../helpers/cn.mjs";
|
|
4
|
+
import { createSafeContext } from "../../../helpers/create-safe-context.mjs";
|
|
5
|
+
import { useControllableState } from "../../../hooks/controllable-state/index.mjs";
|
|
6
|
+
import { SendIcon } from "../../icons/lucide.mjs";
|
|
7
|
+
import { useEffect, useMemo, useRef } from "react";
|
|
8
|
+
import { jsx } from "react/jsx-runtime";
|
|
9
|
+
//#region src/components/ai/prompt-input/prompt-input.tsx
|
|
10
|
+
const [PromptInputProvider, usePromptInputContext] = createSafeContext("PromptInput.Textarea / PromptInput.Submit must be used within <PromptInput.Root>");
|
|
11
|
+
const resizeToContent = (el) => {
|
|
12
|
+
el.style.height = "auto";
|
|
13
|
+
el.style.height = `${el.scrollHeight.toString()}px`;
|
|
14
|
+
};
|
|
15
|
+
const Root = ({ status = "ready", value, defaultValue = "", onChange, onSubmit, onStop, children }) => {
|
|
16
|
+
const isControlled = value !== void 0;
|
|
17
|
+
const [text, setText] = useControllableState({
|
|
18
|
+
value,
|
|
19
|
+
defaultValue,
|
|
20
|
+
onChange
|
|
21
|
+
});
|
|
22
|
+
return /* @__PURE__ */ jsx(PromptInputProvider, {
|
|
23
|
+
value: useMemo(() => ({
|
|
24
|
+
value: text,
|
|
25
|
+
setValue: setText,
|
|
26
|
+
status,
|
|
27
|
+
stop: () => onStop?.()
|
|
28
|
+
}), [
|
|
29
|
+
text,
|
|
30
|
+
setText,
|
|
31
|
+
status,
|
|
32
|
+
onStop
|
|
33
|
+
]),
|
|
34
|
+
children: /* @__PURE__ */ jsx("form", {
|
|
35
|
+
className: cn("flex items-end gap-2 rounded-2xl border border-border-base bg-bg-base p-2", FOCUS_RING_WITHIN),
|
|
36
|
+
onSubmit: (event) => {
|
|
37
|
+
event.preventDefault();
|
|
38
|
+
if (status === "submitted" || status === "streaming") return;
|
|
39
|
+
const trimmed = text.trim();
|
|
40
|
+
if (trimmed === "") return;
|
|
41
|
+
onSubmit?.(trimmed);
|
|
42
|
+
if (!isControlled) setText("");
|
|
43
|
+
},
|
|
44
|
+
children
|
|
45
|
+
})
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
const Textarea = ({ placeholder, onKeyDown, ...rest }) => {
|
|
49
|
+
const { value, setValue, status } = usePromptInputContext();
|
|
50
|
+
const ref = useRef(null);
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (ref.current) resizeToContent(ref.current);
|
|
53
|
+
}, [value]);
|
|
54
|
+
const handleKeyDown = (event) => {
|
|
55
|
+
onKeyDown?.(event);
|
|
56
|
+
if (event.defaultPrevented) return;
|
|
57
|
+
if (event.key !== "Enter" || event.shiftKey) return;
|
|
58
|
+
if (event.nativeEvent.isComposing) return;
|
|
59
|
+
if (status !== "submitted" && status !== "streaming") {
|
|
60
|
+
event.preventDefault();
|
|
61
|
+
event.currentTarget.form?.requestSubmit();
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
return /* @__PURE__ */ jsx("textarea", {
|
|
65
|
+
...rest,
|
|
66
|
+
className: cn("max-h-48 min-h-10 flex-1 resize-none bg-transparent text-fg-base outline-hidden p-2", "placeholder:text-fg-subtle"),
|
|
67
|
+
onChange: (event) => {
|
|
68
|
+
setValue(event.target.value);
|
|
69
|
+
},
|
|
70
|
+
onKeyDown: handleKeyDown,
|
|
71
|
+
placeholder,
|
|
72
|
+
ref,
|
|
73
|
+
rows: 1,
|
|
74
|
+
value
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
const Submit = ({ sendLabel = "送信", stopLabel = "停止" }) => {
|
|
78
|
+
const { value, status, stop } = usePromptInputContext();
|
|
79
|
+
if (status === "submitted" || status === "streaming") return /* @__PURE__ */ jsx("button", {
|
|
80
|
+
"aria-label": stopLabel,
|
|
81
|
+
className: cn("flex size-9 shrink-0 items-center justify-center rounded-full bg-primary-bg text-primary-fg transition-colors duration-150 ease-out", FOCUS_RING),
|
|
82
|
+
onClick: stop,
|
|
83
|
+
type: "button",
|
|
84
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
85
|
+
"aria-hidden": true,
|
|
86
|
+
className: "size-3 rounded-xs bg-current"
|
|
87
|
+
})
|
|
88
|
+
});
|
|
89
|
+
return /* @__PURE__ */ jsx("button", {
|
|
90
|
+
"aria-label": sendLabel,
|
|
91
|
+
className: cn("flex size-9 shrink-0 items-center justify-center rounded-full bg-primary-bg text-primary-fg transition-colors duration-150 ease-out", "disabled:cursor-not-allowed disabled:opacity-50", FOCUS_RING),
|
|
92
|
+
disabled: value.trim() === "",
|
|
93
|
+
type: "submit",
|
|
94
|
+
children: /* @__PURE__ */ jsx(SendIcon, { size: "sm" })
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
const PromptInput = {
|
|
98
|
+
Root,
|
|
99
|
+
Textarea,
|
|
100
|
+
Submit
|
|
101
|
+
};
|
|
102
|
+
//#endregion
|
|
103
|
+
export { PromptInput };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FC, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/components/ai/reasoning/reasoning.d.ts
|
|
4
|
+
type Props = {
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
isStreaming?: boolean;
|
|
7
|
+
isOpen?: boolean;
|
|
8
|
+
defaultOpen?: boolean;
|
|
9
|
+
onChange?: (isOpen: boolean) => void;
|
|
10
|
+
};
|
|
11
|
+
declare const Reasoning: FC<Props>;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { Reasoning };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { SparklesIcon } from "../../icons/lucide.mjs";
|
|
3
|
+
import { Collapsible } from "../_internal/collapsible.mjs";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/ai/reasoning/reasoning.tsx
|
|
6
|
+
const Reasoning = ({ children, isStreaming = false, isOpen, defaultOpen = false, onChange }) => /* @__PURE__ */ jsx(Collapsible, {
|
|
7
|
+
defaultOpen,
|
|
8
|
+
isOpen,
|
|
9
|
+
onChange,
|
|
10
|
+
icon: /* @__PURE__ */ jsx("span", {
|
|
11
|
+
className: "text-fg-subtle",
|
|
12
|
+
children: /* @__PURE__ */ jsx(SparklesIcon, { size: "sm" })
|
|
13
|
+
}),
|
|
14
|
+
label: isStreaming ? "思考中…" : "思考の過程",
|
|
15
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
16
|
+
className: "text-fg-mute text-sm leading-relaxed whitespace-pre-wrap",
|
|
17
|
+
children
|
|
18
|
+
})
|
|
19
|
+
});
|
|
20
|
+
//#endregion
|
|
21
|
+
export { Reasoning };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { FC } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/components/ai/response/response.d.ts
|
|
4
|
+
type Props = {
|
|
5
|
+
children: string;
|
|
6
|
+
isStreaming?: boolean;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* ストリーミング対応の Markdown レンダラ。未クローズのコードブロックなど
|
|
10
|
+
* 途中の Markdown も streamdown が破綻なく描画する。
|
|
11
|
+
*
|
|
12
|
+
* このコンポーネントは streamdown(optional peer)に依存する。利用側は
|
|
13
|
+
* `pnpm add streamdown` に加え、`streamdown/styles.css` の読み込みと
|
|
14
|
+
* Tailwind の `@source` 設定が必要。詳細は docs を参照。
|
|
15
|
+
*/
|
|
16
|
+
declare const Response: FC<Props>;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { Response };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Streamdown } from "streamdown";
|
|
4
|
+
//#region src/components/ai/response/response.tsx
|
|
5
|
+
/**
|
|
6
|
+
* ストリーミング対応の Markdown レンダラ。未クローズのコードブロックなど
|
|
7
|
+
* 途中の Markdown も streamdown が破綻なく描画する。
|
|
8
|
+
*
|
|
9
|
+
* このコンポーネントは streamdown(optional peer)に依存する。利用側は
|
|
10
|
+
* `pnpm add streamdown` に加え、`streamdown/styles.css` の読み込みと
|
|
11
|
+
* Tailwind の `@source` 設定が必要。詳細は docs を参照。
|
|
12
|
+
*/
|
|
13
|
+
const Response = ({ children, isStreaming = false }) => /* @__PURE__ */ jsx(Streamdown, {
|
|
14
|
+
className: "text-fg-base",
|
|
15
|
+
mode: isStreaming ? "streaming" : "static",
|
|
16
|
+
parseIncompleteMarkdown: true,
|
|
17
|
+
children
|
|
18
|
+
});
|
|
19
|
+
//#endregion
|
|
20
|
+
export { Response };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { FC, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/components/ai/suggestion/suggestion.d.ts
|
|
4
|
+
type ListProps = {
|
|
5
|
+
label?: string;
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
};
|
|
8
|
+
type ItemProps = {
|
|
9
|
+
value: string;
|
|
10
|
+
onSelect?: (value: string) => void;
|
|
11
|
+
children?: ReactNode;
|
|
12
|
+
};
|
|
13
|
+
declare const Suggestion: {
|
|
14
|
+
readonly List: FC<ListProps>;
|
|
15
|
+
readonly Item: FC<ItemProps>;
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { Suggestion };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { FOCUS_RING } from "../../_internal/focus-ring.mjs";
|
|
3
|
+
import { cn } from "../../../helpers/cn.mjs";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/ai/suggestion/suggestion.tsx
|
|
6
|
+
const List = ({ label = "候補", children }) => /* @__PURE__ */ jsx("div", {
|
|
7
|
+
"aria-label": label,
|
|
8
|
+
className: "flex flex-wrap gap-2",
|
|
9
|
+
role: "group",
|
|
10
|
+
children
|
|
11
|
+
});
|
|
12
|
+
const Item = ({ value, onSelect, children }) => /* @__PURE__ */ jsx("button", {
|
|
13
|
+
className: cn("rounded-full border border-border-base bg-bg-subtle px-3 py-1.5 text-sm text-fg-base transition-colors duration-150 ease-out hover:bg-bg-mute", FOCUS_RING),
|
|
14
|
+
onClick: () => {
|
|
15
|
+
onSelect?.(value);
|
|
16
|
+
},
|
|
17
|
+
type: "button",
|
|
18
|
+
children: children ?? value
|
|
19
|
+
});
|
|
20
|
+
const Suggestion = {
|
|
21
|
+
List,
|
|
22
|
+
Item
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
export { Suggestion };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ToolState } from "../types.mjs";
|
|
2
|
+
import { FC, ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
//#region src/components/ai/tool-invocation/tool-invocation.d.ts
|
|
5
|
+
type Props = {
|
|
6
|
+
name: string;
|
|
7
|
+
state: ToolState;
|
|
8
|
+
input?: unknown;
|
|
9
|
+
output?: ReactNode;
|
|
10
|
+
errorText?: string;
|
|
11
|
+
isOpen?: boolean;
|
|
12
|
+
defaultOpen?: boolean;
|
|
13
|
+
onChange?: (isOpen: boolean) => void;
|
|
14
|
+
};
|
|
15
|
+
declare const ToolInvocation: FC<Props>;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { ToolInvocation };
|