diff --git a/AGENTS.md b/AGENTS.md index 0731b9a..c5455dc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,8 +26,8 @@ of the product. the behavior implied by its name; do not move unrelated actions into it to hide coordination. An optional feature must be removable by deleting its import and composition node without breaking sibling capabilities. -- Express UI variants with focused components and early returns. Do not accumulate - JSX in mutable variables or turn one component into a dispatcher for unrelated UI. +- Mutable JSX variables are an antipattern. Compose focused components with early + returns instead of assigning JSX to `let` variables. - Keep one source of truth and derive the rest. Model state with one explicit status, not overlapping booleans or synchronization effects. - Keep logic above JSX. Avoid ternaries and boolean chains in markup. Use diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 9f97e10..003fd56 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -226,11 +226,17 @@ lib/ lowest-level reusable modules and extractable feature packages chat-voice-input/ self-contained voice input package components/ + app-header.tsx + shared route header and optional workspace/preview actions + app-sidebar.tsx + responsive application navigation and session management + session-list-item.tsx + session navigation row, rename, and row actions ui/ generic visual primitives - composer/ message input, optional controls, and submit composition - code/ Pierre-backed source and diff facades - session/ conversation, activity, navigation, and preview control - workspace/ file navigation, tree, queries, and panel + chat/ reusable chat layout, actions, scrolling, and composition + code/ lazy Pierre-backed diff facade + session/ session lifecycle, messages, activity, and preview control + workspace/ file navigation, tree, queries, source viewer, and panel app/ routes, pages, providers, and top-level wiring tests/ pure contract and runtime tests vercel.json Vite web service plus Eve service diff --git a/app/app.tsx b/app/app.tsx index dc359b2..d60a74d 100644 --- a/app/app.tsx +++ b/app/app.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import { href, Outlet, useMatch, useNavigate } from "react-router"; -import { SessionSidebar } from "@/components/session/sidebar"; +import { AppSidebar } from "@/components/app-sidebar"; import { useComposerStore } from "@/lib/composer-store"; function useMediaQuery(query: string): boolean { @@ -44,7 +44,7 @@ export function App() { return (
- setSidebarOpen(false)} diff --git a/app/home-page.tsx b/app/home-page.tsx index 2eb0b6a..9ec3e1a 100644 --- a/app/home-page.tsx +++ b/app/home-page.tsx @@ -1,7 +1,7 @@ import { useConvexMutation } from "@convex-dev/react-query"; import { href, useNavigate } from "react-router"; -import { PageHeader } from "@/components/session/page-header"; +import { AppHeader } from "@/components/app-header"; import { SessionStart } from "@/components/session/session-start"; import { api } from "@/convex/_generated/api"; import type { GitRepository } from "@/lib/github"; @@ -37,7 +37,7 @@ export function HomePage() { return (
- +
); diff --git a/biome.json b/biome.json index 48f07f1..41279a5 100644 --- a/biome.json +++ b/biome.json @@ -41,7 +41,6 @@ { "includes": [ "components/code/diff-renderer.tsx", - "components/code/file-viewer.tsx", "components/workspace/use-workspace-tree.ts", "components/workspace/workspace-browser.tsx", "components/workspace/workspace-panel.tsx", diff --git a/components/app-header.tsx b/components/app-header.tsx new file mode 100644 index 0000000..98bb40f --- /dev/null +++ b/components/app-header.tsx @@ -0,0 +1,182 @@ +import { Download, GitFork, PanelLeft, PanelRight, SquarePen, X } from "lucide-react"; +import { useOutletContext } from "react-router"; + +import { PreviewControl } from "@/components/session/preview-control"; +import { Button } from "@/components/ui/button"; +import { MenuItem } from "@/components/ui/menu"; +import type { Preview } from "@/lib/preview"; +import { getWorkspaceUrl } from "@/lib/workspace"; + +type AppHeaderProps = { + readonly branch?: string; + readonly isWorkspaceOpen?: boolean; + readonly onToggleWorkspace?: () => void; + readonly preview?: Preview; + readonly repository?: string; + readonly title: string; + readonly workspaceSessionId?: string; +}; + +type SidebarContext = { + readonly isSidebarExpanded: boolean; + readonly openNewSession: () => void; + readonly toggleSidebar: () => void; +}; + +function WorkspaceToggleIcon({ isOpen }: { readonly isOpen: boolean }) { + if (!isOpen) return