@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.
Files changed (48) hide show
  1. package/dist/ai-chat.feature.js +93 -1
  2. package/dist/context/context-builder.js +147 -2
  3. package/dist/context/file-operations.js +174 -1
  4. package/dist/context/index.js +5 -1
  5. package/dist/context/workspace-context.js +123 -2
  6. package/dist/core/chat-service.js +211 -2
  7. package/dist/core/conversation-store.js +108 -1
  8. package/dist/core/index.js +4 -1
  9. package/dist/index.js +22 -1
  10. package/dist/libs/ai-providers/dist/factory.js +225 -1
  11. package/dist/libs/ai-providers/dist/index.js +4 -1
  12. package/dist/libs/ai-providers/dist/legacy.js +2 -1
  13. package/dist/libs/ai-providers/dist/models.js +299 -1
  14. package/dist/libs/ai-providers/dist/validation.js +60 -1
  15. package/dist/libs/design-system/dist/_virtual/rolldown_runtime.js +5 -1
  16. package/dist/libs/design-system/dist/components/atoms/Button.js +33 -1
  17. package/dist/libs/design-system/dist/components/atoms/Textarea.js +35 -1
  18. package/dist/libs/design-system/dist/lib/keyboard.js +193 -1
  19. package/dist/libs/design-system/dist/ui-kit-web/dist/ui/button.js +55 -1
  20. package/dist/libs/design-system/dist/ui-kit-web/dist/ui/textarea.js +16 -1
  21. package/dist/libs/design-system/dist/ui-kit-web/dist/ui-kit-core/dist/utils.js +13 -1
  22. package/dist/libs/ui-kit-web/dist/ui/avatar.js +25 -1
  23. package/dist/libs/ui-kit-web/dist/ui/badge.js +26 -1
  24. package/dist/libs/ui-kit-web/dist/ui/scroll-area.js +39 -1
  25. package/dist/libs/ui-kit-web/dist/ui/select.js +79 -1
  26. package/dist/libs/ui-kit-web/dist/ui/skeleton.js +14 -1
  27. package/dist/libs/ui-kit-web/dist/ui/tooltip.js +39 -1
  28. package/dist/libs/ui-kit-web/dist/ui/utils.js +10 -1
  29. package/dist/libs/ui-kit-web/dist/ui-kit-core/dist/utils.js +10 -1
  30. package/dist/presentation/components/ChatContainer.js +62 -1
  31. package/dist/presentation/components/ChatInput.d.ts +2 -2
  32. package/dist/presentation/components/ChatInput.js +149 -1
  33. package/dist/presentation/components/ChatMessage.d.ts +2 -2
  34. package/dist/presentation/components/ChatMessage.js +135 -1
  35. package/dist/presentation/components/CodePreview.d.ts +2 -2
  36. package/dist/presentation/components/CodePreview.js +126 -2
  37. package/dist/presentation/components/ContextIndicator.d.ts +2 -2
  38. package/dist/presentation/components/ContextIndicator.js +96 -1
  39. package/dist/presentation/components/ModelPicker.d.ts +2 -2
  40. package/dist/presentation/components/ModelPicker.js +197 -1
  41. package/dist/presentation/components/index.js +8 -1
  42. package/dist/presentation/hooks/index.js +4 -1
  43. package/dist/presentation/hooks/useChat.js +171 -1
  44. package/dist/presentation/hooks/useProviders.js +42 -1
  45. package/dist/presentation/index.js +12 -1
  46. package/dist/providers/chat-utilities.js +16 -1
  47. package/dist/providers/index.js +7 -1
  48. package/package.json +10 -10
