Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
5f9bfd6
πŸ€– Adopt shadcn/ui components - Phase 1
kylecarbs Oct 23, 2025
91d1b39
πŸ€– Auto-fix ESLint warnings
kylecarbs Oct 23, 2025
7145544
πŸ€– Fix nullish coalescing in toggle-group
kylecarbs Oct 23, 2025
ee22483
πŸ€– Format docs/AGENTS.md
kylecarbs Oct 23, 2025
526498f
πŸ€– Migrate all Tooltip components to shadcn/ui
kylecarbs Oct 23, 2025
efd7d73
Add shadcn CSS variables for tooltip styling
kylecarbs Oct 23, 2025
1e8dcdf
Add TooltipProvider to all story decorators that use tooltips
kylecarbs Oct 23, 2025
34cd708
Add TooltipProvider to Message component stories
kylecarbs Oct 23, 2025
e21fede
Fix StatusIndicator test selector for tooltip-wrapped indicator
kylecarbs Oct 23, 2025
04509c9
Fix StatusIndicator tooltip test for shadcn tooltips
kylecarbs Oct 23, 2025
c57d3a3
Simplify WithTooltipInteraction test waitFor callbacks
kylecarbs Oct 23, 2025
620fbae
Temporarily disable WithTooltipInteraction test
kylecarbs Oct 23, 2025
7dd3d86
Trigger CI rerun
kylecarbs Oct 23, 2025
b3e9e00
Improve tooltip styling: fix border color and reduce font size
kylecarbs Oct 23, 2025
cf6b693
Add arrow/caret to tooltips
kylecarbs Oct 23, 2025
a0573c2
Fix HelpIndicator tooltips and remove fixed height from toggle buttons
kylecarbs Oct 23, 2025
8554d6b
Merge branch 'main' into shadcnify
kylecarbs Oct 23, 2025
c71c995
Fix tooltip styling by using explicit CSS variable references
kylecarbs Oct 23, 2025
b17cade
Retrigger CI
kylecarbs Oct 23, 2025
68c6ca4
Revert to native Tailwind utilities for tooltip styling
kylecarbs Oct 24, 2025
a4a6b22
Use inline styles for tooltip colors to ensure they render
kylecarbs Oct 24, 2025
2c94789
Format tooltip.tsx
kylecarbs Oct 24, 2025
71ccbe5
Add popover colors to :root for inline style access
kylecarbs Oct 27, 2025
af14b02
Merge branch 'main' into shadcnify
kylecarbs Oct 27, 2025
b5daf5f
Migrate RuntimeBadge to shadcn tooltips
kylecarbs Oct 27, 2025
75a126f
Use hardcoded HSL values for tooltip colors
kylecarbs Oct 27, 2025
8f68ea7
Simplify tooltip styling to bare minimum for debugging
kylecarbs Oct 27, 2025
a9ebbb0
Use native shadcn color scheme for tooltips
kylecarbs Oct 27, 2025
2371f3b
Add explicit CSS classes for tooltip utilities
kylecarbs Oct 27, 2025
9f2f17f
Merge branch 'main' into shadcnify
kylecarbs Oct 27, 2025
d9e76c1
Add direct Radix UI tooltip styling with @layer utilities
kylecarbs Oct 30, 2025
02bb1e3
Merge remote-tracking branch 'origin/main' into shadcnify
kylecarbs Oct 30, 2025
356e214
Fix tooltip imports in new files from merge
kylecarbs Oct 30, 2025
0c0cfdc
Fix storybook imports in story files
kylecarbs Oct 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion bun.lock
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-toggle": "^1.1.10",
"@radix-ui/react-toggle-group": "^1.1.11",
"@radix-ui/react-tooltip": "^1.2.8",
"ai": "^5.0.72",
Expand All @@ -26,6 +27,7 @@
"express": "^5.1.0",
"jsonc-parser": "^3.3.1",
"lru-cache": "^11.2.2",
"lucide-react": "^0.546.0",
"markdown-it": "^14.1.0",
"minimist": "^1.2.8",
"rehype-harden": "^1.1.5",
Expand Down Expand Up @@ -2042,7 +2044,7 @@

"lru-cache": ["[email protected]", "", {}, "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg=="],

"lucide-react": ["lucide-react@0.542.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw=="],
"lucide-react": ["lucide-react@0.546.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Z94u6fKT43lKeYHiVyvyR8fT7pwCzDu7RyMPpTvh054+xahSgj4HFQ+NmflvzdXsoAjYGdCguGaFKYuvq0ThCQ=="],

