@arcote.tech/arc-ds 0.5.0 → 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.0",
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.0",
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="p-3 space-y-2">
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
+ }
@@ -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
- {answeringMessage?.questions ? (
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,