@tigmart/ai-ui 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.
@@ -0,0 +1,76 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
3
+ import { ReactNode } from 'react';
4
+ import { MessageRole, MessageStatus, Message } from '@topsoft/ai-types';
5
+ import { ProviderId } from '@topsoft/ai-react';
6
+ export { ProviderId } from '@topsoft/ai-react';
7
+
8
+ interface ChatContainerProps {
9
+ children: ReactNode;
10
+ }
11
+ declare function ChatContainer({ children }: ChatContainerProps): react_jsx_runtime.JSX.Element;
12
+
13
+ interface MessageBubbleProps {
14
+ role: MessageRole;
15
+ text: string;
16
+ status: MessageStatus;
17
+ errorDetail?: string;
18
+ }
19
+ declare function MessageBubble({ role, text, status, errorDetail }: MessageBubbleProps): react_jsx_runtime.JSX.Element;
20
+
21
+ interface MessageListProps {
22
+ messages: Message[];
23
+ isLoading?: boolean;
24
+ emptyText?: string;
25
+ loadingText?: string;
26
+ error?: string | null;
27
+ }
28
+ declare const MessageList: react.ForwardRefExoticComponent<MessageListProps & react.RefAttributes<HTMLDivElement>>;
29
+
30
+ interface ChatComposerProps {
31
+ value: string;
32
+ onChange: (value: string) => void;
33
+ onSend: () => void;
34
+ onStop: () => void;
35
+ isLoading: boolean;
36
+ disabled?: boolean;
37
+ }
38
+ declare const ChatComposer: react.ForwardRefExoticComponent<ChatComposerProps & react.RefAttributes<HTMLTextAreaElement>>;
39
+
40
+ interface ConversationItem {
41
+ id: string;
42
+ title: string;
43
+ projectId?: string | null;
44
+ }
45
+ interface ProjectItem {
46
+ id: string;
47
+ name: string;
48
+ }
49
+ interface ChatSidebarProps {
50
+ conversations: ConversationItem[];
51
+ currentId: string;
52
+ onSelect: (id: string) => void;
53
+ onNewChat: () => void;
54
+ onRename: (id: string, newTitle: string) => void;
55
+ onDelete: (id: string) => void;
56
+ projects: ProjectItem[];
57
+ onNewProject: () => void;
58
+ onAssignToProject: (threadId: string, projectId: string | null) => void;
59
+ }
60
+ declare function ChatSidebar({ conversations, currentId, onSelect, onNewChat, onRename, onDelete, projects, onNewProject, onAssignToProject, }: ChatSidebarProps): react_jsx_runtime.JSX.Element;
61
+
62
+ interface ProviderSelectorProps {
63
+ value: ProviderId;
64
+ onChange: (value: ProviderId) => void;
65
+ disabled?: boolean;
66
+ }
67
+ declare function ProviderSelector({ value, onChange, disabled }: ProviderSelectorProps): react_jsx_runtime.JSX.Element;
68
+
69
+ interface ComparisonToggleProps {
70
+ enabled: boolean;
71
+ onChange: (enabled: boolean) => void;
72
+ disabled?: boolean;
73
+ }
74
+ declare function ComparisonToggle({ enabled, onChange, disabled }: ComparisonToggleProps): react_jsx_runtime.JSX.Element;
75
+
76
+ export { ChatComposer, type ChatComposerProps, ChatContainer, type ChatContainerProps, ChatSidebar, type ChatSidebarProps, ComparisonToggle, type ComparisonToggleProps, type ConversationItem, MessageBubble, type MessageBubbleProps, MessageList, type MessageListProps, type ProjectItem, ProviderSelector, type ProviderSelectorProps };
@@ -0,0 +1,76 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
3
+ import { ReactNode } from 'react';
4
+ import { MessageRole, MessageStatus, Message } from '@topsoft/ai-types';
5
+ import { ProviderId } from '@topsoft/ai-react';
6
+ export { ProviderId } from '@topsoft/ai-react';
7
+
8
+ interface ChatContainerProps {
9
+ children: ReactNode;
10
+ }
11
+ declare function ChatContainer({ children }: ChatContainerProps): react_jsx_runtime.JSX.Element;
12
+
13
+ interface MessageBubbleProps {
14
+ role: MessageRole;
15
+ text: string;
16
+ status: MessageStatus;
17
+ errorDetail?: string;
18
+ }
19
+ declare function MessageBubble({ role, text, status, errorDetail }: MessageBubbleProps): react_jsx_runtime.JSX.Element;
20
+
21
+ interface MessageListProps {
22
+ messages: Message[];
23
+ isLoading?: boolean;
24
+ emptyText?: string;
25
+ loadingText?: string;
26
+ error?: string | null;
27
+ }
28
+ declare const MessageList: react.ForwardRefExoticComponent<MessageListProps & react.RefAttributes<HTMLDivElement>>;
29
+
30
+ interface ChatComposerProps {
31
+ value: string;
32
+ onChange: (value: string) => void;
33
+ onSend: () => void;
34
+ onStop: () => void;
35
+ isLoading: boolean;
36
+ disabled?: boolean;
37
+ }
38
+ declare const ChatComposer: react.ForwardRefExoticComponent<ChatComposerProps & react.RefAttributes<HTMLTextAreaElement>>;
39
+
40
+ interface ConversationItem {
41
+ id: string;
42
+ title: string;
43
+ projectId?: string | null;
44
+ }
45
+ interface ProjectItem {
46
+ id: string;
47
+ name: string;
48
+ }
49
+ interface ChatSidebarProps {
50
+ conversations: ConversationItem[];
51
+ currentId: string;
52
+ onSelect: (id: string) => void;
53
+ onNewChat: () => void;
54
+ onRename: (id: string, newTitle: string) => void;
55
+ onDelete: (id: string) => void;
56
+ projects: ProjectItem[];
57
+ onNewProject: () => void;
58
+ onAssignToProject: (threadId: string, projectId: string | null) => void;
59
+ }
60
+ declare function ChatSidebar({ conversations, currentId, onSelect, onNewChat, onRename, onDelete, projects, onNewProject, onAssignToProject, }: ChatSidebarProps): react_jsx_runtime.JSX.Element;
61
+
62
+ interface ProviderSelectorProps {
63
+ value: ProviderId;
64
+ onChange: (value: ProviderId) => void;
65
+ disabled?: boolean;
66
+ }
67
+ declare function ProviderSelector({ value, onChange, disabled }: ProviderSelectorProps): react_jsx_runtime.JSX.Element;
68
+
69
+ interface ComparisonToggleProps {
70
+ enabled: boolean;
71
+ onChange: (enabled: boolean) => void;
72
+ disabled?: boolean;
73
+ }
74
+ declare function ComparisonToggle({ enabled, onChange, disabled }: ComparisonToggleProps): react_jsx_runtime.JSX.Element;
75
+
76
+ export { ChatComposer, type ChatComposerProps, ChatContainer, type ChatContainerProps, ChatSidebar, type ChatSidebarProps, ComparisonToggle, type ComparisonToggleProps, type ConversationItem, MessageBubble, type MessageBubbleProps, MessageList, type MessageListProps, type ProjectItem, ProviderSelector, type ProviderSelectorProps };
package/dist/index.js ADDED
@@ -0,0 +1,564 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ChatComposer: () => ChatComposer,
24
+ ChatContainer: () => ChatContainer,
25
+ ChatSidebar: () => ChatSidebar,
26
+ ComparisonToggle: () => ComparisonToggle,
27
+ MessageBubble: () => MessageBubble,
28
+ MessageList: () => MessageList,
29
+ ProviderSelector: () => ProviderSelector
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/ChatContainer.tsx
34
+ var import_jsx_runtime = require("react/jsx-runtime");
35
+ function ChatContainer({ children }) {
36
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
37
+ "div",
38
+ {
39
+ style: {
40
+ display: "flex",
41
+ flexDirection: "column",
42
+ width: "100%",
43
+ maxWidth: "720px",
44
+ flex: 1
45
+ },
46
+ children
47
+ }
48
+ );
49
+ }
50
+
51
+ // src/MessageBubble.tsx
52
+ var import_jsx_runtime2 = require("react/jsx-runtime");
53
+ function MessageBubble({ role, text, status, errorDetail }) {
54
+ const isError = status === "error";
55
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
56
+ "div",
57
+ {
58
+ style: {
59
+ alignSelf: role === "user" ? "flex-end" : "flex-start",
60
+ maxWidth: "85%"
61
+ },
62
+ children: [
63
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
64
+ "div",
65
+ {
66
+ style: {
67
+ fontSize: "0.7rem",
68
+ fontWeight: 600,
69
+ marginBottom: "4px",
70
+ textTransform: "uppercase",
71
+ opacity: 0.6
72
+ },
73
+ children: role
74
+ }
75
+ ),
76
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
77
+ "div",
78
+ {
79
+ style: {
80
+ padding: "10px 14px",
81
+ borderRadius: "8px",
82
+ backgroundColor: isError ? "#fee2e2" : role === "user" ? "#1d4ed8" : "#f0f0f0",
83
+ color: isError ? "#991b1b" : role === "user" ? "#fff" : "#111",
84
+ fontSize: "0.9rem",
85
+ lineHeight: 1.5,
86
+ whiteSpace: "pre-wrap",
87
+ wordBreak: "break-word"
88
+ },
89
+ children: [
90
+ text || (status === "streaming" ? "\u2026" : ""),
91
+ isError && errorDetail && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginTop: "6px", fontSize: "0.8rem" }, children: [
92
+ "Error: ",
93
+ errorDetail
94
+ ] })
95
+ ]
96
+ }
97
+ )
98
+ ]
99
+ }
100
+ );
101
+ }
102
+
103
+ // src/MessageList.tsx
104
+ var import_react = require("react");
105
+ var import_jsx_runtime3 = require("react/jsx-runtime");
106
+ function extractText(msg) {
107
+ return msg.parts.filter((p) => p.type === "text").map((p) => p.type === "text" ? p.text : "").join("");
108
+ }
109
+ var MessageList = (0, import_react.forwardRef)(
110
+ function MessageList2({
111
+ messages,
112
+ isLoading = false,
113
+ emptyText = "Start a conversation\u2026",
114
+ loadingText = "Assistant is typing\u2026",
115
+ error
116
+ }, ref) {
117
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "4px" }, children: [
118
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
119
+ "div",
120
+ {
121
+ style: {
122
+ flex: 1,
123
+ overflowY: "auto",
124
+ display: "flex",
125
+ flexDirection: "column",
126
+ gap: "12px",
127
+ marginBottom: "16px",
128
+ minHeight: "400px",
129
+ maxHeight: "600px",
130
+ border: "1px solid #ddd",
131
+ borderRadius: "8px",
132
+ padding: "16px",
133
+ backgroundColor: "#fff"
134
+ },
135
+ children: [
136
+ messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
137
+ "p",
138
+ {
139
+ style: {
140
+ color: "#999",
141
+ fontSize: "0.875rem",
142
+ margin: "auto",
143
+ textAlign: "center"
144
+ },
145
+ children: emptyText
146
+ }
147
+ ) : messages.map((msg) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
148
+ MessageBubble,
149
+ {
150
+ role: msg.role,
151
+ text: extractText(msg),
152
+ status: msg.status,
153
+ errorDetail: msg.error
154
+ },
155
+ msg.id
156
+ )),
157
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ref })
158
+ ]
159
+ }
160
+ ),
161
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
162
+ "div",
163
+ {
164
+ style: {
165
+ display: "flex",
166
+ alignItems: "center",
167
+ gap: "8px",
168
+ minHeight: "24px",
169
+ marginBottom: "8px",
170
+ fontSize: "0.8rem",
171
+ color: "#555"
172
+ },
173
+ children: isLoading && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: loadingText })
174
+ }
175
+ ),
176
+ error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
177
+ "div",
178
+ {
179
+ style: {
180
+ padding: "10px 14px",
181
+ borderRadius: "8px",
182
+ backgroundColor: "#fee2e2",
183
+ color: "#991b1b",
184
+ fontSize: "0.85rem",
185
+ marginBottom: "8px"
186
+ },
187
+ children: error
188
+ }
189
+ )
190
+ ] });
191
+ }
192
+ );
193
+
194
+ // src/ChatComposer.tsx
195
+ var import_react2 = require("react");
196
+ var import_jsx_runtime4 = require("react/jsx-runtime");
197
+ var ChatComposer = (0, import_react2.forwardRef)(
198
+ function ChatComposer2({ value, onChange, onSend, onStop, isLoading, disabled = false }, ref) {
199
+ const sendDisabled = isLoading || disabled || !value.trim();
200
+ function handleKeyDown(e) {
201
+ if (e.key === "Enter" && !e.shiftKey) {
202
+ e.preventDefault();
203
+ if (!sendDisabled) onSend();
204
+ }
205
+ }
206
+ function handleChange(e) {
207
+ onChange(e.target.value);
208
+ }
209
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: "8px", alignItems: "flex-end" }, children: [
210
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
211
+ "textarea",
212
+ {
213
+ ref,
214
+ style: {
215
+ flex: 1,
216
+ padding: "10px 12px",
217
+ fontSize: "0.9rem",
218
+ borderRadius: "8px",
219
+ border: "1px solid #ccc",
220
+ resize: "none",
221
+ lineHeight: 1.5,
222
+ outline: "none",
223
+ fontFamily: "inherit"
224
+ },
225
+ rows: 3,
226
+ value,
227
+ onChange: handleChange,
228
+ onKeyDown: handleKeyDown,
229
+ placeholder: "Type a message\u2026 (Enter to send, Shift+Enter for newline)",
230
+ disabled
231
+ }
232
+ ),
233
+ isLoading && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
234
+ "button",
235
+ {
236
+ style: {
237
+ padding: "10px 18px",
238
+ borderRadius: "8px",
239
+ border: "none",
240
+ cursor: "pointer",
241
+ fontWeight: 600,
242
+ fontSize: "0.875rem",
243
+ backgroundColor: "#e5e7eb",
244
+ color: "#374151",
245
+ flexShrink: 0
246
+ },
247
+ onClick: onStop,
248
+ children: "Stop"
249
+ }
250
+ ),
251
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
252
+ "button",
253
+ {
254
+ style: {
255
+ padding: "10px 18px",
256
+ borderRadius: "8px",
257
+ border: "none",
258
+ cursor: sendDisabled ? "not-allowed" : "pointer",
259
+ fontWeight: 600,
260
+ fontSize: "0.875rem",
261
+ backgroundColor: sendDisabled ? "#93c5fd" : "#1d4ed8",
262
+ color: "#fff",
263
+ opacity: sendDisabled ? 0.7 : 1,
264
+ flexShrink: 0
265
+ },
266
+ onClick: onSend,
267
+ disabled: sendDisabled,
268
+ children: "Send"
269
+ }
270
+ )
271
+ ] });
272
+ }
273
+ );
274
+
275
+ // src/ChatSidebar.tsx
276
+ var import_jsx_runtime5 = require("react/jsx-runtime");
277
+ var sidebarStyle = {
278
+ width: "240px",
279
+ flexShrink: 0,
280
+ borderRight: "1px solid #e2e8f0",
281
+ backgroundColor: "#f8fafc",
282
+ display: "flex",
283
+ flexDirection: "column",
284
+ overflow: "hidden"
285
+ };
286
+ var headerStyle = {
287
+ padding: "12px",
288
+ borderBottom: "1px solid #e2e8f0",
289
+ display: "flex",
290
+ flexDirection: "column",
291
+ gap: "6px"
292
+ };
293
+ var newChatButtonStyle = {
294
+ width: "100%",
295
+ padding: "8px 12px",
296
+ backgroundColor: "#2563eb",
297
+ color: "#fff",
298
+ border: "none",
299
+ borderRadius: "6px",
300
+ fontSize: "14px",
301
+ fontWeight: 500,
302
+ cursor: "pointer",
303
+ textAlign: "left"
304
+ };
305
+ var newProjectButtonStyle = {
306
+ width: "100%",
307
+ padding: "6px 12px",
308
+ backgroundColor: "transparent",
309
+ color: "#6b7280",
310
+ border: "1px dashed #cbd5e1",
311
+ borderRadius: "6px",
312
+ fontSize: "13px",
313
+ cursor: "pointer",
314
+ textAlign: "left"
315
+ };
316
+ var listStyle = {
317
+ flex: 1,
318
+ overflowY: "auto",
319
+ padding: "8px 6px"
320
+ };
321
+ var sectionLabelStyle = {
322
+ padding: "6px 8px 2px",
323
+ fontSize: "11px",
324
+ fontWeight: 600,
325
+ color: "#94a3b8",
326
+ textTransform: "uppercase",
327
+ letterSpacing: "0.05em"
328
+ };
329
+ function rowStyle(isActive) {
330
+ return {
331
+ display: "flex",
332
+ alignItems: "center",
333
+ width: "100%",
334
+ marginBottom: "2px",
335
+ borderRadius: "6px",
336
+ backgroundColor: isActive ? "#dbeafe" : "transparent"
337
+ };
338
+ }
339
+ function labelButtonStyle(isActive) {
340
+ return {
341
+ flex: 1,
342
+ minWidth: 0,
343
+ padding: "7px 8px",
344
+ backgroundColor: "transparent",
345
+ color: isActive ? "#1d4ed8" : "#374151",
346
+ border: "none",
347
+ borderRadius: "6px",
348
+ fontSize: "13px",
349
+ fontWeight: isActive ? 500 : 400,
350
+ cursor: "pointer",
351
+ textAlign: "left",
352
+ whiteSpace: "nowrap",
353
+ overflow: "hidden",
354
+ textOverflow: "ellipsis"
355
+ };
356
+ }
357
+ var actionButtonStyle = {
358
+ flexShrink: 0,
359
+ padding: "4px 5px",
360
+ backgroundColor: "transparent",
361
+ border: "none",
362
+ borderRadius: "4px",
363
+ fontSize: "12px",
364
+ cursor: "pointer",
365
+ color: "#6b7280",
366
+ lineHeight: 1
367
+ };
368
+ var projectSelectStyle = {
369
+ flexShrink: 0,
370
+ fontSize: "11px",
371
+ border: "1px solid #e2e8f0",
372
+ borderRadius: "4px",
373
+ backgroundColor: "#fff",
374
+ color: "#6b7280",
375
+ padding: "2px 2px",
376
+ cursor: "pointer",
377
+ maxWidth: "70px"
378
+ };
379
+ function ChatSidebar({
380
+ conversations,
381
+ currentId,
382
+ onSelect,
383
+ onNewChat,
384
+ onRename,
385
+ onDelete,
386
+ projects,
387
+ onNewProject,
388
+ onAssignToProject
389
+ }) {
390
+ function handleRename(id, currentTitle) {
391
+ const next = window.prompt("Rename chat:", currentTitle);
392
+ if (next !== null && next.trim()) {
393
+ onRename(id, next.trim());
394
+ }
395
+ }
396
+ function renderChatRow(conv) {
397
+ const isActive = conv.id === currentId;
398
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: rowStyle(isActive), children: [
399
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
400
+ "button",
401
+ {
402
+ style: labelButtonStyle(isActive),
403
+ onClick: () => onSelect(conv.id),
404
+ title: conv.title,
405
+ children: conv.title
406
+ }
407
+ ),
408
+ projects.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
409
+ "select",
410
+ {
411
+ style: projectSelectStyle,
412
+ value: conv.projectId ?? "",
413
+ onChange: (e) => onAssignToProject(conv.id, e.target.value || null),
414
+ title: "Move to project",
415
+ "aria-label": "Assign to project",
416
+ children: [
417
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: "", children: "\u2014" }),
418
+ projects.map((p) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: p.id, children: p.name }, p.id))
419
+ ]
420
+ }
421
+ ),
422
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
423
+ "button",
424
+ {
425
+ style: actionButtonStyle,
426
+ onClick: () => handleRename(conv.id, conv.title),
427
+ title: "Rename",
428
+ "aria-label": "Rename chat",
429
+ children: "\u270F\uFE0F"
430
+ }
431
+ ),
432
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
433
+ "button",
434
+ {
435
+ style: { ...actionButtonStyle, marginRight: "4px" },
436
+ onClick: () => onDelete(conv.id),
437
+ title: "Delete",
438
+ "aria-label": "Delete chat",
439
+ children: "\u{1F5D1}\uFE0F"
440
+ }
441
+ )
442
+ ] }, conv.id);
443
+ }
444
+ const ungrouped = conversations.filter((c) => !c.projectId);
445
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("aside", { style: sidebarStyle, children: [
446
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: headerStyle, children: [
447
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { style: newChatButtonStyle, onClick: onNewChat, children: "+ New chat" }),
448
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { style: newProjectButtonStyle, onClick: onNewProject, children: "\u{1F4C1} New project" })
449
+ ] }),
450
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: listStyle, children: [
451
+ projects.map((project) => {
452
+ const chats = conversations.filter((c) => c.projectId === project.id);
453
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { marginBottom: "8px" }, children: [
454
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: sectionLabelStyle, children: [
455
+ "\u{1F4C1} ",
456
+ project.name
457
+ ] }),
458
+ chats.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
459
+ "div",
460
+ {
461
+ style: {
462
+ padding: "4px 10px",
463
+ fontSize: "12px",
464
+ color: "#cbd5e1",
465
+ fontStyle: "italic"
466
+ },
467
+ children: "No chats yet"
468
+ }
469
+ ) : chats.map(renderChatRow)
470
+ ] }, project.id);
471
+ }),
472
+ ungrouped.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
473
+ projects.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: sectionLabelStyle, children: "Ungrouped" }),
474
+ ungrouped.map(renderChatRow)
475
+ ] })
476
+ ] })
477
+ ] });
478
+ }
479
+
480
+ // src/ProviderSelector.tsx
481
+ var import_jsx_runtime6 = require("react/jsx-runtime");
482
+ var PROVIDERS = [
483
+ { id: "openai", label: "OpenAI" },
484
+ { id: "claude", label: "Claude" },
485
+ { id: "gemini", label: "Gemini" }
486
+ ];
487
+ var wrapperStyle = {
488
+ display: "flex",
489
+ alignItems: "center",
490
+ gap: "8px",
491
+ marginBottom: "16px"
492
+ };
493
+ var labelStyle = {
494
+ fontSize: "13px",
495
+ fontWeight: 500,
496
+ color: "#374151"
497
+ };
498
+ var selectStyle = {
499
+ padding: "5px 10px",
500
+ fontSize: "13px",
501
+ borderRadius: "6px",
502
+ border: "1px solid #d1d5db",
503
+ backgroundColor: "#fff",
504
+ cursor: "pointer",
505
+ color: "#111827"
506
+ };
507
+ function ProviderSelector({ value, onChange, disabled }) {
508
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: wrapperStyle, children: [
509
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { htmlFor: "provider-select", style: labelStyle, children: "Provider:" }),
510
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
511
+ "select",
512
+ {
513
+ id: "provider-select",
514
+ style: selectStyle,
515
+ value,
516
+ disabled,
517
+ onChange: (e) => onChange(e.target.value),
518
+ children: PROVIDERS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: p.id, children: p.label }, p.id))
519
+ }
520
+ )
521
+ ] });
522
+ }
523
+
524
+ // src/ComparisonToggle.tsx
525
+ var import_jsx_runtime7 = require("react/jsx-runtime");
526
+ function ComparisonToggle({ enabled, onChange, disabled }) {
527
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
528
+ "label",
529
+ {
530
+ style: {
531
+ display: "flex",
532
+ alignItems: "center",
533
+ gap: "8px",
534
+ fontSize: "0.875rem",
535
+ cursor: disabled ? "not-allowed" : "pointer",
536
+ opacity: disabled ? 0.5 : 1,
537
+ userSelect: "none"
538
+ },
539
+ children: [
540
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
541
+ "input",
542
+ {
543
+ type: "checkbox",
544
+ checked: enabled,
545
+ onChange: (e) => onChange(e.target.checked),
546
+ disabled,
547
+ style: { cursor: disabled ? "not-allowed" : "pointer" }
548
+ }
549
+ ),
550
+ "Comparison mode"
551
+ ]
552
+ }
553
+ );
554
+ }
555
+ // Annotate the CommonJS export names for ESM import in node:
556
+ 0 && (module.exports = {
557
+ ChatComposer,
558
+ ChatContainer,
559
+ ChatSidebar,
560
+ ComparisonToggle,
561
+ MessageBubble,
562
+ MessageList,
563
+ ProviderSelector
564
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,531 @@
1
+ // src/ChatContainer.tsx
2
+ import { jsx } from "react/jsx-runtime";
3
+ function ChatContainer({ children }) {
4
+ return /* @__PURE__ */ jsx(
5
+ "div",
6
+ {
7
+ style: {
8
+ display: "flex",
9
+ flexDirection: "column",
10
+ width: "100%",
11
+ maxWidth: "720px",
12
+ flex: 1
13
+ },
14
+ children
15
+ }
16
+ );
17
+ }
18
+
19
+ // src/MessageBubble.tsx
20
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
21
+ function MessageBubble({ role, text, status, errorDetail }) {
22
+ const isError = status === "error";
23
+ return /* @__PURE__ */ jsxs(
24
+ "div",
25
+ {
26
+ style: {
27
+ alignSelf: role === "user" ? "flex-end" : "flex-start",
28
+ maxWidth: "85%"
29
+ },
30
+ children: [
31
+ /* @__PURE__ */ jsx2(
32
+ "div",
33
+ {
34
+ style: {
35
+ fontSize: "0.7rem",
36
+ fontWeight: 600,
37
+ marginBottom: "4px",
38
+ textTransform: "uppercase",
39
+ opacity: 0.6
40
+ },
41
+ children: role
42
+ }
43
+ ),
44
+ /* @__PURE__ */ jsxs(
45
+ "div",
46
+ {
47
+ style: {
48
+ padding: "10px 14px",
49
+ borderRadius: "8px",
50
+ backgroundColor: isError ? "#fee2e2" : role === "user" ? "#1d4ed8" : "#f0f0f0",
51
+ color: isError ? "#991b1b" : role === "user" ? "#fff" : "#111",
52
+ fontSize: "0.9rem",
53
+ lineHeight: 1.5,
54
+ whiteSpace: "pre-wrap",
55
+ wordBreak: "break-word"
56
+ },
57
+ children: [
58
+ text || (status === "streaming" ? "\u2026" : ""),
59
+ isError && errorDetail && /* @__PURE__ */ jsxs("div", { style: { marginTop: "6px", fontSize: "0.8rem" }, children: [
60
+ "Error: ",
61
+ errorDetail
62
+ ] })
63
+ ]
64
+ }
65
+ )
66
+ ]
67
+ }
68
+ );
69
+ }
70
+
71
+ // src/MessageList.tsx
72
+ import { forwardRef } from "react";
73
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
74
+ function extractText(msg) {
75
+ return msg.parts.filter((p) => p.type === "text").map((p) => p.type === "text" ? p.text : "").join("");
76
+ }
77
+ var MessageList = forwardRef(
78
+ function MessageList2({
79
+ messages,
80
+ isLoading = false,
81
+ emptyText = "Start a conversation\u2026",
82
+ loadingText = "Assistant is typing\u2026",
83
+ error
84
+ }, ref) {
85
+ return /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: "column", gap: "4px" }, children: [
86
+ /* @__PURE__ */ jsxs2(
87
+ "div",
88
+ {
89
+ style: {
90
+ flex: 1,
91
+ overflowY: "auto",
92
+ display: "flex",
93
+ flexDirection: "column",
94
+ gap: "12px",
95
+ marginBottom: "16px",
96
+ minHeight: "400px",
97
+ maxHeight: "600px",
98
+ border: "1px solid #ddd",
99
+ borderRadius: "8px",
100
+ padding: "16px",
101
+ backgroundColor: "#fff"
102
+ },
103
+ children: [
104
+ messages.length === 0 ? /* @__PURE__ */ jsx3(
105
+ "p",
106
+ {
107
+ style: {
108
+ color: "#999",
109
+ fontSize: "0.875rem",
110
+ margin: "auto",
111
+ textAlign: "center"
112
+ },
113
+ children: emptyText
114
+ }
115
+ ) : messages.map((msg) => /* @__PURE__ */ jsx3(
116
+ MessageBubble,
117
+ {
118
+ role: msg.role,
119
+ text: extractText(msg),
120
+ status: msg.status,
121
+ errorDetail: msg.error
122
+ },
123
+ msg.id
124
+ )),
125
+ /* @__PURE__ */ jsx3("div", { ref })
126
+ ]
127
+ }
128
+ ),
129
+ /* @__PURE__ */ jsx3(
130
+ "div",
131
+ {
132
+ style: {
133
+ display: "flex",
134
+ alignItems: "center",
135
+ gap: "8px",
136
+ minHeight: "24px",
137
+ marginBottom: "8px",
138
+ fontSize: "0.8rem",
139
+ color: "#555"
140
+ },
141
+ children: isLoading && /* @__PURE__ */ jsx3("span", { children: loadingText })
142
+ }
143
+ ),
144
+ error && /* @__PURE__ */ jsx3(
145
+ "div",
146
+ {
147
+ style: {
148
+ padding: "10px 14px",
149
+ borderRadius: "8px",
150
+ backgroundColor: "#fee2e2",
151
+ color: "#991b1b",
152
+ fontSize: "0.85rem",
153
+ marginBottom: "8px"
154
+ },
155
+ children: error
156
+ }
157
+ )
158
+ ] });
159
+ }
160
+ );
161
+
162
+ // src/ChatComposer.tsx
163
+ import { forwardRef as forwardRef2 } from "react";
164
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
165
+ var ChatComposer = forwardRef2(
166
+ function ChatComposer2({ value, onChange, onSend, onStop, isLoading, disabled = false }, ref) {
167
+ const sendDisabled = isLoading || disabled || !value.trim();
168
+ function handleKeyDown(e) {
169
+ if (e.key === "Enter" && !e.shiftKey) {
170
+ e.preventDefault();
171
+ if (!sendDisabled) onSend();
172
+ }
173
+ }
174
+ function handleChange(e) {
175
+ onChange(e.target.value);
176
+ }
177
+ return /* @__PURE__ */ jsxs3("div", { style: { display: "flex", gap: "8px", alignItems: "flex-end" }, children: [
178
+ /* @__PURE__ */ jsx4(
179
+ "textarea",
180
+ {
181
+ ref,
182
+ style: {
183
+ flex: 1,
184
+ padding: "10px 12px",
185
+ fontSize: "0.9rem",
186
+ borderRadius: "8px",
187
+ border: "1px solid #ccc",
188
+ resize: "none",
189
+ lineHeight: 1.5,
190
+ outline: "none",
191
+ fontFamily: "inherit"
192
+ },
193
+ rows: 3,
194
+ value,
195
+ onChange: handleChange,
196
+ onKeyDown: handleKeyDown,
197
+ placeholder: "Type a message\u2026 (Enter to send, Shift+Enter for newline)",
198
+ disabled
199
+ }
200
+ ),
201
+ isLoading && /* @__PURE__ */ jsx4(
202
+ "button",
203
+ {
204
+ style: {
205
+ padding: "10px 18px",
206
+ borderRadius: "8px",
207
+ border: "none",
208
+ cursor: "pointer",
209
+ fontWeight: 600,
210
+ fontSize: "0.875rem",
211
+ backgroundColor: "#e5e7eb",
212
+ color: "#374151",
213
+ flexShrink: 0
214
+ },
215
+ onClick: onStop,
216
+ children: "Stop"
217
+ }
218
+ ),
219
+ /* @__PURE__ */ jsx4(
220
+ "button",
221
+ {
222
+ style: {
223
+ padding: "10px 18px",
224
+ borderRadius: "8px",
225
+ border: "none",
226
+ cursor: sendDisabled ? "not-allowed" : "pointer",
227
+ fontWeight: 600,
228
+ fontSize: "0.875rem",
229
+ backgroundColor: sendDisabled ? "#93c5fd" : "#1d4ed8",
230
+ color: "#fff",
231
+ opacity: sendDisabled ? 0.7 : 1,
232
+ flexShrink: 0
233
+ },
234
+ onClick: onSend,
235
+ disabled: sendDisabled,
236
+ children: "Send"
237
+ }
238
+ )
239
+ ] });
240
+ }
241
+ );
242
+
243
+ // src/ChatSidebar.tsx
244
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
245
+ var sidebarStyle = {
246
+ width: "240px",
247
+ flexShrink: 0,
248
+ borderRight: "1px solid #e2e8f0",
249
+ backgroundColor: "#f8fafc",
250
+ display: "flex",
251
+ flexDirection: "column",
252
+ overflow: "hidden"
253
+ };
254
+ var headerStyle = {
255
+ padding: "12px",
256
+ borderBottom: "1px solid #e2e8f0",
257
+ display: "flex",
258
+ flexDirection: "column",
259
+ gap: "6px"
260
+ };
261
+ var newChatButtonStyle = {
262
+ width: "100%",
263
+ padding: "8px 12px",
264
+ backgroundColor: "#2563eb",
265
+ color: "#fff",
266
+ border: "none",
267
+ borderRadius: "6px",
268
+ fontSize: "14px",
269
+ fontWeight: 500,
270
+ cursor: "pointer",
271
+ textAlign: "left"
272
+ };
273
+ var newProjectButtonStyle = {
274
+ width: "100%",
275
+ padding: "6px 12px",
276
+ backgroundColor: "transparent",
277
+ color: "#6b7280",
278
+ border: "1px dashed #cbd5e1",
279
+ borderRadius: "6px",
280
+ fontSize: "13px",
281
+ cursor: "pointer",
282
+ textAlign: "left"
283
+ };
284
+ var listStyle = {
285
+ flex: 1,
286
+ overflowY: "auto",
287
+ padding: "8px 6px"
288
+ };
289
+ var sectionLabelStyle = {
290
+ padding: "6px 8px 2px",
291
+ fontSize: "11px",
292
+ fontWeight: 600,
293
+ color: "#94a3b8",
294
+ textTransform: "uppercase",
295
+ letterSpacing: "0.05em"
296
+ };
297
+ function rowStyle(isActive) {
298
+ return {
299
+ display: "flex",
300
+ alignItems: "center",
301
+ width: "100%",
302
+ marginBottom: "2px",
303
+ borderRadius: "6px",
304
+ backgroundColor: isActive ? "#dbeafe" : "transparent"
305
+ };
306
+ }
307
+ function labelButtonStyle(isActive) {
308
+ return {
309
+ flex: 1,
310
+ minWidth: 0,
311
+ padding: "7px 8px",
312
+ backgroundColor: "transparent",
313
+ color: isActive ? "#1d4ed8" : "#374151",
314
+ border: "none",
315
+ borderRadius: "6px",
316
+ fontSize: "13px",
317
+ fontWeight: isActive ? 500 : 400,
318
+ cursor: "pointer",
319
+ textAlign: "left",
320
+ whiteSpace: "nowrap",
321
+ overflow: "hidden",
322
+ textOverflow: "ellipsis"
323
+ };
324
+ }
325
+ var actionButtonStyle = {
326
+ flexShrink: 0,
327
+ padding: "4px 5px",
328
+ backgroundColor: "transparent",
329
+ border: "none",
330
+ borderRadius: "4px",
331
+ fontSize: "12px",
332
+ cursor: "pointer",
333
+ color: "#6b7280",
334
+ lineHeight: 1
335
+ };
336
+ var projectSelectStyle = {
337
+ flexShrink: 0,
338
+ fontSize: "11px",
339
+ border: "1px solid #e2e8f0",
340
+ borderRadius: "4px",
341
+ backgroundColor: "#fff",
342
+ color: "#6b7280",
343
+ padding: "2px 2px",
344
+ cursor: "pointer",
345
+ maxWidth: "70px"
346
+ };
347
+ function ChatSidebar({
348
+ conversations,
349
+ currentId,
350
+ onSelect,
351
+ onNewChat,
352
+ onRename,
353
+ onDelete,
354
+ projects,
355
+ onNewProject,
356
+ onAssignToProject
357
+ }) {
358
+ function handleRename(id, currentTitle) {
359
+ const next = window.prompt("Rename chat:", currentTitle);
360
+ if (next !== null && next.trim()) {
361
+ onRename(id, next.trim());
362
+ }
363
+ }
364
+ function renderChatRow(conv) {
365
+ const isActive = conv.id === currentId;
366
+ return /* @__PURE__ */ jsxs4("div", { style: rowStyle(isActive), children: [
367
+ /* @__PURE__ */ jsx5(
368
+ "button",
369
+ {
370
+ style: labelButtonStyle(isActive),
371
+ onClick: () => onSelect(conv.id),
372
+ title: conv.title,
373
+ children: conv.title
374
+ }
375
+ ),
376
+ projects.length > 0 && /* @__PURE__ */ jsxs4(
377
+ "select",
378
+ {
379
+ style: projectSelectStyle,
380
+ value: conv.projectId ?? "",
381
+ onChange: (e) => onAssignToProject(conv.id, e.target.value || null),
382
+ title: "Move to project",
383
+ "aria-label": "Assign to project",
384
+ children: [
385
+ /* @__PURE__ */ jsx5("option", { value: "", children: "\u2014" }),
386
+ projects.map((p) => /* @__PURE__ */ jsx5("option", { value: p.id, children: p.name }, p.id))
387
+ ]
388
+ }
389
+ ),
390
+ /* @__PURE__ */ jsx5(
391
+ "button",
392
+ {
393
+ style: actionButtonStyle,
394
+ onClick: () => handleRename(conv.id, conv.title),
395
+ title: "Rename",
396
+ "aria-label": "Rename chat",
397
+ children: "\u270F\uFE0F"
398
+ }
399
+ ),
400
+ /* @__PURE__ */ jsx5(
401
+ "button",
402
+ {
403
+ style: { ...actionButtonStyle, marginRight: "4px" },
404
+ onClick: () => onDelete(conv.id),
405
+ title: "Delete",
406
+ "aria-label": "Delete chat",
407
+ children: "\u{1F5D1}\uFE0F"
408
+ }
409
+ )
410
+ ] }, conv.id);
411
+ }
412
+ const ungrouped = conversations.filter((c) => !c.projectId);
413
+ return /* @__PURE__ */ jsxs4("aside", { style: sidebarStyle, children: [
414
+ /* @__PURE__ */ jsxs4("div", { style: headerStyle, children: [
415
+ /* @__PURE__ */ jsx5("button", { style: newChatButtonStyle, onClick: onNewChat, children: "+ New chat" }),
416
+ /* @__PURE__ */ jsx5("button", { style: newProjectButtonStyle, onClick: onNewProject, children: "\u{1F4C1} New project" })
417
+ ] }),
418
+ /* @__PURE__ */ jsxs4("div", { style: listStyle, children: [
419
+ projects.map((project) => {
420
+ const chats = conversations.filter((c) => c.projectId === project.id);
421
+ return /* @__PURE__ */ jsxs4("div", { style: { marginBottom: "8px" }, children: [
422
+ /* @__PURE__ */ jsxs4("div", { style: sectionLabelStyle, children: [
423
+ "\u{1F4C1} ",
424
+ project.name
425
+ ] }),
426
+ chats.length === 0 ? /* @__PURE__ */ jsx5(
427
+ "div",
428
+ {
429
+ style: {
430
+ padding: "4px 10px",
431
+ fontSize: "12px",
432
+ color: "#cbd5e1",
433
+ fontStyle: "italic"
434
+ },
435
+ children: "No chats yet"
436
+ }
437
+ ) : chats.map(renderChatRow)
438
+ ] }, project.id);
439
+ }),
440
+ ungrouped.length > 0 && /* @__PURE__ */ jsxs4("div", { children: [
441
+ projects.length > 0 && /* @__PURE__ */ jsx5("div", { style: sectionLabelStyle, children: "Ungrouped" }),
442
+ ungrouped.map(renderChatRow)
443
+ ] })
444
+ ] })
445
+ ] });
446
+ }
447
+
448
+ // src/ProviderSelector.tsx
449
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
450
+ var PROVIDERS = [
451
+ { id: "openai", label: "OpenAI" },
452
+ { id: "claude", label: "Claude" },
453
+ { id: "gemini", label: "Gemini" }
454
+ ];
455
+ var wrapperStyle = {
456
+ display: "flex",
457
+ alignItems: "center",
458
+ gap: "8px",
459
+ marginBottom: "16px"
460
+ };
461
+ var labelStyle = {
462
+ fontSize: "13px",
463
+ fontWeight: 500,
464
+ color: "#374151"
465
+ };
466
+ var selectStyle = {
467
+ padding: "5px 10px",
468
+ fontSize: "13px",
469
+ borderRadius: "6px",
470
+ border: "1px solid #d1d5db",
471
+ backgroundColor: "#fff",
472
+ cursor: "pointer",
473
+ color: "#111827"
474
+ };
475
+ function ProviderSelector({ value, onChange, disabled }) {
476
+ return /* @__PURE__ */ jsxs5("div", { style: wrapperStyle, children: [
477
+ /* @__PURE__ */ jsx6("label", { htmlFor: "provider-select", style: labelStyle, children: "Provider:" }),
478
+ /* @__PURE__ */ jsx6(
479
+ "select",
480
+ {
481
+ id: "provider-select",
482
+ style: selectStyle,
483
+ value,
484
+ disabled,
485
+ onChange: (e) => onChange(e.target.value),
486
+ children: PROVIDERS.map((p) => /* @__PURE__ */ jsx6("option", { value: p.id, children: p.label }, p.id))
487
+ }
488
+ )
489
+ ] });
490
+ }
491
+
492
+ // src/ComparisonToggle.tsx
493
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
494
+ function ComparisonToggle({ enabled, onChange, disabled }) {
495
+ return /* @__PURE__ */ jsxs6(
496
+ "label",
497
+ {
498
+ style: {
499
+ display: "flex",
500
+ alignItems: "center",
501
+ gap: "8px",
502
+ fontSize: "0.875rem",
503
+ cursor: disabled ? "not-allowed" : "pointer",
504
+ opacity: disabled ? 0.5 : 1,
505
+ userSelect: "none"
506
+ },
507
+ children: [
508
+ /* @__PURE__ */ jsx7(
509
+ "input",
510
+ {
511
+ type: "checkbox",
512
+ checked: enabled,
513
+ onChange: (e) => onChange(e.target.checked),
514
+ disabled,
515
+ style: { cursor: disabled ? "not-allowed" : "pointer" }
516
+ }
517
+ ),
518
+ "Comparison mode"
519
+ ]
520
+ }
521
+ );
522
+ }
523
+ export {
524
+ ChatComposer,
525
+ ChatContainer,
526
+ ChatSidebar,
527
+ ComparisonToggle,
528
+ MessageBubble,
529
+ MessageList,
530
+ ProviderSelector
531
+ };
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@tigmart/ai-ui",
3
+ "version": "0.0.1",
4
+ "description": "Pre-built chat UI components",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": ["dist"],
16
+ "license": "MIT",
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "scripts": {
21
+ "build": "tsup",
22
+ "dev": "tsup --watch",
23
+ "typecheck": "tsc --noEmit"
24
+ },
25
+ "dependencies": {
26
+ "@tigmart/ai-react": "workspace:*",
27
+ "@tigmart/ai-types": "workspace:*",
28
+ "@radix-ui/react-scroll-area": "^1.2.0"
29
+ },
30
+ "peerDependencies": {
31
+ "react": "^18.0.0 || ^19.0.0",
32
+ "react-dom": "^18.0.0 || ^19.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/react": "^19.0.0",
36
+ "@types/react-dom": "^19.0.0",
37
+ "react": "^19.0.0",
38
+ "react-dom": "^19.0.0",
39
+ "tsup": "^8.3.0",
40
+ "typescript": "^5.7.0"
41
+ }
42
+ }