feat(kanban): add tooltips to kanban cards and improve layout

- Add @radix-ui/react-tooltip dependency
- Implement tooltip component for better UX
- Add tooltips to branch badges with full branch names
- Convert revert button to icon-only with tooltip
- Improve button layout and spacing in waiting_approval cards
- Update feature statuses and add new profile feature

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
This commit is contained in:
Kacper
2025-12-10 13:08:26 +01:00
parent a78b6763de
commit 1210dbb8ef
7 changed files with 223 additions and 46 deletions

View File

@@ -242,7 +242,7 @@
"category": "Core",
"description": "I want to have some abbility when executing a task on project to have some type of rewing / checkpoint system so if the changes made by agent in the project dont satisfy me / break something i can click in the ui to revert them. The best way for it would be to implement github worktress so when spin up new task claude take a look at it generate new branch that fit task issue and make it as gihub worktree then we would create a a new folder in project .automaker/worktree with branch name and clone of repo so agent can freely work one something like that ",
"steps": [],
"status": "waiting_approval",
"status": "verified",
"startedAt": "2025-12-10T11:11:06.115Z",
"imagePaths": [],
"skipTests": true,
@@ -255,12 +255,43 @@
"category": "Kanban",
"description": "When a agent is workig on task or when its in waiting approval column its would be nice to have some type of git diff panel and see what files got changed as well as reusing our custom themes we have in settings for the editor view of it take a look at codebase and create implementation for it",
"steps": [],
"status": "waiting_approval",
"status": "verified",
"startedAt": "2025-12-10T11:16:54.069Z",
"imagePaths": [],
"skipTests": true,
"summary": "Added git diff panel for in-progress and waiting approval features. Created GitDiffPanel component with themed syntax highlighting. Modified: git-diff-panel.tsx (new), agent-output-modal.tsx, worktree-manager.js, auto-mode-service.js, main.js, preload.js, electron.d.ts. The panel shows changed files with +/- stats and expandable unified diff view using CSS theme variables.",
"model": "opus",
"thinkingLevel": "ultrathink"
},
{
"id": "feature-1765366278888-fobz39cc4",
"category": "Core",
"description": "Implement profile view and in the sidebar the profile view would allow user to defined different ai provider profiels like heavy-task would be claude opus model with ultrathink or debugging would be codex max. This will give user flexibillity in our model tab to quickly use own defined profiles preset of models.",
"steps": [],
"status": "waiting_approval",
"startedAt": "2025-12-10T11:31:20.842Z",
"imagePaths": [],
"skipTests": true,
"model": "opus",
"thinkingLevel": "high"
},
{
"id": "feature-1765367758697-ihrkgxiaq",
"category": "Kanban",
"description": "So we added ai profiles add a default option to setting view that would determinate to show only profiles fir a new feautre modal in modal tab instead of showing profiles + custom selection this functionallity will clean up model prompt when someone will rely on own profiles ",
"steps": [],
"status": "waiting_approval",
"startedAt": "2025-12-10T11:56:01.001Z",
"imagePaths": [
{
"id": "img-1765367746901-1n1jqfkej",
"path": "/Users/shirone/Library/Application Support/automaker/images/1765367746897-ymoyjh19i_SCR-20251210-lpki.png",
"filename": "SCR-20251210-lpki.png",
"mimeType": "image/png"
}
],
"skipTests": true,
"model": "sonnet",
"thinkingLevel": "medium"
}
]

Submodule .automaker/worktrees/176536627888-implement-profile-view-and-in-the-sideba added at a78b6763de

Submodule .automaker/worktrees/176536775869-so-we-added-ai-profiles-add-a-default-op added at a78b6763de

76
app/package-lock.json generated
View File

@@ -20,6 +20,7 @@
"@radix-ui/react-slider": "^1.3.6",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-tooltip": "^1.2.8",
"@tanstack/react-query": "^5.90.12",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
@@ -3186,6 +3187,58 @@
}
}
},
"node_modules/@radix-ui/react-tooltip": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz",
"integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-dismissable-layer": "1.1.11",
"@radix-ui/react-id": "1.1.1",
"@radix-ui/react-popper": "1.2.8",
"@radix-ui/react-portal": "1.1.9",
"@radix-ui/react-presence": "1.1.5",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-slot": "1.2.3",
"@radix-ui/react-use-controllable-state": "1.2.2",
"@radix-ui/react-visually-hidden": "1.2.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
@@ -3322,6 +3375,29 @@
}
}
},
"node_modules/@radix-ui/react-visually-hidden": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz",
"integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-primitive": "2.1.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/rect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",

View File

