@arcote.tech/arc-ds 0.5.1 → 0.5.2
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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcote.tech/arc-ds",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.2",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Przemysław Krasiński [arcote.tech]",
|
|
7
7
|
"description": "Design System for Arc framework — CVA-based components with display modes and variant overrides",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"tailwind-merge": "^3.5.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"@arcote.tech/arc": "^0.5.
|
|
31
|
+
"@arcote.tech/arc": "^0.5.2",
|
|
32
32
|
"framer-motion": "^12.0.0",
|
|
33
33
|
"lucide-react": ">=0.400.0",
|
|
34
34
|
"radix-ui": "^1.0.0",
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createContext, useContext, useState, type ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
interface ChatInputContextType {
|
|
4
|
+
inputOverride: ReactNode | null;
|
|
5
|
+
registerInputOverride: (content: ReactNode) => void;
|
|
6
|
+
clearInputOverride: () => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const ChatInputContext = createContext<ChatInputContextType | null>(null);
|
|
10
|
+
|
|
11
|
+
export function ChatInputProvider({ children }: { children: ReactNode }) {
|
|
12
|
+
const [inputOverride, setInputOverride] = useState<ReactNode | null>(null);
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<ChatInputContext.Provider
|
|
16
|
+
value={{
|
|
17
|
+
inputOverride,
|
|
18
|
+
registerInputOverride: setInputOverride,
|
|
19
|
+
clearInputOverride: () => setInputOverride(null),
|
|
20
|
+
}}
|
|
21
|
+
>
|
|
22
|
+
{children}
|
|
23
|
+
</ChatInputContext.Provider>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const noopContext: ChatInputContextType = {
|
|
28
|
+
inputOverride: null,
|
|
29
|
+
registerInputOverride: () => {},
|
|
30
|
+
clearInputOverride: () => {},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export function useChatInput(): ChatInputContextType {
|
|
34
|
+
return useContext(ChatInputContext) ?? noopContext;
|
|
35
|
+
}
|
|
@@ -16,6 +16,8 @@ interface ChatInputProps {
|
|
|
16
16
|
webSearchLabel?: string;
|
|
17
17
|
/** Placeholder for message input */
|
|
18
18
|
placeholder?: string;
|
|
19
|
+
/** Disable input (e.g., during generation) */
|
|
20
|
+
disabled?: boolean;
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
export function ChatInput({
|
|
@@ -25,12 +27,14 @@ export function ChatInput({
|
|
|
25
27
|
toolbar,
|
|
26
28
|
webSearchLabel = "Web",
|
|
27
29
|
placeholder = "Type a message...",
|
|
30
|
+
disabled = false,
|
|
28
31
|
}: ChatInputProps) {
|
|
29
32
|
const [message, setMessage] = useState("");
|
|
30
33
|
const [model, setModel] = useState(defaultModel ?? models[0]?.value ?? "");
|
|
31
34
|
const [webSearch, setWebSearch] = useState(false);
|
|
32
35
|
|
|
33
36
|
const handleSend = () => {
|
|
37
|
+
if (disabled) return;
|
|
34
38
|
const text = message.trim();
|
|
35
39
|
if (!text) return;
|
|
36
40
|
onSend(text, { model, webSearch });
|
|
@@ -38,14 +42,14 @@ export function ChatInput({
|
|
|
38
42
|
};
|
|
39
43
|
|
|
40
44
|
return (
|
|
41
|
-
<Box className=
|
|
45
|
+
<Box className={`p-3 space-y-2 ${disabled ? "opacity-50 pointer-events-none" : ""}`}>
|
|
42
46
|
{/* Input + send */}
|
|
43
47
|
<div className="flex items-end gap-2">
|
|
44
48
|
<div className="flex-1">
|
|
45
49
|
<TextareaField
|
|
46
50
|
value={message}
|
|
47
51
|
onChange={(val) => setMessage(val ?? "")}
|
|
48
|
-
placeholder={placeholder}
|
|
52
|
+
placeholder={disabled ? "Generowanie..." : placeholder}
|
|
49
53
|
rows={1}
|
|
50
54
|
/>
|
|
51
55
|
</div>
|
|
@@ -53,7 +57,7 @@ export function ChatInput({
|
|
|
53
57
|
size="sm"
|
|
54
58
|
icon={Send}
|
|
55
59
|
onClick={handleSend}
|
|
56
|
-
disabled={!message.trim()}
|
|
60
|
+
disabled={disabled || !message.trim()}
|
|
57
61
|
/>
|
|
58
62
|
</div>
|
|
59
63
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { CheckCircle2, Loader2, AlertCircle } from "lucide-react";
|
|
3
|
+
|
|
4
|
+
export interface ChatToolLogProps {
|
|
5
|
+
/** Tool label displayed in the header */
|
|
6
|
+
label: string;
|
|
7
|
+
/** Whether the tool is currently executing */
|
|
8
|
+
calling: boolean;
|
|
9
|
+
/** Error message (overrides success state) */
|
|
10
|
+
error?: string;
|
|
11
|
+
/** Optional icon name — reserved for future use */
|
|
12
|
+
icon?: string;
|
|
13
|
+
/** Additional details rendered below the label */
|
|
14
|
+
children?: ReactNode;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function ChatToolLog({
|
|
18
|
+
label,
|
|
19
|
+
calling,
|
|
20
|
+
error,
|
|
21
|
+
children,
|
|
22
|
+
}: ChatToolLogProps) {
|
|
23
|
+
const hasError = !!error;
|
|
24
|
+
|
|
25
|
+
const borderColor = hasError
|
|
26
|
+
? "border-red-500/20 bg-red-500/5"
|
|
27
|
+
: calling
|
|
28
|
+
? "border-blue-500/20 bg-blue-500/5"
|
|
29
|
+
: "border-green-500/20 bg-green-500/5";
|
|
30
|
+
|
|
31
|
+
const iconColor = hasError
|
|
32
|
+
? "text-red-500"
|
|
33
|
+
: calling
|
|
34
|
+
? "text-blue-500"
|
|
35
|
+
: "text-green-500";
|
|
36
|
+
|
|
37
|
+
const labelColor = hasError
|
|
38
|
+
? "text-red-700 dark:text-red-400"
|
|
39
|
+
: calling
|
|
40
|
+
? "text-blue-700 dark:text-blue-400"
|
|
41
|
+
: "text-green-700 dark:text-green-400";
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div
|
|
45
|
+
className={`flex items-start gap-3 w-full rounded-xl border p-3 transition-colors ${borderColor}`}
|
|
46
|
+
>
|
|
47
|
+
<div className={`mt-0.5 shrink-0 ${iconColor}`}>
|
|
48
|
+
{hasError ? (
|
|
49
|
+
<AlertCircle className="h-4 w-4" />
|
|
50
|
+
) : calling ? (
|
|
51
|
+
<Loader2 className="h-4 w-4 animate-spin" />
|
|
52
|
+
) : (
|
|
53
|
+
<CheckCircle2 className="h-4 w-4" />
|
|
54
|
+
)}
|
|
55
|
+
</div>
|
|
56
|
+
<div className="flex-1 min-w-0">
|
|
57
|
+
<p className={`text-xs font-medium ${labelColor}`}>{label}</p>
|
|
58
|
+
{hasError && (
|
|
59
|
+
<p className="text-xs text-red-600 dark:text-red-400 mt-0.5">
|
|
60
|
+
{error}
|
|
61
|
+
</p>
|
|
62
|
+
)}
|
|
63
|
+
{children && (
|
|
64
|
+
<div className="text-xs text-muted-foreground mt-0.5">
|
|
65
|
+
{children}
|
|
66
|
+
</div>
|
|
67
|
+
)}
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { MessageCircleQuestion, CheckCircle2 } from "lucide-react";
|
|
3
|
+
|
|
4
|
+
export interface ChatToolQuestionProps {
|
|
5
|
+
/** Whether the tool is waiting for user response */
|
|
6
|
+
calling: boolean;
|
|
7
|
+
/** Content — question text + action buttons */
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function ChatToolQuestion({
|
|
12
|
+
calling,
|
|
13
|
+
children,
|
|
14
|
+
}: ChatToolQuestionProps) {
|
|
15
|
+
const borderColor = calling
|
|
16
|
+
? "border-amber-500/20 bg-amber-500/5"
|
|
17
|
+
: "border-green-500/20 bg-green-500/5";
|
|
18
|
+
|
|
19
|
+
const iconColor = calling
|
|
20
|
+
? "text-amber-500"
|
|
21
|
+
: "text-green-500";
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div
|
|
25
|
+
className={`flex items-start gap-3 w-full rounded-xl border p-3 transition-colors ${borderColor}`}
|
|
26
|
+
>
|
|
27
|
+
<div className={`mt-0.5 shrink-0 ${iconColor}`}>
|
|
28
|
+
{calling ? (
|
|
29
|
+
<MessageCircleQuestion className="h-4 w-4" />
|
|
30
|
+
) : (
|
|
31
|
+
<CheckCircle2 className="h-4 w-4" />
|
|
32
|
+
)}
|
|
33
|
+
</div>
|
|
34
|
+
<div className="flex-1 min-w-0 space-y-2 text-sm">{children}</div>
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
}
|
package/src/ds/chat/chat.tsx
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
} from "./types";
|
|
8
8
|
import { ChatMessage } from "./chat-message";
|
|
9
9
|
import { ChatInput } from "./chat-input";
|
|
10
|
+
import { useChatInput } from "./chat-input-provider";
|
|
10
11
|
import { QuestionTabs } from "./question-tabs";
|
|
11
12
|
|
|
12
13
|
export interface ChatProps {
|
|
@@ -33,6 +34,8 @@ export interface ChatProps {
|
|
|
33
34
|
toolbar?: ReactNode;
|
|
34
35
|
/** Max width class for the message area */
|
|
35
36
|
maxWidth?: string;
|
|
37
|
+
/** Disable input (during generation) */
|
|
38
|
+
disabled?: boolean;
|
|
36
39
|
/** Labels for i18n */
|
|
37
40
|
labels?: {
|
|
38
41
|
questionsLabel?: string;
|
|
@@ -56,8 +59,10 @@ export function Chat({
|
|
|
56
59
|
sidebar,
|
|
57
60
|
toolbar,
|
|
58
61
|
maxWidth = "max-w-3xl",
|
|
62
|
+
disabled = false,
|
|
59
63
|
labels,
|
|
60
64
|
}: ChatProps) {
|
|
65
|
+
const { inputOverride } = useChatInput();
|
|
61
66
|
const [answeringMessageId, setAnsweringMessageId] = useState<string | null>(
|
|
62
67
|
null,
|
|
63
68
|
);
|
|
@@ -107,7 +112,9 @@ export function Chat({
|
|
|
107
112
|
|
|
108
113
|
{/* Input area — sticky at bottom */}
|
|
109
114
|
<div className={`sticky bottom-0 ${maxWidth} mx-auto w-full pb-4`}>
|
|
110
|
-
{
|
|
115
|
+
{inputOverride ? (
|
|
116
|
+
inputOverride
|
|
117
|
+
) : answeringMessage?.questions ? (
|
|
111
118
|
<QuestionTabs
|
|
112
119
|
questions={answeringMessage.questions}
|
|
113
120
|
onSubmit={(answers) => {
|
|
@@ -126,6 +133,7 @@ export function Chat({
|
|
|
126
133
|
toolbar={toolbar}
|
|
127
134
|
webSearchLabel={labels?.webSearchLabel}
|
|
128
135
|
placeholder={labels?.placeholder}
|
|
136
|
+
disabled={disabled}
|
|
129
137
|
/>
|
|
130
138
|
)}
|
|
131
139
|
</div>
|
package/src/index.ts
CHANGED
|
@@ -106,6 +106,11 @@ export { ChatMessage } from "./ds/chat/chat-message";
|
|
|
106
106
|
export { ChatInput } from "./ds/chat/chat-input";
|
|
107
107
|
export { QuestionTabs } from "./ds/chat/question-tabs";
|
|
108
108
|
export { ToolUseBlock } from "./ds/chat/tool-use-block";
|
|
109
|
+
export { ChatToolLog } from "./ds/chat/chat-tool-log";
|
|
110
|
+
export type { ChatToolLogProps } from "./ds/chat/chat-tool-log";
|
|
111
|
+
export { ChatToolQuestion } from "./ds/chat/chat-tool-question";
|
|
112
|
+
export type { ChatToolQuestionProps } from "./ds/chat/chat-tool-question";
|
|
113
|
+
export { ChatInputProvider, useChatInput } from "./ds/chat/chat-input-provider";
|
|
109
114
|
export type {
|
|
110
115
|
ChatMessageData,
|
|
111
116
|
ChatModel,
|