@contractspec/module.ai-chat 1.57.0 → 1.59.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.
Files changed (120) hide show
  1. package/dist/ai-chat.capability.d.ts +2 -0
  2. package/dist/ai-chat.capability.d.ts.map +1 -0
  3. package/dist/ai-chat.feature.d.ts +1 -6
  4. package/dist/ai-chat.feature.d.ts.map +1 -1
  5. package/dist/ai-chat.operations.d.ts +217 -223
  6. package/dist/ai-chat.operations.d.ts.map +1 -1
  7. package/dist/browser/context/index.js +415 -0
  8. package/dist/browser/core/index.js +336 -0
  9. package/dist/browser/index.js +2291 -0
  10. package/dist/browser/presentation/components/index.js +974 -0
  11. package/dist/browser/presentation/hooks/index.js +556 -0
  12. package/dist/browser/presentation/index.js +1520 -0
  13. package/dist/browser/providers/index.js +51 -0
  14. package/dist/context/chat.test.d.ts +2 -0
  15. package/dist/context/chat.test.d.ts.map +1 -0
  16. package/dist/context/context-builder.d.ts +37 -36
  17. package/dist/context/context-builder.d.ts.map +1 -1
  18. package/dist/context/file-operations.d.ts +64 -67
  19. package/dist/context/file-operations.d.ts.map +1 -1
  20. package/dist/context/index.d.ts +7 -4
  21. package/dist/context/index.d.ts.map +1 -0
  22. package/dist/context/index.js +409 -4
  23. package/dist/context/workspace-context.d.ts +84 -87
  24. package/dist/context/workspace-context.d.ts.map +1 -1
  25. package/dist/core/chat-service.d.ts +56 -60
  26. package/dist/core/chat-service.d.ts.map +1 -1
  27. package/dist/core/conversation-store.d.ts +60 -61
  28. package/dist/core/conversation-store.d.ts.map +1 -1
  29. package/dist/core/index.d.ts +7 -4
  30. package/dist/core/index.d.ts.map +1 -0
  31. package/dist/core/index.js +330 -3
  32. package/dist/core/message-types.d.ts +94 -97
  33. package/dist/core/message-types.d.ts.map +1 -1
  34. package/dist/docs/ai-chat.docblock.d.ts +2 -0
  35. package/dist/docs/ai-chat.docblock.d.ts.map +1 -0
  36. package/dist/docs/index.d.ts +7 -0
  37. package/dist/docs/index.d.ts.map +1 -0
  38. package/dist/events.d.ts +103 -109
  39. package/dist/events.d.ts.map +1 -1
  40. package/dist/index.d.ts +16 -21
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +2286 -23
  43. package/dist/node/context/index.js +410 -0
  44. package/dist/node/core/index.js +331 -0
  45. package/dist/node/index.js +2286 -0
  46. package/dist/node/presentation/components/index.js +969 -0
  47. package/dist/node/presentation/hooks/index.js +551 -0
  48. package/dist/node/presentation/index.js +1515 -0
  49. package/dist/node/providers/index.js +46 -0
  50. package/dist/presentation/components/ChatContainer.d.ts +7 -16
  51. package/dist/presentation/components/ChatContainer.d.ts.map +1 -1
  52. package/dist/presentation/components/ChatInput.d.ts +17 -30
  53. package/dist/presentation/components/ChatInput.d.ts.map +1 -1
  54. package/dist/presentation/components/ChatMessage.d.ts +9 -19
  55. package/dist/presentation/components/ChatMessage.d.ts.map +1 -1
  56. package/dist/presentation/components/CodePreview.d.ts +20 -35
  57. package/dist/presentation/components/CodePreview.d.ts.map +1 -1
  58. package/dist/presentation/components/ContextIndicator.d.ts +11 -21
  59. package/dist/presentation/components/ContextIndicator.d.ts.map +1 -1
  60. package/dist/presentation/components/ModelPicker.d.ts +21 -32
  61. package/dist/presentation/components/ModelPicker.d.ts.map +1 -1
  62. package/dist/presentation/components/index.d.ts +10 -7
  63. package/dist/presentation/components/index.d.ts.map +1 -0
  64. package/dist/presentation/components/index.js +968 -7
  65. package/dist/presentation/hooks/index.d.ts +6 -3
  66. package/dist/presentation/hooks/index.d.ts.map +1 -0
  67. package/dist/presentation/hooks/index.js +550 -3
  68. package/dist/presentation/hooks/use-chat.test.d.ts +2 -0
  69. package/dist/presentation/hooks/use-chat.test.d.ts.map +1 -0
  70. package/dist/presentation/hooks/useChat.d.ts +50 -54
  71. package/dist/presentation/hooks/useChat.d.ts.map +1 -1
  72. package/dist/presentation/hooks/useProviders.d.ts +21 -25
  73. package/dist/presentation/hooks/useProviders.d.ts.map +1 -1
  74. package/dist/presentation/index.d.ts +8 -11
  75. package/dist/presentation/index.d.ts.map +1 -0
  76. package/dist/presentation/index.js +1515 -12
  77. package/dist/providers/chat-utilities.d.ts +18 -7
  78. package/dist/providers/chat-utilities.d.ts.map +1 -1
  79. package/dist/providers/index.d.ts +8 -3
  80. package/dist/providers/index.d.ts.map +1 -0
  81. package/dist/providers/index.js +45 -3
  82. package/dist/schema.d.ts +195 -200
  83. package/dist/schema.d.ts.map +1 -1
  84. package/package.json +123 -34
  85. package/dist/ai-chat.feature.js +0 -102
  86. package/dist/ai-chat.feature.js.map +0 -1
  87. package/dist/ai-chat.operations.js +0 -172
  88. package/dist/ai-chat.operations.js.map +0 -1
  89. package/dist/context/context-builder.js +0 -148
  90. package/dist/context/context-builder.js.map +0 -1
  91. package/dist/context/file-operations.js +0 -175
  92. package/dist/context/file-operations.js.map +0 -1
  93. package/dist/context/workspace-context.js +0 -124
  94. package/dist/context/workspace-context.js.map +0 -1
  95. package/dist/core/chat-service.js +0 -227
  96. package/dist/core/chat-service.js.map +0 -1
  97. package/dist/core/conversation-store.js +0 -109
  98. package/dist/core/conversation-store.js.map +0 -1
  99. package/dist/events.js +0 -98
  100. package/dist/events.js.map +0 -1
  101. package/dist/presentation/components/ChatContainer.js +0 -63
  102. package/dist/presentation/components/ChatContainer.js.map +0 -1
  103. package/dist/presentation/components/ChatInput.js +0 -149
  104. package/dist/presentation/components/ChatInput.js.map +0 -1
  105. package/dist/presentation/components/ChatMessage.js +0 -136
  106. package/dist/presentation/components/ChatMessage.js.map +0 -1
  107. package/dist/presentation/components/CodePreview.js +0 -127
  108. package/dist/presentation/components/CodePreview.js.map +0 -1
  109. package/dist/presentation/components/ContextIndicator.js +0 -97
  110. package/dist/presentation/components/ContextIndicator.js.map +0 -1
  111. package/dist/presentation/components/ModelPicker.js +0 -202
  112. package/dist/presentation/components/ModelPicker.js.map +0 -1
  113. package/dist/presentation/hooks/useChat.js +0 -172
  114. package/dist/presentation/hooks/useChat.js.map +0 -1
  115. package/dist/presentation/hooks/useProviders.js +0 -41
  116. package/dist/presentation/hooks/useProviders.js.map +0 -1
  117. package/dist/providers/chat-utilities.js +0 -17
  118. package/dist/providers/chat-utilities.js.map +0 -1
  119. package/dist/schema.js +0 -100
  120. package/dist/schema.js.map +0 -1