@@ -27,6 +27,7 @@
"@radix-ui/react-slider": "^1.3.6",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-tooltip": "^1.2.8",
"@tanstack/react-query": "^5.90.12",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
@@ -72,7 +73,9 @@
{
"from": ".env",
"to": ".env",
"filter": ["**/*"]
"filter": [
"**/*"
]
}
],
"mac": {
@@ -80,11 +83,17 @@
"target": [
{
"target": "dmg",
"arch": ["x64", "arm64"]
"arch": [
"x64",
"arm64"
]
},
{
"target": "zip",
"arch": ["x64", "arm64"]
"arch": [
"x64",
"arm64"
]
}
],
"icon": "public/logo.png"
@@ -93,7 +102,9 @@
"target": [
{
"target": "nsis",
"arch": ["x64"]
"arch": [
"x64"
]
}
],
"icon": "public/logo.png"
@@ -102,11 +113,15 @@
"target": [
{
"target": "AppImage",
"arch": ["x64"]
"arch": [
"x64"
]
},
{
"target": "deb",
"arch": ["x64"]
"arch": [
"x64"
]
}
],
"category": "Development",

View File

@@ -0,0 +1,32 @@
"use client"
import * as React from "react"
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
import { cn } from "@/lib/utils"
const TooltipProvider = TooltipPrimitive.Provider
const Tooltip = TooltipPrimitive.Root
const TooltipTrigger = TooltipPrimitive.Trigger
const TooltipContent = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<TooltipPrimitive.Portal>
<TooltipPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
</TooltipPrimitive.Portal>
))
TooltipContent.displayName = TooltipPrimitive.Content.displayName
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }

View File

@@ -62,6 +62,12 @@ import {
DEFAULT_MODEL,
} from "@/lib/agent-context-parser";
import { Markdown } from "@/components/ui/markdown";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
interface KanbanCardProps {
feature: Feature;
@@ -259,23 +265,31 @@ export function KanbanCard({
)}
{/* Branch badge - show when feature has a worktree */}
{hasWorktree && !isCurrentAutoTask && (
<div
className={cn(
"absolute px-1.5 py-0.5 text-[10px] font-medium rounded flex items-center gap-1 z-10",
"bg-purple-500/20 border border-purple-500/50 text-purple-400",
// Position below error badge if present, otherwise use normal position
feature.error || feature.skipTests
? "top-8 left-2"
: shortcutKey
? "top-2 left-10"
: "top-2 left-2"
)}
data-testid={`branch-badge-${feature.id}`}
title={`Branch: ${feature.branchName}`}
>
<GitBranch className="w-3 h-3" />
<span className="truncate max-w-[100px]">{feature.branchName?.replace("feature/", "")}</span>
</div>
<TooltipProvider delayDuration={300}>
<Tooltip>
<TooltipTrigger asChild>
<div
className={cn(
"absolute px-1.5 py-0.5 text-[10px] font-medium rounded flex items-center gap-1 z-10 cursor-default",
"bg-purple-500/20 border border-purple-500/50 text-purple-400",
// Position below error badge if present, otherwise use normal position
feature.error || feature.skipTests
? "top-8 left-2"
: shortcutKey
? "top-2 left-10"
: "top-2 left-2"
)}
data-testid={`branch-badge-${feature.id}`}
>
<GitBranch className="w-3 h-3 shrink-0" />
<span className="truncate max-w-[80px]">{feature.branchName?.replace("feature/", "")}</span>
</div>
</TooltipTrigger>
<TooltipContent side="bottom" className="max-w-[300px]">
<p className="font-mono text-xs break-all">{feature.branchName}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
<CardHeader
className={cn(
@@ -648,37 +662,44 @@ export function KanbanCard({
)}
{!isCurrentAutoTask && feature.status === "waiting_approval" && (
<>
{/* Revert button - only show when worktree exists */}
{/* Revert button - only show when worktree exists (icon only to save space) */}
{hasWorktree && onRevert && (
<Button
variant="ghost"
size="sm"
className="h-7 text-xs text-red-400 hover:text-red-300 hover:bg-red-500/20"
onClick={(e) => {
e.stopPropagation();
setIsRevertDialogOpen(true);
}}
data-testid={`revert-${feature.id}`}
title="Discard all changes and move back to backlog"
>
<Undo2 className="w-3 h-3 mr-1" />
Revert
</Button>
<TooltipProvider delayDuration={300}>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="sm"
className="h-7 w-7 p-0 text-red-400 hover:text-red-300 hover:bg-red-500/20 shrink-0"
onClick={(e) => {
e.stopPropagation();
setIsRevertDialogOpen(true);
}}
data-testid={`revert-${feature.id}`}
>
<Undo2 className="w-3.5 h-3.5" />
</Button>
</TooltipTrigger>
<TooltipContent side="top">
<p>Revert changes</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{/* Follow-up prompt button */}
{onFollowUp && (
<Button
variant="secondary"
size="sm"
className="flex-1 h-7 text-xs"
className="flex-1 h-7 text-xs min-w-0"
onClick={(e) => {
e.stopPropagation();
onFollowUp();
}}
data-testid={`follow-up-${feature.id}`}
>
<MessageSquare className="w-3 h-3 mr-1" />
Follow-up
<MessageSquare className="w-3 h-3 mr-1 shrink-0" />
<span className="truncate">Follow-up</span>
</Button>
)}
{/* Merge button - only show when worktree exists */}
@@ -686,7 +707,7 @@ export function KanbanCard({
<Button
variant="default"
size="sm"
className="flex-1 h-7 text-xs bg-purple-600 hover:bg-purple-700"
className="flex-1 h-7 text-xs bg-purple-600 hover:bg-purple-700 min-w-0"
onClick={(e) => {
e.stopPropagation();
onMerge();
@@ -694,8 +715,8 @@ export function KanbanCard({
data-testid={`merge-${feature.id}`}
title="Merge changes into main branch"
>
<GitMerge className="w-3 h-3 mr-1" />
Merge
<GitMerge className="w-3 h-3 mr-1 shrink-0" />
<span className="truncate">Merge</span>
</Button>
)}
{/* Commit and verify button - show when no worktree */}