From 1057dbf5db68d30ccb4a3460d8fd0285ea87978a Mon Sep 17 00:00:00 2001 From: israel Date: Tue, 17 Mar 2026 20:15:50 +0100 Subject: [PATCH 1/2] feat: refine dark theme, sidebar styling, and simplify agent selector Update dark mode palette to a blue-tinted scheme for better visual depth. Add dedicated sidebar color variable and apply bg-sidebar to navigation components. Simplify agent selector by removing configure button and radio indicators. Fix dev script to re-read CONVEX_DEPLOYMENT after Convex starts. --- .../navigation/mobile-navigation.stories.tsx | 2 +- .../ui/navigation/mobile-navigation.tsx | 2 +- .../ui/navigation/navigation.stories.tsx | 2 +- .../components/ui/navigation/navigation.tsx | 2 +- .../__tests__/agent-selector.test.tsx | 41 ----------------- .../chat/components/agent-selector.tsx | 45 ++----------------- .../activate-conversations-empty-state.tsx | 2 +- services/platform/app/globals.css | 42 +++++++++-------- .../platform/app/routes/dashboard/$id.tsx | 4 +- services/platform/convex/_generated/api.d.ts | 1 - .../convex/betterAuth/_generated/component.ts | 1 - services/platform/scripts/dev.ts | 11 +++++ 12 files changed, 45 insertions(+), 110 deletions(-) diff --git a/services/platform/app/components/ui/navigation/mobile-navigation.stories.tsx b/services/platform/app/components/ui/navigation/mobile-navigation.stories.tsx index 27771fa6b..45b876b4d 100644 --- a/services/platform/app/components/ui/navigation/mobile-navigation.stories.tsx +++ b/services/platform/app/components/ui/navigation/mobile-navigation.stories.tsx @@ -141,7 +141,7 @@ function MobileNavigationVisual({ className="w-72 p-0" hideClose > - +
T diff --git a/services/platform/app/components/ui/navigation/mobile-navigation.tsx b/services/platform/app/components/ui/navigation/mobile-navigation.tsx index 4f05c4f8f..bf8b7237b 100644 --- a/services/platform/app/components/ui/navigation/mobile-navigation.tsx +++ b/services/platform/app/components/ui/navigation/mobile-navigation.tsx @@ -126,7 +126,7 @@ export function MobileNavigation({ organizationId }: MobileNavigationProps) { className="w-72 p-0" hideClose > - +
+ {showLogo && (
diff --git a/services/platform/app/components/ui/navigation/navigation.tsx b/services/platform/app/components/ui/navigation/navigation.tsx index a62b2bbba..66573ef85 100644 --- a/services/platform/app/components/ui/navigation/navigation.tsx +++ b/services/platform/app/components/ui/navigation/navigation.tsx @@ -112,7 +112,7 @@ export function Navigation({ organizationId }: NavigationProps) { const navigationItems = useNavigationItems(organizationId); return ( - +
({ 'agentSelector.searchPlaceholder': 'Search agents...', 'agentSelector.noResults': 'No agents found', 'agentSelector.addAgent': 'Add agent', - 'agentSelector.configureAgent': 'Configure agent', }; return translations[key] ?? key; }, @@ -112,10 +111,8 @@ vi.mock( }), ); -const mockNavigate = vi.fn(); vi.mock('@tanstack/react-router', () => ({ useSearch: () => ({}), - useNavigate: () => mockNavigate, useLocation: () => ({ pathname: '/dashboard/org-1/chat' }), })); @@ -226,44 +223,6 @@ describe('AgentSelector', () => { }); }); - it('shows configure button for all agents when user has write permission', async () => { - const { user } = render(); - - const trigger = screen.getByLabelText('Select agent'); - await user.click(trigger); - - const configButtons = screen.getAllByLabelText('Configure agent'); - expect(configButtons).toHaveLength(2); - }); - - it('does not show configure button when user lacks write permission', async () => { - mockCanWrite = false; - - const { user } = render(); - - const trigger = screen.getByLabelText('Select agent'); - await user.click(trigger); - - expect(screen.queryByLabelText('Configure agent')).not.toBeInTheDocument(); - }); - - it('navigates to agent config when configure button is clicked', async () => { - const { user } = render(); - - const trigger = screen.getByLabelText('Select agent'); - await user.click(trigger); - - const configButtons = screen.getAllByLabelText('Configure agent'); - const customAgentConfig = configButtons[1]; - expect(customAgentConfig).toBeDefined(); - await user.click(customAgentConfig); - - expect(mockNavigate).toHaveBeenCalledWith({ - to: '/dashboard/$id/custom-agents/$agentId', - params: { id: 'org-1', agentId: 'root-2' }, - }); - }); - it('only highlights one agent when multiple have isSystemDefault', async () => { mockAgents = [ { diff --git a/services/platform/app/features/chat/components/agent-selector.tsx b/services/platform/app/features/chat/components/agent-selector.tsx index 87bfd708e..dd8308c69 100644 --- a/services/platform/app/features/chat/components/agent-selector.tsx +++ b/services/platform/app/features/chat/components/agent-selector.tsx @@ -1,15 +1,10 @@ 'use client'; -import { useNavigate } from '@tanstack/react-router'; -import { Bot, ChevronDown, Plus, Settings } from 'lucide-react'; -import { type MouseEvent, useCallback, useMemo, useState } from 'react'; +import { Bot, ChevronDown, Plus } from 'lucide-react'; +import { useCallback, useMemo, useState } from 'react'; -import { - SearchableSelect, - type SearchableSelectOption, -} from '@/app/components/ui/forms/searchable-select'; +import { SearchableSelect } from '@/app/components/ui/forms/searchable-select'; import { Button } from '@/app/components/ui/primitives/button'; -import { IconButton } from '@/app/components/ui/primitives/icon-button'; import { CreateCustomAgentDialog } from '@/app/features/custom-agents/components/custom-agent-create-dialog'; import { useAbility } from '@/app/hooks/use-ability'; import { useDialogSearchParam } from '@/app/hooks/use-dialog-search-param'; @@ -26,7 +21,6 @@ interface AgentSelectorProps { export function AgentSelector({ organizationId }: AgentSelectorProps) { const { t } = useT('chat'); const ability = useAbility(); - const navigate = useNavigate(); const { setSelectedAgent } = useChatLayout(); const effectiveAgent = useEffectiveAgent(organizationId); const { agents: allAgents } = useChatAgents(organizationId); @@ -79,35 +73,6 @@ export function AgentSelector({ organizationId }: AgentSelectorProps) { createAgentDialog.open(); }, [createAgentDialog]); - const handleConfigClick = useCallback( - (option: SearchableSelectOption, e: MouseEvent) => { - e.stopPropagation(); - setOpen(false); - void navigate({ - to: '/dashboard/$id/custom-agents/$agentId', - params: { id: organizationId, agentId: option.value }, - }); - }, - [navigate, organizationId], - ); - - const renderOptionAction = useCallback( - (option: SearchableSelectOption) => { - if (!canManageAgents) return null; - return ( - handleConfigClick(option, e)} - /> - ); - }, - [canManageAgents, t, handleConfigClick], - ); - return ( <> +
diff --git a/services/platform/app/globals.css b/services/platform/app/globals.css index 5289197f9..1d936fa9e 100644 --- a/services/platform/app/globals.css +++ b/services/platform/app/globals.css @@ -47,6 +47,8 @@ --color-input: hsl(var(--input)); --color-ring: hsl(var(--ring)); + --color-sidebar: hsl(var(--sidebar)); + --color-chart-1: hsl(var(--chart-1)); --color-chart-2: hsl(var(--chart-2)); --color-chart-3: hsl(var(--chart-3)); @@ -117,6 +119,7 @@ --border: 240 5.9% 90%; --input: 240 5.9% 90%; --ring: 240 10% 3.9%; + --sidebar: 0 0% 98.8%; --chart-1: 12 76% 61%; --chart-2: 173 58% 39%; --chart-3: 197 37% 24%; @@ -128,31 +131,32 @@ /* CSS variables for dark mode */ .dark { - --background: 240 10% 3.9%; - --foreground: 0 0% 98%; - --card: 240 10% 3.9%; - --card-foreground: 0 0% 98%; - --popover: 240 10% 3.9%; - --popover-foreground: 0 0% 98%; - --primary: 0 0% 98%; - --primary-foreground: 240 5.9% 10%; - --secondary: 220 12% 75%; - --secondary-foreground: 0 0% 98%; - --muted: 240 3.7% 15.9%; - --muted-foreground: 240 5% 64.9%; - --accent: 240 3.7% 15.9%; - --accent-foreground: 0 0% 98%; - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 0 0% 98%; + --background: 224 71% 4%; + --foreground: 0 0% 100%; + --card: 224 71% 4%; + --card-foreground: 0 0% 100%; + --popover: 224 71% 4%; + --popover-foreground: 0 0% 100%; + --sidebar: 218 31% 7%; + --primary: 0 0% 100%; + --primary-foreground: 222 73% 3%; + --secondary: 218 11% 85%; + --secondary-foreground: 0 0% 100%; + --muted: 217 19% 17%; + --muted-foreground: 218 11% 65%; + --accent: 217 19% 17%; + --accent-foreground: 0 0% 100%; + --destructive: 0 91% 71%; + --destructive-foreground: 0 0% 100%; --success: 155 85% 35%; --success-foreground: 0 0% 98%; --warning: 38 92% 50%; --warning-foreground: 0 0% 98%; --info: 214 35% 18%; --info-foreground: 214 80% 65%; - --border: 240 3.7% 15.9%; - --input: 240 3.7% 15.9%; - --ring: 240 4.9% 83.9%; + --border: 217 13% 34%; + --input: 217 13% 34%; + --ring: 217 100% 66%; --chart-1: 220 70% 50%; --chart-2: 160 60% 45%; --chart-3: 30 80% 55%; diff --git a/services/platform/app/routes/dashboard/$id.tsx b/services/platform/app/routes/dashboard/$id.tsx index 150e20ac1..fc9c38dce 100644 --- a/services/platform/app/routes/dashboard/$id.tsx +++ b/services/platform/app/routes/dashboard/$id.tsx @@ -63,12 +63,12 @@ function DashboardLayout() {
-
+
-
+
diff --git a/services/platform/convex/_generated/api.d.ts b/services/platform/convex/_generated/api.d.ts index fe1c3d16e..169840f59 100644 --- a/services/platform/convex/_generated/api.d.ts +++ b/services/platform/convex/_generated/api.d.ts @@ -2690,7 +2690,6 @@ export declare const components: { maximumRowsRead?: number; numItems: number; }; - select?: Array; sortBy?: { direction: "asc" | "desc"; field: string }; where?: Array<{ connector?: "AND" | "OR"; diff --git a/services/platform/convex/betterAuth/_generated/component.ts b/services/platform/convex/betterAuth/_generated/component.ts index 4fe852e34..5dfb05b48 100644 --- a/services/platform/convex/betterAuth/_generated/component.ts +++ b/services/platform/convex/betterAuth/_generated/component.ts @@ -980,7 +980,6 @@ export type ComponentApi = maximumRowsRead?: number; numItems: number; }; - select?: Array; sortBy?: { direction: "asc" | "desc"; field: string }; where?: Array<{ connector?: "AND" | "OR"; diff --git a/services/platform/scripts/dev.ts b/services/platform/scripts/dev.ts index 7033af6ec..9ae7274fe 100644 --- a/services/platform/scripts/dev.ts +++ b/services/platform/scripts/dev.ts @@ -374,6 +374,17 @@ async function main() { await waitForConvex(); + // Re-read CONVEX_DEPLOYMENT from .env.local in case `convex dev` wrote it + // after our initial loadEnvFiles() call (happens on first run with fresh DB) + const platformEnvLocalPath = join(platformRoot, '.env.local'); + const freshEnv = parseDotEnv(platformEnvLocalPath); + if (freshEnv.CONVEX_DEPLOYMENT && !process.env.CONVEX_DEPLOYMENT) { + process.env.CONVEX_DEPLOYMENT = freshEnv.CONVEX_DEPLOYMENT; + console.log( + `[dev] â„šī¸ Picked up new deployment: ${freshEnv.CONVEX_DEPLOYMENT}`, + ); + } + console.log('[dev] 🔄 Syncing environment variables...'); try { await runCommand('bun', ['scripts/sync-convex-env-from-dotenv.ts']); From de75e7772504f9aee37b656ad2b864a128ae6b54 Mon Sep 17 00:00:00 2001 From: israel Date: Tue, 17 Mar 2026 21:16:16 +0100 Subject: [PATCH 2/2] fix: sort Tailwind classes in dashboard layout --- services/platform/app/routes/dashboard/$id.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/platform/app/routes/dashboard/$id.tsx b/services/platform/app/routes/dashboard/$id.tsx index fc9c38dce..714497370 100644 --- a/services/platform/app/routes/dashboard/$id.tsx +++ b/services/platform/app/routes/dashboard/$id.tsx @@ -68,7 +68,7 @@ function DashboardLayout() {
-
+