Summary
The annotation panel is fixed to the right side of the viewport (position: fixed; right: 0; width: 380px; height: 100vh). This obscures any page content on the right-hand side, making it impossible to review or annotate elements positioned there.
Add the ability to dock the panel to the left, right, or bottom edge, with the preference persisted across sessions.
Problem
When the panel is open, 380px of the right side of the page is completely hidden. Reviewers cannot see, select, or annotate content in that region. This is particularly problematic for:
- Right-aligned layouts (sidebars, cards, navigation)
- Wide content that extends to the viewport edge
- Any page where the area of interest happens to be on the right
Simply toggling between left and right does not fully solve this — if content spans the full width of the page, it is obscured regardless of which side the panel is on.
Proposed Solution: Left / Right / Bottom Dock Toggle
Add a dock-position control to the panel header that cycles between left, right, and bottom edges. This follows established patterns from browser DevTools, VS Code, and other developer tools.
Dock positions
| Position |
Panel dimensions |
Panel slide direction |
Use case |
| Right (default, current) |
width: 380px; height: 100vh |
Slides from right |
Content of interest is on the left |
| Left |
width: 380px; height: 100vh |
Slides from left |
Content of interest is on the right |
| Bottom |
width: 100vw; height: ~250px |
Slides from bottom |
Content spans full width; reviewing horizontal layouts |
Why bottom works without a layout rewrite
The panel is already `display: flex; flex-direction: column` — header, tabs, scrollable content area, shortcuts footer. In a bottom-docked layout, only the outer dimensions change (`width: 100vw; height: 250px` instead of `width: 380px; height: 100vh`). The content area scrolls vertically within whatever height is available, so annotation cards work the same way — just in a shorter scroll region.
What about top docking?
Top docking was considered but is not proposed for the initial implementation:
- Top of viewport is conventionally where page navigation lives — a panel there feels out of place
- It pushes page content downward, which is disorienting for reviewers
- Bottom is the established "tools/panels" location (browser DevTools, VS Code terminal, IDE consoles)
- If the infrastructure supports left/right/bottom, adding top later is trivial (same CSS pattern, just `top: 0` instead of `bottom: 0`)
What changes per dock position
| Element |
Right-docked (current) |
Left-docked |
Bottom-docked |
| Panel |
`right: 0; width: 380px; height: 100vh; translateX(100%)` |
`left: 0; width: 380px; height: 100vh; translateX(-100%)` |
`bottom: 0; width: 100vw; height: 250px; translateY(100%)` |
| Panel border |
`border-left: 1px solid #333` |
`border-right: 1px solid #333` |
`border-top: 1px solid #333` |
| FAB |
`bottom: 24px; right: 24px` |
`bottom: 24px; left: 24px` |
`bottom: 274px; right: 24px` (above panel) |
| Toast |
`bottom: 80px; right: 24px` |
`bottom: 80px; left: 24px` |
`bottom: 330px; right: 24px` (above panel) |
| Popup |
Clamps right boundary to panel left edge |
Clamps left boundary to panel right edge |
Clamps bottom boundary to panel top edge |
| Mobile (≤480px) |
Full width (current) |
Full width |
Full height (same as side-docked) |
Implementation notes
-
Toggle control: Small icon button(s) in the panel header — e.g., dock-left / dock-right / dock-bottom icons. Could be a single cycle button or a small icon group. A keyboard shortcut would be useful too (e.g., `Ctrl+Shift+,` to complement the existing `Ctrl+Shift+.` panel toggle).
-
Persistence: Store preference in `localStorage` under a namespaced key (e.g., `review-loop:dock-position`). Default to right (current behaviour).
-
CSS approach: Add a `data-dock="left" | "right" | "bottom"` attribute to the host element. Use attribute selectors for positioning variants:
```css
/* Left */
:host([data-dock="left"]) .air-panel { left: 0; right: auto; transform: translateX(-100%); border-left: none; border-right: 1px solid #333; }
:host([data-dock="left"]) .air-panel.open { transform: translateX(0); }
:host([data-dock="left"]) .air-fab { left: 24px; right: auto; }
/* Bottom */
:host([data-dock="bottom"]) .air-panel { bottom: 0; top: auto; right: 0; width: 100%; height: 250px; transform: translateY(100%); border-left: none; border-top: 1px solid #333; }
:host([data-dock="bottom"]) .air-panel.open { transform: translateY(0); }
:host([data-dock="bottom"]) .air-fab { bottom: 274px; }
```
-
Popup avoidance: The popup positioning logic in `popup.ts` already checks the panel position via `panelEl.offsetLeft`. For left-docked, the left boundary clamps to `panelEl.offsetLeft + panelEl.offsetWidth`. For bottom-docked, the bottom boundary clamps to `panelEl.offsetTop`.
-
Empty state arrow: The "←" arrow hint in the empty panel (`air-panel__empty-arrow`) should flip direction based on dock position (← for right, → for left, ↑ for bottom).
-
Shadow DOM: All positioning is within the shadow root using `position: fixed`, so the dock attribute on the host element cleanly controls everything.
Scope
- Panel, FAB, and toast positioning adapt based on dock position
- Popup boundary logic adapts to panel position (partially already works)
- Preference persisted in localStorage
- Keyboard shortcut to cycle dock position
- Empty state arrow direction adapts
Alternatives Considered
| Approach |
Verdict |
| Left/right only |
Insufficient — if content spans full width, left/right just moves the obstruction |
| Draggable floating panel |
Too complex — risk of panel in awkward positions, needs resize logic, poor mobile UX, z-index nightmares |
| Push content mode (panel pushes page aside) |
Breaks page layouts, especially flex/grid. Too intrusive for a dev overlay. |
| Top docking |
Unusual location for a tools panel. Can be added trivially later if the left/right/bottom infrastructure exists. |
| Collapsible/peek mode |
Adds clicks to the workflow. Does not solve the problem when the panel is expanded. Could be a complementary future feature. |
| Panel width/height resize |
Reduces but does not eliminate the problem. Could be added as a follow-up enhancement. |
Affected Files
- `src/client/styles.ts` — panel, FAB, toast positioning (attribute-selector variants for each dock position)
- `src/client/ui/panel.ts` — dock toggle button, localStorage read/write, host attribute management, empty state arrow direction
- `src/client/ui/fab.ts` — read dock position for positioning (or handled purely via CSS)
- `src/client/ui/popup.ts` — adapt boundary logic for left-docked and bottom-docked panel
- `src/client/ui/host.ts` — initialise `data-dock` attribute from localStorage
- Tests for dock toggle behaviour and position-specific rendering
Summary
The annotation panel is fixed to the right side of the viewport (
position: fixed; right: 0; width: 380px; height: 100vh). This obscures any page content on the right-hand side, making it impossible to review or annotate elements positioned there.Add the ability to dock the panel to the left, right, or bottom edge, with the preference persisted across sessions.
Problem
When the panel is open, 380px of the right side of the page is completely hidden. Reviewers cannot see, select, or annotate content in that region. This is particularly problematic for:
Simply toggling between left and right does not fully solve this — if content spans the full width of the page, it is obscured regardless of which side the panel is on.
Proposed Solution: Left / Right / Bottom Dock Toggle
Add a dock-position control to the panel header that cycles between left, right, and bottom edges. This follows established patterns from browser DevTools, VS Code, and other developer tools.
Dock positions
width: 380px; height: 100vhwidth: 380px; height: 100vhwidth: 100vw; height: ~250pxWhy bottom works without a layout rewrite
The panel is already `display: flex; flex-direction: column` — header, tabs, scrollable content area, shortcuts footer. In a bottom-docked layout, only the outer dimensions change (`width: 100vw; height: 250px` instead of `width: 380px; height: 100vh`). The content area scrolls vertically within whatever height is available, so annotation cards work the same way — just in a shorter scroll region.
What about top docking?
Top docking was considered but is not proposed for the initial implementation:
What changes per dock position
Implementation notes
Toggle control: Small icon button(s) in the panel header — e.g., dock-left / dock-right / dock-bottom icons. Could be a single cycle button or a small icon group. A keyboard shortcut would be useful too (e.g., `Ctrl+Shift+,` to complement the existing `Ctrl+Shift+.` panel toggle).
Persistence: Store preference in `localStorage` under a namespaced key (e.g., `review-loop:dock-position`). Default to right (current behaviour).
CSS approach: Add a `data-dock="left" | "right" | "bottom"` attribute to the host element. Use attribute selectors for positioning variants:
```css
/* Left */
:host([data-dock="left"]) .air-panel { left: 0; right: auto; transform: translateX(-100%); border-left: none; border-right: 1px solid #333; }
:host([data-dock="left"]) .air-panel.open { transform: translateX(0); }
:host([data-dock="left"]) .air-fab { left: 24px; right: auto; }
/* Bottom */
:host([data-dock="bottom"]) .air-panel { bottom: 0; top: auto; right: 0; width: 100%; height: 250px; transform: translateY(100%); border-left: none; border-top: 1px solid #333; }
:host([data-dock="bottom"]) .air-panel.open { transform: translateY(0); }
:host([data-dock="bottom"]) .air-fab { bottom: 274px; }
```
Popup avoidance: The popup positioning logic in `popup.ts` already checks the panel position via `panelEl.offsetLeft`. For left-docked, the left boundary clamps to `panelEl.offsetLeft + panelEl.offsetWidth`. For bottom-docked, the bottom boundary clamps to `panelEl.offsetTop`.
Empty state arrow: The "←" arrow hint in the empty panel (`air-panel__empty-arrow`) should flip direction based on dock position (← for right, → for left, ↑ for bottom).
Shadow DOM: All positioning is within the shadow root using `position: fixed`, so the dock attribute on the host element cleanly controls everything.
Scope
Alternatives Considered
Affected Files