@@ -1 +1,135 @@
1
- "use client";import{cn as e}from"../../libs/ui-kit-web/dist/ui/utils.js";import{Avatar as t,AvatarFallback as n}from"../../libs/ui-kit-web/dist/ui/avatar.js";import{Skeleton as r}from"../../libs/ui-kit-web/dist/ui/skeleton.js";import{Button$1 as i}from"../../libs/design-system/dist/components/atoms/Button.js";import{CodePreview as a}from"./CodePreview.js";import*as o from"react";import{Fragment as s,jsx as c,jsxs as l}from"react/jsx-runtime";import{AlertCircle as u,Bot as d,Check as f,Copy as p,User as m}from"lucide-react";function h(e){let t=/```(\w+)?\n([\s\S]*?)```/g,n=[],r;for(;(r=t.exec(e))!==null;)n.push({language:r[1]??`text`,code:r[2]??``,raw:r[0]});return n}function g({content:e}){let t=h(e);if(t.length===0)return c(`p`,{className:`whitespace-pre-wrap`,children:e});let n=e,r=[],i=0;for(let e of t){let[t,o]=n.split(e.raw);t&&r.push(c(`p`,{className:`whitespace-pre-wrap`,children:t.trim()},i++)),r.push(c(a,{code:e.code,language:e.language,className:`my-2`},i++)),n=o??``}return n.trim()&&r.push(c(`p`,{className:`whitespace-pre-wrap`,children:n.trim()},i++)),c(s,{children:r})}function _({message:a,className:s,showCopy:h=!0,showAvatar:_=!0}){let[v,y]=o.useState(!1),b=a.role===`user`,x=a.status===`error`,S=a.status===`streaming`,C=o.useCallback(async()=>{await navigator.clipboard.writeText(a.content),y(!0),setTimeout(()=>y(!1),2e3)},[a.content]);return l(`div`,{className:e(`group flex gap-3`,b&&`flex-row-reverse`,s),children:[_&&c(t,{className:`h-8 w-8 shrink-0`,children:c(n,{className:e(b?`bg-primary text-primary-foreground`:`bg-muted`),children:c(b?m:d,{className:`h-4 w-4`})})}),l(`div`,{className:e(`flex max-w-[80%] flex-col gap-1`,b&&`items-end`),children:[c(`div`,{className:e(`rounded-2xl px-4 py-2`,b?`bg-primary text-primary-foreground`:`bg-muted text-foreground`,x&&`border-destructive bg-destructive/10 border`),children:x&&a.error?l(`div`,{className:`flex items-start gap-2`,children:[c(u,{className:`text-destructive mt-0.5 h-4 w-4 shrink-0`}),l(`div`,{children:[c(`p`,{className:`text-destructive font-medium`,children:a.error.code}),c(`p`,{className:`text-muted-foreground text-sm`,children:a.error.message})]})]}):S&&!a.content?l(`div`,{className:`flex flex-col gap-2`,children:[c(r,{className:`h-4 w-48`}),c(r,{className:`h-4 w-32`})]}):c(g,{content:a.content})}),l(`div`,{className:e(`flex items-center gap-2 text-xs`,`text-muted-foreground opacity-0 transition-opacity`,`group-hover:opacity-100`),children:[c(`span`,{children:new Date(a.createdAt).toLocaleTimeString([],{hour:`2-digit`,minute:`2-digit`})}),a.usage&&l(`span`,{children:[a.usage.inputTokens+a.usage.outputTokens,` tokens`]}),h&&!b&&a.content&&c(i,{variant:`ghost`,size:`sm`,className:`h-6 w-6 p-0`,onPress:C,"aria-label":v?`Copied`:`Copy message`,children:c(v?f:p,{className:`h-3 w-3`})})]}),a.reasoning&&l(`details`,{className:`text-muted-foreground mt-2 text-sm`,children:[c(`summary`,{className:`cursor-pointer hover:underline`,children:`View reasoning`}),c(`div`,{className:`bg-muted mt-1 rounded-md p-2`,children:c(`p`,{className:`whitespace-pre-wrap`,children:a.reasoning})})]})]})]})}export{_ as ChatMessage};
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 react_jsx_runtime4 from "react/jsx-runtime";
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): react_jsx_runtime4.JSX.Element;
37
+ }: CodePreviewProps): react_jsx_runtime0.JSX.Element;
38
38
  //#endregion
39
39
  export { CodePreview };
