@4djs/assistant 0.0.0 → 0.0.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 (188) hide show
  1. package/dist/core/chat-activity.d.ts +19 -0
  2. package/dist/core/chat-activity.d.ts.map +1 -0
  3. package/dist/core/chat-commands.d.ts +33 -0
  4. package/dist/core/chat-commands.d.ts.map +1 -0
  5. package/dist/core/chat-history.d.ts +14 -0
  6. package/dist/core/chat-history.d.ts.map +1 -0
  7. package/dist/core/chat-reply-suggestions-parse.d.ts +20 -0
  8. package/dist/core/chat-reply-suggestions-parse.d.ts.map +1 -0
  9. package/dist/core/code-highlight.d.ts +3 -0
  10. package/dist/core/code-highlight.d.ts.map +1 -0
  11. package/dist/core/create-assistant-store.d.ts +33 -0
  12. package/dist/core/create-assistant-store.d.ts.map +1 -0
  13. package/dist/core/fetch-suggested-prompts.d.ts +11 -0
  14. package/dist/core/fetch-suggested-prompts.d.ts.map +1 -0
  15. package/dist/core/index.d.ts +19 -0
  16. package/dist/core/index.d.ts.map +1 -0
  17. package/dist/core/index.js +2876 -0
  18. package/dist/core/interactive-tools/choices.d.ts +22 -0
  19. package/dist/core/interactive-tools/choices.d.ts.map +1 -0
  20. package/dist/core/interactive-tools/confirmation.d.ts +15 -0
  21. package/dist/core/interactive-tools/confirmation.d.ts.map +1 -0
  22. package/dist/core/interactive-tools/constants.d.ts +6 -0
  23. package/dist/core/interactive-tools/constants.d.ts.map +1 -0
  24. package/dist/core/interactive-tools/execute.d.ts +11 -0
  25. package/dist/core/interactive-tools/execute.d.ts.map +1 -0
  26. package/dist/core/interactive-tools/index.d.ts +7 -0
  27. package/dist/core/interactive-tools/index.d.ts.map +1 -0
  28. package/dist/core/interactive-tools/suggestions.d.ts +13 -0
  29. package/dist/core/interactive-tools/suggestions.d.ts.map +1 -0
  30. package/dist/core/interactive-tools/waiters.d.ts +4 -0
  31. package/dist/core/interactive-tools/waiters.d.ts.map +1 -0
  32. package/dist/core/llm-chat.d.ts +96 -0
  33. package/dist/core/llm-chat.d.ts.map +1 -0
  34. package/dist/core/llm-config.d.ts +24 -0
  35. package/dist/core/llm-config.d.ts.map +1 -0
  36. package/dist/core/llm-models.d.ts +14 -0
  37. package/dist/core/llm-models.d.ts.map +1 -0
  38. package/dist/core/llm-provider.d.ts +13 -0
  39. package/dist/core/llm-provider.d.ts.map +1 -0
  40. package/dist/core/llm-settings-storage.d.ts +47 -0
  41. package/dist/core/llm-settings-storage.d.ts.map +1 -0
  42. package/dist/core/llm-sse.d.ts +13 -0
  43. package/dist/core/llm-sse.d.ts.map +1 -0
  44. package/dist/core/llm-types.d.ts +49 -0
  45. package/dist/core/llm-types.d.ts.map +1 -0
  46. package/dist/core/markdown-utils.d.ts +3 -0
  47. package/dist/core/markdown-utils.d.ts.map +1 -0
  48. package/dist/core/prepare-markdown.d.ts +7 -0
  49. package/dist/core/prepare-markdown.d.ts.map +1 -0
  50. package/dist/core/types.d.ts +74 -0
  51. package/dist/core/types.d.ts.map +1 -0
  52. package/dist/index.css +1195 -0
  53. package/dist/index.js +184948 -0
  54. package/dist/react/Assistant.d.ts +10 -0
  55. package/dist/react/Assistant.d.ts.map +1 -0
  56. package/dist/react/components/HighlightedJsonCode.d.ts +6 -0
  57. package/dist/react/components/HighlightedJsonCode.d.ts.map +1 -0
  58. package/dist/react/components/MarkdownContent.d.ts +10 -0
  59. package/dist/react/components/MarkdownContent.d.ts.map +1 -0
  60. package/dist/react/components/MarkdownEditor.d.ts +11 -0
  61. package/dist/react/components/MarkdownEditor.d.ts.map +1 -0
  62. package/dist/react/components/MermaidDiagram.d.ts +8 -0
  63. package/dist/react/components/MermaidDiagram.d.ts.map +1 -0
  64. package/dist/react/components/ModelSelector.d.ts +8 -0
  65. package/dist/react/components/ModelSelector.d.ts.map +1 -0
  66. package/dist/react/components/chat/AssistantErrorCallout.d.ts +11 -0
  67. package/dist/react/components/chat/AssistantErrorCallout.d.ts.map +1 -0
  68. package/dist/react/components/chat/ChatActivity.d.ts +8 -0
  69. package/dist/react/components/chat/ChatActivity.d.ts.map +1 -0
  70. package/dist/react/components/chat/ChatComposer.d.ts +36 -0
  71. package/dist/react/components/chat/ChatComposer.d.ts.map +1 -0
  72. package/dist/react/components/chat/ChatEmptyState.d.ts +10 -0
  73. package/dist/react/components/chat/ChatEmptyState.d.ts.map +1 -0
  74. package/dist/react/components/chat/ChatInteractivePrompt/choices-prompt.d.ts +7 -0
  75. package/dist/react/components/chat/ChatInteractivePrompt/choices-prompt.d.ts.map +1 -0
  76. package/dist/react/components/chat/ChatInteractivePrompt/confirmation-prompt.d.ts +7 -0
  77. package/dist/react/components/chat/ChatInteractivePrompt/confirmation-prompt.d.ts.map +1 -0
  78. package/dist/react/components/chat/ChatInteractivePrompt/index.d.ts +7 -0
  79. package/dist/react/components/chat/ChatInteractivePrompt/index.d.ts.map +1 -0
  80. package/dist/react/components/chat/ChatInteractivePrompt/shell.d.ts +13 -0
  81. package/dist/react/components/chat/ChatInteractivePrompt/shell.d.ts.map +1 -0
  82. package/dist/react/components/chat/ChatInteractivePrompt/utils.d.ts +4 -0
  83. package/dist/react/components/chat/ChatInteractivePrompt/utils.d.ts.map +1 -0
  84. package/dist/react/components/chat/ChatMessage.d.ts +11 -0
  85. package/dist/react/components/chat/ChatMessage.d.ts.map +1 -0
  86. package/dist/react/components/chat/ChatMessageScroll.d.ts +8 -0
  87. package/dist/react/components/chat/ChatMessageScroll.d.ts.map +1 -0
  88. package/dist/react/components/chat/ChatReplySuggestions.d.ts +9 -0
  89. package/dist/react/components/chat/ChatReplySuggestions.d.ts.map +1 -0
  90. package/dist/react/components/chat/ComposerCommandMenu.d.ts +10 -0
  91. package/dist/react/components/chat/ComposerCommandMenu.d.ts.map +1 -0
  92. package/dist/react/components/chat/LlmSettingsStrip.d.ts +7 -0
  93. package/dist/react/components/chat/LlmSettingsStrip.d.ts.map +1 -0
  94. package/dist/react/components/chat/LlmSetupPrompt.d.ts +7 -0
  95. package/dist/react/components/chat/LlmSetupPrompt.d.ts.map +1 -0
  96. package/dist/react/components/chat/LlmUnavailableBanner.d.ts +6 -0
  97. package/dist/react/components/chat/LlmUnavailableBanner.d.ts.map +1 -0
  98. package/dist/react/components/chat/SuggestedPromptsList.d.ts +14 -0
  99. package/dist/react/components/chat/SuggestedPromptsList.d.ts.map +1 -0
  100. package/dist/react/components/chat/SuggestedPromptsStrip.d.ts +11 -0
  101. package/dist/react/components/chat/SuggestedPromptsStrip.d.ts.map +1 -0
  102. package/dist/react/components/chat/SystemPromptField.d.ts +10 -0
  103. package/dist/react/components/chat/SystemPromptField.d.ts.map +1 -0
  104. package/dist/react/components/highlighted-code.d.ts +8 -0
  105. package/dist/react/components/highlighted-code.d.ts.map +1 -0
  106. package/dist/react/context.d.ts +11 -0
  107. package/dist/react/context.d.ts.map +1 -0
  108. package/dist/react/hooks/use-composer-commands.d.ts +21 -0
  109. package/dist/react/hooks/use-composer-commands.d.ts.map +1 -0
  110. package/dist/react/hooks/use-suggested-prompts.d.ts +29 -0
  111. package/dist/react/hooks/use-suggested-prompts.d.ts.map +1 -0
  112. package/dist/react/index.d.ts +17 -0
  113. package/dist/react/index.d.ts.map +1 -0
  114. package/dist/react/lib/parse-assistant-error.d.ts +9 -0
  115. package/dist/react/lib/parse-assistant-error.d.ts.map +1 -0
  116. package/dist/react/lib/prompt-icons.d.ts +5 -0
  117. package/dist/react/lib/prompt-icons.d.ts.map +1 -0
  118. package/dist/react/types.d.ts +69 -0
  119. package/dist/react/types.d.ts.map +1 -0
  120. package/dist/react/utils/cn.d.ts +2 -0
  121. package/dist/react/utils/cn.d.ts.map +1 -0
  122. package/package.json +16 -5
  123. package/src/core/chat-activity.ts +0 -107
  124. package/src/core/chat-commands.ts +0 -173
  125. package/src/core/chat-history.ts +0 -113
  126. package/src/core/chat-reply-suggestions-parse.ts +0 -119
  127. package/src/core/code-highlight.ts +0 -20
  128. package/src/core/create-assistant-store.ts +0 -639
  129. package/src/core/fetch-suggested-prompts.ts +0 -53
  130. package/src/core/index.ts +0 -125
  131. package/src/core/interactive-tools/choices.ts +0 -155
  132. package/src/core/interactive-tools/confirmation.ts +0 -63
  133. package/src/core/interactive-tools/constants.ts +0 -22
  134. package/src/core/interactive-tools/execute.ts +0 -70
  135. package/src/core/interactive-tools/index.ts +0 -41
  136. package/src/core/interactive-tools/suggestions.ts +0 -87
  137. package/src/core/interactive-tools/waiters.ts +0 -55
  138. package/src/core/llm-chat.ts +0 -686
  139. package/src/core/llm-config.ts +0 -101
  140. package/src/core/llm-models.ts +0 -96
  141. package/src/core/llm-provider.ts +0 -99
  142. package/src/core/llm-settings-storage.ts +0 -331
  143. package/src/core/llm-sse.ts +0 -166
  144. package/src/core/llm-types.ts +0 -52
  145. package/src/core/markdown-utils.ts +0 -11
  146. package/src/core/prepare-markdown.ts +0 -38
  147. package/src/core/types.ts +0 -86
  148. package/src/css.d.ts +0 -1
  149. package/src/react/Assistant.tsx +0 -358
  150. package/src/react/components/HighlightedJsonCode.tsx +0 -24
  151. package/src/react/components/MarkdownContent.tsx +0 -98
  152. package/src/react/components/MarkdownEditor.tsx +0 -60
  153. package/src/react/components/MermaidDiagram.tsx +0 -139
  154. package/src/react/components/ModelSelector.tsx +0 -243
  155. package/src/react/components/chat/AssistantErrorCallout.tsx +0 -79
  156. package/src/react/components/chat/ChatActivity.tsx +0 -274
  157. package/src/react/components/chat/ChatComposer.tsx +0 -189
  158. package/src/react/components/chat/ChatEmptyState.tsx +0 -145
  159. package/src/react/components/chat/ChatInteractivePrompt/choices-prompt.tsx +0 -262
  160. package/src/react/components/chat/ChatInteractivePrompt/confirmation-prompt.tsx +0 -97
  161. package/src/react/components/chat/ChatInteractivePrompt/index.tsx +0 -60
  162. package/src/react/components/chat/ChatInteractivePrompt/shell.tsx +0 -60
  163. package/src/react/components/chat/ChatInteractivePrompt/utils.ts +0 -14
  164. package/src/react/components/chat/ChatMessage.tsx +0 -150
  165. package/src/react/components/chat/ChatMessageScroll.tsx +0 -116
  166. package/src/react/components/chat/ChatReplySuggestions.tsx +0 -231
  167. package/src/react/components/chat/ComposerCommandMenu.tsx +0 -69
  168. package/src/react/components/chat/LlmSettingsStrip.tsx +0 -348
  169. package/src/react/components/chat/LlmSetupPrompt.tsx +0 -58
  170. package/src/react/components/chat/LlmUnavailableBanner.tsx +0 -11
  171. package/src/react/components/chat/SuggestedPromptsList.tsx +0 -121
  172. package/src/react/components/chat/SuggestedPromptsStrip.tsx +0 -72
  173. package/src/react/components/chat/SystemPromptField.tsx +0 -107
  174. package/src/react/components/highlighted-code.tsx +0 -107
  175. package/src/react/context.tsx +0 -72
  176. package/src/react/hooks/use-composer-commands.ts +0 -129
  177. package/src/react/hooks/use-suggested-prompts.ts +0 -128
  178. package/src/react/index.ts +0 -39
  179. package/src/react/lib/parse-assistant-error.ts +0 -96
  180. package/src/react/lib/prompt-icons.ts +0 -40
  181. package/src/react/types.ts +0 -83
  182. package/src/react/utils/cn.ts +0 -5
  183. package/test/buildLlmHistory.test.ts +0 -95
  184. package/test/llm-config.test.ts +0 -72
  185. package/test/llmSettingsStorage.test.ts +0 -121
  186. package/test/parse-assistant-error.test.ts +0 -24
  187. package/tsconfig.json +0 -8
  188. /package/{src/styles/assistant.css → dist/styles.css} +0 -0
