Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
96 changes: 5 additions & 91 deletions bun.lock

Large diffs are not rendered by default.

28 changes: 28 additions & 0 deletions packages/review-editor/.migration/checkbox.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# checkbox

2026-07-07, transformation engine (hand-rolled Radix, no shadcn wrapper), clean 1:1 migration.

## Changed

- `components/tour/QAChecklist.tsx:3` — `import * as Checkbox from '@radix-ui/react-checkbox'` → `import { Checkbox } from '@base-ui/react/checkbox'`.
- `components/tour/QAChecklist.tsx:42` — class rewrites: `data-[state=checked]:bg-primary data-[state=checked]:border-primary data-[state=checked]:shadow-none` → `data-checked:*` (Base UI presence attributes).
- Props: `checked` (boolean) and `onCheckedChange={() => onToggle(i)}` are signature-compatible (Base UI adds an `eventDetails` second arg; the handler ignores args). No `asChild` in this file.
- Leftover scan: `grep -n "radix-ui|@radix-ui" components/tour/QAChecklist.tsx` → clean.

## Left alone

- `.tour-checkbox` CSS (index.css:1433, :1454) — element-agnostic (color transitions + reduced-motion), works unchanged.
- `@radix-ui/react-checkbox` stays in package.json until the final cleanup commit (both engines coexist during migration).

## Behavior changes

- Root element changes from `<button>` (Radix) to `<span>` + hidden `<input>` (Base UI). The checkbox sits inside a `<motion.label>`; label-click toggling now flows through the hidden native input instead of a button activation. Keyboard: Space toggles via the hidden input focus, not a button. Flagged for hand-verification; no code patched.

## Verify by hand