@@ -1,2 +1,126 @@
1
- "use client";import{cn as e}from"../../libs/ui-kit-web/dist/ui/utils.js";import{Button$1 as t}from"../../libs/design-system/dist/components/atoms/Button.js";import*as n from"react";import{jsx as r,jsxs as i}from"react/jsx-runtime";import{Check as a,Copy as o,Download as s,Play as c}from"lucide-react";const l={ts:`TypeScript`,tsx:`TypeScript (React)`,typescript:`TypeScript`,js:`JavaScript`,jsx:`JavaScript (React)`,javascript:`JavaScript`,json:`JSON`,md:`Markdown`,yaml:`YAML`,yml:`YAML`,bash:`Bash`,sh:`Shell`,sql:`SQL`,py:`Python`,python:`Python`,go:`Go`,rust:`Rust`,rs:`Rust`};function u({code:u,language:d=`text`,filename:f,className:p,showCopy:m=!0,showExecute:h=!1,onExecute:g,showDownload:_=!1,maxHeight:v=400}){let[y,b]=n.useState(!1),x=l[d.toLowerCase()]??d,S=u.split(`
2
- `),C=n.useCallback(async()=>{await navigator.clipboard.writeText(u),b(!0),setTimeout(()=>b(!1),2e3)},[u]),w=n.useCallback(()=>{let e=new Blob([u],{type:`text/plain`}),t=URL.createObjectURL(e),n=document.createElement(`a`);n.href=t,n.download=f??`code.${d}`,document.body.appendChild(n),n.click(),document.body.removeChild(n),URL.revokeObjectURL(t)},[u,f,d]);return i(`div`,{className:e(`overflow-hidden rounded-lg border`,`bg-muted/50`,p),children:[i(`div`,{className:e(`flex items-center justify-between px-3 py-1.5`,`border-b bg-muted/80`),children:[i(`div`,{className:`flex items-center gap-2 text-sm`,children:[f&&r(`span`,{className:`text-foreground font-mono`,children:f}),r(`span`,{className:`text-muted-foreground`,children:x})]}),i(`div`,{className:`flex items-center gap-1`,children:[h&&g&&r(t,{variant:`ghost`,size:`sm`,onPress:()=>g(u),className:`h-7 w-7 p-0`,"aria-label":`Execute code`,children:r(c,{className:`h-3.5 w-3.5`})}),_&&r(t,{variant:`ghost`,size:`sm`,onPress:w,className:`h-7 w-7 p-0`,"aria-label":`Download code`,children:r(s,{className:`h-3.5 w-3.5`})}),m&&r(t,{variant:`ghost`,size:`sm`,onPress:C,className:`h-7 w-7 p-0`,"aria-label":y?`Copied`:`Copy code`,children:y?r(a,{className:`h-3.5 w-3.5 text-green-500`}):r(o,{className:`h-3.5 w-3.5`})})]})]}),r(`div`,{className:`overflow-auto`,style:{maxHeight:v},children:r(`pre`,{className:`p-3`,children:r(`code`,{className:`text-sm`,children:S.map((e,t)=>i(`div`,{className:`flex`,children:[r(`span`,{className:`text-muted-foreground mr-4 select-none text-right w-8`,children:t+1}),r(`span`,{className:`flex-1`,children:e||` `})]},t))})})})]})}export{u as CodePreview};
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 react_jsx_runtime1 from "react/jsx-runtime";
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): react_jsx_runtime1.JSX.Element;
23
+ }: ContextIndicatorProps): react_jsx_runtime4.JSX.Element;
24
24
  //#endregion
25
25
  export { ContextIndicator };
