@amaster.ai/components-templates 1.4.1 → 1.4.3

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 (25) hide show
  1. package/components/ai-assistant/others.md +13 -3
  2. package/components/ai-assistant/package.json +112 -30
  3. package/components/ai-assistant/template/ai-assistant.tsx +12 -3
  4. package/components/ai-assistant/template/components/chat-assistant-message.tsx +25 -9
  5. package/components/ai-assistant/template/components/chat-display-mode-switcher.tsx +1 -6
  6. package/components/ai-assistant/template/components/chat-floating-button.tsx +2 -2
  7. package/components/ai-assistant/template/components/chat-floating-card.tsx +1 -1
  8. package/components/ai-assistant/template/components/chat-header.tsx +16 -14
  9. package/components/ai-assistant/template/components/chat-input.tsx +80 -28
  10. package/components/ai-assistant/template/components/chat-messages.tsx +75 -5
  11. package/components/ai-assistant/template/components/chat-recommends.tsx +3 -1
  12. package/components/ai-assistant/template/components/chat-speech-button.tsx +25 -7
  13. package/components/ai-assistant/template/components/ui-renderer.tsx +10 -1
  14. package/components/ai-assistant/template/hooks/useAssistantStore.tsx +35 -6
  15. package/components/ai-assistant/template/hooks/useConversation.ts +898 -0
  16. package/components/ai-assistant/template/hooks/useDisplayMode.tsx +1 -1
  17. package/components/ai-assistant/template/hooks/useSpeak.ts +89 -6
  18. package/components/ai-assistant/template/hooks/useVoiceInput.ts +1 -1
  19. package/components/ai-assistant/template/i18n.ts +21 -3
  20. package/components/ai-assistant/template/inline-ai-assistant.tsx +21 -9
  21. package/components/ai-assistant/template/types.ts +16 -0
  22. package/package.json +4 -2
  23. package/packages/cli/dist/index.js +26 -3
  24. package/packages/cli/dist/index.js.map +1 -1
  25. package/packages/cli/package.json +1 -1
@@ -1,6 +1,16 @@
1
1
  ## tailwind.config.js
2
2
 
3
- content 添加
3
+ content 添加 "./node_modules/@json-render/shadcn/dist/**/*.js"
4
+ ```
5
+ {
6
+ content: [
7
+ "./node_modules/@json-render/shadcn/dist/**/*.js"
8
+ ]
9
+ }
10
+ ```
11
+
12
+ ## src/lib/client 保证是新的
13
+ ```
14
+ import { createClient } from "@amaster.ai/client";
15
+ export const client = createClient({});
4
16
  ```
