diff --git a/OPENGUI.png b/OPENGUI.png deleted file mode 100644 index c533569..0000000 Binary files a/OPENGUI.png and /dev/null differ diff --git a/package.json b/package.json index 3e849ac..3be9d46 100644 --- a/package.json +++ b/package.json @@ -67,9 +67,9 @@ "@opencode-ai/sdk": "^1.2.15", "@radix-ui/react-label": "^2.1.8", "@radix-ui/react-select": "^2.2.6", - "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-tabs": "^1.1.13", - "amadeus": "^11.0.0", + "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "katex": "^0.16.33", diff --git a/src/App.tsx b/src/App.tsx index 3d6b89d..6120005 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -310,5 +310,3 @@ export function App() { ); } - -export default App; diff --git a/src/components/AppSidebar.tsx b/src/components/AppSidebar.tsx index d8cfa82..1393330 100644 --- a/src/components/AppSidebar.tsx +++ b/src/components/AppSidebar.tsx @@ -60,9 +60,6 @@ import { MergeDialog } from "./MergeDialog"; import { getColorBorderClass, SessionContextMenu } from "./SessionContextMenu"; import { WorktreeDialog } from "./WorktreeDialog"; -/** @deprecated Use getProjectName from @/lib/utils instead. */ -const projectName = (directory: string) => getProjectName(directory, directory); - /** Build a "create pull request" URL from a git remote URL and branch. */ function buildPRUrl( remoteUrl: string, @@ -209,9 +206,19 @@ export function AppSidebar() { branch: string; worktreePath: string; } | null>(null); + const fixWithAiTimeoutRef = useRef(null); // Per-project: remote URL (for PR links) const [remoteUrls, setRemoteUrls] = useState>({}); + useEffect(() => { + return () => { + if (fixWithAiTimeoutRef.current !== null) { + window.clearTimeout(fixWithAiTimeoutRef.current); + fixWithAiTimeoutRef.current = null; + } + }; + }, []); + /** Refresh git info for a project directory (is repo + worktree list + remote). */ const refreshGitInfo = useCallback(async (directory: string) => { const git = window.electronAPI?.git; @@ -384,7 +391,7 @@ export function AppSidebar() { /> )} - {projectName(directory)} + {getProjectName(directory)} {/* New session for this project */} {isProjectConnected && @@ -537,7 +544,7 @@ export function AppSidebar() { > {wt.branch ?? - projectName(wt.path)} + getProjectName(wt.path)} @@ -672,7 +679,7 @@ export function AppSidebar() { > - {projectName(directory)} + {getProjectName(directory)} main @@ -692,7 +699,7 @@ export function AppSidebar() { > - {projectName(wtDir)} + {getProjectName(wtDir)} ))} @@ -730,7 +737,7 @@ export function AppSidebar() { worktreeParents[session.directory] === directory; const worktreeBranch = isWorktreeSession - ? projectName(session.directory) + ? getProjectName(session.directory) : null; return (
- {projectName(projectPopover.directory)} + {getProjectName(projectPopover.directory)}
{popoverSessions.length} @@ -1090,11 +1097,15 @@ export function AppSidebar() { // Start a new session in the main directory and send the conflict resolution prompt startDraftSession(mergeInfo.mainDir); // Use a small delay so the draft session is active before sending - setTimeout(() => { + if (fixWithAiTimeoutRef.current !== null) { + window.clearTimeout(fixWithAiTimeoutRef.current); + } + fixWithAiTimeoutRef.current = window.setTimeout(() => { const fileList = conflicts.map((f) => `- ${f}`).join("\n"); sendPrompt( `There are git merge conflicts from merging branch "${mergeInfo.branch}" into the current branch.\n\nThe following files have unresolved conflicts:\n${fileList}\n\nPlease resolve all merge conflicts in these files. Remove all conflict markers (<<<<<<, ======, >>>>>>) and produce the correct merged code. After resolving all conflicts, stage the resolved files with \`git add\` for each file.`, ); + fixWithAiTimeoutRef.current = null; }, POST_MERGE_DELAY_MS); }} /> diff --git a/src/components/ConnectionPanel.tsx b/src/components/ConnectionPanel.tsx index 7f957b1..2c25534 100644 --- a/src/components/ConnectionPanel.tsx +++ b/src/components/ConnectionPanel.tsx @@ -38,7 +38,6 @@ import { Spinner } from "@/components/ui/spinner"; import { Switch } from "@/components/ui/switch"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { - hasAnyConnection, NOTIFICATIONS_ENABLED_KEY, useActions, useConnectionState, @@ -114,9 +113,8 @@ type ServerState = | "error"; function AddProjectForm({ onDone }: { onDone: () => void }) { - const { addProject, connectToProject, disconnect, clearError } = useActions(); + const { addProject, connectToProject, clearError } = useActions(); const { connections } = useConnectionState(); - const isConnected = hasAnyConnection(connections); const isElectron = !!window.electronAPI; const [url, setUrl] = useState( diff --git a/src/components/MergeDialog.tsx b/src/components/MergeDialog.tsx index 8726ada..8942a0e 100644 --- a/src/components/MergeDialog.tsx +++ b/src/components/MergeDialog.tsx @@ -24,7 +24,7 @@ type MergeState = | { step: "conflicts"; files: string[] } | { step: "error"; message: string }; -export interface MergeDialogProps { +interface MergeDialogProps { open: boolean; onOpenChange: (open: boolean) => void; /** The main project directory (merge target). */ diff --git a/src/components/QueueList.tsx b/src/components/QueueList.tsx index 873dba7..7c75de5 100644 --- a/src/components/QueueList.tsx +++ b/src/components/QueueList.tsx @@ -13,7 +13,7 @@ import * as React from "react"; import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; -export interface QueueItem { +interface QueueItem { id: string; text: string; variant?: string; diff --git a/src/components/SessionContextMenu.tsx b/src/components/SessionContextMenu.tsx index 18478f9..b91e697 100644 --- a/src/components/SessionContextMenu.tsx +++ b/src/components/SessionContextMenu.tsx @@ -14,7 +14,7 @@ import { cn } from "@/lib/utils"; // Color config // --------------------------------------------------------------------------- -export const SESSION_COLORS: { +const SESSION_COLORS: { value: SessionColor; label: string; className: string; diff --git a/src/components/provider-icons/ProviderIcon.tsx b/src/components/provider-icons/ProviderIcon.tsx index 59194c2..67725ac 100644 --- a/src/components/provider-icons/ProviderIcon.tsx +++ b/src/components/provider-icons/ProviderIcon.tsx @@ -9,7 +9,7 @@ import type { SVGAttributes } from "react"; import sprite from "./sprite.svg"; import { type ProviderIconName, providerIconNames } from "./types"; -export type ProviderIconProps = SVGAttributes & { +type ProviderIconProps = SVGAttributes & { /** Provider ID (must match a key in the sprite sheet). */ provider: string; }; @@ -17,7 +17,7 @@ export type ProviderIconProps = SVGAttributes & { const iconNameSet = new Set(providerIconNames); /** Resolve a provider ID to a valid icon name, falling back to "synthetic". */ -export function resolveProviderIcon(id: string): ProviderIconName { +function resolveProviderIcon(id: string): ProviderIconName { if (iconNameSet.has(id)) return id as ProviderIconName; return "synthetic"; } diff --git a/src/components/provider-icons/index.ts b/src/components/provider-icons/index.ts index cde9428..d39e734 100644 --- a/src/components/provider-icons/index.ts +++ b/src/components/provider-icons/index.ts @@ -1,4 +1 @@ -export type { ProviderIconProps } from "./ProviderIcon"; -export { ProviderIcon, resolveProviderIcon } from "./ProviderIcon"; -export type { ProviderIconName } from "./types"; -export { providerIconNames } from "./types"; +export { ProviderIcon } from "./ProviderIcon"; diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx index 86bed41..9e30e7f 100644 --- a/src/components/ui/badge.tsx +++ b/src/components/ui/badge.tsx @@ -23,9 +23,8 @@ const badgeVariants = cva( }, ); -export interface BadgeProps - extends React.HTMLAttributes, - VariantProps {} +type BadgeProps = React.HTMLAttributes & + VariantProps; function Badge({ className, variant, ...props }: BadgeProps) { return ( @@ -33,4 +32,4 @@ function Badge({ className, variant, ...props }: BadgeProps) { ); } -export { Badge, badgeVariants }; +export { Badge }; diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 8f484d6..542cc75 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -61,4 +61,4 @@ function Button({ ); } -export { Button, buttonVariants }; +export { Button }; diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx index 2cb8c5d..1c462c6 100644 --- a/src/components/ui/card.tsx +++ b/src/components/ui/card.tsx @@ -38,16 +38,6 @@ function CardTitle({ className, ...props }: React.ComponentProps<"div">) { ); } -function CardDescription({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ); -} - function CardAction({ className, ...props }: React.ComponentProps<"div">) { return (
) { ); } -function CardFooter({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ); -} - -export { - Card, - CardAction, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -}; +export { Card, CardAction, CardContent, CardHeader, CardTitle }; diff --git a/src/components/ui/collapsible.tsx b/src/components/ui/collapsible.tsx deleted file mode 100644 index 439b44d..0000000 --- a/src/components/ui/collapsible.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { Collapsible as CollapsiblePrimitive } from "radix-ui"; - -function Collapsible({ - ...props -}: React.ComponentProps) { - return ; -} - -function CollapsibleTrigger({ - ...props -}: React.ComponentProps) { - return ( - - ); -} - -function CollapsibleContent({ - ...props -}: React.ComponentProps) { - return ( - - ); -} - -export { Collapsible, CollapsibleTrigger, CollapsibleContent }; diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx index 4251e4e..8cfd97b 100644 --- a/src/components/ui/dialog.tsx +++ b/src/components/ui/dialog.tsx @@ -22,12 +22,6 @@ function DialogPortal({ return ; } -function DialogClose({ - ...props -}: React.ComponentProps) { - return ; -} - function DialogOverlay({ className, ...props @@ -143,13 +137,10 @@ function DialogDescription({ export { Dialog, - DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, - DialogOverlay, - DialogPortal, DialogTitle, DialogTrigger, }; diff --git a/src/components/ui/sheet.tsx b/src/components/ui/sheet.tsx index 2bf2ce4..6050140 100644 --- a/src/components/ui/sheet.tsx +++ b/src/components/ui/sheet.tsx @@ -8,18 +8,6 @@ function Sheet({ ...props }: React.ComponentProps) { return ; } -function SheetTrigger({ - ...props -}: React.ComponentProps) { - return ; -} - -function SheetClose({ - ...props -}: React.ComponentProps) { - return ; -} - function SheetPortal({ ...props }: React.ComponentProps) { @@ -93,16 +81,6 @@ function SheetHeader({ className, ...props }: React.ComponentProps<"div">) { ); } -function SheetFooter({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ); -} - function SheetTitle({ className, ...props @@ -129,13 +107,4 @@ function SheetDescription({ ); } -export { - Sheet, - SheetTrigger, - SheetClose, - SheetContent, - SheetHeader, - SheetFooter, - SheetTitle, - SheetDescription, -}; +export { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle }; diff --git a/src/components/ui/sidebar.tsx b/src/components/ui/sidebar.tsx index ab6d836..0a8a9fc 100644 --- a/src/components/ui/sidebar.tsx +++ b/src/components/ui/sidebar.tsx @@ -3,8 +3,7 @@ import { PanelLeftIcon } from "lucide-react"; import { Slot } from "radix-ui"; import * as React from "react"; import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Separator } from "@/components/ui/separator"; + import { Sheet, SheetContent, @@ -12,7 +11,7 @@ import { SheetHeader, SheetTitle, } from "@/components/ui/sheet"; -import { Skeleton } from "@/components/ui/skeleton"; + import { Tooltip, TooltipContent, @@ -318,20 +317,6 @@ function SidebarInset({ className, ...props }: React.ComponentProps<"main">) { ); } -function SidebarInput({ - className, - ...props -}: React.ComponentProps) { - return ( - - ); -} - function SidebarHeader({ className, ...props }: React.ComponentProps<"div">) { return (
) { ); } -function SidebarSeparator({ - className, - ...props -}: React.ComponentProps) { - return ( - - ); -} - function SidebarContent({ className, ...props }: React.ComponentProps<"div">) { return (
& { asChild?: boolean }) { - const Comp = asChild ? Slot.Root : "button"; - - return ( - svg]:size-4 [&>svg]:shrink-0", - // Increases the hit area of the button on mobile. - "after:absolute after:-inset-2 md:after:hidden", - "group-data-[collapsible=icon]:hidden", - className, - )} - {...props} - /> - ); -} - function SidebarGroupContent({ className, ...props @@ -545,159 +493,6 @@ function SidebarMenuButton({ ); } -function SidebarMenuAction({ - className, - asChild = false, - showOnHover = false, - ...props -}: React.ComponentProps<"button"> & { - asChild?: boolean; - showOnHover?: boolean; -}) { - const Comp = asChild ? Slot.Root : "button"; - - return ( - svg]:size-4 [&>svg]:shrink-0", - // Increases the hit area of the button on mobile. - "after:absolute after:-inset-2 md:after:hidden", - "peer-data-[size=sm]/menu-button:top-1", - "peer-data-[size=default]/menu-button:top-1.5", - "peer-data-[size=lg]/menu-button:top-2.5", - "group-data-[collapsible=icon]:hidden", - showOnHover && - "peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0", - className, - )} - {...props} - /> - ); -} - -function SidebarMenuBadge({ - className, - ...props -}: React.ComponentProps<"div">) { - return ( -
- ); -} - -function SidebarMenuSkeleton({ - className, - showIcon = false, - ...props -}: React.ComponentProps<"div"> & { - showIcon?: boolean; -}) { - // Random width between 50 to 90%. - const width = React.useMemo(() => { - return `${Math.floor(Math.random() * 40) + 50}%`; - }, []); - - return ( -
- {showIcon && ( - - )} - -
- ); -} - -function SidebarMenuSub({ className, ...props }: React.ComponentProps<"ul">) { - return ( -
    - ); -} - -function SidebarMenuSubItem({ - className, - ...props -}: React.ComponentProps<"li">) { - return ( -
  • - ); -} - -function SidebarMenuSubButton({ - asChild = false, - size = "md", - isActive = false, - className, - ...props -}: React.ComponentProps<"a"> & { - asChild?: boolean; - size?: "sm" | "md"; - isActive?: boolean; -}) { - const Comp = asChild ? Slot.Root : "a"; - - return ( - svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0", - "data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground", - size === "sm" && "text-xs", - size === "md" && "text-sm", - "group-data-[collapsible=icon]:hidden", - className, - )} - {...props} - /> - ); -} - // --------------------------------------------------------------------------- // Right Sidebar – independent provider that re-uses SidebarContext so all // standard Sidebar sub-components (SidebarHeader, SidebarContent, …) work @@ -831,24 +626,15 @@ export { SidebarContent, SidebarFooter, SidebarGroup, - SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, - SidebarInput, SidebarInset, SidebarMenu, - SidebarMenuAction, - SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, - SidebarMenuSkeleton, - SidebarMenuSub, - SidebarMenuSubButton, - SidebarMenuSubItem, SidebarProvider, SidebarRail, - SidebarSeparator, SidebarTrigger, useSidebar, RightSidebarProvider, diff --git a/src/components/ui/textarea.tsx b/src/components/ui/textarea.tsx deleted file mode 100644 index 70e5b64..0000000 --- a/src/components/ui/textarea.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import type * as React from "react"; - -import { cn } from "@/lib/utils"; - -function Textarea({ className, ...props }: React.ComponentProps<"textarea">) { - return ( -