184 lines
8.9 KiB
Markdown
184 lines
8.9 KiB
Markdown
## ADDED Requirements
|
|
|
|
### Requirement: Theme toggle in sidebar
|
|
The UI shell SHALL include a theme toggle button in the sidebar. The button SHALL be positioned at the bottom of the sidebar, visually separated from the tool buttons.
|
|
|
|
#### Scenario: Toggle button renders
|
|
- **WHEN** the application loads
|
|
- **THEN** a theme toggle button is visible at the bottom of the sidebar
|
|
|
|
#### Scenario: Toggle button displays correct icon
|
|
- **WHEN** the current theme mode is "system"
|
|
- **THEN** the button shows a monitor icon (lucide-react `Monitor`)
|
|
- **WHEN** the current theme mode is "light"
|
|
- **THEN** the button shows a sun icon (lucide-react `Sun`)
|
|
- **WHEN** the current theme mode is "dark"
|
|
- **THEN** the button shows a moon icon (lucide-react `Moon`)
|
|
|
|
#### Scenario: Toggle button has tooltip
|
|
- **WHEN** user hovers over the theme toggle button
|
|
- **THEN** a tooltip displays the current mode name (e.g., "Theme: System", "Theme: Light", "Theme: Dark")
|
|
|
|
### Requirement: Accessibility on interactive elements
|
|
All interactive elements (buttons, dropdowns, modals) SHALL have `aria-label` attributes describing their function. Toggle buttons SHALL use `aria-pressed`. The keyboard shortcuts modal SHALL have `role="dialog"` and `aria-modal="true"`.
|
|
|
|
#### Scenario: Button has aria-label
|
|
- **WHEN** a toolbar button renders
|
|
- **THEN** it has an `aria-label` attribute describing its action (e.g., "Draw span annotation")
|
|
|
|
#### Scenario: Modal has dialog role
|
|
- **WHEN** the keyboard shortcuts modal opens
|
|
- **THEN** it has `role="dialog"` and `aria-modal="true"`
|
|
|
|
### Requirement: Focus trapping in modals
|
|
Modal dialogs (keyboard shortcuts, confirmation dialogs) SHALL trap focus within the modal while open. Tab and Shift+Tab SHALL cycle through focusable elements within the modal.
|
|
|
|
#### Scenario: Focus trapped in modal
|
|
- **WHEN** a modal is open and the user presses Tab
|
|
- **THEN** focus cycles through focusable elements within the modal only
|
|
|
|
### Requirement: Dark theme on settings pages
|
|
The annotation-types and span-label-types settings pages SHALL use theme-aware CSS variables instead of hardcoded light colors. They SHALL render correctly in both light and dark themes.
|
|
|
|
#### Scenario: Settings page in dark mode
|
|
- **WHEN** the theme is set to dark and user navigates to annotation-types page
|
|
- **THEN** the page renders with dark background and light text (no hardcoded white backgrounds)
|
|
|
|
### Requirement: next/font for Google Fonts
|
|
The application SHALL use `next/font/google` to load Google Fonts instead of CSS `@import` in `globals.css`. This prevents render-blocking font loading.
|
|
|
|
#### Scenario: Font loaded via next/font
|
|
- **WHEN** the application loads
|
|
- **THEN** Google Fonts are loaded via `next/font/google` (not via CSS `@import url()` in globals.css)
|
|
|
|
### Requirement: Confidence slider debounce
|
|
The confidence threshold slider in PredictionPanel SHALL debounce chart re-renders. The slider SHALL update the displayed value immediately but only trigger chart updates after 150ms of inactivity (or on `onPointerUp`).
|
|
|
|
#### Scenario: Dragging slider does not re-render chart per pixel
|
|
- **WHEN** the user drags the confidence slider
|
|
- **THEN** the chart re-renders at most once every 150ms (not on every pixel movement)
|
|
|
|
### Requirement: ChartSelector closes on outside click
|
|
The custom chart selector dropdown SHALL close when the user clicks outside of it.
|
|
|
|
#### Scenario: Click outside closes dropdown
|
|
- **WHEN** the chart selector dropdown is open and the user clicks elsewhere on the page
|
|
- **THEN** the dropdown closes
|
|
|
|
### Requirement: Dead code removal
|
|
The following dead code SHALL be removed:
|
|
- `src/lib/db/migrate.ts` (SQLite migration code)
|
|
- `get_db_session()` in `services/ml/app/db.py` (unused session leak)
|
|
- Dead filter code (TODO comment, no-op) in `page.tsx`
|
|
- Dead `inference*` package reference in `pyproject.toml`
|
|
|
|
#### Scenario: No dead migration code
|
|
- **WHEN** `src/lib/db/migrate.ts` is searched for
|
|
- **THEN** the file does not exist
|
|
|
|
#### Scenario: No unused db session function
|
|
- **WHEN** `services/ml/app/db.py` is inspected
|
|
- **THEN** `get_db_session()` function is absent
|
|
|
|
### Requirement: Deprecated Python API replacements
|
|
The ML service SHALL replace deprecated Python APIs:
|
|
- `@app.on_event("startup")` replaced with lifespan pattern
|
|
- `declarative_base()` replaced with `class Base(DeclarativeBase)`
|
|
- `datetime.utcnow()` replaced with `datetime.now(datetime.UTC)`
|
|
|
|
#### Scenario: No deprecated startup event
|
|
- **WHEN** `services/ml/app/main.py` is inspected
|
|
- **THEN** startup logic uses the FastAPI lifespan pattern (not `@app.on_event`)
|
|
|
|
#### Scenario: No deprecated utcnow
|
|
- **WHEN** `datetime.utcnow()` is searched for in the ML service
|
|
- **THEN** zero matches are found (all replaced with `datetime.now(datetime.UTC)`)
|
|
|
|
### Requirement: Unused import cleanup
|
|
Components SHALL not have unused imports. Specifically, `Toolbox.tsx` SHALL remove unused `TrendingUp` and `ChevronUp` imports.
|
|
|
|
#### Scenario: No unused imports in Toolbox
|
|
- **WHEN** `Toolbox.tsx` is inspected
|
|
- **THEN** only imported symbols that are used in the component are present
|
|
|
|
### Requirement: Tooltip component functional or removed
|
|
The `ui/tooltip.tsx` component SHALL either be implemented using Radix Tooltip or removed if unused.
|
|
|
|
#### Scenario: Tooltip is functional
|
|
- **WHEN** the Tooltip component is used
|
|
- **THEN** it renders an actual tooltip on hover (not a no-op passthrough)
|
|
|
|
### Requirement: SpanAnnotationList confidence check for zero
|
|
The confidence display in SpanAnnotationList SHALL use `!= null` instead of a falsy check, so that a confidence value of `0` is correctly displayed.
|
|
|
|
#### Scenario: Confidence zero displayed
|
|
- **WHEN** a span annotation has confidence value `0`
|
|
- **THEN** the list displays "0%" (not hidden as if confidence is absent)
|
|
|
|
## MODIFIED Requirements (user-accounts)
|
|
|
|
### Requirement: Theme toggle in sidebar
|
|
The UI shell SHALL include a theme toggle button in the sidebar. The button SHALL be positioned at the bottom of the sidebar, visually separated from the tool buttons.
|
|
|
|
#### Scenario: Toggle button renders
|
|
- **WHEN** the application loads at `/app`
|
|
- **THEN** a theme toggle button is visible at the bottom of the sidebar
|
|
|
|
#### Scenario: Toggle button displays correct icon
|
|
- **WHEN** the current theme mode is "system"
|
|
- **THEN** the button shows a monitor icon (lucide-react `Monitor`)
|
|
- **WHEN** the current theme mode is "light"
|
|
- **THEN** the button shows a sun icon (lucide-react `Sun`)
|
|
- **WHEN** the current theme mode is "dark"
|
|
- **THEN** the button shows a moon icon (lucide-react `Moon`)
|
|
|
|
#### Scenario: Toggle button has tooltip
|
|
- **WHEN** user hovers over the theme toggle button
|
|
- **THEN** a tooltip displays the current mode name (e.g., "Theme: System", "Theme: Light", "Theme: Dark")
|
|
|
|
## ADDED Requirements (user-accounts)
|
|
|
|
### Requirement: App workspace route at /app
|
|
The main workspace page SHALL be served at `/app` (route `src/app/app/page.tsx`). The existing `src/app/page.tsx` content (chart, annotations, toolbox, panels) SHALL move to this new route. The `/app` route SHALL be protected by the auth proxy.
|
|
|
|
#### Scenario: Workspace at /app
|
|
- **WHEN** an authenticated user navigates to `/app`
|
|
- **THEN** the full workspace renders (chart, toolbox, panels — same as current `/`)
|
|
|
|
#### Scenario: Unauthenticated redirect
|
|
- **WHEN** an unauthenticated user navigates to `/app`
|
|
- **THEN** they are redirected to `/login`
|
|
|
|
### Requirement: App layout with user menu
|
|
The `/app` layout SHALL include a top navigation bar with the CandleAnnotator logo (linking to `/app`), and a user menu on the right side. The user menu SHALL show the user's name/email and provide links to Settings and Sign Out.
|
|
|
|
#### Scenario: User menu displays identity
|
|
- **WHEN** an authenticated user views the app
|
|
- **THEN** the top nav shows the user's name or email with an avatar/initial
|
|
|
|
#### Scenario: User menu dropdown
|
|
- **WHEN** a user clicks the user menu
|
|
- **THEN** a dropdown shows: "Settings" (links to `/app/settings`) and "Sign Out" (calls `signOut()`)
|
|
|
|
#### Scenario: Sign out
|
|
- **WHEN** a user clicks "Sign Out" in the user menu
|
|
- **THEN** the session is destroyed and the user is redirected to `/login`
|
|
|
|
### Requirement: Settings link in sidebar
|
|
The sidebar SHALL include a settings gear icon button that navigates to `/app/settings`.
|
|
|
|
#### Scenario: Settings button renders
|
|
- **WHEN** the app workspace loads
|
|
- **THEN** a settings icon button is visible in the sidebar (near the theme toggle)
|
|
|
|
#### Scenario: Navigate to settings
|
|
- **WHEN** a user clicks the settings icon
|
|
- **THEN** they are navigated to `/app/settings`
|
|
|
|
### Requirement: Public page layout
|
|
Public pages (`/`, `/login`, `/register`) SHALL use a separate layout (`src/app/(public)/layout.tsx`) without the workspace sidebar, toolbox, or user menu. They SHALL share the same root layout (fonts, ThemeProvider).
|
|
|
|
#### Scenario: Public layout structure
|
|
- **WHEN** a user views a public page
|
|
- **THEN** the page renders without sidebar, toolbox, or user menu
|
|
- **AND** the root layout fonts and theme provider are still active
|