@@ -1,274 +0,0 @@
1
- import { Check, ChevronRight, Loader2, Wrench } from "lucide-react";
2
- import { useEffect, useState } from "react";
3
- import {
4
- type ChatActivityStep,
5
- chatActivityStepLabel,
6
- formatActivityJsonValue,
7
- isEmptyActivityJson,
8
- stepHasDetails,
9
- } from "../../../core/chat-activity.ts";
10
- import { isInteractiveChatTool } from "../../../core/interactive-tools/index.ts";
11
- import { HighlightedJsonCode } from "../HighlightedJsonCode.tsx";
12
- import { ChatInteractivePrompt } from "./ChatInteractivePrompt/index.tsx";
13
-
14
- interface ChatActivityProps {
15
- steps: ChatActivityStep[];
16
- streaming?: boolean;
17
- }
18
-
19
- const JSON_SCROLL_LINE_THRESHOLD = 10;
20
-
21
- function ActivityJsonCode({ value }: { value: string }) {
22
- const lineCount = value.split("\n").length;
23
- const needsScroll = lineCount > JSON_SCROLL_LINE_THRESHOLD;
24
-
25
- return (
26
- <pre
27
- className="font-mono text-[11px] leading-snug wrap-break-word whitespace-pre-wrap"
28
- style={needsScroll ? { maxHeight: "8rem", overflowY: "auto" } : undefined}
29
- >
30
- <HighlightedJsonCode code={value} className="text-[11px] leading-snug" />
31
- </pre>
32
- );
33
- }
34
-
35
- function JsonBlock({ label, value }: { label: string; value: string }) {
36
- return (
37
- <div className="min-w-0">
38
- <p className="assistant-activity-step__json-label">{label}</p>
39
- <div className="assistant-activity-step__json-block">
40
- <ActivityJsonCode value={value} />
41
- </div>
42
- </div>
43
- );
44
- }
45
-
46
- function StepDetailsBody({ step }: { step: ChatActivityStep }) {
47
- const isInteractive =
48
- step.kind === "tool" && isInteractiveChatTool(step.name);
49
- const isWaiting = isInteractive && step.status === "active";
50
-
51
- if (isInteractive) {
52
- const formattedResult = step.error
53
- ? step.error
54
- : formatActivityJsonValue(step.result);
55
-
56
- return (
57
- <div
58
- className={
59
- isWaiting ? "assistant-activity-step-details--interactive" : undefined
60
- }
61
- >
62
- <ChatInteractivePrompt
63
- toolName={step.name}
64
- args={step.args}
65
- callId={step.callId}
66
- active={step.status === "active"}
67
- />
68
- {formattedResult && step.status !== "active" ? (
69
- <JsonBlock
70
- label={step.status === "error" ? "Error" : "Result"}
71
- value={formattedResult}
72
- />
73
- ) : null}
74
- </div>
75
- );
76
- }
77
-
78
- const formattedArgs = formatActivityJsonValue(step.args);
79
- const formattedResult = step.error
80
- ? step.error
81
- : formatActivityJsonValue(step.result);
82
- const showArgs = formattedArgs && !isEmptyActivityJson(step.args);
83
-
84
- return (
85
- <div className="space-y-2">
86
- {showArgs ? <JsonBlock label="Arguments" value={formattedArgs} /> : null}
87
- {step.status === "active" && !isInteractiveChatTool(step.name) ? (
88
- <p className="text-[11px] text-[var(--text-chat-tertiary)]">Running…</p>
89
- ) : formattedResult ? (
90
- <JsonBlock
91
- label={step.status === "error" ? "Error" : "Result"}
92
- value={formattedResult}
93
- />
94
- ) : null}
95
- </div>
96
- );
97
- }
98
-
99
- function StepStatusIcon({ step }: { step: ChatActivityStep }) {
100
- if (step.status === "active") {
101
- return <Loader2 size={12} className="assistant-icon-spin" aria-hidden />;
102
- }
103
- if (step.status === "error") {
104
- return <Wrench size={12} aria-hidden />;
105
- }
106
- return <Check size={12} strokeWidth={2.5} aria-hidden />;
107
- }
108
-
109
- function stepIconClasses(step: ChatActivityStep): string {
110
- if (step.status === "active") {
111
- return "assistant-trace-icon assistant-trace-icon--active";
112
- }
113
- if (step.status === "error") {
114
- return "assistant-trace-icon assistant-trace-icon--error";
115
- }
116
- return "assistant-trace-icon";
117
- }
118
-
119
- function ActivityStepRow({
120
- step,
121
- expanded,
122
- onToggle,
123
- isLast,
124
- }: {
125
- step: ChatActivityStep;
126
- expanded: boolean;
127
- onToggle: () => void;
128
- isLast: boolean;
129
- }) {
130
- const isExpandable =
131
- stepHasDetails(step) || step.kind === "tool" || step.status === "active";
132
- const isWaitingInteractive =
133
- step.kind === "tool" &&
134
- isInteractiveChatTool(step.name) &&
135
- step.status === "active";
136
- const label = chatActivityStepLabel(step);
137
-
138
- return (
139
- <li
140
- className={
141
- isWaitingInteractive
142
- ? "assistant-activity-step assistant-activity-step--waiting"
143
- : "assistant-activity-step"
144
- }
145
- >
146
- <div className="assistant-activity-step__rail">
147
- <span aria-hidden className={stepIconClasses(step)}>
148
- <StepStatusIcon step={step} />
149
- </span>
150
- {!isLast ? (
151
- <span aria-hidden className="assistant-activity-step__connector" />
152
- ) : null}
153
- </div>
154
-
155
- <div className="assistant-activity-step__body">
156
- {isExpandable ? (
157
- <button
158
- type="button"
159
- className={`assistant-activity-step__header${step.status === "active" ? " assistant-activity-step__header--active" : ""}`}
160
- aria-expanded={expanded}
161
- onClick={onToggle}
162
- >
163
- <ChevronRight
164
- size={14}
165
- className={`assistant-activity-step__chevron${expanded ? " assistant-activity-step__chevron--open" : ""}`}
166
- aria-hidden
167
- />
168
- <span className="assistant-activity-step__label">
169
- <Wrench
170
- size={12}
171
- className="assistant-activity-step__tool-icon"
172
- aria-hidden
173
- />
174
- <span className="assistant-activity-step__label-text">
175
- {label}
176
- </span>
177
- </span>
178
- </button>
179
- ) : (
180
- <div className="assistant-activity-step__header assistant-activity-step__header--static">
181
- <span className="assistant-activity-step__label">
182
- <span className="assistant-activity-step__label-text">
183
- {label}
184
- </span>
185
- </span>
186
- </div>
187
- )}
188
-
189
- {expanded && isExpandable ? (
190
- <div
191
- className={`assistant-activity-step__details${isWaitingInteractive ? " assistant-activity-step__details--interactive" : ""}`}
192
- >
193
- <StepDetailsBody step={step} />
194
- </div>
195
- ) : null}
196
- </div>
197
- </li>
198
- );
199
- }
200
-
201
- function useActivityExpansion(steps: ChatActivityStep[]) {
202
- const [expandedStepId, setExpandedStepId] = useState<string | null>(null);
203
-
204
- useEffect(() => {
205
- const activeStep = steps.find((step) => step.status === "active");
206
- if (!activeStep) return;
207
- setExpandedStepId((current) =>
208
- current === activeStep.id ? current : activeStep.id,
209
- );
210
- }, [steps]);
211
-
212
- function toggleStep(stepId: string) {
213
- setExpandedStepId((current) => (current === stepId ? null : stepId));
214
- }
215
-
216
- return { expandedStepId, toggleStep };
217
- }
218
-
219
- export function ChatActivity({ steps, streaming }: ChatActivityProps) {
220
- const [panelCollapsed, setPanelCollapsed] = useState(false);
221
- const { expandedStepId, toggleStep } = useActivityExpansion(steps);
222
-
223
- const hasActiveStep = steps.some((step) => step.status === "active");
224
-
225
- useEffect(() => {
226
- if (streaming || hasActiveStep) {
227
- setPanelCollapsed(false);
228
- }
229
- }, [streaming, hasActiveStep]);
230
-
231
- if (steps.length === 0) return null;
232
-
233
- return (
234
- <div
235
- className="assistant-activity assistant-activity--embedded"
236
- role="status"
237
- aria-live={streaming || hasActiveStep ? "polite" : "off"}
238
- >
239
- <div className="assistant-activity__header">
240
- <button
241
- type="button"
242
- className="assistant-activity__toggle"
243
- aria-expanded={!panelCollapsed}
244
- onClick={() => setPanelCollapsed((value) => !value)}
245
- >
246
- <ChevronRight
247
- size={14}
248
- className={`assistant-activity__toggle-chevron${panelCollapsed ? "" : " assistant-activity__toggle-chevron--open"}`}
249
- aria-hidden
250
- />
251
- <span className="assistant-activity__title">Trace</span>
252
- <span className="assistant-activity__count">{steps.length}</span>
253
- </button>
254
- {hasActiveStep ? (
255
- <span className="assistant-activity__live">Live</span>
256
- ) : null}
257
- </div>
258
-
259
- {!panelCollapsed ? (
260
- <ol className="assistant-activity__steps">
261
- {steps.map((step, index) => (
262
- <ActivityStepRow
263
- key={step.id}
264
- step={step}
265
- expanded={expandedStepId === step.id}
266
- onToggle={() => toggleStep(step.id)}
267
- isLast={index === steps.length - 1}
268
- />
269
- ))}
270
- </ol>
271
- ) : null}
272
- </div>
273
- );
274
- }
@@ -1,189 +0,0 @@
1
- import {
2
- Loader2,
3
- SendHorizontal,
4
- Settings2,
5
- Sparkles,
6
- Square,
7
- Trash2,
8
- } from "lucide-react";
9
- import type { KeyboardEvent, RefObject } from "react";
10
- import type { ChatCommandSuggestion } from "../../../core/chat-commands.ts";
11
- import { listChatCommands } from "../../../core/chat-commands.ts";
12
- import { ModelSelector } from "../ModelSelector.tsx";
13
- import { ComposerCommandMenu } from "./ComposerCommandMenu.tsx";
14
-
15
- interface ChatComposerToolbar {
16
- showLlmSettings?: boolean;
17
- onOpenLlmSettings?: () => void;
18
- showGenerateSuggestions?: boolean;
19
- onGenerateSuggestions?: () => void;
20
- suggestionsLoading?: boolean;
21
- showClear?: boolean;
22
- onClear?: () => void;
23
- clearDisabled?: boolean;
24
- }
25
-
26
- interface ChatComposerProps {
27
- input: string;
28
- onInputChange: (value: string) => void;
29
- onSubmit: () => void;
30
- onKeyDown: (event: KeyboardEvent<HTMLTextAreaElement>) => void;
31
- streaming: boolean;
32
- onStop: () => void;
33
- textareaRef?: RefObject<HTMLTextAreaElement | null>;
34
- llmEnabled: boolean;
35
- disabled?: boolean;
36
- /** When true, only blocks message input and send — toolbar actions stay available. */
37
- inputDisabled?: boolean;
38
- placeholder?: string;
39
- toolbar?: ChatComposerToolbar;
40
- commandMenu?: {
41
- open: boolean;
42
- commands: ChatCommandSuggestion[];
43
- selectedIndex: number;
44
- onSelect: (command: ChatCommandSuggestion) => void;
45
- };
46
- }
47
-
48
- export function ChatComposer({
49
- input,
50
- onInputChange,
51
- onSubmit,
52
- onKeyDown,
53
- streaming,
54
- onStop,
55
- textareaRef,
56
- llmEnabled,
57
- disabled,
58
- inputDisabled,
59
- placeholder = "Ask the assistant…",
60
- toolbar,
61
- commandMenu,
62
- }: ChatComposerProps) {
63
- const commandHint = listChatCommands("assistant")
64
- .map((command) => `/${command.name}`)
65
- .join(" · ");
66
-
67
- const blockInput = inputDisabled ?? disabled ?? false;
68
-
69
- const showToolbar =
70
- toolbar?.showLlmSettings ||
71
- toolbar?.showGenerateSuggestions ||
72
- toolbar?.showClear ||
73
- llmEnabled;
74
- const showLlmSettings = toolbar?.showLlmSettings === true;
75
- const showGenerateSuggestions = toolbar?.showGenerateSuggestions === true;
76
- const showClear = toolbar?.showClear === true;
77
-
78
- return (
79
- <div>
80
- <div className="assistant-composer">
81
- {commandMenu?.open ? (
82
- <ComposerCommandMenu
83
- commands={commandMenu.commands}
84
- selectedIndex={commandMenu.selectedIndex}
85
- onSelect={commandMenu.onSelect}
86
- />
87
- ) : null}
88
- <div className="assistant-composer__input-row">
89
- <textarea
90
- ref={textareaRef}
91
- className="assistant-composer__textarea"
92
- value={input}
93
- onChange={(event) => onInputChange(event.target.value)}
94
- onKeyDown={onKeyDown}
95
- placeholder={placeholder}
96
- rows={2}
97
- disabled={blockInput || streaming}
98
- aria-label="Chat message"
99
- />
100
- {streaming ? (
101
- <button
102
- type="button"
103
- className="assistant-btn assistant-btn--secondary assistant-composer__stop"
104
- aria-label="Stop"
105
- onClick={onStop}
106
- >
107
- <Square size={14} className="fill-current" aria-hidden />
108
- </button>
109
- ) : (
110
- <button
111
- type="button"
112
- className="assistant-btn assistant-btn--primary assistant-composer__send"
113
- aria-label="Send"
114
- disabled={blockInput || !input.trim()}
115
- onClick={onSubmit}
116
- >
117
- <SendHorizontal size={16} aria-hidden />
118
- </button>
119
- )}
120
- </div>
121
- {showToolbar ? (
122
- <div className="assistant-composer__footer">
123
- {llmEnabled ? (
124
- <ModelSelector
125
- disabled={blockInput || streaming}
126
- variant="footer"
127
- dropUp
128
- />
129
- ) : null}
130
- {showLlmSettings || showGenerateSuggestions || showClear ? (
131
- <div className="assistant-composer__tools">
132
- {showLlmSettings ? (
133
- <button
134
- type="button"
135
- className="assistant-btn assistant-btn--ghost assistant-composer__tool"
136
- onClick={toolbar?.onOpenLlmSettings}
137
- disabled={streaming}
138
- aria-label="LLM settings"
139
- title="LLM settings"
140
- >
141
- <Settings2 size={16} aria-hidden />
142
- </button>
143
- ) : null}
144
- {showGenerateSuggestions ? (
145
- <button
146
- type="button"
147
- className="assistant-btn assistant-btn--ghost assistant-composer__tool"
148
- onClick={toolbar?.onGenerateSuggestions}
149
- disabled={
150
- blockInput || streaming || toolbar?.suggestionsLoading
151
- }
152
- aria-label="Generate suggestions"
153
- title="Generate suggestions"
154
- >
155
- {toolbar?.suggestionsLoading ? (
156
- <Loader2
157
- size={16}
158
- className="assistant-composer__tool-spinner"
159
- aria-hidden
160
- />
161
- ) : (
162
- <Sparkles size={16} aria-hidden />
163
- )}
164
- </button>
165
- ) : null}
166
- {showClear ? (
167
- <button
168
- type="button"
169
- className="assistant-btn assistant-btn--ghost assistant-composer__tool"
170
- onClick={toolbar?.onClear}
171
- disabled={streaming || toolbar?.clearDisabled}
172
- aria-label="Clear conversation"
173
- title="Clear conversation"
174
- >
175
- <Trash2 size={16} aria-hidden />
176
- </button>
177
- ) : null}
178
- </div>
179
- ) : null}
180
- </div>
181
- ) : null}
182
- </div>
183
- <p className="assistant-composer__hint">
184
- Enter to send · Shift+Enter for new line
185
- {commandHint ? <> · {commandHint}</> : null}
186
- </p>
187
- </div>
188
- );
189
- }
@@ -1,145 +0,0 @@
1
- import {
2
- Database,
3
- Loader2,
4
- RefreshCw,
5
- Search,
6
- Sparkles,
7
- Zap,
8
- } from "lucide-react";
9
- import type { useSuggestedPrompts } from "../../hooks/use-suggested-prompts.ts";
10
- import type { AssistantEmptyStateConfig } from "../../types.ts";
11
- import { SuggestedPromptsList } from "./SuggestedPromptsList.tsx";
12
-
13
- const CAPABILITIES = [
14
- { icon: Database, label: "Catalog" },
15
- { icon: Search, label: "Query" },
16
- { icon: Zap, label: "Mutate" },
17
- ] as const;
18
-
19
- interface ChatEmptyStateProps {
20
- config: AssistantEmptyStateConfig;
21
- suggestions: ReturnType<typeof useSuggestedPrompts>;
22
- onSelect: (prompt: string) => void;
23
- }
24
-
25
- export function ChatEmptyState({
26
- config,
27
- suggestions,
28
- onSelect,
29
- }: ChatEmptyStateProps) {
30
- const { prompts, loading, error, useDynamic, refresh, hasFetched } =
31
- suggestions;
32
-
33
- function handleFetch() {
34
- void refresh();
35
- }
36
-
37
- const showStaticPrompts = !useDynamic && prompts.length > 0;
38
- const showDynamicFetch = useDynamic && !hasFetched && !loading;
39
- const showDynamicResults = useDynamic && (hasFetched || loading);
40
-
41
- return (
42
- <div className="assistant-empty-state">
43
- <div className="assistant-empty-state__card">
44
- <div className="assistant-empty-state__card-glow" aria-hidden />
45
- <div className="assistant-empty-state__card-inner">
46
- <p className="assistant-empty-state__eyebrow">AI Assistant</p>
47
-
48
- <div className="assistant-empty-state__icon-ring" aria-hidden>
49
- <Sparkles size={22} strokeWidth={2} />
50
- </div>
51
-
52
- <h3 className="assistant-empty-state__title">{config.title}</h3>
53
- <p className="assistant-empty-state__description">
54
- {config.description}
55
- </p>
56
-
57
- <div className="assistant-empty-state__capabilities">
58
- {CAPABILITIES.map(({ icon: Icon, label }) => (
59
- <span key={label} className="assistant-empty-state__capability">
60
- <Icon size={12} strokeWidth={2} aria-hidden />
61
- {label}
62
- </span>
63
- ))}
64
- </div>
65
-
66
- {showDynamicFetch ? (
67
- <button
68
- type="button"
69
- className="assistant-empty-state__cta"
70
- onClick={handleFetch}
71
- >
72
- <span className="assistant-empty-state__cta-icon" aria-hidden>
73
- <Sparkles size={18} strokeWidth={2} />
74
- </span>
75
- <span className="assistant-empty-state__cta-text">
76
- <span className="assistant-empty-state__cta-label">
77
- Generate suggestions
78
- </span>
79
- <span className="assistant-empty-state__cta-hint">
80
- Tailored prompts based on your live catalog
81
- </span>
82
- </span>
83
- </button>
84
- ) : null}
85
- </div>
86
- </div>
87
-
88
- {showDynamicResults ? (
89
- <section
90
- className="assistant-empty-state__section"
91
- aria-busy={loading || undefined}
92
- >
93
- <header className="assistant-empty-state__section-header">
94
- <h4 className="assistant-empty-state__section-title">
95
- {loading ? (
96
- <>
97
- <Loader2
98
- size={14}
99
- className="assistant-empty-state__section-spinner"
100
- aria-hidden
101
- />
102
- Generating suggestions
103
- </>
104
- ) : (
105
- <>
106
- <Sparkles size={14} aria-hidden />
107
- Suggested for you
108
- </>
109
- )}
110
- </h4>
111
- {hasFetched && !loading ? (
112
- <button
113
- type="button"
114
- className="assistant-btn assistant-btn--ghost assistant-empty-state__section-refresh"
115
- onClick={handleFetch}
116
- >
117
- <RefreshCw size={14} aria-hidden />
118
- Regenerate
119
- </button>
120
- ) : null}
121
- </header>
122
- <SuggestedPromptsList
123
- prompts={prompts}
124
- loading={loading}
125
- error={error}
126
- onRetry={handleFetch}
127
- retryLoading={loading}
128
- onSelect={onSelect}
129
- />
130
- </section>
131
- ) : null}
132
-
133
- {showStaticPrompts ? (
134
- <section className="assistant-empty-state__section">
135
- <header className="assistant-empty-state__section-header">
136
- <h4 className="assistant-empty-state__section-title">
137
- Quick starts
138
- </h4>
139
- </header>
140
- <SuggestedPromptsList prompts={prompts} onSelect={onSelect} />
141
- </section>
142
- ) : null}
143
- </div>
144
- );
145
- }