@lssm/module.ai-chat 0.0.0-canary-20251217062139 → 0.0.0-canary-20251217072406
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-chat.feature.js +93 -1
- package/dist/context/context-builder.js +147 -2
- package/dist/context/file-operations.js +174 -1
- package/dist/context/index.js +5 -1
- package/dist/context/workspace-context.js +123 -2
- package/dist/core/chat-service.js +211 -2
- package/dist/core/conversation-store.js +108 -1
- package/dist/core/index.js +4 -1
- package/dist/index.js +22 -1
- package/dist/libs/ai-providers/dist/factory.js +225 -1
- package/dist/libs/ai-providers/dist/index.js +4 -1
- package/dist/libs/ai-providers/dist/legacy.js +2 -1
- package/dist/libs/ai-providers/dist/models.js +299 -1
- package/dist/libs/ai-providers/dist/validation.js +60 -1
- package/dist/libs/design-system/dist/_virtual/rolldown_runtime.js +5 -1
- package/dist/libs/design-system/dist/components/atoms/Button.js +33 -1
- package/dist/libs/design-system/dist/components/atoms/Textarea.js +35 -1
- package/dist/libs/design-system/dist/lib/keyboard.js +193 -1
- package/dist/libs/design-system/dist/ui-kit-web/dist/ui/button.js +55 -1
- package/dist/libs/design-system/dist/ui-kit-web/dist/ui/textarea.js +16 -1
- package/dist/libs/design-system/dist/ui-kit-web/dist/ui-kit-core/dist/utils.js +13 -1
- package/dist/libs/ui-kit-web/dist/ui/avatar.js +25 -1
- package/dist/libs/ui-kit-web/dist/ui/badge.js +26 -1
- package/dist/libs/ui-kit-web/dist/ui/scroll-area.js +39 -1
- package/dist/libs/ui-kit-web/dist/ui/select.js +79 -1
- package/dist/libs/ui-kit-web/dist/ui/skeleton.js +14 -1
- package/dist/libs/ui-kit-web/dist/ui/tooltip.js +39 -1
- package/dist/libs/ui-kit-web/dist/ui/utils.js +10 -1
- package/dist/libs/ui-kit-web/dist/ui-kit-core/dist/utils.js +10 -1
- package/dist/presentation/components/ChatContainer.js +62 -1
- package/dist/presentation/components/ChatInput.d.ts +2 -2
- package/dist/presentation/components/ChatInput.js +149 -1
- package/dist/presentation/components/ChatMessage.d.ts +2 -2
- package/dist/presentation/components/ChatMessage.js +135 -1
- package/dist/presentation/components/CodePreview.d.ts +2 -2
- package/dist/presentation/components/CodePreview.js +126 -2
- package/dist/presentation/components/ContextIndicator.d.ts +2 -2
- package/dist/presentation/components/ContextIndicator.js +96 -1
- package/dist/presentation/components/ModelPicker.d.ts +2 -2
- package/dist/presentation/components/ModelPicker.js +197 -1
- package/dist/presentation/components/index.js +8 -1
- package/dist/presentation/hooks/index.js +4 -1
- package/dist/presentation/hooks/useChat.js +171 -1
- package/dist/presentation/hooks/useProviders.js +42 -1
- package/dist/presentation/index.js +12 -1
- package/dist/providers/chat-utilities.js +16 -1
- package/dist/providers/index.js +7 -1
- package/package.json +10 -10
|
@@ -1 +1,135 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { cn } from "../../libs/ui-kit-web/dist/ui/utils.js";
|
|
4
|
+
import { Avatar, AvatarFallback } from "../../libs/ui-kit-web/dist/ui/avatar.js";
|
|
5
|
+
import { Skeleton } from "../../libs/ui-kit-web/dist/ui/skeleton.js";
|
|
6
|
+
import { Button$1 } from "../../libs/design-system/dist/components/atoms/Button.js";
|
|
7
|
+
import { CodePreview } from "./CodePreview.js";
|
|
8
|
+
import * as React from "react";
|
|
9
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
import { AlertCircle, Bot, Check, Copy, User } from "lucide-react";
|
|
11
|
+
|
|
12
|
+
//#region src/presentation/components/ChatMessage.tsx
|
|
13
|
+
/**
|
|
14
|
+
* Extract code blocks from message content
|
|
15
|
+
*/
|
|
16
|
+
function extractCodeBlocks(content) {
|
|
17
|
+
const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
|
|
18
|
+
const blocks = [];
|
|
19
|
+
let match;
|
|
20
|
+
while ((match = codeBlockRegex.exec(content)) !== null) blocks.push({
|
|
21
|
+
language: match[1] ?? "text",
|
|
22
|
+
code: match[2] ?? "",
|
|
23
|
+
raw: match[0]
|
|
24
|
+
});
|
|
25
|
+
return blocks;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Render message content with code blocks
|
|
29
|
+
*/
|
|
30
|
+
function MessageContent({ content }) {
|
|
31
|
+
const codeBlocks = extractCodeBlocks(content);
|
|
32
|
+
if (codeBlocks.length === 0) return /* @__PURE__ */ jsx("p", {
|
|
33
|
+
className: "whitespace-pre-wrap",
|
|
34
|
+
children: content
|
|
35
|
+
});
|
|
36
|
+
let remaining = content;
|
|
37
|
+
const parts = [];
|
|
38
|
+
let key = 0;
|
|
39
|
+
for (const block of codeBlocks) {
|
|
40
|
+
const [before, after] = remaining.split(block.raw);
|
|
41
|
+
if (before) parts.push(/* @__PURE__ */ jsx("p", {
|
|
42
|
+
className: "whitespace-pre-wrap",
|
|
43
|
+
children: before.trim()
|
|
44
|
+
}, key++));
|
|
45
|
+
parts.push(/* @__PURE__ */ jsx(CodePreview, {
|
|
46
|
+
code: block.code,
|
|
47
|
+
language: block.language,
|
|
48
|
+
className: "my-2"
|
|
49
|
+
}, key++));
|
|
50
|
+
remaining = after ?? "";
|
|
51
|
+
}
|
|
52
|
+
if (remaining.trim()) parts.push(/* @__PURE__ */ jsx("p", {
|
|
53
|
+
className: "whitespace-pre-wrap",
|
|
54
|
+
children: remaining.trim()
|
|
55
|
+
}, key++));
|
|
56
|
+
return /* @__PURE__ */ jsx(Fragment, { children: parts });
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Chat message component
|
|
60
|
+
*/
|
|
61
|
+
function ChatMessage({ message, className, showCopy = true, showAvatar = true }) {
|
|
62
|
+
const [copied, setCopied] = React.useState(false);
|
|
63
|
+
const isUser = message.role === "user";
|
|
64
|
+
const isError = message.status === "error";
|
|
65
|
+
const isStreaming = message.status === "streaming";
|
|
66
|
+
const handleCopy = React.useCallback(async () => {
|
|
67
|
+
await navigator.clipboard.writeText(message.content);
|
|
68
|
+
setCopied(true);
|
|
69
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
70
|
+
}, [message.content]);
|
|
71
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
72
|
+
className: cn("group flex gap-3", isUser && "flex-row-reverse", className),
|
|
73
|
+
children: [showAvatar && /* @__PURE__ */ jsx(Avatar, {
|
|
74
|
+
className: "h-8 w-8 shrink-0",
|
|
75
|
+
children: /* @__PURE__ */ jsx(AvatarFallback, {
|
|
76
|
+
className: cn(isUser ? "bg-primary text-primary-foreground" : "bg-muted"),
|
|
77
|
+
children: isUser ? /* @__PURE__ */ jsx(User, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(Bot, { className: "h-4 w-4" })
|
|
78
|
+
})
|
|
79
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
80
|
+
className: cn("flex max-w-[80%] flex-col gap-1", isUser && "items-end"),
|
|
81
|
+
children: [
|
|
82
|
+
/* @__PURE__ */ jsx("div", {
|
|
83
|
+
className: cn("rounded-2xl px-4 py-2", isUser ? "bg-primary text-primary-foreground" : "bg-muted text-foreground", isError && "border-destructive bg-destructive/10 border"),
|
|
84
|
+
children: isError && message.error ? /* @__PURE__ */ jsxs("div", {
|
|
85
|
+
className: "flex items-start gap-2",
|
|
86
|
+
children: [/* @__PURE__ */ jsx(AlertCircle, { className: "text-destructive mt-0.5 h-4 w-4 shrink-0" }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", {
|
|
87
|
+
className: "text-destructive font-medium",
|
|
88
|
+
children: message.error.code
|
|
89
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
90
|
+
className: "text-muted-foreground text-sm",
|
|
91
|
+
children: message.error.message
|
|
92
|
+
})] })]
|
|
93
|
+
}) : isStreaming && !message.content ? /* @__PURE__ */ jsxs("div", {
|
|
94
|
+
className: "flex flex-col gap-2",
|
|
95
|
+
children: [/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-48" }), /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-32" })]
|
|
96
|
+
}) : /* @__PURE__ */ jsx(MessageContent, { content: message.content })
|
|
97
|
+
}),
|
|
98
|
+
/* @__PURE__ */ jsxs("div", {
|
|
99
|
+
className: cn("flex items-center gap-2 text-xs", "text-muted-foreground opacity-0 transition-opacity", "group-hover:opacity-100"),
|
|
100
|
+
children: [
|
|
101
|
+
/* @__PURE__ */ jsx("span", { children: new Date(message.createdAt).toLocaleTimeString([], {
|
|
102
|
+
hour: "2-digit",
|
|
103
|
+
minute: "2-digit"
|
|
104
|
+
}) }),
|
|
105
|
+
message.usage && /* @__PURE__ */ jsxs("span", { children: [message.usage.inputTokens + message.usage.outputTokens, " tokens"] }),
|
|
106
|
+
showCopy && !isUser && message.content && /* @__PURE__ */ jsx(Button$1, {
|
|
107
|
+
variant: "ghost",
|
|
108
|
+
size: "sm",
|
|
109
|
+
className: "h-6 w-6 p-0",
|
|
110
|
+
onPress: handleCopy,
|
|
111
|
+
"aria-label": copied ? "Copied" : "Copy message",
|
|
112
|
+
children: copied ? /* @__PURE__ */ jsx(Check, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(Copy, { className: "h-3 w-3" })
|
|
113
|
+
})
|
|
114
|
+
]
|
|
115
|
+
}),
|
|
116
|
+
message.reasoning && /* @__PURE__ */ jsxs("details", {
|
|
117
|
+
className: "text-muted-foreground mt-2 text-sm",
|
|
118
|
+
children: [/* @__PURE__ */ jsx("summary", {
|
|
119
|
+
className: "cursor-pointer hover:underline",
|
|
120
|
+
children: "View reasoning"
|
|
121
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
122
|
+
className: "bg-muted mt-1 rounded-md p-2",
|
|
123
|
+
children: /* @__PURE__ */ jsx("p", {
|
|
124
|
+
className: "whitespace-pre-wrap",
|
|
125
|
+
children: message.reasoning
|
|
126
|
+
})
|
|
127
|
+
})]
|
|
128
|
+
})
|
|
129
|
+
]
|
|
130
|
+
})]
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
//#endregion
|
|
135
|
+
export { ChatMessage };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
2
2
|
|
|
3
3
|
//#region src/presentation/components/CodePreview.d.ts
|
|
4
4
|
interface CodePreviewProps {
|
|
@@ -34,6 +34,6 @@ declare function CodePreview({
|
|
|
34
34
|
onExecute,
|
|
35
35
|
showDownload,
|
|
36
36
|
maxHeight
|
|
37
|
-
}: CodePreviewProps):
|
|
37
|
+
}: CodePreviewProps): react_jsx_runtime0.JSX.Element;
|
|
38
38
|
//#endregion
|
|
39
39
|
export { CodePreview };
|
|
@@ -1,2 +1,126 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { cn } from "../../libs/ui-kit-web/dist/ui/utils.js";
|
|
4
|
+
import { Button$1 } from "../../libs/design-system/dist/components/atoms/Button.js";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { Check, Copy, Download, Play } from "lucide-react";
|
|
8
|
+
|
|
9
|
+
//#region src/presentation/components/CodePreview.tsx
|
|
10
|
+
/**
|
|
11
|
+
* Language display names
|
|
12
|
+
*/
|
|
13
|
+
const LANGUAGE_NAMES = {
|
|
14
|
+
ts: "TypeScript",
|
|
15
|
+
tsx: "TypeScript (React)",
|
|
16
|
+
typescript: "TypeScript",
|
|
17
|
+
js: "JavaScript",
|
|
18
|
+
jsx: "JavaScript (React)",
|
|
19
|
+
javascript: "JavaScript",
|
|
20
|
+
json: "JSON",
|
|
21
|
+
md: "Markdown",
|
|
22
|
+
yaml: "YAML",
|
|
23
|
+
yml: "YAML",
|
|
24
|
+
bash: "Bash",
|
|
25
|
+
sh: "Shell",
|
|
26
|
+
sql: "SQL",
|
|
27
|
+
py: "Python",
|
|
28
|
+
python: "Python",
|
|
29
|
+
go: "Go",
|
|
30
|
+
rust: "Rust",
|
|
31
|
+
rs: "Rust"
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Code preview component with syntax highlighting placeholder
|
|
35
|
+
*/
|
|
36
|
+
function CodePreview({ code, language = "text", filename, className, showCopy = true, showExecute = false, onExecute, showDownload = false, maxHeight = 400 }) {
|
|
37
|
+
const [copied, setCopied] = React.useState(false);
|
|
38
|
+
const displayLanguage = LANGUAGE_NAMES[language.toLowerCase()] ?? language;
|
|
39
|
+
const lines = code.split("\n");
|
|
40
|
+
const handleCopy = React.useCallback(async () => {
|
|
41
|
+
await navigator.clipboard.writeText(code);
|
|
42
|
+
setCopied(true);
|
|
43
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
44
|
+
}, [code]);
|
|
45
|
+
const handleDownload = React.useCallback(() => {
|
|
46
|
+
const blob = new Blob([code], { type: "text/plain" });
|
|
47
|
+
const url = URL.createObjectURL(blob);
|
|
48
|
+
const a = document.createElement("a");
|
|
49
|
+
a.href = url;
|
|
50
|
+
a.download = filename ?? `code.${language}`;
|
|
51
|
+
document.body.appendChild(a);
|
|
52
|
+
a.click();
|
|
53
|
+
document.body.removeChild(a);
|
|
54
|
+
URL.revokeObjectURL(url);
|
|
55
|
+
}, [
|
|
56
|
+
code,
|
|
57
|
+
filename,
|
|
58
|
+
language
|
|
59
|
+
]);
|
|
60
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
61
|
+
className: cn("overflow-hidden rounded-lg border", "bg-muted/50", className),
|
|
62
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
63
|
+
className: cn("flex items-center justify-between px-3 py-1.5", "border-b bg-muted/80"),
|
|
64
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
65
|
+
className: "flex items-center gap-2 text-sm",
|
|
66
|
+
children: [filename && /* @__PURE__ */ jsx("span", {
|
|
67
|
+
className: "text-foreground font-mono",
|
|
68
|
+
children: filename
|
|
69
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
70
|
+
className: "text-muted-foreground",
|
|
71
|
+
children: displayLanguage
|
|
72
|
+
})]
|
|
73
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
74
|
+
className: "flex items-center gap-1",
|
|
75
|
+
children: [
|
|
76
|
+
showExecute && onExecute && /* @__PURE__ */ jsx(Button$1, {
|
|
77
|
+
variant: "ghost",
|
|
78
|
+
size: "sm",
|
|
79
|
+
onPress: () => onExecute(code),
|
|
80
|
+
className: "h-7 w-7 p-0",
|
|
81
|
+
"aria-label": "Execute code",
|
|
82
|
+
children: /* @__PURE__ */ jsx(Play, { className: "h-3.5 w-3.5" })
|
|
83
|
+
}),
|
|
84
|
+
showDownload && /* @__PURE__ */ jsx(Button$1, {
|
|
85
|
+
variant: "ghost",
|
|
86
|
+
size: "sm",
|
|
87
|
+
onPress: handleDownload,
|
|
88
|
+
className: "h-7 w-7 p-0",
|
|
89
|
+
"aria-label": "Download code",
|
|
90
|
+
children: /* @__PURE__ */ jsx(Download, { className: "h-3.5 w-3.5" })
|
|
91
|
+
}),
|
|
92
|
+
showCopy && /* @__PURE__ */ jsx(Button$1, {
|
|
93
|
+
variant: "ghost",
|
|
94
|
+
size: "sm",
|
|
95
|
+
onPress: handleCopy,
|
|
96
|
+
className: "h-7 w-7 p-0",
|
|
97
|
+
"aria-label": copied ? "Copied" : "Copy code",
|
|
98
|
+
children: copied ? /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5 text-green-500" }) : /* @__PURE__ */ jsx(Copy, { className: "h-3.5 w-3.5" })
|
|
99
|
+
})
|
|
100
|
+
]
|
|
101
|
+
})]
|
|
102
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
103
|
+
className: "overflow-auto",
|
|
104
|
+
style: { maxHeight },
|
|
105
|
+
children: /* @__PURE__ */ jsx("pre", {
|
|
106
|
+
className: "p-3",
|
|
107
|
+
children: /* @__PURE__ */ jsx("code", {
|
|
108
|
+
className: "text-sm",
|
|
109
|
+
children: lines.map((line, i) => /* @__PURE__ */ jsxs("div", {
|
|
110
|
+
className: "flex",
|
|
111
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
112
|
+
className: "text-muted-foreground mr-4 select-none text-right w-8",
|
|
113
|
+
children: i + 1
|
|
114
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
115
|
+
className: "flex-1",
|
|
116
|
+
children: line || " "
|
|
117
|
+
})]
|
|
118
|
+
}, i))
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
})]
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
//#endregion
|
|
126
|
+
export { CodePreview };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WorkspaceSummary } from "../../context/workspace-context.js";
|
|
2
|
-
import * as
|
|
2
|
+
import * as react_jsx_runtime4 from "react/jsx-runtime";
|
|
3
3
|
|
|
4
4
|
//#region src/presentation/components/ContextIndicator.d.ts
|
|
5
5
|
interface ContextIndicatorProps {
|
|
@@ -20,6 +20,6 @@ declare function ContextIndicator({
|
|
|
20
20
|
active,
|
|
21
21
|
className,
|
|
22
22
|
showDetails
|
|
23
|
-
}: ContextIndicatorProps):
|
|
23
|
+
}: ContextIndicatorProps): react_jsx_runtime4.JSX.Element;
|
|
24
24
|
//#endregion
|
|
25
25
|
export { ContextIndicator };
|
|
@@ -1 +1,96 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { cn } from "../../libs/ui-kit-web/dist/ui/utils.js";
|
|
4
|
+
import { Badge } from "../../libs/ui-kit-web/dist/ui/badge.js";
|
|
5
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../../libs/ui-kit-web/dist/ui/tooltip.js";
|
|
6
|
+
import "react";
|
|
7
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { FileCode, FolderOpen, Info, Zap } from "lucide-react";
|
|
9
|
+
|
|
10
|
+
//#region src/presentation/components/ContextIndicator.tsx
|
|
11
|
+
/**
|
|
12
|
+
* Indicator showing active workspace context
|
|
13
|
+
*/
|
|
14
|
+
function ContextIndicator({ summary, active = false, className, showDetails = true }) {
|
|
15
|
+
if (!summary && !active) return /* @__PURE__ */ jsxs("div", {
|
|
16
|
+
className: cn("flex items-center gap-1.5 text-sm", "text-muted-foreground", className),
|
|
17
|
+
children: [/* @__PURE__ */ jsx(Info, { className: "h-4 w-4" }), /* @__PURE__ */ jsx("span", { children: "No workspace context" })]
|
|
18
|
+
});
|
|
19
|
+
const content = /* @__PURE__ */ jsxs("div", {
|
|
20
|
+
className: cn("flex items-center gap-2", active ? "text-foreground" : "text-muted-foreground", className),
|
|
21
|
+
children: [/* @__PURE__ */ jsxs(Badge, {
|
|
22
|
+
variant: active ? "default" : "secondary",
|
|
23
|
+
className: "flex items-center gap-1",
|
|
24
|
+
children: [/* @__PURE__ */ jsx(Zap, { className: "h-3 w-3" }), "Context"]
|
|
25
|
+
}), summary && showDetails && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
|
|
26
|
+
className: "flex items-center gap-1 text-xs",
|
|
27
|
+
children: [/* @__PURE__ */ jsx(FolderOpen, { className: "h-3.5 w-3.5" }), /* @__PURE__ */ jsx("span", { children: summary.name })]
|
|
28
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
29
|
+
className: "flex items-center gap-1 text-xs",
|
|
30
|
+
children: [/* @__PURE__ */ jsx(FileCode, { className: "h-3.5 w-3.5" }), /* @__PURE__ */ jsxs("span", { children: [summary.specs.total, " specs"] })]
|
|
31
|
+
})] })]
|
|
32
|
+
});
|
|
33
|
+
if (!summary) return content;
|
|
34
|
+
return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
35
|
+
asChild: true,
|
|
36
|
+
children: content
|
|
37
|
+
}), /* @__PURE__ */ jsx(TooltipContent, {
|
|
38
|
+
side: "bottom",
|
|
39
|
+
className: "max-w-[300px]",
|
|
40
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
41
|
+
className: "flex flex-col gap-2 text-sm",
|
|
42
|
+
children: [
|
|
43
|
+
/* @__PURE__ */ jsx("div", {
|
|
44
|
+
className: "font-medium",
|
|
45
|
+
children: summary.name
|
|
46
|
+
}),
|
|
47
|
+
/* @__PURE__ */ jsx("div", {
|
|
48
|
+
className: "text-muted-foreground text-xs",
|
|
49
|
+
children: summary.path
|
|
50
|
+
}),
|
|
51
|
+
/* @__PURE__ */ jsx("div", {
|
|
52
|
+
className: "border-t pt-2",
|
|
53
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
54
|
+
className: "grid grid-cols-2 gap-1 text-xs",
|
|
55
|
+
children: [
|
|
56
|
+
/* @__PURE__ */ jsx("span", { children: "Commands:" }),
|
|
57
|
+
/* @__PURE__ */ jsx("span", {
|
|
58
|
+
className: "text-right",
|
|
59
|
+
children: summary.specs.commands
|
|
60
|
+
}),
|
|
61
|
+
/* @__PURE__ */ jsx("span", { children: "Queries:" }),
|
|
62
|
+
/* @__PURE__ */ jsx("span", {
|
|
63
|
+
className: "text-right",
|
|
64
|
+
children: summary.specs.queries
|
|
65
|
+
}),
|
|
66
|
+
/* @__PURE__ */ jsx("span", { children: "Events:" }),
|
|
67
|
+
/* @__PURE__ */ jsx("span", {
|
|
68
|
+
className: "text-right",
|
|
69
|
+
children: summary.specs.events
|
|
70
|
+
}),
|
|
71
|
+
/* @__PURE__ */ jsx("span", { children: "Presentations:" }),
|
|
72
|
+
/* @__PURE__ */ jsx("span", {
|
|
73
|
+
className: "text-right",
|
|
74
|
+
children: summary.specs.presentations
|
|
75
|
+
})
|
|
76
|
+
]
|
|
77
|
+
})
|
|
78
|
+
}),
|
|
79
|
+
/* @__PURE__ */ jsxs("div", {
|
|
80
|
+
className: "border-t pt-2 text-xs",
|
|
81
|
+
children: [
|
|
82
|
+
/* @__PURE__ */ jsxs("span", { children: [summary.files.total, " files"] }),
|
|
83
|
+
/* @__PURE__ */ jsx("span", {
|
|
84
|
+
className: "mx-1",
|
|
85
|
+
children: "•"
|
|
86
|
+
}),
|
|
87
|
+
/* @__PURE__ */ jsxs("span", { children: [summary.files.specFiles, " spec files"] })
|
|
88
|
+
]
|
|
89
|
+
})
|
|
90
|
+
]
|
|
91
|
+
})
|
|
92
|
+
})] }) });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
//#endregion
|
|
96
|
+
export { ContextIndicator };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react_jsx_runtime3 from "react/jsx-runtime";
|
|
2
2
|
import { ProviderMode, ProviderName } from "@lssm/lib.ai-providers";
|
|
3
3
|
|
|
4
4
|
//#region src/presentation/components/ModelPicker.d.ts
|
|
@@ -33,6 +33,6 @@ declare function ModelPicker({
|
|
|
33
33
|
availableProviders,
|
|
34
34
|
className,
|
|
35
35
|
compact
|
|
36
|
-
}: ModelPickerProps):
|
|
36
|
+
}: ModelPickerProps): react_jsx_runtime3.JSX.Element;
|
|
37
37
|
//#endregion
|
|
38
38
|
export { ModelPicker };
|
|
@@ -1 +1,197 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { getModelsForProvider } from "../../libs/ai-providers/dist/models.js";
|
|
4
|
+
import "../../libs/ai-providers/dist/index.js";
|
|
5
|
+
import { cn } from "../../libs/ui-kit-web/dist/ui/utils.js";
|
|
6
|
+
import { Button$1 } from "../../libs/design-system/dist/components/atoms/Button.js";
|
|
7
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../../libs/ui-kit-web/dist/ui/select.js";
|
|
8
|
+
import { Badge } from "../../libs/ui-kit-web/dist/ui/badge.js";
|
|
9
|
+
import * as React from "react";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
import { Bot, Cloud, Cpu, Sparkles } from "lucide-react";
|
|
12
|
+
|
|
13
|
+
//#region src/presentation/components/ModelPicker.tsx
|
|
14
|
+
const PROVIDER_ICONS = {
|
|
15
|
+
ollama: /* @__PURE__ */ jsx(Cpu, { className: "h-4 w-4" }),
|
|
16
|
+
openai: /* @__PURE__ */ jsx(Bot, { className: "h-4 w-4" }),
|
|
17
|
+
anthropic: /* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4" }),
|
|
18
|
+
mistral: /* @__PURE__ */ jsx(Cloud, { className: "h-4 w-4" }),
|
|
19
|
+
gemini: /* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4" })
|
|
20
|
+
};
|
|
21
|
+
const PROVIDER_NAMES = {
|
|
22
|
+
ollama: "Ollama (Local)",
|
|
23
|
+
openai: "OpenAI",
|
|
24
|
+
anthropic: "Anthropic",
|
|
25
|
+
mistral: "Mistral",
|
|
26
|
+
gemini: "Google Gemini"
|
|
27
|
+
};
|
|
28
|
+
const MODE_BADGES = {
|
|
29
|
+
local: {
|
|
30
|
+
label: "Local",
|
|
31
|
+
variant: "secondary"
|
|
32
|
+
},
|
|
33
|
+
byok: {
|
|
34
|
+
label: "BYOK",
|
|
35
|
+
variant: "outline"
|
|
36
|
+
},
|
|
37
|
+
managed: {
|
|
38
|
+
label: "Managed",
|
|
39
|
+
variant: "default"
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Model picker component for selecting AI provider and model
|
|
44
|
+
*/
|
|
45
|
+
function ModelPicker({ value, onChange, availableProviders, className, compact = false }) {
|
|
46
|
+
const providers = availableProviders ?? [
|
|
47
|
+
{
|
|
48
|
+
provider: "ollama",
|
|
49
|
+
available: true,
|
|
50
|
+
mode: "local"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
provider: "openai",
|
|
54
|
+
available: true,
|
|
55
|
+
mode: "byok"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
provider: "anthropic",
|
|
59
|
+
available: true,
|
|
60
|
+
mode: "byok"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
provider: "mistral",
|
|
64
|
+
available: true,
|
|
65
|
+
mode: "byok"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
provider: "gemini",
|
|
69
|
+
available: true,
|
|
70
|
+
mode: "byok"
|
|
71
|
+
}
|
|
72
|
+
];
|
|
73
|
+
const models = getModelsForProvider(value.provider);
|
|
74
|
+
const selectedModel = models.find((m) => m.id === value.model);
|
|
75
|
+
const handleProviderChange = React.useCallback((providerName) => {
|
|
76
|
+
const provider = providerName;
|
|
77
|
+
const providerInfo = providers.find((p) => p.provider === provider);
|
|
78
|
+
onChange({
|
|
79
|
+
provider,
|
|
80
|
+
model: getModelsForProvider(provider)[0]?.id ?? "",
|
|
81
|
+
mode: providerInfo?.mode ?? "byok"
|
|
82
|
+
});
|
|
83
|
+
}, [onChange, providers]);
|
|
84
|
+
const handleModelChange = React.useCallback((modelId) => {
|
|
85
|
+
onChange({
|
|
86
|
+
...value,
|
|
87
|
+
model: modelId
|
|
88
|
+
});
|
|
89
|
+
}, [onChange, value]);
|
|
90
|
+
if (compact) return /* @__PURE__ */ jsxs("div", {
|
|
91
|
+
className: cn("flex items-center gap-2", className),
|
|
92
|
+
children: [/* @__PURE__ */ jsxs(Select, {
|
|
93
|
+
value: value.provider,
|
|
94
|
+
onValueChange: handleProviderChange,
|
|
95
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
96
|
+
className: "w-[140px]",
|
|
97
|
+
children: /* @__PURE__ */ jsx(SelectValue, {})
|
|
98
|
+
}), /* @__PURE__ */ jsx(SelectContent, { children: providers.map((p) => /* @__PURE__ */ jsx(SelectItem, {
|
|
99
|
+
value: p.provider,
|
|
100
|
+
disabled: !p.available,
|
|
101
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
102
|
+
className: "flex items-center gap-2",
|
|
103
|
+
children: [PROVIDER_ICONS[p.provider], /* @__PURE__ */ jsx("span", { children: PROVIDER_NAMES[p.provider] })]
|
|
104
|
+
})
|
|
105
|
+
}, p.provider)) })]
|
|
106
|
+
}), /* @__PURE__ */ jsxs(Select, {
|
|
107
|
+
value: value.model,
|
|
108
|
+
onValueChange: handleModelChange,
|
|
109
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
110
|
+
className: "w-[160px]",
|
|
111
|
+
children: /* @__PURE__ */ jsx(SelectValue, {})
|
|
112
|
+
}), /* @__PURE__ */ jsx(SelectContent, { children: models.map((m) => /* @__PURE__ */ jsx(SelectItem, {
|
|
113
|
+
value: m.id,
|
|
114
|
+
children: m.name
|
|
115
|
+
}, m.id)) })]
|
|
116
|
+
})]
|
|
117
|
+
});
|
|
118
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
119
|
+
className: cn("flex flex-col gap-3", className),
|
|
120
|
+
children: [
|
|
121
|
+
/* @__PURE__ */ jsxs("div", {
|
|
122
|
+
className: "flex flex-col gap-1.5",
|
|
123
|
+
children: [/* @__PURE__ */ jsx("label", {
|
|
124
|
+
className: "text-sm font-medium",
|
|
125
|
+
children: "Provider"
|
|
126
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
127
|
+
className: "flex flex-wrap gap-2",
|
|
128
|
+
children: providers.map((p) => /* @__PURE__ */ jsxs(Button$1, {
|
|
129
|
+
variant: value.provider === p.provider ? "default" : "outline",
|
|
130
|
+
size: "sm",
|
|
131
|
+
onPress: () => p.available && handleProviderChange(p.provider),
|
|
132
|
+
disabled: !p.available,
|
|
133
|
+
className: cn(!p.available && "opacity-50"),
|
|
134
|
+
children: [
|
|
135
|
+
PROVIDER_ICONS[p.provider],
|
|
136
|
+
/* @__PURE__ */ jsx("span", { children: PROVIDER_NAMES[p.provider] }),
|
|
137
|
+
/* @__PURE__ */ jsx(Badge, {
|
|
138
|
+
variant: MODE_BADGES[p.mode].variant,
|
|
139
|
+
className: "ml-1",
|
|
140
|
+
children: MODE_BADGES[p.mode].label
|
|
141
|
+
})
|
|
142
|
+
]
|
|
143
|
+
}, p.provider))
|
|
144
|
+
})]
|
|
145
|
+
}),
|
|
146
|
+
/* @__PURE__ */ jsxs("div", {
|
|
147
|
+
className: "flex flex-col gap-1.5",
|
|
148
|
+
children: [/* @__PURE__ */ jsx("label", {
|
|
149
|
+
className: "text-sm font-medium",
|
|
150
|
+
children: "Model"
|
|
151
|
+
}), /* @__PURE__ */ jsxs(Select, {
|
|
152
|
+
value: value.model,
|
|
153
|
+
onValueChange: handleModelChange,
|
|
154
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select a model" }) }), /* @__PURE__ */ jsx(SelectContent, { children: models.map((m) => /* @__PURE__ */ jsx(SelectItem, {
|
|
155
|
+
value: m.id,
|
|
156
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
157
|
+
className: "flex items-center gap-2",
|
|
158
|
+
children: [
|
|
159
|
+
/* @__PURE__ */ jsx("span", { children: m.name }),
|
|
160
|
+
/* @__PURE__ */ jsxs("span", {
|
|
161
|
+
className: "text-muted-foreground text-xs",
|
|
162
|
+
children: [Math.round(m.contextWindow / 1e3), "K"]
|
|
163
|
+
}),
|
|
164
|
+
m.capabilities.vision && /* @__PURE__ */ jsx(Badge, {
|
|
165
|
+
variant: "outline",
|
|
166
|
+
className: "text-xs",
|
|
167
|
+
children: "Vision"
|
|
168
|
+
}),
|
|
169
|
+
m.capabilities.reasoning && /* @__PURE__ */ jsx(Badge, {
|
|
170
|
+
variant: "outline",
|
|
171
|
+
className: "text-xs",
|
|
172
|
+
children: "Reasoning"
|
|
173
|
+
})
|
|
174
|
+
]
|
|
175
|
+
})
|
|
176
|
+
}, m.id)) })]
|
|
177
|
+
})]
|
|
178
|
+
}),
|
|
179
|
+
selectedModel && /* @__PURE__ */ jsxs("div", {
|
|
180
|
+
className: "text-muted-foreground flex flex-wrap gap-2 text-xs",
|
|
181
|
+
children: [
|
|
182
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
183
|
+
"Context: ",
|
|
184
|
+
Math.round(selectedModel.contextWindow / 1e3),
|
|
185
|
+
"K tokens"
|
|
186
|
+
] }),
|
|
187
|
+
selectedModel.capabilities.vision && /* @__PURE__ */ jsx("span", { children: "• Vision" }),
|
|
188
|
+
selectedModel.capabilities.tools && /* @__PURE__ */ jsx("span", { children: "• Tools" }),
|
|
189
|
+
selectedModel.capabilities.reasoning && /* @__PURE__ */ jsx("span", { children: "• Reasoning" })
|
|
190
|
+
]
|
|
191
|
+
})
|
|
192
|
+
]
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
//#endregion
|
|
197
|
+
export { ModelPicker };
|
|
@@ -1 +1,8 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import { ChatContainer } from "./ChatContainer.js";
|
|
2
|
+
import { CodePreview } from "./CodePreview.js";
|
|
3
|
+
import { ChatMessage } from "./ChatMessage.js";
|
|
4
|
+
import { ChatInput } from "./ChatInput.js";
|
|
5
|
+
import { ModelPicker } from "./ModelPicker.js";
|
|
6
|
+
import { ContextIndicator } from "./ContextIndicator.js";
|
|
7
|
+
|
|
8
|
+
export { ChatContainer, ChatInput, ChatMessage, CodePreview, ContextIndicator, ModelPicker };
|