"lz-string": ["[email protected]", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="],

Expand Down Expand Up @@ -3256,6 +3258,8 @@

"storybook/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],

"streamdown/lucide-react": ["[email protected]", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw=="],

"string-length/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],

"string_decoder/safe-buffer": ["[email protected]", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
Expand Down
21 changes: 21 additions & 0 deletions docs/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,27 @@ If IPC is hard to test, fix the test infrastructure or IPC layer, don't work aro
- Use CSS variables (e.g., `var(--color-plan-mode)`) instead of hardcoded colors
- Fonts are centralized as CSS variables in `src/styles/fonts.tsx`

## Component Guidelines

**Always use shadcn/ui components from `@/components/ui/*` for standard UI patterns.**

- **Adding components**: `bunx --bun shadcn@latest add <component>`
- **Common components**: button, input, select, dialog, tooltip, toggle-group, checkbox, switch, etc.
- **Documentation**: https://ui.shadcn.com/docs/components
- **Build custom only when**: The component has app-specific behavior with no shadcn equivalent

Example:

```bash
# Add a new shadcn component
bunx --bun shadcn@latest add badge

# Import in your code
import { Badge } from "@/components/ui/badge"
```

Shadcn components automatically respect our CSS variables via the `@/lib/utils` cn() helper and Tailwind config.

## TypeScript Best Practices

- **Avoid `as any` in all contexts** - Never use `as any` casts. Instead:
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-toggle": "^1.1.10",
"@radix-ui/react-toggle-group": "^1.1.11",
"@radix-ui/react-tooltip": "^1.2.8",
"ai": "^5.0.72",
Expand All @@ -67,6 +68,7 @@
"express": "^5.1.0",
"jsonc-parser": "^3.3.1",
"lru-cache": "^11.2.2",
"lucide-react": "^0.546.0",
"markdown-it": "^14.1.0",
"minimist": "^1.2.8",
"rehype-harden": "^1.1.5",
Expand Down
9 changes: 6 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useState, useEffect, useCallback, useRef } from "react";
import { TooltipProvider } from "./components/ui/tooltip";
import "./styles/globals.css";
import { useApp } from "./contexts/AppContext";
import type { WorkspaceSelection } from "./components/ProjectSidebar";
Expand Down Expand Up @@ -702,9 +703,11 @@ function AppInner() {

function App() {
return (
<CommandRegistryProvider>
<AppInner />
</CommandRegistryProvider>
<TooltipProvider>
<CommandRegistryProvider>
<AppInner />
</CommandRegistryProvider>
</TooltipProvider>
);
}

Expand Down
77 changes: 63 additions & 14 deletions src/components/AIView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@ import { useAutoScroll } from "@/hooks/useAutoScroll";
import { usePersistedState } from "@/hooks/usePersistedState";
import { useThinking } from "@/contexts/ThinkingContext";
import { useWorkspaceState, useWorkspaceAggregator } from "@/stores/WorkspaceStore";
import { WorkspaceHeader } from "./WorkspaceHeader";
import { StatusIndicator } from "./StatusIndicator";
import { getModelName } from "@/utils/ai/models";
import { GitStatusIndicator } from "./GitStatusIndicator";
import { RuntimeBadge } from "./RuntimeBadge";

import { useGitStatus } from "@/stores/GitStatusStore";
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
import type { DisplayedMessage } from "@/types/message";
import type { RuntimeConfig } from "@/types/runtime";
import { useAIViewKeybinds } from "@/hooks/useAIViewKeybinds";
Expand Down Expand Up @@ -70,15 +75,27 @@ const AIViewInner: React.FC<AIViewProps> = ({
const workspaceState = useWorkspaceState(workspaceId);
const aggregator = useWorkspaceAggregator(workspaceId);

// Get git status for this workspace
const gitStatus = useGitStatus(workspaceId);

const [editingMessage, setEditingMessage] = useState<{ id: string; content: string } | undefined>(
undefined
);

// Auto-retry state - minimal setter for keybinds and message sent handler
// RetryBarrier manages its own state, but we need this for Ctrl+C keybind
const [, setAutoRetry] = usePersistedState<boolean>(getAutoRetryKey(workspaceId), true, {
listener: true,
});
// Auto-retry state (persisted per workspace, with cross-component sync)
// Semantics:
// true (default): System errors should auto-retry
// false: User stopped this (Ctrl+C), don't auto-retry until user re-engages
// State transitions are EXPLICIT only:
// - User presses Ctrl+C β†’ false
// - User sends a message β†’ true (clear intent: "I'm using this workspace")
// - User clicks manual retry button β†’ true
// No automatic resets on stream events - prevents initialization bugs
const [_autoRetry, _setAutoRetry] = usePersistedState<boolean>(
getAutoRetryKey(workspaceId),
true, // Default to true
{ listener: true } // Enable cross-component synchronization
);

// Use auto-scroll hook for scroll management
const {
Expand Down Expand Up @@ -322,13 +339,43 @@ const AIViewInner: React.FC<AIViewProps> = ({
ref={chatAreaRef}
className="flex min-w-96 flex-1 flex-col [@media(max-width:768px)]:max-h-full [@media(max-width:768px)]:w-full [@media(max-width:768px)]:min-w-0"
>
<WorkspaceHeader
workspaceId={workspaceId}
projectName={projectName}
branch={branch}
namedWorkspacePath={namedWorkspacePath}
runtimeConfig={runtimeConfig}
/>
<div className="bg-separator border-border-light flex items-center justify-between border-b px-[15px] py-1 [@media(max-width:768px)]:flex-wrap [@media(max-width:768px)]:gap-2 [@media(max-width:768px)]:py-2 [@media(max-width:768px)]:pl-[60px]">
<div className="text-foreground flex min-w-0 items-center gap-2 overflow-hidden font-semibold">
<StatusIndicator
streaming={canInterrupt}
title={
canInterrupt && currentModel ? `${getModelName(currentModel)} streaming` : "Idle"
}
/>
<GitStatusIndicator
gitStatus={gitStatus}
workspaceId={workspaceId}
tooltipPosition="bottom"
/>
<RuntimeBadge runtimeConfig={runtimeConfig} />
<span className="min-w-0 truncate font-mono text-xs">
{projectName} / {branch}
</span>
<span className="text-muted min-w-0 truncate font-mono text-[11px] font-normal">
{namedWorkspacePath}
</span>
<Tooltip>
<TooltipTrigger asChild>
<button
onClick={handleOpenTerminal}
className="text-muted hover:text-foreground flex cursor-pointer items-center justify-center border-none bg-transparent p-1 transition-colors [&_svg]:h-4 [&_svg]:w-4"
>
<svg viewBox="0 0 16 16" fill="currentColor">
<path d="M0 2.75C0 1.784.784 1 1.75 1h12.5c.966 0 1.75.784 1.75 1.75v10.5A1.75 1.75 0 0114.25 15H1.75A1.75 1.75 0 010 13.25V2.75zm1.75-.25a.25.25 0 00-.25.25v10.5c0 .138.112.25.25.25h12.5a.25.25 0 00.25-.25V2.75a.25.25 0 00-.25-.25H1.75zM7.25 8a.75.75 0 01-.22.53l-2.25 2.25a.75.75 0 01-1.06-1.06L5.44 8 3.72 6.28a.75.75 0 111.06-1.06l2.25 2.25c.141.14.22.331.22.53zm1.5 1.5a.75.75 0 000 1.5h3a.75.75 0 000-1.5h-3z" />
</svg>
</button>
</TooltipTrigger>
<TooltipContent side="bottom">
Open in terminal ({formatKeybind(KEYBINDS.OPEN_TERMINAL)})
</TooltipContent>
</Tooltip>
</div>
</div>

<div className="relative flex-1 overflow-hidden">
<div
Expand Down Expand Up @@ -399,7 +446,9 @@ const AIViewInner: React.FC<AIViewProps> = ({
);
})}
{/* Show RetryBarrier after the last message if needed */}
{showRetryBarrier && <RetryBarrier workspaceId={workspaceId} />}
{showRetryBarrier && (
<RetryBarrier workspaceId={workspaceId} />
)}
</>
)}
<PinnedTodoList workspaceId={workspaceId} />
Expand Down
12 changes: 5 additions & 7 deletions src/components/AgentStatusIndicator.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useCallback, useMemo } from "react";
import { cn } from "@/lib/utils";
import { TooltipWrapper, Tooltip } from "./Tooltip";
import { Tooltip, TooltipTrigger, TooltipContent } from "./ui/tooltip";
import { useWorkspaceSidebarState } from "@/stores/WorkspaceStore";
import { getStatusTooltip } from "@/utils/ui/statusTooltip";

Expand Down Expand Up @@ -98,12 +98,10 @@ export const AgentStatusIndicator: React.FC<AgentStatusIndicatorProps> = ({
// If tooltip content provided, wrap with proper Tooltip component
if (title) {
return (
<TooltipWrapper inline>
{indicator}
<Tooltip className="tooltip" align="center">
{title}
</Tooltip>
</TooltipWrapper>
<Tooltip>
<TooltipTrigger asChild>{indicator}</TooltipTrigger>
<TooltipContent side="top">{title}</TooltipContent>
</Tooltip>
);
}

Expand Down
Loading
Loading