5
- "./node_modules/@json-render/shadcn/dist/**/*.js"
6
- ```
@@ -1,37 +1,119 @@
1
1
  {
2
- "name": "ai-assistant",
3
- "version": "1.4.1",
4
- "description": "AI Assistant component template",
5
- "main": "template/index.ts",
6
- "template": {
7
- "name": "ai-assistant",
8
- "targetDir": "src/components/ai-assistant",
9
- "files": [
10
- "**/*"
11
- ],
12
- "ignore": [
13
- "node_modules",
14
- "*.log",
15
- ".DS_Store"
16
- ],
17
- "variables": {
18
- "COMPONENT_NAME": {
19
- "description": "Component name",
20
- "default": "AIAssistant"
21
- },
22
- "TARGET_DIR": {
23
- "description": "Target directory",
24
- "default": "src/components/ai-assistant"
25
- }
26
- }
2
+ "name": "amaster-react-project",
3
+ "version": "1.4.3",
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "vite --force",
7
+ "dev:local": "vite --config vite.config.local.ts",
8
+ "dev:restart": "bun scripts/dev-restart.mjs",
9
+ "build": "vite build",
10
+ "lint": "biome check .",
11
+ "lint:fix": "biome check --write .",
12
+ "type-check": "bun scripts/type-check-filter.mjs",
13
+ "check:build": "bun scripts/check-build.mjs",
14
+ "check:ast": "bun scripts/ast-grep-scan.mjs",
15
+ "preview": "vite preview",
16
+ "prepare": "bun scripts/prepare.mjs || true"
27
17
  },
28
18
  "dependencies": {
29
- "@amaster.ai/client": "1.1.0-beta.52",
30
- "react-markdown": "^10.0.0",
31
- "remark-gfm": "^4.0.0",
32
- "immer": "^10.0.0",
19
+ "@a2a-js/sdk": "^0.3.7",
20
+ "@amaster.ai/client": "1.1.0-beta.55",
21
+ "@amaster.ai/vite-plugins": "1.1.0-beta.55",
22
+ "@fontsource-variable/inter": "^5.2.8",
23
+ "@fortawesome/fontawesome-free": "^6.1.1",
24
+ "@hookform/resolvers": "^5.2.2",
33
25
  "@json-render/core": "^0.12.0",
34
26
  "@json-render/react": "^0.12.0",
35
- "@json-render/shadcn": "^0.12.0"
27
+ "@json-render/shadcn": "^0.12.0",
28
+ "@radix-ui/react-accordion": "^1.2.8",
29
+ "@radix-ui/react-alert-dialog": "^1.1.11",
30
+ "@radix-ui/react-aspect-ratio": "^1.1.4",
31
+ "@radix-ui/react-avatar": "^1.1.7",
32
+ "@radix-ui/react-checkbox": "^1.2.3",
33
+ "@radix-ui/react-collapsible": "^1.1.8",
34
+ "@radix-ui/react-context-menu": "^2.2.15",
35
+ "@radix-ui/react-dialog": "^1.1.11",
36
+ "@radix-ui/react-dropdown-menu": "^2.1.12",
37
+ "@radix-ui/react-hover-card": "^1.1.14",
38
+ "@radix-ui/react-icons": "^1.3.2",
39
+ "@radix-ui/react-label": "^2.1.4",
40
+ "@radix-ui/react-menubar": "^1.1.12",
41
+ "@radix-ui/react-navigation-menu": "^1.2.10",
42
+ "@radix-ui/react-popover": "^1.1.7",
43
+ "@radix-ui/react-progress": "^1.1.3",
44
+ "@radix-ui/react-radio-group": "^1.3.4",
45
+ "@radix-ui/react-scroll-area": "^1.2.6",
46
+ "@radix-ui/react-select": "^2.1.7",
47
+ "@radix-ui/react-separator": "^1.1.4",
48
+ "@radix-ui/react-slider": "^1.3.2",
49
+ "@radix-ui/react-slot": "^1.2.0",
50
+ "@radix-ui/react-switch": "^1.2.2",
51
+ "@radix-ui/react-tabs": "^1.1.9",
52
+ "@radix-ui/react-toast": "^1.1.5",
53
+ "@radix-ui/react-toggle": "^1.1.6",
54
+ "@radix-ui/react-toggle-group": "^1.1.7",
55
+ "@radix-ui/react-tooltip": "^1.2.4",
56
+ "@tanstack/react-table": "^8.21.3",
57
+ "bpmn-auto-layout": "^1.2.0",
58
+ "bpmn-js": "18.1.1",
59
+ "class-variance-authority": "^0.7.1",
60
+ "clsx": "^2.1.1",
61
+ "cmdk": "^1.1.1",
62
+ "date-fns": "^3.6.0",
63
+ "embla-carousel-react": "^8.6.0",
64
+ "eventsource-parser": "^3.0.6",
65
+ "immer": "^11.1.4",
66
+ "input-otp": "^1.4.2",
67
+ "ky": "^1.9.1",
68
+ "lucide-react": "^0.544.0",
69
+ "next-themes": "^0.4.6",
70
+ "qrcode": "^1.5.4",
71
+ "qs": "6.9.7",
72
+ "react": "^18.3.1",
73
+ "react-day-picker": "^8.10.1",
74
+ "react-dom": "^18.3.1",
75
+ "react-dropzone": "^14.3.8",
76
+ "react-helmet-async": "^2.0.5",
77
+ "react-hook-form": "^7.71.1",
78
+ "react-markdown": "^10.1.0",
79
+ "react-resizable-panels": "^2.1.8",
80
+ "react-router": "^7.13.0",
81
+ "react-router-dom": "^6.30.1",
82
+ "recharts": "^2.15.3",
83
+ "remark-gfm": "^4.0.1",
84
+ "sonner": "^2.0.3",
85
+ "streamdown": "^1.1.6",
86
+ "tailwind-merge": "^3.2.0",
87
+ "tailwindcss-animate": "^1.0.7",
88
+ "vaul": "^1.1.2",
89
+ "video-react": "^0.16.0",
90
+ "xss": "^1.0.15",
91
+ "zod": "^3.24.3"
92
+ },
93
+ "devDependencies": {
94
+ "@ast-grep/napi": "^0.40.5",
95
+ "@ast-grep/napi-linux-arm64-musl": "^0.40.5",
96
+ "@ast-grep/napi-linux-x64-musl": "^0.40.5",
97
+ "@biomejs/biome": "^2.3.4",
98
+ "@types/glob": "^9.0.0",
99
+ "@types/http-proxy": "^1.17.15",
100
+ "@types/lodash": "^4.17.16",
101
+ "@types/node": "^22.10.2",
102
+ "@types/qs": "^6.14.0",
103
+ "@types/react": "^18.3.23",
104
+ "@types/react-dom": "^18.3.7",
105
+ "@types/video-react": "^0.15.8",
106
+ "@typescript/native-preview": "7.0.0-dev.20250819.1",
107
+ "@vitejs/plugin-react-swc": "^4.2.2",
108
+ "autoprefixer": "^10.4.20",
109
+ "glob": "^11.0.0",
110
+ "http-proxy": "^1.18.1",
111
+ "husky": "^9.1.7",
112
+ "postcss": "^8.5.2",
113
+ "prettier": "^3.8.1",
114
+ "tailwindcss": "^3.4.11",
115
+ "typescript": "~5.7.2",
116
+ "vite": "^5.4.21",
117
+ "vite-plugin-svgr": "^4.3.0"
36
118
  }
37
119
  }
@@ -10,8 +10,10 @@ import ChatDisplayModeSwitcher from "./components/chat-display-mode-switcher";
10
10
  import { useDisplayMode } from "./hooks/useDisplayMode";
11
11
  import InlineAIAssistant from "./inline-ai-assistant";
12
12
 
13
- const AIAssistant: React.FC = () => {
14
-
13
+ const AIAssistant: React.FC<{
14
+ greeting?: string;
15
+ recommends?: string[];
16
+ }> = ({ greeting, recommends }) => {
15
17
  const [isOpen, setIsOpen] = useState(false);
16
18
  const [displayMode, setDisplayMode] = useDisplayMode(isOpen);
17
19
  const isFullscreen = displayMode === "fullscreen" && isOpen;
@@ -67,6 +69,7 @@ const AIAssistant: React.FC = () => {
67
69
  <ChatHeader
68
70
  disabledDrag={displayMode !== "floating"}
69
71
  isDragging={isDragging}
72
+ displayMode={displayMode}
70
73
  onMouseDown={handleDragStart}
71
74
  onTouchStart={handleTouchStart}
72
75
  onClose={handleClose}
@@ -77,7 +80,13 @@ const AIAssistant: React.FC = () => {
77
80
  />
78
81
  </ChatHeader>
79
82
 
80
- <InlineAIAssistant showBanner={false} className="flex-1" greeting={getText().greeting} />
83
+ <InlineAIAssistant
84
+ showBanner={false}
85
+ className="flex-1"
86
+ greeting={greeting || getText().greeting}
87
+ recommends={recommends}
88
+ displayMode={displayMode}
89
+ />
81
90
  </ChatFloatingCard>
82
91
  )}
83
92
  </>
@@ -30,14 +30,14 @@ export const ChatLoading: React.FC<{ className?: string }> = ({
30
30
  className,
31
31
  }) => {
32
32
  return (
33
- <div className={`inline-flex items-center gap-1`}>
34
- <div className="h-2 w-2 rounded-full bg-[#7C3AED] animate-pulse" />
33
+ <div className={`inline-flex items-center gap-1 animate-in fade-in-0 slide-in-from-bottom-2 duration-300 ${className}`}>
34
+ <div className="h-2 w-2 rounded-full bg-primary animate-pulse" />
35
35
  <div
36
- className="h-2 w-2 rounded-full bg-[#7C3AED] animate-pulse"
36
+ className="h-2 w-2 rounded-full bg-primary animate-pulse"
37
37
  style={{ animationDelay: "150ms" }}
38
38
  />
39
39
  <div
40
- className="h-2 w-2 rounded-full bg-[#7C3AED] animate-pulse"
40
+ className="h-2 w-2 rounded-full bg-primary animate-pulse"
41
41
  style={{ animationDelay: "300ms" }}
42
42
  />
43
43
  </div>
@@ -58,7 +58,6 @@ const ChatTextMessage: React.FC<
58
58
  </div>
59
59
  <TTSReader
60
60
  text={message.content}
61
- className="opacity-0 group-hover:opacity-100"
62
61
  />
63
62
  </>
64
63
  );
@@ -116,7 +115,6 @@ const ChatToolMessage: React.FC<
116
115
  <span className="flex-1 truncate flex-shrink-0">
117
116
  {message.toolName || getText().unknownTool}
118
117
  </span>
119
- {/* {message.toolDescription && <div className="flex-1 truncate max-w-1/2">{message.toolDescription}</div>} */}
120
118
  </div>
121
119
  );
122
120
  };
@@ -129,11 +127,25 @@ const ChatErrorMessage: React.FC<{ message: TextMessage }> = ({ message }) => {
129
127
  );
130
128
  };
131
129
 
130
+ export const ChatDivider = () => {
131
+ return (
132
+ <div className="flex items-center justify-center gap-4 w-full my-4">
133
+ <div className="h-px bg-border flex-1" />
134
+ <span className="text-xs text-muted-foreground">
135
+ {getText().newConversation}
136
+ </span>
137
+ <div className="h-px bg-border flex-1" />
138
+ </div>
139
+ );
140
+ };
141
+
132
142
  const ChatUIRenderMessage: React.FC<{ message: UIRenderMessage }> = ({
133
143
  message,
134
144
  }) => {
135
145
  return (
136
- <UIRenderer spec={message.spec} className="mb-2" />
146
+ <div className="flex-1 overflow-hidden">
147
+ <UIRenderer spec={message.spec} className="mb-2" />
148
+ </div>
137
149
  );
138
150
  };
139
151
 
@@ -154,7 +166,9 @@ const MessageContentRenderer: React.FC<
154
166
  case "error":
155
167
  return <ChatErrorMessage message={message as TextMessage} {...rest} />;
156
168
  case "ui-render":
157
- return <ChatUIRenderMessage message={message as UIRenderMessage} />;
169
+ return (
170
+ <ChatUIRenderMessage message={message as UIRenderMessage} {...rest} />
171
+ );
158
172
  default:
159
173
  return <div>{getText().unknownTool}</div>;
160
174
  }
@@ -166,15 +180,17 @@ const ChatAssistantMessage: React.FC<
166
180
  showAvatar?: boolean;
167
181
  } & MessageCommonProps
168
182
  > = ({ message, showAvatar, ...rest }) => {
183
+
169
184
  return (
170
185
  <div
186
+ data-conversation-message-id={(message as any).messageId}
171
187
  className={cn(
172
188
  "flex gap-3 animate-in fade-in-0 slide-in-from-bottom-2 duration-300 overflow-hidden w-full chat-assistant-message &+.chat-assistant-message:mt-4 group",
173
189
  )}
174
190
  >
175
191
  <div
176
192
  className={cn(
177
- "flex-shrink-0 h-7 w-7 rounded-full bg-gradient-to-br from-[#6366F1] to-[#8B5CF6] flex items-center justify-center text-left",
193
+ "flex-shrink-0 h-7 w-7 rounded-full bg-gradient-to-br from-primary to-primary/40 flex items-center justify-center text-left",
178
194
  !showAvatar && "invisible",
179
195
  )}
180
196
  >
@@ -3,12 +3,7 @@ import { getText } from "../i18n";
3
3
  import { HoverCard } from "@radix-ui/react-hover-card";
4
4
  import { HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card";
5
5
  import { Button } from "@/components/ui/button";
6
-
7
- export type IDisplayMode =
8
- | "fullscreen"
9
- | "floating"
10
- | "side-left"
11
- | "side-right";
6
+ import type { IDisplayMode } from "../types";
12
7
 
13
8
  const ChatDisplayModeSwitcher: React.FC<{
14
9
  displayMode: IDisplayMode;
@@ -31,7 +31,7 @@ const ChatFloatingButton: React.FC<ChatFloatingButtonProps> = ({
31
31
  size="lg"
32
32
  className={`