In the review tour's QA checklist (guided review → QA step):
1. Click a checkbox directly — it checks, primary background appears. ✓ (automated QA 2026-07-07: `.tour-checkbox`'s `data-unchecked` attribute cleared to `data-checked` after a direct click.)
2. Click the row's label text — the checkbox should also toggle (label→hidden-input association). ✓ (automated QA 2026-07-07: clicking the label's text span toggled a different row's checkbox state.)
3. Tab to a checkbox — focus visible; Space toggles. ✓ (automated QA 2026-07-07: programmatically focused a checkbox then pressed Space — state toggled. Did not exercise literal Tab-key traversal or verify the focus-ring renders — feel-level.)
4. Check an item — text gets line-through; animation still smooth. ✓ (automated QA 2026-07-07: line-through class present on the row text after checking. Animation smoothness not evaluated — feel-level.)
5. "Stop N" links inside a row still navigate without toggling the checkbox.
29 changes: 29 additions & 0 deletions packages/review-editor/.migration/context-menu.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# context-menu

2026-07-07, transformation engine. Core product interaction (file-tree right-click). Typecheck/tests/build green.

## Changed

- `components/FileTreeNode.tsx:2` — `import * as ContextMenu from '@radix-ui/react-context-menu'` → `import { ContextMenu } from '@base-ui/react/context-menu'` (part names verified against `context-menu/index.parts.d.ts`: Base UI re-exports Menu's Positioner/Popup/Item under ContextMenu).
- Trigger `asChild` → `render={<button …/>}`; the file-row button keeps its `onClick` (select file) / `onDoubleClick` handlers — left-click behavior unchanged, right-click opens the menu.
- `Content` → `Portal > Positioner > Popup` (`z-50` on Positioner; no side/align props existed — context menus position at the pointer in both engines); animation classes → `transition-opacity data-starting-style:opacity-0 data-ending-style:opacity-0`.
- Items ×3 (Copy path / Copy filename / Copy full path): `onSelect` → `onClick`; `data-[highlighted]` classes unchanged (same attribute in Base UI).
- Leftover scan: clean.

## Left alone

- `FileRowBits` imports (ViewedControl, StageControl, …) — plain components, no Radix.
- `.file-tree-item` CSS — class-based, element unchanged (still a `<button>` via render).

## Behavior changes

- None identified beyond the shared family notes (collision defaults; exit fade now animates). Items close on click in both engines.

## Verify by hand

1. Right-click a file row → menu opens at the pointer. ✓ (automated QA 2026-07-07: right-click opened `[role="menu"]`; trigger row carries `data-popup-open` while open.)
2. "Copy path" / "Copy filename" / (with a local repo) "Copy full path" put the right strings on the clipboard and close the menu. ✓ (automated QA 2026-07-07: "Copy path" → clipboard read back `src/components/Button.tsx`, menu closed; "Copy filename" → clipboard read back `Button.tsx`. "Copy full path" doesn't render in the demo session — no repoRoot without a local worktree — not exercised.) Re-checked against a real `bun apps/review/server/index.ts main` session in this worktree (2026-07-07): "Copy full path" still does NOT render — right-click menu items were exactly `["Copy path", "Copy filename"]`. Root cause is the same class of gap as dropdown-menu.md/popover.md's not-testable items: `App.tsx`'s `repoRoot` prop (~L3229: `activeWorktreePath ?? agentCwd ?? gitContext?.cwd ?? null`) resolves to `null` because `apps/review/server/index.ts` never passes `gitContext` or `agentCwd` into `startReviewServer` — this is true even in a real, non-demo repo with a real worktree on disk, so "with a local repo" isn't sufficient by itself; the entrypoint has to actually wire local access through. Not exercised (item doesn't exist in this session either).
3. Left-click still selects the file; double-click still opens it; right-click does NOT select the file. ✓ (automated QA 2026-07-07: left-click on a second row made it `.active` with no menu open; right-click left the previously-active file unchanged. Double-click not exercised by automation.)
4. Arrow keys navigate the menu; Enter activates; Escape closes and the file tree keeps keyboard focus. ✓ (automated QA 2026-07-07: Escape closed the menu — confirmed. Arrow-key navigation / Enter-to-activate inside this menu not exercised by automation.)
5. Right-click near the window bottom — menu flips above the pointer.
6. Open menu, left-click elsewhere — menu closes without selecting that row.
28 changes: 28 additions & 0 deletions packages/review-editor/.migration/dropdown-menu.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# dropdown-menu

2026-07-07, transformation engine. Radix DropdownMenu → Base UI Menu (renamed primitive). One file; typecheck clean.

## Changed

- `components/DiffTypePicker.tsx:2` — `import * as DropdownMenu from '@radix-ui/react-dropdown-menu'` → `import { Menu } from '@base-ui/react/menu'`; all parts renamed `DropdownMenu.*` → `Menu.*`.
- Trigger `asChild` → `render={<button …/>}` (spinner/chevron stay as Trigger children).
- `Content` → `Portal > Positioner > Popup`: `side`/`align`/`sideOffset` and `z-50` on Positioner; classes `min-w-[var(--radix-dropdown-menu-trigger-width)]` → `min-w-[var(--anchor-width)]`, `origin-[var(--radix-dropdown-menu-content-transform-origin)]` → `origin-[var(--transform-origin)]`, animate-in/out → `transition-opacity data-starting-style:opacity-0 data-ending-style:opacity-0`.
- Item `onSelect={() => onSelect(opt.id)}` → `onClick` (Base UI rename); `data-[highlighted]:bg-muted` unchanged (same attribute both engines). Items close the menu on click in both engines (Base `closeOnClick` defaults true).
- Leftover scan: clean.

## Left alone

- The `Tooltip` inside each item comes from `@plannotator/ui` (Radix-based, being migrated in parallel by another agent) — import and `delayDuration` prop untouched; the seam is that package's wrapper API, not a raw Radix surface. The `stopPropagation` handlers on the info icon still suppress item activation with `onClick` semantics.

## Behavior changes

- **Focus loop default flips**: Radix `loop` defaulted to false; Base UI `loopFocus` (on Root) defaults to true — ArrowDown past the last item now wraps to the first. Left at the Base UI default (idiomatic target), flagged here.
- Typeahead: both engines support it; Base matches on item text content (no `textValue` was used).

## Verify by hand

1. Open the diff-type dropdown (review header). Arrow keys walk options; past-the-end wraps (new). Not testable via automated QA in either the demo (2026-07-07) or a real `bun apps/review/server/index.ts main` session (2026-07-07): `apps/review/server/index.ts` never constructs or passes a `gitContext`/`agentCwd` to `startReviewServer` (packages/server/review.ts:211 `hasLocalAccess = !!gitContext` stays `false` for that entrypoint), so `App.tsx`'s `gitContext` state is never populated and `FileTree.tsx:409-410`'s render guard `(worktrees?.length>0)||(diffOptions?.length>0)` is false — the whole DiffTypePicker/WorktreePicker/BaseBranchPicker row never mounts. Confirmed empirically against a real session with 88 real worktrees on disk: no matching trigger in the DOM. (`apps/hook/server/index.ts` does wire `gitContext` via `prepareLocalReviewDiff` — a different entrypoint from the one this QA pass was asked to exercise.)
2. Type the first letters of an option — typeahead highlights it. Not testable, same reason as #1.
3. Enter/click selects, menu closes, diff reloads. Not testable, same reason as #1.
4. Hover the ⓘ icon — tooltip shows without closing the menu; clicking ⓘ does not select the item. Not testable, same reason as #1.
5. Escape closes and returns focus to the trigger; menu min-width still matches the trigger width. Not testable, same reason as #1.
39 changes: 39 additions & 0 deletions packages/review-editor/.migration/popover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# popover

2026-07-07, transformation engine (7 hand-rolled Radix popover files, no shadcn wrappers). All migrated; typecheck/tests/build green.

## Changed

All files: `import * as Popover from '@radix-ui/react-popover'` → `import { Popover } from '@base-ui/react/popover'`; `Trigger asChild` → `Trigger render={<button …/>}` (trigger children stay as Trigger children); `Portal > Content` → `Portal > Positioner > Popup` with positioning props (`side`, `align`, `sideOffset`) and `z-50`/`z-[100]` moved to Positioner; class rewrites `origin-[var(--radix-popover-content-transform-origin)]` → `origin-[var(--transform-origin)]` and `data-[state=open]:animate-in …` → `transition-opacity data-starting-style:opacity-0 data-ending-style:opacity-0`.

- `components/SemanticFileBadge.tsx` — also: `onOpenAutoFocus={(e) => e.preventDefault()}` → `initialFocus={false}` on Popup; hover open/close timers unchanged (kept manual timers rather than Base UI's `openOnHover` to preserve exact behavior); `index.css:480` trigger selector `.semantic-file-badge[data-state="open"]` → `[data-popup-open]`.
- `components/EvoLogPicker.tsx` — plain transform, no focus handling.
- `components/DiffOptionsPopover.tsx` — trigger classes `data-[state=open]:*` → `data-popup-open:*` (Base UI trigger presence attribute).
- `components/WorktreePicker.tsx` — conditional `onOpenAutoFocus` (focus search input only when rendered) → `initialFocus={() => searchRef.current}` (function form: returning an element focuses it; returning null falls back to Base UI's default focus. NOT `?? undefined` — undefined means "do nothing", which would strand focus on the trigger for short lists. Semantics per PopoverPopup.d.ts:44-47 JSDoc, caught in self-review).
- `components/StackedPRLabel.tsx` — plain transform; trigger chevron rotation driven by controlled `open` state, untouched.
- `components/BaseBranchPicker.tsx` — unconditional `onOpenAutoFocus` + manual focus → `initialFocus={() => searchRef.current}` (same null-fallback semantics as WorktreePicker).
- `components/PRCommentsTab.tsx` — trigger `data-[state=open]:*` → `data-popup-open:*`; popup transform as above.

Leftover scan: `grep -rn "radix-ui|@radix-ui" <all 7 files>` → clean. `--radix-*` CSS vars: none remain in these files.

## Left alone

- `.popover-enter` CSS keyframe class (index.css) — mount animation, plays on mount for Base UI popups the same way; kept where used (SemanticFileBadge).
- `@radix-ui/react-popover` stays in package.json until final cleanup.
- Imports from `@plannotator/ui` in DiffTypePicker/DiffOptionsPopover/StackedPRLabel/PRCommentsTab — that package is being migrated in parallel by another agent; its seam is untouched.

## Behavior changes

- **Collision defaults differ**: Base UI `collisionPadding` defaults to 5 (Radix 0/10 depending on prop) and `collisionBoundary` defaults to clipping ancestors (Radix: viewport). Near viewport/scroll-container edges, popovers may flip/shift at slightly different thresholds. Not patched — idiomatic Base UI target.
- **Enter/exit animation idiom changed**: fade now runs via CSS transitions (`data-starting-style`/`data-ending-style`) instead of tw-animate keyframes. Exit fade now actually animates (Radix version unmounted immediately unless forceMount was used); visual difference is a subtle ~150ms fade-out where there was none.
- **`onOpenChange` extra args**: Base UI passes `(open, eventDetails)`; all call sites here pass state setters or `(v) => …` lambdas, which ignore the extra argument. No functional change.

## Verify by hand

1. File header "sem · N" badge: hover opens the semantic popover, moving into the popover keeps it open, leaving both closes after ~140ms; clicking a row navigates and closes; badge highlights while open. ✓ (automated QA vs real session 2026-07-07: hovered `.semantic-file-badge` → `[role="dialog"]` opened and trigger gained `data-popup-open`; moving the pointer into the open dialog kept it visible; moving out closed it in ~157ms and `data-popup-open` was removed from the trigger. Clicking a row to navigate not exercised.)
2. Worktree picker (review header): open with >3 worktrees → search input is focused; with ≤3 → first row focused; arrow keys don't leak to the file tree; Escape closes and returns focus to the trigger. Not testable via automated QA in either the demo (2026-07-07) or a real `bun apps/review/server/index.ts main` session (2026-07-07) — despite this worktree (`feat-review-editor-base-ui`) sitting in a repo with 88 real git worktrees on disk, the trigger never mounts: `apps/review/server/index.ts` never passes `gitContext` to `startReviewServer` (packages/server/review.ts:211), so `App.tsx` never receives `worktrees`/`availableBranches` and `FileTree.tsx:409-410`'s render guard is false. The `>3` vs `≤3` split from the prior QA pass was never reached; the actual blocker is upstream of that split (this entrypoint never sets gitContext at all, not a matter of worktree count). Confirmed empirically: no title-matching trigger in the real session's DOM.
3. Base branch picker: open → search focused immediately; type a SHA → Enter selects; Escape closes. Not testable, same root cause as #2 (`availableBranches` also comes from the unset `gitContext`) — confirmed empirically, no matching trigger in the real session's DOM.
4. Diff options gear: opens aligned right, no layout jump; trigger stays highlighted while open. ✓ (automated QA 2026-07-07: click opens `[role="dialog"]`; trigger carries `data-popup-open` while open; clicking outside closes it. "no layout jump" not measured — feel-level.)
5. Stacked-PR label: popover opens under the label; chevron rotates; switching scope closes it.
6. PR comments Filters button: opens; toggling switches doesn't close the popover; button shows active state while open.
7. All of the above near the bottom/right edge of the window — verify flip/shift feels right (collision defaults changed).
44 changes: 44 additions & 0 deletions packages/review-editor/.migration/project.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# project — packages/review-editor Radix → Base UI

2026-07-07, whole-package migration (internal package, no npm consumers). Complete: zero `@radix-ui/*` deps or references remain.

## Dependency swap

- Added: `@base-ui/react ^1.6.0` (dependency), `@types/react` / `@types/react-dom` `^19.2.0` (devDependencies, new — see gates note).
- Removed (7): `@radix-ui/react-checkbox`, `-collapsible`, `-context-menu`, `-dialog`, `-dropdown-menu`, `-popover`, `-tooltip`.
- **Never imported anywhere: `-collapsible`, `-dialog`, `-tooltip`** — dead dependencies removed without a code change. (The package's dialogs/tooltips come from `@plannotator/ui` or hand-rolled CSS, not from these deps.)
- `bun.lock` regenerated. Note: the committed lock was already stale vs several package.jsons at HEAD (any install rewrites it); the first migration commit absorbed that pre-existing drift.

## Components migrated

| Component | Files | Report |
|---|---|---|
| checkbox | tour/QAChecklist.tsx | checkbox.md |
| popover | SemanticFileBadge, EvoLogPicker, DiffOptionsPopover, WorktreePicker, StackedPRLabel, BaseBranchPicker, PRCommentsTab (+ index.css trigger selector) | popover.md |
| dropdown-menu → Menu | DiffTypePicker.tsx | dropdown-menu.md |
| context-menu | FileTreeNode.tsx | context-menu.md |

## App-code sweep (beyond imports)

- `FileTree.tsx` and `SectionsPanel.tsx`: keyboard-nav guards used `closest('[data-radix-popper-content-wrapper]')` — a Radix portal attribute Base UI never renders. Dead post-migration; removed. Role selectors (`[role="menu"], [role="dialog"], [role="listbox"]`) cover all Base UI popups (Menu.Popup role="menu" — verified; Popover.Popup role="dialog" — verified in PopoverPopup.js:102).
- `--radix-*` CSS vars: all rewritten (`--transform-origin`, `--anchor-width`).
- `data-[state=*]` classes: all rewritten to presence attributes (`data-checked:`, `data-popup-open:`); one plain-CSS selector updated (index.css `.semantic-file-badge[data-state="open"]` → `[data-popup-open]`).

## Gates (all green at final commit)

- Root `bun run typecheck` (includes packages/ui strict-consumer — untouched by this migration and still green).
- `bun test`: 1955 tests, 0 fail.
- `bun run build:review`: green.
- New package-local `tsc -p packages/review-editor/tsconfig.json` (gate added by this migration; the package previously had NO typecheck coverage — it's absent from the root chain and `tsc -p apps/review` is pre-broken for unrelated reasons). 53 pre-existing errors recorded (import.meta.env typing, response-type mismatches) — untouched; zero errors in every migrated file.

## Boundary

`packages/ui` is being migrated in parallel by another agent: not touched (verified via git status), and imports of `@plannotator/ui` from this package (Tooltip in DiffTypePicker, others in DiffOptionsPopover/StackedPRLabel/PRCommentsTab) were left exactly as-is — that seam is the other migration's contract.

**Coordination note for the ui migration**: three call sites here pass Radix-vocabulary props through @plannotator/ui's Tooltip wrapper API — `delayDuration` (FileRowBits.tsx:21, DiffTypePicker.tsx:106) and `delayDuration`/`skipDelayDuration` on TooltipProvider (App.tsx:2651). If the ui package's migration renames its wrapper props (Base UI uses `delay`), these call sites need a one-line consumer sweep; if the wrapper keeps its prop names and maps internally, nothing to do. Check against the ui package's 0.23.0 HANDOFF notes.

## Deliberately left for a human to eyeball

Everything compiles and tests pass, but the four hand-verify checklists (per-component reports) cover feel-level behavior no test asserts: hover-popover timing on the sem badge, focus landing in picker search inputs, file-tree right-click + j/k nav interplay, edge-of-viewport flip behavior under Base UI's different collision defaults, and the new ~150ms exit fades.

Derived status: 0 wrappers/files remain on Radix in packages/review-editor.
Loading