diff --git a/package-lock.json b/package-lock.json index d76a96f..6f3e7c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "codepilot", - "version": "0.6.1", + "version": "0.6.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codepilot", - "version": "0.6.1", + "version": "0.6.2", "dependencies": { "@anthropic-ai/claude-agent-sdk": "^0.2.33", "@hugeicons/core-free-icons": "^3.1.1", diff --git a/package.json b/package.json index e8ffff8..fa0000a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codepilot", - "version": "0.6.1", + "version": "0.6.2", "private": true, "main": "dist-electron/main.js", "scripts": { diff --git a/src/__tests__/e2e/chat-enhanced.spec.ts b/src/__tests__/e2e/chat-enhanced.spec.ts index 12ce90c..aa04cd4 100644 --- a/src/__tests__/e2e/chat-enhanced.spec.ts +++ b/src/__tests__/e2e/chat-enhanced.spec.ts @@ -30,7 +30,7 @@ test.describe('Chat UI Enhanced (V2)', () => { // Wait for assistant response -- V2 uses gradient avatar instead of text label // Detect assistant response via the gradient avatar await expect( - page.locator('.from-violet-500') + page.locator('[data-role="assistant"]') ).toBeVisible({ timeout: 30_000 }); // Wait for streaming to complete @@ -50,7 +50,7 @@ test.describe('Chat UI Enhanced (V2)', () => { await sendMessage(page, 'Show me a Python hello world'); await expect( - page.locator('.from-violet-500') + page.locator('[data-role="assistant"]') ).toBeVisible({ timeout: 30_000 }); await waitForStreamingEnd(page); @@ -103,9 +103,9 @@ test.describe('Chat UI Enhanced (V2)', () => { await goToChat(page); await sendMessage(page, 'Hi there'); - // V2: Assistant avatar uses gradient bg (from-violet-500 to-purple-600) + // Detect assistant response via data-role attribute await expect( - page.locator('.from-violet-500') + page.locator('[data-role="assistant"]') ).toBeVisible({ timeout: 30_000 }); }); @@ -125,7 +125,7 @@ test.describe('Chat UI Enhanced (V2)', () => { // V2: Assistant avatar is gradient, not bg-primary await expect( - page.locator('.from-violet-500:has(svg)') + page.locator('[data-role="assistant"]') ).toBeVisible({ timeout: 30_000 }); }); }); diff --git a/src/__tests__/e2e/chat.spec.ts b/src/__tests__/e2e/chat.spec.ts index e3854fb..a1dfd3b 100644 --- a/src/__tests__/e2e/chat.spec.ts +++ b/src/__tests__/e2e/chat.spec.ts @@ -116,9 +116,9 @@ test.describe('Chat Page', () => { await goToChat(page); await sendMessage(page, 'Say hello'); - // V2: Wait for assistant response -- gradient avatar appears + // Wait for assistant response await expect( - page.locator('.from-violet-500') + page.locator('[data-role="assistant"]') ).toBeVisible({ timeout: 10_000 }); }); diff --git a/src/app/globals.css b/src/app/globals.css index 5bc066c..3663c3e 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -54,7 +54,7 @@ --card-foreground: oklch(0.147 0.004 49.25); --popover: oklch(1 0 0); --popover-foreground: oklch(0.147 0.004 49.25); - --primary: oklch(0.457 0.24 277.023); + --primary: oklch(0.546 0.245 262.881); --primary-foreground: oklch(0.985 0.001 106.423); --secondary: oklch(0.97 0.001 106.424); --secondary-foreground: oklch(0.216 0.006 56.043); @@ -65,20 +65,20 @@ --destructive: oklch(0.577 0.245 27.325); --border: oklch(0.923 0.003 48.717); --input: oklch(0.923 0.003 48.717); - --ring: oklch(0.457 0.24 277.023); - --chart-1: oklch(0.457 0.24 277.023); + --ring: oklch(0.546 0.245 262.881); + --chart-1: oklch(0.546 0.245 262.881); --chart-2: oklch(0.6 0.118 184.704); --chart-3: oklch(0.398 0.07 227.392); --chart-4: oklch(0.828 0.189 84.429); --chart-5: oklch(0.769 0.188 70.08); --sidebar: oklch(0.985 0.001 106.423); --sidebar-foreground: oklch(0.147 0.004 49.25); - --sidebar-primary: oklch(0.457 0.24 277.023); + --sidebar-primary: oklch(0.546 0.245 262.881); --sidebar-primary-foreground: oklch(0.985 0.001 106.423); --sidebar-accent: oklch(0.97 0.001 106.424); --sidebar-accent-foreground: oklch(0.216 0.006 56.043); --sidebar-border: oklch(0.923 0.003 48.717); - --sidebar-ring: oklch(0.457 0.24 277.023); + --sidebar-ring: oklch(0.546 0.245 262.881); } .dark { @@ -88,7 +88,7 @@ --card-foreground: oklch(0.985 0.001 106.423); --popover: oklch(0.216 0.006 56.043); --popover-foreground: oklch(0.985 0.001 106.423); - --primary: oklch(0.585 0.233 277.117); + --primary: oklch(0.623 0.214 259.815); --primary-foreground: oklch(0.985 0.001 106.423); --secondary: oklch(0.268 0.007 34.298); --secondary-foreground: oklch(0.985 0.001 106.423); @@ -99,20 +99,20 @@ --destructive: oklch(0.704 0.191 22.216); --border: oklch(1 0 0 / 10%); --input: oklch(1 0 0 / 15%); - --ring: oklch(0.585 0.233 277.117); - --chart-1: oklch(0.585 0.233 277.117); + --ring: oklch(0.623 0.214 259.815); + --chart-1: oklch(0.623 0.214 259.815); --chart-2: oklch(0.696 0.17 162.48); --chart-3: oklch(0.769 0.188 70.08); --chart-4: oklch(0.627 0.265 303.9); --chart-5: oklch(0.645 0.246 16.439); --sidebar: oklch(0.216 0.006 56.043); --sidebar-foreground: oklch(0.985 0.001 106.423); - --sidebar-primary: oklch(0.585 0.233 277.117); + --sidebar-primary: oklch(0.623 0.214 259.815); --sidebar-primary-foreground: oklch(0.985 0.001 106.423); --sidebar-accent: oklch(0.268 0.007 34.298); --sidebar-accent-foreground: oklch(0.985 0.001 106.423); --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.585 0.233 277.117); + --sidebar-ring: oklch(0.623 0.214 259.815); } @layer base { @@ -125,10 +125,10 @@ /* Selection color */ ::selection { - background: oklch(0.7 0.15 280 / 30%); + background: oklch(0.7 0.15 260 / 30%); } .dark ::selection { - background: oklch(0.6 0.2 280 / 35%); + background: oklch(0.6 0.2 260 / 35%); } /* Custom scrollbar */ @@ -179,16 +179,16 @@ margin-bottom: 0.25em; } .prose blockquote { - border-left-color: oklch(0.7 0.15 280 / 40%); + border-left-color: oklch(0.7 0.15 260 / 40%); font-style: normal; } .prose a { - color: oklch(0.55 0.2 260); + color: oklch(0.55 0.2 250); text-decoration: underline; text-underline-offset: 2px; } .dark .prose a { - color: oklch(0.7 0.18 260); + color: oklch(0.7 0.18 250); } .prose hr { border-color: var(--border); @@ -197,3 +197,13 @@ font-weight: 600; } } + +/* Global icon stroke weight — make Hugeicons strokes slightly thicker */ +svg.hugeicons, +svg.hugeicons path, +svg.hugeicons line, +svg.hugeicons circle, +svg.hugeicons polyline, +svg.hugeicons rect { + stroke-width: 2; +} diff --git a/src/components/chat/MessageInput.tsx b/src/components/chat/MessageInput.tsx index 78913b2..1c4ab4e 100644 --- a/src/components/chat/MessageInput.tsx +++ b/src/components/chat/MessageInput.tsx @@ -9,6 +9,7 @@ import { ClipboardIcon, HelpCircleIcon, ArrowDown01Icon, + ArrowUp02Icon, CommandLineIcon, Attachment01Icon, Cancel01Icon, @@ -32,6 +33,7 @@ import { PromptInputSubmit, usePromptInputAttachments, } from '@/components/ai-elements/prompt-input'; +import { SquareIcon } from 'lucide-react'; import type { ChatStatus } from 'ai'; import type { FileAttachment } from '@/types'; import { nanoid } from 'nanoid'; @@ -221,7 +223,14 @@ function FileAwareSubmitButton({ status={status} onStop={onStop} disabled={disabled || (!isStreaming && !inputValue.trim() && !hasBadge && !hasFiles)} - /> + className="rounded-full" + > + {isStreaming ? ( + + ) : ( + + )} + ); } @@ -887,7 +896,7 @@ export function MessageInput({ > {currentMode.label} - + {/* Mode dropdown */} @@ -931,7 +940,7 @@ export function MessageInput({ onClick={() => setModelMenuOpen((prev) => !prev)} > {currentModelOption.label} - + {modelMenuOpen && ( diff --git a/src/components/chat/ToolCallBlock.tsx b/src/components/chat/ToolCallBlock.tsx index c9e02c5..b6973b8 100644 --- a/src/components/chat/ToolCallBlock.tsx +++ b/src/components/chat/ToolCallBlock.tsx @@ -312,7 +312,7 @@ export function ToolCallBlock({ category === 'read' && "text-blue-500", category === 'write' && "text-amber-500", category === 'bash' && "text-green-500", - category === 'search' && "text-purple-500", + category === 'search' && "text-indigo-500", category === 'other' && "text-zinc-500", )} /> {summary} diff --git a/src/components/layout/ChatListPanel.tsx b/src/components/layout/ChatListPanel.tsx index 1cae414..35d36ea 100644 --- a/src/components/layout/ChatListPanel.tsx +++ b/src/components/layout/ChatListPanel.tsx @@ -65,7 +65,7 @@ function groupSessionsByDate( const MODE_BADGE_CONFIG = { code: { label: "Code", className: "bg-blue-500/10 text-blue-500" }, - plan: { label: "Plan", className: "bg-purple-500/10 text-purple-500" }, + plan: { label: "Plan", className: "bg-sky-500/10 text-sky-500" }, ask: { label: "Ask", className: "bg-green-500/10 text-green-500" }, } as const; diff --git a/src/components/skills/SkillEditor.tsx b/src/components/skills/SkillEditor.tsx index 3df9775..348fcf5 100644 --- a/src/components/skills/SkillEditor.tsx +++ b/src/components/skills/SkillEditor.tsx @@ -124,7 +124,7 @@ export function SkillEditor({ skill, onSave, onDelete }: SkillEditorProps) { : skill.source === "installed" ? "border-orange-500/40 text-orange-600 dark:text-orange-400" : skill.source === "plugin" - ? "border-purple-500/40 text-purple-600 dark:text-purple-400" + ? "border-indigo-500/40 text-indigo-600 dark:text-indigo-400" : "border-blue-500/40 text-blue-600 dark:text-blue-400" )} > diff --git a/src/components/skills/SkillListItem.tsx b/src/components/skills/SkillListItem.tsx index 06e32ee..8746964 100644 --- a/src/components/skills/SkillListItem.tsx +++ b/src/components/skills/SkillListItem.tsx @@ -77,7 +77,7 @@ export function SkillListItem({ : skill.source === "installed" ? "border-orange-500/40 text-orange-600 dark:text-orange-400" : skill.source === "plugin" - ? "border-purple-500/40 text-purple-600 dark:text-purple-400" + ? "border-indigo-500/40 text-indigo-600 dark:text-indigo-400" : "border-blue-500/40 text-blue-600 dark:text-blue-400" )} > diff --git a/src/components/ui/input-group.tsx b/src/components/ui/input-group.tsx index 3d1f9d9..819388d 100644 --- a/src/components/ui/input-group.tsx +++ b/src/components/ui/input-group.tsx @@ -14,7 +14,7 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) { data-slot="input-group" role="group" className={cn( - "group/input-group border-input dark:bg-input/30 relative flex w-full items-center rounded-md border shadow-xs transition-[color,box-shadow] outline-none", + "group/input-group border-input dark:bg-input/30 relative flex w-full items-center rounded-2xl border shadow-md transition-[color,box-shadow] outline-none", "h-9 min-w-0 has-[>textarea]:h-auto", // Variants based on alignment. @@ -23,8 +23,8 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) { "has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3", "has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3", - // Focus state. - "has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-[3px]", + // Focus state — neutral border only, no colored ring. + "has-[[data-slot=input-group-control]:focus-visible]:border-border", // Error state. "has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",