33
33
  group relative h-full w-full rounded-full
34
- bg-gradient-to-br from-[#6366F1] to-[#8B5CF6]
34
+ bg-gradient-to-br from-primary to-primary/40
35
35
  text-white
36
36
  border-0
37
37
  transition-all duration-300 ease-out select-none
@@ -43,7 +43,7 @@ const ChatFloatingButton: React.FC<ChatFloatingButtonProps> = ({
43
43
  }
44
44
  `}
45
45
  >
46
- <span className="absolute inset-0 rounded-full bg-[#6366F1] animate-ping opacity-20" />
46
+ <span className="absolute inset-0 rounded-full bg-primary animate-ping opacity-20" />
47
47
  <MessageSquare
48
48
  className="relative h-5 w-5 pointer-events-none"
49
49
  strokeWidth={1.75}
@@ -1,8 +1,8 @@
1
1
  import type React from "react";
2
+ import type { IDisplayMode } from "../types";
2
3
  import { useRef } from "react";
3
4
  import { Card } from "@/components/ui/card";
4
5
  import { cn } from "@/lib/utils";
5
- import { IDisplayMode } from "./chat-display-mode-switcher";
6
6
 
7
7
  interface ChatFloatingCardProps {
8
8
  displayMode: IDisplayMode;
@@ -2,8 +2,11 @@ import { MessageSquare, X } from "lucide-react";
2
2
  import type React from "react";
3
3
  import { Button } from "@/components/ui/button";
4
4
  import { getText } from "../i18n";
5
+ import { cn } from "@/lib/utils";
6
+ import type { IDisplayMode } from "../types";
5
7
 
6
8
  interface ChatHeaderProps {
9
+ displayMode?: IDisplayMode;
7
10
  disabledDrag?: boolean;
8
11
  isDragging?: boolean;
9
12
  onMouseDown?: (e: React.MouseEvent) => void;
@@ -13,6 +16,7 @@ interface ChatHeaderProps {
13
16
  }
14
17
 
15
18
  const ChatHeader: React.FC<ChatHeaderProps> = ({
19
+ displayMode,
16
20
  disabledDrag,
17
21
  isDragging,
18
22
  onMouseDown,
@@ -22,27 +26,25 @@ const ChatHeader: React.FC<ChatHeaderProps> = ({
22
26
  }) => {
23
27
  return (
24
28
  <div
25
- className={`
26
- flex items-center justify-between px-4 py-3
27
- border-b border-[#E5E7EB]
28
- bg-white
29
- ${!disabledDrag && onMouseDown ? (isDragging ? "cursor-grabbing" : "cursor-grab") : ""}
30
- `}
29
+ className={cn(
30
+ "flex items-center justify-between px-4",
31
+ !disabledDrag && onMouseDown
32
+ ? isDragging
33
+ ? "cursor-grabbing"
34
+ : "cursor-grab"
35
+ : "",
36
+ {
37
+ 'border-b border-[#E5E7EB] bg-white': displayMode === "floating",
38
+ },
39
+ )}
31
40
  onMouseDown={!disabledDrag ? onMouseDown : undefined}
32
41
  onTouchStart={!disabledDrag ? onTouchStart : undefined}
33
42
  >
34
43
  <div className="flex items-center gap-2.5">
35
- <div className="flex items-center justify-center h-8 w-8 rounded-full bg-gradient-to-br from-[#6366F1] to-[#8B5CF6]">
36
- <MessageSquare className="h-4 w-4 text-white" strokeWidth={2} />
37
- </div>
38
44
  <div className="flex flex-col">
39
45
  <span className="text-sm font-semibold text-[#111827]">
40
- AI Assistant
46
+ {getText().assistantName}
41
47
  </span>
42
- <div className="flex items-center gap-1.5">
43
- <span className="h-1.5 w-1.5 rounded-full bg-[#10B981] animate-pulse" />
44
- <span className="text-xs text-[#6B7280]">{getText().online}</span>
45
- </div>
46
48
  </div>
47
49
  </div>
48
50
  <div className="flex items-center gap-1">
@@ -1,9 +1,15 @@
1
- import { ArrowUp, MessageCirclePlus } from "lucide-react";
1
+ import {
2
+ ArrowUp,
3
+ LoaderCircle,
4
+ MessageCirclePlus,
5
+ RotateCw,
6
+ Square,
7
+ } from "lucide-react";
2
8
  import type React from "react";
3
9
  import { Button } from "@/components/ui/button";
4
10
  import { getText } from "../i18n";
5
- import type { Conversation } from "../types";
6
- import { useState } from "react";
11
+ import type { Conversation, IDisplayMode } from "../types";
12
+ import { useMemo, useState } from "react";
7
13
  import { cn } from "@/lib/utils";
8
14
  import {
9
15
  Tooltip,
@@ -13,18 +19,19 @@ import {
13
19
  } from "@/components/ui/tooltip";
14
20
  import VoiceInputButton from "./voice-input";
15
21
 
16
- const ResetButton: React.FC<{ onReset?: () => void }> = ({ onReset }) => {
17
- if (!onReset) return null;
22
+ const NewConvButton: React.FC<{ onNew?: () => void; disabled?: boolean }> = ({ onNew, disabled }) => {
23
+ if (!onNew) return null;
18
24
  return (
19
25
  <TooltipProvider>
20
26
  <Tooltip delayDuration={0}>
21
- <TooltipContent>{getText().resetConversation}</TooltipContent>
27
+ <TooltipContent>{getText().newConversation}</TooltipContent>
22
28
  <TooltipTrigger asChild>
23
29
  <Button
24
30
  type="button"
25
31
  variant="ghost"
26
32
  size="icon"
27
- onClick={onReset}
33
+ onClick={onNew}
34
+ disabled={disabled}
28
35
  className="h-8 w-8 text-[#6B7280] hover:text-[#374151] hover:bg-[#F3F4F6] rounded-lg transition-colors duration-200 cursor-pointer"
29
36
  >
30
37
  <MessageCirclePlus className="size-5" />
@@ -35,29 +42,56 @@ const ResetButton: React.FC<{ onReset?: () => void }> = ({ onReset }) => {
35
42
  );
36
43
  };
37
44
 
38
- const SubmitButton: React.FC<{ disabled: boolean; onClick: () => void }> = ({
39
- disabled,
45
+ const SubmitButton: React.FC<{
46
+ disabled: boolean;
47
+ starting?: boolean;
48
+ onClick: () => void;
49
+ }> = ({ disabled, starting, onClick }) => {
50
+ return (
51
+ <Button
52
+ type="button"
53
+ onClick={onClick}
54
+ disabled={disabled || starting}
55
+ size="icon"
56
+ className={cn(
57
+ "size-8 text-white rounded-xl transition-all duration-200 cursor-pointer shadow-[0_2px_8px_rgba(99,102,241,0.3)] hover:shadow-[0_4px_12px_rgba(99,102,241,0.4)]",
58
+ "bg-gradient-to-r from-primary to-primary/80",
59
+ "hover:from-primary/90 hover:to-primary/70",
60
+ "disabled:opacity-40 disabled:cursor-not-allowed",
61
+ starting && "bg-gray-600",
62
+ )}
63
+ >
64
+ {starting ? (
65
+ <LoaderCircle className="h-4 w-4 animate-spin" strokeWidth={2} />
66
+ ) : (
67
+ <ArrowUp className="h-4 w-4" strokeWidth={2} />
68
+ )}
69
+ </Button>
70
+ );
71
+ };
72
+
73
+ const StopButton: React.FC<{ onClick: () => void; disabled?: boolean }> = ({
40
74
  onClick,
75
+ disabled,
41
76
  }) => {
42
77
  return (
43
78
  <Button
44
79
  type="button"
45
80
  onClick={onClick}
46
- disabled={disabled}
47
81
  size="icon"
82
+ disabled={disabled}
48
83
  className="
49
84
  size-8
50
- bg-gradient-to-r from-[#6366F1] to-[#8B5CF6]
51
- hover:from-[#4F46E5] hover:to-[#7C3AED]
85
+ bg-foreground
86
+ hover:bg-foreground/50
52
87
  text-white
53
88
  rounded-xl
54
89
  transition-all duration-200
55
90
  cursor-pointer
56
- disabled:opacity-40 disabled:cursor-not-allowed
57
- shadow-[0_2px_8px_rgba(99,102,241,0.3)]
58
- hover:shadow-[0_4px_12px_rgba(99,102,241,0.4)]"
91
+ shadow-[0_2px_8px_rgba(239,68,68,0.3)]
92
+ hover:shadow-[0_4px_12px_rgba(239,68,68,0.4)]"
59
93
  >
60
- <ArrowUp className="h-4 w-4" strokeWidth={2} />
94
+ <Square className="h-3 w-3 fill-current" strokeWidth={2} />
61
95
  </Button>
62
96
  );
63
97
  };
@@ -68,7 +102,10 @@ interface ChatInputProps {
68
102
  inputValue: string;
69
103
  onInputChange: (value: string) => void;
70
104
  onSendMessage: () => void;
71
- onReset?: () => void;
105
+ onNew?: () => void;
106
+ onCancel?: () => void;
107
+ starting?: boolean;
108
+ displayMode?: IDisplayMode;
72
109
  }
73
110
 
74
111
  const ChatInput: React.FC<ChatInputProps> = ({
@@ -77,9 +114,19 @@ const ChatInput: React.FC<ChatInputProps> = ({
77
114
  inputValue,
78
115
  onInputChange,
79
116
  onSendMessage,
80
- onReset,
117
+ onNew,
118
+ onCancel,
119
+ starting,
120
+ displayMode
81
121
  }) => {
82
- const hasConversations = conversations.length > 1;
122
+ const hasConversations = conversations.length > 0;
123
+ const lastConv =
124
+ conversations.length > 0 ? conversations[conversations.length - 1] : null;
125
+ const lastIsDivider = useMemo(() => {
126
+ if (!lastConv) return false;
127
+ return (lastConv.system?.level === "newConversation");
128
+ }, [lastConv]);
129
+
83
130
  const [focus, setFocus] = useState(false);
84
131
  const handleKeyPress = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
85
132
  if (e.key === "Enter" && !e.shiftKey) {
@@ -92,9 +139,9 @@ const ChatInput: React.FC<ChatInputProps> = ({
92
139
  <div className="py-3 px-4">
93
140
  <div
94
141
  className={cn(
95
- "w-full rounded-xl bg-white transition-all duration-200 p-3 border border-[#6366F1]/80",
142
+ "w-full rounded-xl bg-white transition-all duration-200 p-3 border border-primary/80",
96
143
  {
97
- "border-[#6366F1] ring-2 ring-[#6366F1]/20": focus
144
+ "border-primary ring-2 ring-primary/20": focus,
98
145
  },
99
146
  )}
100
147
  >
@@ -112,7 +159,7 @@ const ChatInput: React.FC<ChatInputProps> = ({
112
159
  />
113
160
  <div className="flex items-center justify-between gap-2 mt-1">
114
161
  <div className="flex items-center gap-1">
115
- <ResetButton onReset={hasConversations ? onReset : undefined} />
162
+ {onNew && <NewConvButton onNew={onNew} disabled={!hasConversations || lastIsDivider || starting || isLoading} />}
116
163
  </div>
117
164
  <div className="flex items-center gap-2">
118
165
  <VoiceInputButton
@@ -121,17 +168,22 @@ const ChatInput: React.FC<ChatInputProps> = ({
121
168
  setFocus(true);
122
169
  }}
123
170
  value={inputValue}
124
- disabled={isLoading}
125
- />
126
- <SubmitButton
127
- onClick={() => onSendMessage()}
128
- disabled={!inputValue.trim() || isLoading}
171
+ disabled={isLoading || starting}
129
172
  />
173
+ {isLoading ? (
174
+ <StopButton onClick={onCancel || (() => {})} />
175
+ ) : (
176
+ <SubmitButton
177
+ onClick={() => onSendMessage()}
178
+ disabled={!inputValue.trim() || isLoading}
179
+ starting={starting}
180
+ />
181
+ )}
130
182
  </div>
131
183
  </div>
132
184
  </div>
133
185
 
134
- {conversations.length > 0 && (
186
+ {(conversations.length > 0 || displayMode !== "inline") && (
135
187
  <p className="text-[10px] text-[#9CA3AF] mt-2 text-center">
136
188
  {getText().footerAiWarning}
137
189
  </p>