@@ -0,0 +1,969 @@
1
+ import { createRequire } from "node:module";
2
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
+
4
+ // src/presentation/components/ChatContainer.tsx
5
+ import * as React from "react";
6
+ import { ScrollArea } from "@contractspec/lib.ui-kit-web/ui/scroll-area";
7
+ import { cn } from "@contractspec/lib.ui-kit-web/ui/utils";
8
+ import { jsxDEV } from "react/jsx-dev-runtime";
9
+ "use client";
10
+ function ChatContainer({
11
+ children,
12
+ className,
13
+ showScrollButton = true
14
+ }) {
15
+ const scrollRef = React.useRef(null);
16
+ const [showScrollDown, setShowScrollDown] = React.useState(false);
17
+ React.useEffect(() => {
18
+ const container = scrollRef.current;
19
+ if (!container)
20
+ return;
21
+ const isAtBottom = container.scrollHeight - container.scrollTop <= container.clientHeight + 100;
22
+ if (isAtBottom) {
23
+ container.scrollTop = container.scrollHeight;
24
+ }
25
+ }, [children]);
26
+ const handleScroll = React.useCallback((event) => {
27
+ const container = event.currentTarget;
28
+ const isAtBottom = container.scrollHeight - container.scrollTop <= container.clientHeight + 100;
29
+ setShowScrollDown(!isAtBottom);
30
+ }, []);
31
+ const scrollToBottom = React.useCallback(() => {
32
+ const container = scrollRef.current;
33
+ if (container) {
34
+ container.scrollTo({
35
+ top: container.scrollHeight,
36
+ behavior: "smooth"
37
+ });
38
+ }
39
+ }, []);
40
+ return /* @__PURE__ */ jsxDEV("div", {
41
+ className: cn("relative flex flex-1 flex-col", className),
42
+ children: [
43
+ /* @__PURE__ */ jsxDEV(ScrollArea, {
44
+ ref: scrollRef,
45
+ className: "flex-1",
46
+ onScroll: handleScroll,
47
+ children: /* @__PURE__ */ jsxDEV("div", {
48
+ className: "flex flex-col gap-4 p-4",
49
+ children
50
+ }, undefined, false, undefined, this)
51
+ }, undefined, false, undefined, this),
52
+ showScrollButton && showScrollDown && /* @__PURE__ */ jsxDEV("button", {
53
+ onClick: scrollToBottom,
54
+ className: cn("absolute bottom-4 left-1/2 -translate-x-1/2", "bg-primary text-primary-foreground", "rounded-full px-3 py-1.5 text-sm font-medium shadow-lg", "hover:bg-primary/90 transition-colors", "flex items-center gap-1.5"),
55
+ "aria-label": "Scroll to bottom",
56
+ children: [
57
+ /* @__PURE__ */ jsxDEV("svg", {
58
+ xmlns: "http://www.w3.org/2000/svg",
59
+ width: "16",
60
+ height: "16",
61
+ viewBox: "0 0 24 24",
62
+ fill: "none",
63
+ stroke: "currentColor",
64
+ strokeWidth: "2",
65
+ strokeLinecap: "round",
66
+ strokeLinejoin: "round",
67
+ children: /* @__PURE__ */ jsxDEV("path", {
68
+ d: "m6 9 6 6 6-6"
69
+ }, undefined, false, undefined, this)
70
+ }, undefined, false, undefined, this),
71
+ "New messages"
72
+ ]
73
+ }, undefined, true, undefined, this)
74
+ ]
75
+ }, undefined, true, undefined, this);
76
+ }
77
+ // src/presentation/components/ChatMessage.tsx
78
+ import * as React3 from "react";
79
+ import { cn as cn3 } from "@contractspec/lib.ui-kit-web/ui/utils";
80
+ import { Avatar, AvatarFallback } from "@contractspec/lib.ui-kit-web/ui/avatar";
81
+ import { Skeleton } from "@contractspec/lib.ui-kit-web/ui/skeleton";
82
+ import { Bot, User, AlertCircle, Copy as Copy2, Check as Check2 } from "lucide-react";
83
+ import { Button as Button2 } from "@contractspec/lib.design-system";
84
+
85
+ // src/presentation/components/CodePreview.tsx
86
+ import * as React2 from "react";
87
+ import { cn as cn2 } from "@contractspec/lib.ui-kit-web/ui/utils";
88
+ import { Button } from "@contractspec/lib.design-system";
89
+ import { Copy, Check, Play, Download } from "lucide-react";
90
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
91
+ "use client";
92
+ var LANGUAGE_NAMES = {
93
+ ts: "TypeScript",
94
+ tsx: "TypeScript (React)",
95
+ typescript: "TypeScript",
96
+ js: "JavaScript",
97
+ jsx: "JavaScript (React)",
98
+ javascript: "JavaScript",
99
+ json: "JSON",
100
+ md: "Markdown",
101
+ yaml: "YAML",
102
+ yml: "YAML",
103
+ bash: "Bash",
104
+ sh: "Shell",
105
+ sql: "SQL",
106
+ py: "Python",
107
+ python: "Python",
108
+ go: "Go",
109
+ rust: "Rust",
110
+ rs: "Rust"
111
+ };
112
+ function CodePreview({
113
+ code,
114
+ language = "text",
115
+ filename,
116
+ className,
117
+ showCopy = true,
118
+ showExecute = false,
119
+ onExecute,
120
+ showDownload = false,
121
+ maxHeight = 400
122
+ }) {
123
+ const [copied, setCopied] = React2.useState(false);
124
+ const displayLanguage = LANGUAGE_NAMES[language.toLowerCase()] ?? language;
125
+ const lines = code.split(`
126
+ `);
127
+ const handleCopy = React2.useCallback(async () => {
128
+ await navigator.clipboard.writeText(code);
129
+ setCopied(true);
130
+ setTimeout(() => setCopied(false), 2000);
131
+ }, [code]);
132
+ const handleDownload = React2.useCallback(() => {
133
+ const blob = new Blob([code], { type: "text/plain" });
134
+ const url = URL.createObjectURL(blob);
135
+ const a = document.createElement("a");
136
+ a.href = url;
137
+ a.download = filename ?? `code.${language}`;
138
+ document.body.appendChild(a);
139
+ a.click();
140
+ document.body.removeChild(a);
141
+ URL.revokeObjectURL(url);
142
+ }, [code, filename, language]);
143
+ return /* @__PURE__ */ jsxDEV2("div", {
144
+ className: cn2("overflow-hidden rounded-lg border", "bg-muted/50", className),
145
+ children: [
146
+ /* @__PURE__ */ jsxDEV2("div", {
147
+ className: cn2("flex items-center justify-between px-3 py-1.5", "bg-muted/80 border-b"),
148
+ children: [
149
+ /* @__PURE__ */ jsxDEV2("div", {
150
+ className: "flex items-center gap-2 text-sm",
151
+ children: [
152
+ filename && /* @__PURE__ */ jsxDEV2("span", {
153
+ className: "text-foreground font-mono",
154
+ children: filename
155
+ }, undefined, false, undefined, this),
156
+ /* @__PURE__ */ jsxDEV2("span", {
157
+ className: "text-muted-foreground",
158
+ children: displayLanguage
159
+ }, undefined, false, undefined, this)
160
+ ]
161
+ }, undefined, true, undefined, this),
162
+ /* @__PURE__ */ jsxDEV2("div", {
163
+ className: "flex items-center gap-1",
164
+ children: [
165
+ showExecute && onExecute && /* @__PURE__ */ jsxDEV2(Button, {
166
+ variant: "ghost",
167
+ size: "sm",
168
+ onPress: () => onExecute(code),
169
+ className: "h-7 w-7 p-0",
170
+ "aria-label": "Execute code",
171
+ children: /* @__PURE__ */ jsxDEV2(Play, {
172
+ className: "h-3.5 w-3.5"
173
+ }, undefined, false, undefined, this)
174
+ }, undefined, false, undefined, this),
175
+ showDownload && /* @__PURE__ */ jsxDEV2(Button, {
176
+ variant: "ghost",
177
+ size: "sm",
178
+ onPress: handleDownload,
179
+ className: "h-7 w-7 p-0",
180
+ "aria-label": "Download code",
181
+ children: /* @__PURE__ */ jsxDEV2(Download, {
182
+ className: "h-3.5 w-3.5"
183
+ }, undefined, false, undefined, this)
184
+ }, undefined, false, undefined, this),
185
+ showCopy && /* @__PURE__ */ jsxDEV2(Button, {
186
+ variant: "ghost",
187
+ size: "sm",
188
+ onPress: handleCopy,
189
+ className: "h-7 w-7 p-0",
190
+ "aria-label": copied ? "Copied" : "Copy code",
191
+ children: copied ? /* @__PURE__ */ jsxDEV2(Check, {
192
+ className: "h-3.5 w-3.5 text-green-500"
193
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV2(Copy, {
194
+ className: "h-3.5 w-3.5"
195
+ }, undefined, false, undefined, this)
196
+ }, undefined, false, undefined, this)
197
+ ]
198
+ }, undefined, true, undefined, this)
199
+ ]
200
+ }, undefined, true, undefined, this),
201
+ /* @__PURE__ */ jsxDEV2("div", {
202
+ className: "overflow-auto",
203
+ style: { maxHeight },
204
+ children: /* @__PURE__ */ jsxDEV2("pre", {
205
+ className: "p-3",
206
+ children: /* @__PURE__ */ jsxDEV2("code", {
207
+ className: "text-sm",
208
+ children: lines.map((line, i) => /* @__PURE__ */ jsxDEV2("div", {
209
+ className: "flex",
210
+ children: [
211
+ /* @__PURE__ */ jsxDEV2("span", {
212
+ className: "text-muted-foreground mr-4 w-8 text-right select-none",
213
+ children: i + 1
214
+ }, undefined, false, undefined, this),
215
+ /* @__PURE__ */ jsxDEV2("span", {
216
+ className: "flex-1",
217
+ children: line || " "
218
+ }, undefined, false, undefined, this)
219
+ ]
220
+ }, i, true, undefined, this))
221
+ }, undefined, false, undefined, this)
222
+ }, undefined, false, undefined, this)
223
+ }, undefined, false, undefined, this)
224
+ ]
225
+ }, undefined, true, undefined, this);
226
+ }
227
+
228
+ // src/presentation/components/ChatMessage.tsx
229
+ import { jsxDEV as jsxDEV3, Fragment } from "react/jsx-dev-runtime";
230
+ "use client";
231
+ function extractCodeBlocks(content) {
232
+ const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
233
+ const blocks = [];
234
+ let match;
235
+ while ((match = codeBlockRegex.exec(content)) !== null) {
236
+ blocks.push({
237
+ language: match[1] ?? "text",
238
+ code: match[2] ?? "",
239
+ raw: match[0]
240
+ });
241
+ }
242
+ return blocks;
243
+ }
244
+ function MessageContent({ content }) {
245
+ const codeBlocks = extractCodeBlocks(content);
246
+ if (codeBlocks.length === 0) {
247
+ return /* @__PURE__ */ jsxDEV3("p", {
248
+ className: "whitespace-pre-wrap",
249
+ children: content
250
+ }, undefined, false, undefined, this);
251
+ }
252
+ let remaining = content;
253
+ const parts = [];
254
+ let key = 0;
255
+ for (const block of codeBlocks) {
256
+ const [before, after] = remaining.split(block.raw);
257
+ if (before) {
258
+ parts.push(/* @__PURE__ */ jsxDEV3("p", {
259
+ className: "whitespace-pre-wrap",
260
+ children: before.trim()
261
+ }, key++, false, undefined, this));
262
+ }
263
+ parts.push(/* @__PURE__ */ jsxDEV3(CodePreview, {
264
+ code: block.code,
265
+ language: block.language,
266
+ className: "my-2"
267
+ }, key++, false, undefined, this));
268
+ remaining = after ?? "";
269
+ }
270
+ if (remaining.trim()) {
271
+ parts.push(/* @__PURE__ */ jsxDEV3("p", {
272
+ className: "whitespace-pre-wrap",
273
+ children: remaining.trim()
274
+ }, key++, false, undefined, this));
275
+ }
276
+ return /* @__PURE__ */ jsxDEV3(Fragment, {
277
+ children: parts
278
+ }, undefined, false, undefined, this);
279
+ }
280
+ function ChatMessage({
281
+ message,
282
+ className,
283
+ showCopy = true,
284
+ showAvatar = true
285
+ }) {
286
+ const [copied, setCopied] = React3.useState(false);
287
+ const isUser = message.role === "user";
288
+ const isError = message.status === "error";
289
+ const isStreaming = message.status === "streaming";
290
+ const handleCopy = React3.useCallback(async () => {
291
+ await navigator.clipboard.writeText(message.content);
292
+ setCopied(true);
293
+ setTimeout(() => setCopied(false), 2000);
294
+ }, [message.content]);
295
+ return /* @__PURE__ */ jsxDEV3("div", {
296
+ className: cn3("group flex gap-3", isUser && "flex-row-reverse", className),
297
+ children: [
298
+ showAvatar && /* @__PURE__ */ jsxDEV3(Avatar, {
299
+ className: "h-8 w-8 shrink-0",
300
+ children: /* @__PURE__ */ jsxDEV3(AvatarFallback, {
301
+ className: cn3(isUser ? "bg-primary text-primary-foreground" : "bg-muted"),
302
+ children: isUser ? /* @__PURE__ */ jsxDEV3(User, {
303
+ className: "h-4 w-4"
304
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV3(Bot, {
305
+ className: "h-4 w-4"
306
+ }, undefined, false, undefined, this)
307
+ }, undefined, false, undefined, this)
308
+ }, undefined, false, undefined, this),
309
+ /* @__PURE__ */ jsxDEV3("div", {
310
+ className: cn3("flex max-w-[80%] flex-col gap-1", isUser && "items-end"),
311
+ children: [
312
+ /* @__PURE__ */ jsxDEV3("div", {
313
+ className: cn3("rounded-2xl px-4 py-2", isUser ? "bg-primary text-primary-foreground" : "bg-muted text-foreground", isError && "border-destructive bg-destructive/10 border"),
314
+ children: isError && message.error ? /* @__PURE__ */ jsxDEV3("div", {
315
+ className: "flex items-start gap-2",
316
+ children: [
317
+ /* @__PURE__ */ jsxDEV3(AlertCircle, {
318
+ className: "text-destructive mt-0.5 h-4 w-4 shrink-0"
319
+ }, undefined, false, undefined, this),
320
+ /* @__PURE__ */ jsxDEV3("div", {
321
+ children: [
322
+ /* @__PURE__ */ jsxDEV3("p", {
323
+ className: "text-destructive font-medium",
324
+ children: message.error.code
325
+ }, undefined, false, undefined, this),
326
+ /* @__PURE__ */ jsxDEV3("p", {
327
+ className: "text-muted-foreground text-sm",
328
+ children: message.error.message
329
+ }, undefined, false, undefined, this)
330
+ ]
331
+ }, undefined, true, undefined, this)
332
+ ]
333
+ }, undefined, true, undefined, this) : isStreaming && !message.content ? /* @__PURE__ */ jsxDEV3("div", {
334
+ className: "flex flex-col gap-2",
335
+ children: [
336
+ /* @__PURE__ */ jsxDEV3(Skeleton, {
337
+ className: "h-4 w-48"
338
+ }, undefined, false, undefined, this),
339
+ /* @__PURE__ */ jsxDEV3(Skeleton, {
340
+ className: "h-4 w-32"
341
+ }, undefined, false, undefined, this)
342
+ ]
343
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV3(MessageContent, {
344
+ content: message.content
345
+ }, undefined, false, undefined, this)
346
+ }, undefined, false, undefined, this),
347
+ /* @__PURE__ */ jsxDEV3("div", {
348
+ className: cn3("flex items-center gap-2 text-xs", "text-muted-foreground opacity-0 transition-opacity", "group-hover:opacity-100"),
349
+ children: [
350
+ /* @__PURE__ */ jsxDEV3("span", {
351
+ children: new Date(message.createdAt).toLocaleTimeString([], {
352
+ hour: "2-digit",
353
+ minute: "2-digit"
354
+ })
355
+ }, undefined, false, undefined, this),
356
+ message.usage && /* @__PURE__ */ jsxDEV3("span", {
357
+ children: [
358
+ message.usage.inputTokens + message.usage.outputTokens,
359
+ " tokens"
360
+ ]
361
+ }, undefined, true, undefined, this),
362
+ showCopy && !isUser && message.content && /* @__PURE__ */ jsxDEV3(Button2, {
363
+ variant: "ghost",
364
+ size: "sm",
365
+ className: "h-6 w-6 p-0",
366
+ onPress: handleCopy,
367
+ "aria-label": copied ? "Copied" : "Copy message",
368
+ children: copied ? /* @__PURE__ */ jsxDEV3(Check2, {
369
+ className: "h-3 w-3"
370
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV3(Copy2, {
371
+ className: "h-3 w-3"
372
+ }, undefined, false, undefined, this)
373
+ }, undefined, false, undefined, this)
374
+ ]
375
+ }, undefined, true, undefined, this),
376
+ message.reasoning && /* @__PURE__ */ jsxDEV3("details", {
377
+ className: "text-muted-foreground mt-2 text-sm",
378
+ children: [
379
+ /* @__PURE__ */ jsxDEV3("summary", {
380
+ className: "cursor-pointer hover:underline",
381
+ children: "View reasoning"
382
+ }, undefined, false, undefined, this),
383
+ /* @__PURE__ */ jsxDEV3("div", {
384
+ className: "bg-muted mt-1 rounded-md p-2",
385
+ children: /* @__PURE__ */ jsxDEV3("p", {
386
+ className: "whitespace-pre-wrap",
387
+ children: message.reasoning
388
+ }, undefined, false, undefined, this)
389
+ }, undefined, false, undefined, this)
390
+ ]
391
+ }, undefined, true, undefined, this)
392
+ ]
393
+ }, undefined, true, undefined, this)
394
+ ]
395
+ }, undefined, true, undefined, this);
396
+ }
397
+ // src/presentation/components/ChatInput.tsx
398
+ import * as React4 from "react";
399
+ import { cn as cn4 } from "@contractspec/lib.ui-kit-web/ui/utils";
400
+ import { Textarea } from "@contractspec/lib.design-system";
401
+ import { Button as Button3 } from "@contractspec/lib.design-system";
402
+ import { Send, Paperclip, X, Loader2, FileText, Code } from "lucide-react";
403
+ import { jsxDEV as jsxDEV4, Fragment as Fragment2 } from "react/jsx-dev-runtime";
404
+ "use client";
405
+ function ChatInput({
406
+ onSend,
407
+ disabled = false,
408
+ isLoading = false,
409
+ placeholder = "Type a message...",
410
+ className,
411
+ showAttachments = true,
412
+ maxAttachments = 5
413
+ }) {
414
+ const [content, setContent] = React4.useState("");
415
+ const [attachments, setAttachments] = React4.useState([]);
416
+ const textareaRef = React4.useRef(null);
417
+ const fileInputRef = React4.useRef(null);
418
+ const canSend = content.trim().length > 0 || attachments.length > 0;
419
+ const handleSubmit = React4.useCallback((e) => {
420
+ e?.preventDefault();
421
+ if (!canSend || disabled || isLoading)
422
+ return;
423
+ onSend(content.trim(), attachments.length > 0 ? attachments : undefined);
424
+ setContent("");
425
+ setAttachments([]);
426
+ textareaRef.current?.focus();
427
+ }, [canSend, content, attachments, disabled, isLoading, onSend]);
428
+ const handleKeyDown = React4.useCallback((e) => {
429
+ if (e.key === "Enter" && !e.shiftKey) {
430
+ e.preventDefault();
431
+ handleSubmit();
432
+ }
433
+ }, [handleSubmit]);
434
+ const handleFileSelect = React4.useCallback(async (e) => {
435
+ const files = e.target.files;
436
+ if (!files)
437
+ return;
438
+ const newAttachments = [];
439
+ for (const file of Array.from(files)) {
440
+ if (attachments.length + newAttachments.length >= maxAttachments)
441
+ break;
442
+ const content2 = await file.text();
443
+ const extension = file.name.split(".").pop()?.toLowerCase() ?? "";
444
+ const isCode = [
445
+ "ts",
446
+ "tsx",
447
+ "js",
448
+ "jsx",
449
+ "py",
450
+ "go",
451
+ "rs",
452
+ "java"
453
+ ].includes(extension);
454
+ newAttachments.push({
455
+ id: `att_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
456
+ type: isCode ? "code" : "file",
457
+ name: file.name,
458
+ content: content2,
459
+ mimeType: file.type,
460
+ size: file.size
461
+ });
462
+ }
463
+ setAttachments((prev) => [...prev, ...newAttachments]);
464
+ e.target.value = "";
465
+ }, [attachments.length, maxAttachments]);
466
+ const removeAttachment = React4.useCallback((id) => {
467
+ setAttachments((prev) => prev.filter((a) => a.id !== id));
468
+ }, []);
469
+ return /* @__PURE__ */ jsxDEV4("div", {
470
+ className: cn4("flex flex-col gap-2", className),
471
+ children: [
472
+ attachments.length > 0 && /* @__PURE__ */ jsxDEV4("div", {
473
+ className: "flex flex-wrap gap-2",
474
+ children: attachments.map((attachment) => /* @__PURE__ */ jsxDEV4("div", {
475
+ className: cn4("flex items-center gap-1.5 rounded-md px-2 py-1", "bg-muted text-muted-foreground text-sm"),
476
+ children: [
477
+ attachment.type === "code" ? /* @__PURE__ */ jsxDEV4(Code, {
478
+ className: "h-3.5 w-3.5"
479
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV4(FileText, {
480
+ className: "h-3.5 w-3.5"
481
+ }, undefined, false, undefined, this),
482
+ /* @__PURE__ */ jsxDEV4("span", {
483
+ className: "max-w-[150px] truncate",
484
+ children: attachment.name
485
+ }, undefined, false, undefined, this),
486
+ /* @__PURE__ */ jsxDEV4("button", {
487
+ type: "button",
488
+ onClick: () => removeAttachment(attachment.id),
489
+ className: "hover:text-foreground",
490
+ "aria-label": `Remove ${attachment.name}`,
491
+ children: /* @__PURE__ */ jsxDEV4(X, {
492
+ className: "h-3.5 w-3.5"
493
+ }, undefined, false, undefined, this)
494
+ }, undefined, false, undefined, this)
495
+ ]
496
+ }, attachment.id, true, undefined, this))
497
+ }, undefined, false, undefined, this),
498
+ /* @__PURE__ */ jsxDEV4("form", {
499
+ onSubmit: handleSubmit,
500
+ className: "flex items-end gap-2",
501
+ children: [
502
+ showAttachments && /* @__PURE__ */ jsxDEV4(Fragment2, {
503
+ children: [
504
+ /* @__PURE__ */ jsxDEV4("input", {
505
+ ref: fileInputRef,
506
+ type: "file",
507
+ multiple: true,
508
+ accept: ".ts,.tsx,.js,.jsx,.json,.md,.txt,.py,.go,.rs,.java,.yaml,.yml",
509
+ onChange: handleFileSelect,
510
+ className: "hidden",
511
+ "aria-label": "Attach files"
512
+ }, undefined, false, undefined, this),
513
+ /* @__PURE__ */ jsxDEV4(Button3, {
514
+ type: "button",
515
+ variant: "ghost",
516
+ size: "sm",
517
+ onPress: () => fileInputRef.current?.click(),
518
+ disabled: disabled || attachments.length >= maxAttachments,
519
+ "aria-label": "Attach files",
520
+ children: /* @__PURE__ */ jsxDEV4(Paperclip, {
521
+ className: "h-4 w-4"
522
+ }, undefined, false, undefined, this)
523
+ }, undefined, false, undefined, this)
524
+ ]
525
+ }, undefined, true, undefined, this),
526
+ /* @__PURE__ */ jsxDEV4("div", {
527
+ className: "relative flex-1",
528
+ children: /* @__PURE__ */ jsxDEV4(Textarea, {
529
+ value: content,
530
+ onChange: (e) => setContent(e.target.value),
531
+ onKeyDown: handleKeyDown,
532
+ placeholder,
533
+ disabled,
534
+ className: cn4("max-h-[200px] min-h-[44px] resize-none pr-12", "focus-visible:ring-1"),
535
+ rows: 1,
536
+ "aria-label": "Chat message"
537
+ }, undefined, false, undefined, this)
538
+ }, undefined, false, undefined, this),
539
+ /* @__PURE__ */ jsxDEV4(Button3, {
540
+ type: "submit",
541
+ disabled: !canSend || disabled || isLoading,
542
+ size: "sm",
543
+ "aria-label": isLoading ? "Sending..." : "Send message",
544
+ children: isLoading ? /* @__PURE__ */ jsxDEV4(Loader2, {
545
+ className: "h-4 w-4 animate-spin"
546
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV4(Send, {
547
+ className: "h-4 w-4"
548
+ }, undefined, false, undefined, this)
549
+ }, undefined, false, undefined, this)
550
+ ]
551
+ }, undefined, true, undefined, this),
552
+ /* @__PURE__ */ jsxDEV4("p", {
553
+ className: "text-muted-foreground text-xs",
554
+ children: "Press Enter to send, Shift+Enter for new line"
555
+ }, undefined, false, undefined, this)
556
+ ]
557
+ }, undefined, true, undefined, this);
558
+ }
559
+ // src/presentation/components/ModelPicker.tsx
560
+ import * as React5 from "react";
561
+ import { cn as cn5 } from "@contractspec/lib.ui-kit-web/ui/utils";
562
+ import { Button as Button4 } from "@contractspec/lib.design-system";
563
+ import {
564
+ Select,
565
+ SelectContent,
566
+ SelectItem,
567
+ SelectTrigger,
568
+ SelectValue
569
+ } from "@contractspec/lib.ui-kit-web/ui/select";
570
+ import { Badge } from "@contractspec/lib.ui-kit-web/ui/badge";
571
+ import { Label } from "@contractspec/lib.ui-kit-web/ui/label";
572
+ import { Bot as Bot2, Cloud, Cpu, Sparkles } from "lucide-react";
573
+ import {
574
+ getModelsForProvider
575
+ } from "@contractspec/lib.ai-providers";
576
+ import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
577
+ "use client";
578
+ var PROVIDER_ICONS = {
579
+ ollama: /* @__PURE__ */ jsxDEV5(Cpu, {
580
+ className: "h-4 w-4"
581
+ }, undefined, false, undefined, this),
582
+ openai: /* @__PURE__ */ jsxDEV5(Bot2, {
583
+ className: "h-4 w-4"
584
+ }, undefined, false, undefined, this),
585
+ anthropic: /* @__PURE__ */ jsxDEV5(Sparkles, {
586
+ className: "h-4 w-4"
587
+ }, undefined, false, undefined, this),
588
+ mistral: /* @__PURE__ */ jsxDEV5(Cloud, {
589
+ className: "h-4 w-4"
590
+ }, undefined, false, undefined, this),
591
+ gemini: /* @__PURE__ */ jsxDEV5(Sparkles, {
592
+ className: "h-4 w-4"
593
+ }, undefined, false, undefined, this)
594
+ };
595
+ var PROVIDER_NAMES = {
596
+ ollama: "Ollama (Local)",
597
+ openai: "OpenAI",
598
+ anthropic: "Anthropic",
599
+ mistral: "Mistral",
600
+ gemini: "Google Gemini"
601
+ };
602
+ var MODE_BADGES = {
603
+ local: { label: "Local", variant: "secondary" },
604
+ byok: { label: "BYOK", variant: "outline" },
605
+ managed: { label: "Managed", variant: "default" }
606
+ };
607
+ function ModelPicker({
608
+ value,
609
+ onChange,
610
+ availableProviders,
611
+ className,
612
+ compact = false
613
+ }) {
614
+ const providers = availableProviders ?? [
615
+ { provider: "ollama", available: true, mode: "local" },
616
+ { provider: "openai", available: true, mode: "byok" },
617
+ { provider: "anthropic", available: true, mode: "byok" },
618
+ { provider: "mistral", available: true, mode: "byok" },
619
+ { provider: "gemini", available: true, mode: "byok" }
620
+ ];
621
+ const models = getModelsForProvider(value.provider);
622
+ const selectedModel = models.find((m) => m.id === value.model);
623
+ const handleProviderChange = React5.useCallback((providerName) => {
624
+ const provider = providerName;
625
+ const providerInfo = providers.find((p) => p.provider === provider);
626
+ const providerModels = getModelsForProvider(provider);
627
+ const defaultModel = providerModels[0]?.id ?? "";
628
+ onChange({
629
+ provider,
630
+ model: defaultModel,
631
+ mode: providerInfo?.mode ?? "byok"
632
+ });
633
+ }, [onChange, providers]);
634
+ const handleModelChange = React5.useCallback((modelId) => {
635
+ onChange({
636
+ ...value,
637
+ model: modelId
638
+ });
639
+ }, [onChange, value]);
640
+ if (compact) {
641
+ return /* @__PURE__ */ jsxDEV5("div", {
642
+ className: cn5("flex items-center gap-2", className),
643
+ children: [
644
+ /* @__PURE__ */ jsxDEV5(Select, {
645
+ value: value.provider,
646
+ onValueChange: handleProviderChange,
647
+ children: [
648
+ /* @__PURE__ */ jsxDEV5(SelectTrigger, {
649
+ className: "w-[140px]",
650
+ children: /* @__PURE__ */ jsxDEV5(SelectValue, {}, undefined, false, undefined, this)
651
+ }, undefined, false, undefined, this),
652
+ /* @__PURE__ */ jsxDEV5(SelectContent, {
653
+ children: providers.map((p) => /* @__PURE__ */ jsxDEV5(SelectItem, {
654
+ value: p.provider,
655
+ disabled: !p.available,
656
+ children: /* @__PURE__ */ jsxDEV5("div", {
657
+ className: "flex items-center gap-2",
658
+ children: [
659
+ PROVIDER_ICONS[p.provider],
660
+ /* @__PURE__ */ jsxDEV5("span", {
661
+ children: PROVIDER_NAMES[p.provider]
662
+ }, undefined, false, undefined, this)
663
+ ]
664
+ }, undefined, true, undefined, this)
665
+ }, p.provider, false, undefined, this))
666
+ }, undefined, false, undefined, this)
667
+ ]
668
+ }, undefined, true, undefined, this),
669
+ /* @__PURE__ */ jsxDEV5(Select, {
670
+ value: value.model,
671
+ onValueChange: handleModelChange,
672
+ children: [
673
+ /* @__PURE__ */ jsxDEV5(SelectTrigger, {
674
+ className: "w-[160px]",
675
+ children: /* @__PURE__ */ jsxDEV5(SelectValue, {}, undefined, false, undefined, this)
676
+ }, undefined, false, undefined, this),
677
+ /* @__PURE__ */ jsxDEV5(SelectContent, {
678
+ children: models.map((m) => /* @__PURE__ */ jsxDEV5(SelectItem, {
679
+ value: m.id,
680
+ children: m.name
681
+ }, m.id, false, undefined, this))
682
+ }, undefined, false, undefined, this)
683
+ ]
684
+ }, undefined, true, undefined, this)
685
+ ]
686
+ }, undefined, true, undefined, this);
687
+ }
688
+ return /* @__PURE__ */ jsxDEV5("div", {
689
+ className: cn5("flex flex-col gap-3", className),
690
+ children: [
691
+ /* @__PURE__ */ jsxDEV5("div", {
692
+ className: "flex flex-col gap-1.5",
693
+ children: [
694
+ /* @__PURE__ */ jsxDEV5(Label, {
695
+ htmlFor: "provider-selection",
696
+ className: "text-sm font-medium",
697
+ children: "Provider"
698
+ }, undefined, false, undefined, this),
699
+ /* @__PURE__ */ jsxDEV5("div", {
700
+ className: "flex flex-wrap gap-2",
701
+ id: "provider-selection",
702
+ children: providers.map((p) => /* @__PURE__ */ jsxDEV5(Button4, {
703
+ variant: value.provider === p.provider ? "default" : "outline",
704
+ size: "sm",
705
+ onPress: () => p.available && handleProviderChange(p.provider),
706
+ disabled: !p.available,
707
+ className: cn5(!p.available && "opacity-50"),
708
+ children: [
709
+ PROVIDER_ICONS[p.provider],
710
+ /* @__PURE__ */ jsxDEV5("span", {
711
+ children: PROVIDER_NAMES[p.provider]
712
+ }, undefined, false, undefined, this),
713
+ /* @__PURE__ */ jsxDEV5(Badge, {
714
+ variant: MODE_BADGES[p.mode].variant,
715
+ className: "ml-1",
716
+ children: MODE_BADGES[p.mode].label
717
+ }, undefined, false, undefined, this)
718
+ ]
719
+ }, p.provider, true, undefined, this))
720
+ }, undefined, false, undefined, this)
721
+ ]
722
+ }, undefined, true, undefined, this),
723
+ /* @__PURE__ */ jsxDEV5("div", {
724
+ className: "flex flex-col gap-1.5",
725
+ children: [
726
+ /* @__PURE__ */ jsxDEV5(Label, {
727
+ htmlFor: "model-picker",
728
+ className: "text-sm font-medium",
729
+ children: "Model"
730
+ }, undefined, false, undefined, this),
731
+ /* @__PURE__ */ jsxDEV5(Select, {
732
+ name: "model-picker",
733
+ value: value.model,
734
+ onValueChange: handleModelChange,
735
+ children: [
736
+ /* @__PURE__ */ jsxDEV5(SelectTrigger, {
737
+ children: /* @__PURE__ */ jsxDEV5(SelectValue, {
738
+ placeholder: "Select a model"
739
+ }, undefined, false, undefined, this)
740
+ }, undefined, false, undefined, this),
741
+ /* @__PURE__ */ jsxDEV5(SelectContent, {
742
+ children: models.map((m) => /* @__PURE__ */ jsxDEV5(SelectItem, {
743
+ value: m.id,
744
+ children: /* @__PURE__ */ jsxDEV5("div", {
745
+ className: "flex items-center gap-2",
746
+ children: [
747
+ /* @__PURE__ */ jsxDEV5("span", {
748
+ children: m.name
749
+ }, undefined, false, undefined, this),
750
+ /* @__PURE__ */ jsxDEV5("span", {
751
+ className: "text-muted-foreground text-xs",
752
+ children: [
753
+ Math.round(m.contextWindow / 1000),
754
+ "K"
755
+ ]
756
+ }, undefined, true, undefined, this),
757
+ m.capabilities.vision && /* @__PURE__ */ jsxDEV5(Badge, {
758
+ variant: "outline",
759
+ className: "text-xs",
760
+ children: "Vision"
761
+ }, undefined, false, undefined, this),
762
+ m.capabilities.reasoning && /* @__PURE__ */ jsxDEV5(Badge, {
763
+ variant: "outline",
764
+ className: "text-xs",
765
+ children: "Reasoning"
766
+ }, undefined, false, undefined, this)
767
+ ]
768
+ }, undefined, true, undefined, this)
769
+ }, m.id, false, undefined, this))
770
+ }, undefined, false, undefined, this)
771
+ ]
772
+ }, undefined, true, undefined, this)
773
+ ]
774
+ }, undefined, true, undefined, this),
775
+ selectedModel && /* @__PURE__ */ jsxDEV5("div", {
776
+ className: "text-muted-foreground flex flex-wrap gap-2 text-xs",
777
+ children: [
778
+ /* @__PURE__ */ jsxDEV5("span", {
779
+ children: [
780
+ "Context: ",
781
+ Math.round(selectedModel.contextWindow / 1000),
782
+ "K tokens"
783
+ ]
784
+ }, undefined, true, undefined, this),
785
+ selectedModel.capabilities.vision && /* @__PURE__ */ jsxDEV5("span", {
786
+ children: "• Vision"
787
+ }, undefined, false, undefined, this),
788
+ selectedModel.capabilities.tools && /* @__PURE__ */ jsxDEV5("span", {
789
+ children: "• Tools"
790
+ }, undefined, false, undefined, this),
791
+ selectedModel.capabilities.reasoning && /* @__PURE__ */ jsxDEV5("span", {
792
+ children: "• Reasoning"
793
+ }, undefined, false, undefined, this)
794
+ ]
795
+ }, undefined, true, undefined, this)
796
+ ]
797
+ }, undefined, true, undefined, this);
798
+ }
799
+ // src/presentation/components/ContextIndicator.tsx
800
+ import { cn as cn6 } from "@contractspec/lib.ui-kit-web/ui/utils";
801
+ import { Badge as Badge2 } from "@contractspec/lib.ui-kit-web/ui/badge";
802
+ import {
803
+ Tooltip,
804
+ TooltipContent,
805
+ TooltipProvider,
806
+ TooltipTrigger
807
+ } from "@contractspec/lib.ui-kit-web/ui/tooltip";
808
+ import { FolderOpen, FileCode, Zap, Info } from "lucide-react";
809
+ import { jsxDEV as jsxDEV6, Fragment as Fragment3 } from "react/jsx-dev-runtime";
810
+ "use client";
811
+ function ContextIndicator({
812
+ summary,
813
+ active = false,
814
+ className,
815
+ showDetails = true
816
+ }) {
817
+ if (!summary && !active) {
818
+ return /* @__PURE__ */ jsxDEV6("div", {
819
+ className: cn6("flex items-center gap-1.5 text-sm", "text-muted-foreground", className),
820
+ children: [
821
+ /* @__PURE__ */ jsxDEV6(Info, {
822
+ className: "h-4 w-4"
823
+ }, undefined, false, undefined, this),
824
+ /* @__PURE__ */ jsxDEV6("span", {
825
+ children: "No workspace context"
826
+ }, undefined, false, undefined, this)
827
+ ]
828
+ }, undefined, true, undefined, this);
829
+ }
830
+ const content = /* @__PURE__ */ jsxDEV6("div", {
831
+ className: cn6("flex items-center gap-2", active ? "text-foreground" : "text-muted-foreground", className),
832
+ children: [
833
+ /* @__PURE__ */ jsxDEV6(Badge2, {
834
+ variant: active ? "default" : "secondary",
835
+ className: "flex items-center gap-1",
836
+ children: [
837
+ /* @__PURE__ */ jsxDEV6(Zap, {
838
+ className: "h-3 w-3"
839
+ }, undefined, false, undefined, this),
840
+ "Context"
841
+ ]
842
+ }, undefined, true, undefined, this),
843
+ summary && showDetails && /* @__PURE__ */ jsxDEV6(Fragment3, {
844
+ children: [
845
+ /* @__PURE__ */ jsxDEV6("div", {
846
+ className: "flex items-center gap-1 text-xs",
847
+ children: [
848
+ /* @__PURE__ */ jsxDEV6(FolderOpen, {
849
+ className: "h-3.5 w-3.5"
850
+ }, undefined, false, undefined, this),
851
+ /* @__PURE__ */ jsxDEV6("span", {
852
+ children: summary.name
853
+ }, undefined, false, undefined, this)
854
+ ]
855
+ }, undefined, true, undefined, this),
856
+ /* @__PURE__ */ jsxDEV6("div", {
857
+ className: "flex items-center gap-1 text-xs",
858
+ children: [
859
+ /* @__PURE__ */ jsxDEV6(FileCode, {
860
+ className: "h-3.5 w-3.5"
861
+ }, undefined, false, undefined, this),
862
+ /* @__PURE__ */ jsxDEV6("span", {
863
+ children: [
864
+ summary.specs.total,
865
+ " specs"
866
+ ]
867
+ }, undefined, true, undefined, this)
868
+ ]
869
+ }, undefined, true, undefined, this)
870
+ ]
871
+ }, undefined, true, undefined, this)
872
+ ]
873
+ }, undefined, true, undefined, this);
874
+ if (!summary) {
875
+ return content;
876
+ }
877
+ return /* @__PURE__ */ jsxDEV6(TooltipProvider, {
878
+ children: /* @__PURE__ */ jsxDEV6(Tooltip, {
879
+ children: [
880
+ /* @__PURE__ */ jsxDEV6(TooltipTrigger, {
881
+ asChild: true,
882
+ children: content
883
+ }, undefined, false, undefined, this),
884
+ /* @__PURE__ */ jsxDEV6(TooltipContent, {
885
+ side: "bottom",
886
+ className: "max-w-[300px]",
887
+ children: /* @__PURE__ */ jsxDEV6("div", {
888
+ className: "flex flex-col gap-2 text-sm",
889
+ children: [
890
+ /* @__PURE__ */ jsxDEV6("div", {
891
+ className: "font-medium",
892
+ children: summary.name
893
+ }, undefined, false, undefined, this),
894
+ /* @__PURE__ */ jsxDEV6("div", {
895
+ className: "text-muted-foreground text-xs",
896
+ children: summary.path
897
+ }, undefined, false, undefined, this),
898
+ /* @__PURE__ */ jsxDEV6("div", {
899
+ className: "border-t pt-2",
900
+ children: /* @__PURE__ */ jsxDEV6("div", {
901
+ className: "grid grid-cols-2 gap-1 text-xs",
902
+ children: [
903
+ /* @__PURE__ */ jsxDEV6("span", {
904
+ children: "Commands:"
905
+ }, undefined, false, undefined, this),
906
+ /* @__PURE__ */ jsxDEV6("span", {
907
+ className: "text-right",
908
+ children: summary.specs.commands
909
+ }, undefined, false, undefined, this),
910
+ /* @__PURE__ */ jsxDEV6("span", {
911
+ children: "Queries:"
912
+ }, undefined, false, undefined, this),
913
+ /* @__PURE__ */ jsxDEV6("span", {
914
+ className: "text-right",
915
+ children: summary.specs.queries
916
+ }, undefined, false, undefined, this),
917
+ /* @__PURE__ */ jsxDEV6("span", {
918
+ children: "Events:"
919
+ }, undefined, false, undefined, this),
920
+ /* @__PURE__ */ jsxDEV6("span", {
921
+ className: "text-right",
922
+ children: summary.specs.events
923
+ }, undefined, false, undefined, this),
924
+ /* @__PURE__ */ jsxDEV6("span", {
925
+ children: "Presentations:"
926
+ }, undefined, false, undefined, this),
927
+ /* @__PURE__ */ jsxDEV6("span", {
928
+ className: "text-right",
929
+ children: summary.specs.presentations
930
+ }, undefined, false, undefined, this)
931
+ ]
932
+ }, undefined, true, undefined, this)
933
+ }, undefined, false, undefined, this),
934
+ /* @__PURE__ */ jsxDEV6("div", {
935
+ className: "border-t pt-2 text-xs",
936
+ children: [
937
+ /* @__PURE__ */ jsxDEV6("span", {
938
+ children: [
939
+ summary.files.total,
940
+ " files"
941
+ ]
942
+ }, undefined, true, undefined, this),
943
+ /* @__PURE__ */ jsxDEV6("span", {
944
+ className: "mx-1",
945
+ children: "•"
946
+ }, undefined, false, undefined, this),
947
+ /* @__PURE__ */ jsxDEV6("span", {
948
+ children: [
949
+ summary.files.specFiles,
950
+ " spec files"
951
+ ]
952
+ }, undefined, true, undefined, this)
953
+ ]
954
+ }, undefined, true, undefined, this)
955
+ ]
956
+ }, undefined, true, undefined, this)
957
+ }, undefined, false, undefined, this)
958
+ ]
959
+ }, undefined, true, undefined, this)
960
+ }, undefined, false, undefined, this);
961
+ }
962
+ export {
963
+ ModelPicker,
964
+ ContextIndicator,
965
+ CodePreview,
966
+ ChatMessage,
967
+ ChatInput,
968
+ ChatContainer
969
+ };