@@ -1 +1,96 @@
1
- "use client";import{cn as e}from"../../libs/ui-kit-web/dist/ui/utils.js";import{Badge as t}from"../../libs/ui-kit-web/dist/ui/badge.js";import{Tooltip as n,TooltipContent as r,TooltipProvider as i,TooltipTrigger as a}from"../../libs/ui-kit-web/dist/ui/tooltip.js";import"react";import{Fragment as o,jsx as s,jsxs as c}from"react/jsx-runtime";import{FileCode as l,FolderOpen as u,Info as d,Zap as f}from"lucide-react";function p({summary:p,active:m=!1,className:h,showDetails:g=!0}){if(!p&&!m)return c(`div`,{className:e(`flex items-center gap-1.5 text-sm`,`text-muted-foreground`,h),children:[s(d,{className:`h-4 w-4`}),s(`span`,{children:`No workspace context`})]});let _=c(`div`,{className:e(`flex items-center gap-2`,m?`text-foreground`:`text-muted-foreground`,h),children:[c(t,{variant:m?`default`:`secondary`,className:`flex items-center gap-1`,children:[s(f,{className:`h-3 w-3`}),`Context`]}),p&&g&&c(o,{children:[c(`div`,{className:`flex items-center gap-1 text-xs`,children:[s(u,{className:`h-3.5 w-3.5`}),s(`span`,{children:p.name})]}),c(`div`,{className:`flex items-center gap-1 text-xs`,children:[s(l,{className:`h-3.5 w-3.5`}),c(`span`,{children:[p.specs.total,` specs`]})]})]})]});return p?s(i,{children:c(n,{children:[s(a,{asChild:!0,children:_}),s(r,{side:`bottom`,className:`max-w-[300px]`,children:c(`div`,{className:`flex flex-col gap-2 text-sm`,children:[s(`div`,{className:`font-medium`,children:p.name}),s(`div`,{className:`text-muted-foreground text-xs`,children:p.path}),s(`div`,{className:`border-t pt-2`,children:c(`div`,{className:`grid grid-cols-2 gap-1 text-xs`,children:[s(`span`,{children:`Commands:`}),s(`span`,{className:`text-right`,children:p.specs.commands}),s(`span`,{children:`Queries:`}),s(`span`,{className:`text-right`,children:p.specs.queries}),s(`span`,{children:`Events:`}),s(`span`,{className:`text-right`,children:p.specs.events}),s(`span`,{children:`Presentations:`}),s(`span`,{className:`text-right`,children:p.specs.presentations})]})}),c(`div`,{className:`border-t pt-2 text-xs`,children:[c(`span`,{children:[p.files.total,` files`]}),s(`span`,{className:`mx-1`,children:`•`}),c(`span`,{children:[p.files.specFiles,` spec files`]})]})]})})]})}):_}export{p as ContextIndicator};
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 react_jsx_runtime2 from "react/jsx-runtime";
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): react_jsx_runtime2.JSX.Element;
36
+ }: ModelPickerProps): react_jsx_runtime3.JSX.Element;
37
37
  //#endregion
38
38
  export { ModelPicker };
