+ )}
+ >
);
-}
\ No newline at end of file
+}
diff --git a/context/feature-specs/13-node-shape.md b/context/feature-specs/13-node-shape.md
new file mode 100644
index 0000000..740416e
--- /dev/null
+++ b/context/feature-specs/13-node-shape.md
@@ -0,0 +1,33 @@
+Replace the placeholder node renderer with proper shape rendering and a drag preview.
+
+## Implementation
+
+1. Replace the placeholder node shape rendering.
+ - rectangle, pill, and circle should use CSS styling
+ - diamond, hexagon, and cylinder should render with SVG shapes
+ - SVG shapes should scale with node size
+ - keep borders subtle at rest and brighter when selected
+
+2. Add a shape drag preview.
+ - when dragging a shape from the shape panel, show a ghost preview of that shape
+ - keep the preview attached to the cursor while dragging
+ - use the same shape type and default size that will be used on drop
+ - hide the preview after the shape is dropped or the drag is cancelled
+ - keep this limited to drag preview behavior only
+
+3. Keep node rendering connected to the existing collaborative canvas state.
+
+## Scope Limits
+
+- don't rebuild shape panel layout
+- don't change how dropped nodes are created
+- don't add resize or label editing yet
+- keep drag/drop changes limited to the ghost preview only
+
+## Check When Done
+
+- Nodes render the correct shape variant for each type.
+- CSS shapes render correctly for rectangle, pill, and circle.
+- SVG shapes render and scale correctly for diamond, hexagon, and cylinder.
+- Shape dragging shows a ghost preview matching the dragged shape.
+- `npm run build` passes without type errors.
diff --git a/context/feature-specs/14-node-editing.md b/context/feature-specs/14-node-editing.md
new file mode 100644
index 0000000..8e8b8c7
--- /dev/null
+++ b/context/feature-specs/14-node-editing.md
@@ -0,0 +1,40 @@
+Add resizing and inline label editing to canvas nodes.
+
+## Implementation
+
+1. Add resizing.
+ - selected nodes should show resize handles
+ - prevent nodes from being resized below a minimum size
+ - keep resize handles subtle and consistent with the dark canvas UI
+
+2. Add inline label editing.
+ - keep the node label centered inside the node
+ - double-click the center/label area of a node to edit its label
+ - after clicking enter the text should be at the center of the node
+ - show placeholder text in the same centered position when the label is empty
+ - keep editing smooth without causing layout shifts
+ - show a textarea directly over the label while editing
+ - update the label as users type
+ - close editing on blur or `Escape`
+ - prevent text editing interactions from dragging or panning the canvas
+
+3. Keep all node updates connected to the existing collaborative canvas state.
+
+4. Ensure that all shapes have 4 possible connectable points.
+
+## Scope Limits
+
+- don't change shape rendering from the previous unit
+- don't change the shape panel or drag preview
+- don't change how dropped nodes are created
+- keep this focused on resize and label editing only
+
+## Check When Done
+
+- Selected nodes show resize handles.
+- Resizing updates node dimensions through the existing node state flow.
+- Double-clicking a node opens inline label editing.
+- Label editing updates node labels through the existing sync flow.
+- Editing closes on blur or Escape.
+- Text interactions do not trigger canvas drag or pan.
+- `npm run build` passes without type errors.
diff --git a/context/feature-specs/15-node-color-toolbar.md b/context/feature-specs/15-node-color-toolbar.md
new file mode 100644
index 0000000..6d4b5d5
--- /dev/null
+++ b/context/feature-specs/15-node-color-toolbar.md
@@ -0,0 +1,48 @@
+Add a small floating color toolbar so selected nodes can change both their background and text color directly on the canvas.
+
+## Implementation
+
+1. Check `ui-context.md` for the node color palette.
+ Each palette option includes:
+ - a node background color
+ - a matching text color
+
+ Reuse existing theme colors if they already exist in the `global.css`. Otherwise, keep the palette in the canvas types/constants, such as `types/canvas.ts`.
+
+2. Add a toolbar above selected nodes.
+ - only show it when the node is selected
+ - keep it slightly above the node without overlapping it
+ - show one swatch per color pair
+ - active swatches should feel clearly selected
+ - hovering a swatch should show a subtle glow based on its text color
+ - keep the glow tight and controlled, not overly blurred
+ - prevent toolbar interactions from dragging nodes or panning the canvas
+
+3. When a swatch is selected:
+ - update both the node background color and text color
+ - update the node UI immediately
+ - keep this inside the existing collaborative canvas state
+ - no server calls
+
+4. Selected nodes should visually reflect their active color pair.
+
+ The node background updates to the selected color, and the text automatically updates to its paired text color.
+
+5. Issues:
+ - The font size inside the node also scale along with the node size more dynamically. is their is more text adjust it automatically.
+ - resizing the nodes is not easyly acessable, means cursur is not able to find the expand edge of the node. make it accessable
+
+
+## Scope Limits
+
+- don’t change drag/drop behavior
+- don’t rebuild node selection logic
+- don’t add a full color picker
+- keep this focused on predefined color themes only
+
+## Check When Done
+
+- Nodes use predefined background/text color pairs.
+- Selected nodes show a floating color toolbar.
+- Swatch selection updates both node and text colors.
+- `npm run build` passes without type errors.
diff --git a/context/feature-specs/16-edge-behavior.md b/context/feature-specs/16-edge-behavior.md
new file mode 100644
index 0000000..61dc603
--- /dev/null
+++ b/context/feature-specs/16-edge-behavior.md
@@ -0,0 +1,56 @@
+Replace the default canvas edges with custom edges that feel easier
+to follow, easier to click, and support inline labels.
+
+## Implementation
+
+1. Add connection handles to every node.
+ - place handles on the top, right, bottom, and left sides
+ - users should be able to connect from any handle to any other handle
+ - keep the handles subtle: small white dots with a dark border
+ - hide them by default and fade them in when hovering the node
+
+2. Add a default style for new edges.
+ - use a light stroke with rounded ends
+ - add an arrowhead at the end of each edge
+ - make new connections use the custom canvas edge renderer
+
+3. Create the custom edge renderer.
+ - use clean right-angle routing
+ - keep edges slightly dimmed at rest
+ - brighten edges when hovered or selected
+ - make edges easier to hover and click without increasing the
+ visible line thickness
+
+4. Add inline edge label editing.
+ - double-click an edge to edit its label
+ - use React Flow's `EdgeLabelRenderer` and the path midpoint
+ coordinates from `getSmoothStepPath` to position the label —
+ do not calculate midpoint position manually
+ - use an input that grows with the label text
+ - save the label on blur, Enter, or Escape
+ - show saved labels as small pill badges
+ - when an active edge has no label, show a faint hint
+ - prevent label clicks and typing from dragging or panning
+ the canvas
+ - update labels through the existing collaborative edge
+ data flow
+
+## Scope Limits
+
+- don't change how nodes are created
+- don't change the shape panel
+- don't redesign the node renderer beyond the required
+ connection handles
+- keep this focused on edge rendering, labels, and
+ connection behavior
+
+## Check When Done
+
+- Nodes have handles on all four sides.
+- New edges use the custom canvas edge type with arrows.
+- Edge hover, selection, and label editing are handled in
+ the custom edge renderer.
+- Edge label position uses EdgeLabelRenderer and path
+ midpoint coordinates.
+- Edge labels update through the existing edge data flow.
+- npm run build passes without type errors.
diff --git a/context/feature-specs/17-canvas-ergonomics.md.md b/context/feature-specs/17-canvas-ergonomics.md.md
new file mode 100644
index 0000000..50d625a
--- /dev/null
+++ b/context/feature-specs/17-canvas-ergonomics.md.md
@@ -0,0 +1,56 @@
+Add a floating control bar for zoom and undo/redo, then wire the same actions to keyboard shortcuts.
+
+## Implementation
+
+1. Add a pill-shaped control bar at the bottom-left of the canvas.
+
+ It should sit above the shape panel and include two groups:
+ - zoom controls: zoom out, fit view, zoom in
+ - history controls: undo, redo
+
+ Separate the two groups with a thin divider.
+
+2. Wire the zoom controls to the React Flow instance.
+ - zoom in
+ - zoom out
+ - fit view
+ - use a short animation so the movement feels smooth
+
+3. Wire undo and redo to Liveblocks history.
+ - use the existing Liveblocks undo/redo hooks
+ - disable undo when there is nothing to undo
+ - disable redo when there is nothing to redo
+ - keep disabled buttons visually dimmed
+
+4. Create a `useKeyboardShortcuts` hook in `hooks/`.
+
+ The hook should:
+ - receive the React Flow instance
+ - receive undo and redo handlers
+ - listen for keyboard shortcuts on `window`
+ - ignore shortcuts while typing in inputs, textareas, or editable text fields
+
+5. Support these shortcuts:
+ - `+` or `=` to zoom in
+ - `-` to zoom out
+ - `Cmd/Ctrl + Z` to undo
+ - `Cmd/Ctrl + Shift + Z` to redo
+ - `Cmd/Ctrl + Y` to redo
+
+6. Remove the minimap at the bottem right
+
+## Scope Limits
+
+- don’t change the shape panel
+- don’t change node or edge rendering
+- don’t add extra canvas controls
+- don’t change the existing collaborative state setup
+
+## Check When Done
+
+- Control bar is added to the canvas.
+- Zoom actions use the React Flow instance.
+- Undo and redo use Liveblocks history.
+- Keyboard shortcuts are handled in `hooks/useKeyboardShortcuts`.
+- Shortcut handling skips editable fields.
+- `npm run build` passes.
diff --git a/context/progress-tracker.md b/context/progress-tracker.md
index 9d9dbe0..93304ed 100644
--- a/context/progress-tracker.md
+++ b/context/progress-tracker.md
@@ -1,58 +1,72 @@
# Progress Tracker
-Update this file whenever the current phase, active feature, or implementation state changes.
-
-## Current Phase
-
-- Feature 12 — (compleated)
-
-## Current Goal
-
-- Define the immediate goals here
-
-## Completed
-
-- feature 01: Design system (shadcn/ui + lucide-react) — Button, Card, Dialog, Input, Tabs, Textarea, ScrollArea components installed with dark-only theme with tailwind v4.
-- feature 02: Editor chrome — `EditorNavbar` (top bar with sidebar toggle) and `ProjectSidebar` (floating left overlay with Projects header, Tabs for My Projects / Shared, and full-width New Project button). shadcn `Dialog` is now wired to the project's design tokens and ready for future use.
-- feature 03: Auth — Clerk wired into the Next.js app. `ClerkProvider` wraps the root layout using the `dark` theme from `@clerk/ui/themes` with appearance variables mapped to `globals.css` tokens. Sign-in (`/sign-in/[[...sign-in]]`) and sign-up (`/sign-up/[[...sign-up]]`) use a two-panel layout (compact logo + tagline + feature list on the left, centered Clerk form on the right; form-only on small screens). `proxy.ts` at the project root runs `clerkMiddleware` with public routes `/sign-in(.*)` and `/sign-up(.*)`, protecting everything else by default. The root `/` redirects authenticated users to `/editor` and unauthenticated users to `/sign-in`. `` is added to the editor navbar's right section. `npm run build` passes; no new lint errors.
-- feature 04: Project dialogs & editor home — `useProjectDialogs` hook (in `hooks/use-project-dialogs.ts`) owns the mock project list and centralizes dialog/form/loading state. Mock data lives in `lib/mock-projects.ts` with a shared `slugify` helper and per-project `role: "owner" | "collaborator"`. The editor home view (`components/editor/editor-home.tsx`) renders the heading, description, and a `New Project` button — no card. Three dialogs: `CreateProjectDialog` (name input with live slug preview using `slugify`), `RenameProjectDialog` (prefilled name, current name in the description, auto-focus + select, Enter submits via form `onSubmit`), and `DeleteProjectDialog` (destructive confirm button, no input). The sidebar now lists mock projects per tab; owned rows expose a `MoreHorizontal` menu with Rename / Delete, collaborator rows show no actions. A mobile-only backdrop scrim is rendered behind the sidebar on `md:hidden` and closes the sidebar on tap. `npm run build` passes; no new lint errors.
-- feature 05: Prisma schema & data layer — schema split: `prisma/schema.prisma` keeps the datasource + generator blocks, `prisma/models/project.prisma` defines the models. `Project` carries `ownerId`, `name`, optional `description`, `ProjectStatus` enum (`DRAFT` / `ARCHIVED`) defaulting to `DRAFT`, optional `canvasJsonPath` blob reference, and `createdAt` / `updatedAt` timestamps; indexes on `ownerId` and `createdAt`. `ProjectCollaborator` carries `projectId` (FK to `Project` with `onDelete: Cascade`), `email`, and `createdAt`; unique constraint on `(projectId, email)`; indexes on `email` and `(projectId, createdAt)`. `lib/prisma.ts` is a cached singleton — in dev, stashed on `globalThis` to survive HMR — that branches on `DATABASE_URL`: `prisma+postgres://` uses Prisma Accelerate (`accelerateUrl` + `withAccelerate()` extension), anything else uses `PrismaPg` driver adapter. Generated client lives in `app/generated/prisma` and is gitignored. Initial migration `20260715063438_init_project_models` applied. `npm run build` passes; no new lint errors (the two pre-existing shadcn `interface` warnings in `components/ui/input.tsx` and `components/ui/textarea.tsx` are unchanged).
-- feature 06: Project API routes — `lib/auth.ts` exposes `getCurrentUserId()` which returns the Clerk `userId` from `auth()` (or `null` if unauthenticated). `app/api/projects/route.ts` handles `GET` (lists the current user's projects, ordered by `createdAt` desc, uses the existing `ownerId` index) and `POST` (creates a project owned by the current user, defaults missing/blank name to `Untitled Project`, id stays on Prisma's existing `@default(cuid())` — no sequential IDs added). `app/api/projects/[projectId]/route.ts` handles `PATCH` (renames, requires a non-empty trimmed name) and `DELETE`. Both dynamic-route handlers resolve `params` as a `Promise` per Next.js 16, look up the project with `findUnique({ select: { id, ownerId }})`, return 404 when missing and 403 when the current user is not the owner, and return 401 for unauthenticated requests. `DELETE` returns 204 with an empty body; `PATCH` returns the updated project. Body parsing is wrapped in `try`/`catch` so a missing or malformed body resolves to a 400 on `PATCH` and to the default name on `POST`. `npm run build` passes; no new lint errors (the two pre-existing shadcn warnings are unchanged).
-- feature 07: Wire editor home to project API — Editor page converted to server component that fetches owned and shared projects server-side via `getAllProjects()` from `lib/projects.ts`. Client-side state and mutations managed by new `useProjectActions` hook in `hooks/use-project-actions.ts` which handles create (generates room ID with slug + suffix, POSTs to API, navigates to new workspace), rename (PATCH to API, updates local state on success), and delete (DELETE to API, refreshes router on success). `CreateProjectDialog` now shows "Room ID" preview instead of "Slug". Sidebar updated to accept `ProjectData` type from the hook instead of `MockProject`. `app/editor/page.tsx` now redirects unauthenticated users to `/sign-in`. Old `useProjectDialogs` hook removed. `npm run build` passes; no new lint errors.
-- feature 08: Editor workspace shell — Built `/editor/[roomId]` workspace shell with server-side access checks. Created `lib/project-access.ts` with `getCurrentUser()` (returns `userId` + primary email from Clerk) and `getProjectWithAccess()` (checks owner or collaborator access). Created `components/editor/access-denied.tsx` with centered layout, lock icon, message, and link back to `/editor`. Created `app/editor/[roomId]/page.tsx` as a server component that checks authentication (redirects to `/sign-in`), validates project access (shows `AccessDenied` for missing/unauthorized projects), and passes data to the client shell. Created `components/editor/workspace-shell.tsx` with full-viewport layout: top navbar showing project name, share button, AI chat toggle, left sidebar (with current room highlighted), central canvas placeholder (dark background + centered message), and right sidebar placeholder for future AI chat. `npm run build` passes; no new lint errors (pre-existing shadcn warnings in `components/ui/input.tsx` and `components/ui/textarea.tsx` unchanged).
-- feature 09: Share dialog — Added Share button to the editor navbar that opens the share dialog. Created `app/api/projects/[projectId]/collaborators/route.ts` for listing and inviting collaborators, and `app/api/projects/[projectId]/collaborators/[collaboratorId]/route.ts` for removing collaborators. Both enforce ownership server-side for invite and remove actions. Created `lib/auth.ts` helpers `getClerkUserByEmail()` and `getClerkUsersByEmails()` to enrich collaborator emails with display name and avatar from Clerk (falls back to showing email only when user not found). Created `components/editor/share-dialog.tsx` with the share dialog UI that shows project link (copyable with "Copied!" feedback), invite form (owners only), collaborator list (shows avatar + name when available from Clerk, email otherwise), and remove button (owners only). Created `hooks/use-share-dialog.ts` to manage share dialog state and API interactions. Updated `components/editor/workspace-shell.tsx` to include the share dialog and wire the Share button. Updated `app/editor/[roomId]/page.tsx` to pass `ownerId` to determine owner vs collaborator view. `npm run build` passes; no new lint errors.
-- feature 10: Liveblocks setup — `liveblocks.config.ts` types the required presence and user metadata. `lib/liveblocks.ts` provides a cached node client and deterministic user-color helper. `POST /api/liveblocks-auth` requires Clerk authentication, accepts Liveblocks’ `{ room }` auth payload (the project ID), verifies project access through `lib/project-access`, creates a private room with per-user write access, and returns an ID-token session with name, avatar, and cursor color. `npm run build` passes.
-- feature 11: Base canvas — `types/canvas.ts` defines the shared canvas node and edge types. `components/editor/canvas.tsx` sets up `LiveblocksProvider`, `RoomProvider`, initial presence, error and suspense loading fallbacks, then passes `useLiveblocksFlow`’s synced state and handlers into a basic React Flow canvas with loose connections, fit view, MiniMap, and dot background. It intentionally contains no custom node or edge rendering, persistence, controls, or AI behavior. `workspace-shell.tsx` renders the canvas for the active project. `npm run build` passes.
-- feature 12: Shape panel — Added a floating pill-shaped toolbar at the bottom-center of the canvas with draggable icon buttons for rectangle, diamond, circle, pill, cylinder, and hexagon. Drag payloads include the shape name and default sizes (rectangles wider, circles square, diamonds larger for labels). Added `dragover` and `drop` handlers to the canvas wrapper that read the payload, convert screen position to canvas coordinates using `screenToFlowPosition`, and create new nodes at that position with empty label, default color, and the dragged shape. Node IDs are generated using the shape name, timestamp, and a counter. Created `CanvasNodeRenderer` component to render nodes as bordered rectangles with centered labels (shape-specific visuals to be added later). `npm run build` passes.
-
-## In Progress
+Keep this file short. Read feature specs + code for detail.
+
+## Phase
+
+- **Feature 17** — completed
+- Next: feature 18 (TBD)
+
+## Stack (quick)
+
+| Layer | Tech |
+| --- | --- |
+| App | Next.js 16 + TypeScript |
+| UI | Tailwind v4 + shadcn (dark-only tokens in `globals.css`) |
+| Auth | Clerk (`proxy.ts` middleware) |
+| DB | Prisma 7 + PostgreSQL (`lib/prisma.ts`, `prisma/models/`) |
+| Canvas | Liveblocks + React Flow (`@liveblocks/react-flow`) |
+| Types | `types/canvas.ts` — `CanvasNode` / `CanvasEdge` |
+
+## Done (features 01–17)
+
+| # | Feature | Key locations |
+| --- | --- | --- |
+| 01 | Design system | `components/ui/*`, `app/globals.css` |
+| 02 | Editor chrome | `editor-navbar.tsx`, `project-sidebar.tsx` |
+| 03 | Auth | Clerk provider, `/sign-in`, `/sign-up`, `proxy.ts` |
+| 04 | Project dialogs | create/rename/delete dialogs, mock → later wired |
+| 05 | Prisma | `Project`, `ProjectCollaborator`, `lib/prisma.ts` |
+| 06 | Project APIs | `app/api/projects/**` |
+| 07 | Editor home wired | `hooks/use-project-actions.ts`, `app/editor/page.tsx` |
+| 08 | Workspace shell | `app/editor/[roomId]`, `workspace-shell.tsx`, access checks |
+| 09 | Share dialog | collaborators APIs, `share-dialog.tsx` |
+| 10 | Liveblocks setup | `liveblocks.config.ts`, `POST /api/liveblocks-auth` |
+| 11 | Base canvas | `canvas.tsx` + `useLiveblocksFlow` |
+| 12 | Shape panel | `shape-panel.tsx` drag → drop create nodes |
+| 13 | Node shapes | `node-shape-visual.tsx`, drag ghost |
+| 14 | Node editing | resize, label edit, 4 handles — `canvas-node.tsx` |
+| 15 | Color toolbar | `node-color-toolbar.tsx`, `NODE_COLORS` |
+| 16 | Edge behavior | `canvas-edge.tsx` — smooth-step, arrows, labels |
+| 17 | Canvas ergonomics | `canvas-controls.tsx`, `hooks/useKeyboardShortcuts.ts` |
+
+## Feature 17 (current)
+
+- Bottom-left pill bar: zoom out / fit / zoom in | undo / redo
+- Zoom → React Flow (`duration: 200`)
+- Undo/redo → Liveblocks `useUndo` / `useRedo` / `useCanUndo` / `useCanRedo`
+- Shortcuts (window): `+`/`=` zoom in, `-` zoom out, `Mod+Z` undo, `Mod+Shift+Z` / `Mod+Y` redo
+- Skip shortcuts in input/textarea/contenteditable
+- MiniMap removed
+
+## Architecture invariants
+
+- RSC by default; `"use client"` only for interactivity / realtime
+- Project metadata in Postgres; canvas graph in Liveblocks room storage
+- Auth + ownership checked on every mutation API
+- UI tokens only (`bg-base`, `text-copy-*`, etc.) — no raw color utilities
+- Canvas types stay shared: `types/canvas.ts`
+- Do not invent features — follow `context/feature-specs/*` and user context
+
+## Open
- None
-## Next Up
-
-- feature 13: TBD
-
-## Open Questions
-
-- Not yet
-
-## Architecture Decisions
-
-- shadcn/ui over Tailwind v4 (CSS-based token config via @theme inline in globals.css, no tailwind.config.js).
-
-- Dark-only theme: all shadcn :root variables set to dark values directly — no .dark class switching.
-
-- Do not modify generated components/ui/* files after shadcn installation.
-
-## Session Notes
+## Context files (read order for new work)
-- Add context needed to resume work in the next session.
-- 2026-07-15: Features 10 and 11 were reconciled with their specifications and verified. The Liveblocks auth endpoint now accepts the SDK's `{ room }` payload from `LiveblocksProvider` (the room is the project ID), fixing the previous `roomId is required` 400 and resulting connection failure. It uses the workspace project-access helper before creating the private room and issuing the ID token. `liveblocks.config.ts` now types the specified presence and user metadata. The base canvas remains limited to the specified Liveblocks-backed React Flow foundation; premature custom node and edge renderers were removed. `npm run build` passes with no Liveblocks-related errors.
-- 2026-07-15: Development startup dependencies were aligned with the implemented Next.js 16 App Router and Prisma 7 code: `next` is pinned to `16.2.10`, `prisma` to `^7.8.0`, and Clerk UI to `^1.25.3`. Added the missing `@liveblocks/node@3.22.0` server SDK required by `app/api/liveblocks-auth`. `npm run build` now completes successfully and `npm run dev` is running on `http://localhost:3000`.
-- 2026-07-14: feature 03 auth pages UI refined to match the reference layout — 50/50 grid, brand panel on the left with logo + headline + three icon-chipped feature rows + footer copyright, form panel on the right with a rounded-3xl `bg-bg-elevated` card housing the Clerk form and a contextual "Don't have an account? / Sign up" link below. Left panel uses `bg-bg-surface` so it differentiates from the base. Brand wordmark is "Ghost dev" (matches the screenshot). Geist Sans is the body default from `--font-geist-sans`; no new fonts added.
-- 2026-07-15: feature 04 implemented exactly per spec. Dialog state is centralized in `useProjectDialogs` (no `useReducer`; small surface, `useState` + `useTransition` is enough). The dialog form pattern (controlled name value, ref-based auto-focus on open) means the hook owns the field state, so the dialog components stay presentational. Slug preview uses the same `slugify` helper that mutation uses, so the preview matches the stored value. Mobile backdrop is a sibling `