@@ -1 +1,197 @@
1
- "use client";import{n as e}from"../../libs/ai-providers/dist/models.js";import"../../libs/ai-providers/dist/index.js";import{cn as t}from"../../libs/ui-kit-web/dist/ui/utils.js";import{Button$1 as n}from"../../libs/design-system/dist/components/atoms/Button.js";import{Select as r,SelectContent as i,SelectItem as a,SelectTrigger as o,SelectValue as s}from"../../libs/ui-kit-web/dist/ui/select.js";import{Badge as c}from"../../libs/ui-kit-web/dist/ui/badge.js";import*as l from"react";import{jsx as u,jsxs as d}from"react/jsx-runtime";import{Bot as f,Cloud as p,Cpu as m,Sparkles as h}from"lucide-react";const g={ollama:u(m,{className:`h-4 w-4`}),openai:u(f,{className:`h-4 w-4`}),anthropic:u(h,{className:`h-4 w-4`}),mistral:u(p,{className:`h-4 w-4`}),gemini:u(h,{className:`h-4 w-4`})},_={ollama:`Ollama (Local)`,openai:`OpenAI`,anthropic:`Anthropic`,mistral:`Mistral`,gemini:`Google Gemini`},v={local:{label:`Local`,variant:`secondary`},byok:{label:`BYOK`,variant:`outline`},managed:{label:`Managed`,variant:`default`}};function y({value:f,onChange:p,availableProviders:m,className:h,compact:y=!1}){let b=m??[{provider:`ollama`,available:!0,mode:`local`},{provider:`openai`,available:!0,mode:`byok`},{provider:`anthropic`,available:!0,mode:`byok`},{provider:`mistral`,available:!0,mode:`byok`},{provider:`gemini`,available:!0,mode:`byok`}],x=e(f.provider),S=x.find(e=>e.id===f.model),C=l.useCallback(t=>{let n=t,r=b.find(e=>e.provider===n);p({provider:n,model:e(n)[0]?.id??``,mode:r?.mode??`byok`})},[p,b]),w=l.useCallback(e=>{p({...f,model:e})},[p,f]);return y?d(`div`,{className:t(`flex items-center gap-2`,h),children:[d(r,{value:f.provider,onValueChange:C,children:[u(o,{className:`w-[140px]`,children:u(s,{})}),u(i,{children:b.map(e=>u(a,{value:e.provider,disabled:!e.available,children:d(`div`,{className:`flex items-center gap-2`,children:[g[e.provider],u(`span`,{children:_[e.provider]})]})},e.provider))})]}),d(r,{value:f.model,onValueChange:w,children:[u(o,{className:`w-[160px]`,children:u(s,{})}),u(i,{children:x.map(e=>u(a,{value:e.id,children:e.name},e.id))})]})]}):d(`div`,{className:t(`flex flex-col gap-3`,h),children:[d(`div`,{className:`flex flex-col gap-1.5`,children:[u(`label`,{className:`text-sm font-medium`,children:`Provider`}),u(`div`,{className:`flex flex-wrap gap-2`,children:b.map(e=>d(n,{variant:f.provider===e.provider?`default`:`outline`,size:`sm`,onPress:()=>e.available&&C(e.provider),disabled:!e.available,className:t(!e.available&&`opacity-50`),children:[g[e.provider],u(`span`,{children:_[e.provider]}),u(c,{variant:v[e.mode].variant,className:`ml-1`,children:v[e.mode].label})]},e.provider))})]}),d(`div`,{className:`flex flex-col gap-1.5`,children:[u(`label`,{className:`text-sm font-medium`,children:`Model`}),d(r,{value:f.model,onValueChange:w,children:[u(o,{children:u(s,{placeholder:`Select a model`})}),u(i,{children:x.map(e=>u(a,{value:e.id,children:d(`div`,{className:`flex items-center gap-2`,children:[u(`span`,{children:e.name}),d(`span`,{className:`text-muted-foreground text-xs`,children:[Math.round(e.contextWindow/1e3),`K`]}),e.capabilities.vision&&u(c,{variant:`outline`,className:`text-xs`,children:`Vision`}),e.capabilities.reasoning&&u(c,{variant:`outline`,className:`text-xs`,children:`Reasoning`})]})},e.id))})]})]}),S&&d(`div`,{className:`text-muted-foreground flex flex-wrap gap-2 text-xs`,children:[d(`span`,{children:[`Context: `,Math.round(S.contextWindow/1e3),`K tokens`]}),S.capabilities.vision&&u(`span`,{children:`• Vision`}),S.capabilities.tools&&u(`span`,{children:`• Tools`}),S.capabilities.reasoning&&u(`span`,{children:`• Reasoning`})]})]})}export{y as ModelPicker};
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{ChatContainer as e}from"./ChatContainer.js";import{CodePreview as t}from"./CodePreview.js";import{ChatMessage as n}from"./ChatMessage.js";import{ChatInput as r}from"./ChatInput.js";import{ModelPicker as i}from"./ModelPicker.js";import{ContextIndicator as a}from"./ContextIndicator.js";export{e as ChatContainer,r as ChatInput,n as ChatMessage,t as CodePreview,a as ContextIndicator,i as ModelPicker};
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 };
@@ -1 +1,4 @@
1
- import{useChat as e}from"./useChat.js";import{useProviders as t}from"./useProviders.js";export{e as useChat,t as useProviders};
1
+ import { useChat } from "./useChat.js";
2
+ import { useProviders } from "./useProviders.js";
3
+
4
+ export { useChat, useProviders };