Updated documentation; added PERFORMANCE-HEALTH-CHECK.md
This commit is contained in:
66
CLAUDE.md
66
CLAUDE.md
@@ -91,6 +91,20 @@ When adding new frontend pages or components, use "ResolutionFlow" for any user-
|
||||
- Purple gradient theme, custom fonts (Plus Jakarta Sans, Inter, Outfit)
|
||||
- Custom SVG logo in header and auth pages
|
||||
- Updated favicon and browser tab title
|
||||
- **Token Refresh Fix:**
|
||||
- Silent refresh with single-flight queue (prevents concurrent 401 race conditions)
|
||||
- Backend `get_refresh_token_payload` dependency extracts refresh token from Authorization header
|
||||
- Frontend Axios interceptor queues failed requests during refresh, retries after success
|
||||
- Auth store synced after silent refresh via `setTokens` action
|
||||
- **Session Scratchpad (Floating Overlay):**
|
||||
- Fixed-position overlay panel (420px wide, 55vh tall) on right edge
|
||||
- Floating button when collapsed, slide-in panel when expanded
|
||||
- Ctrl+/ keyboard shortcut to toggle
|
||||
- Auto-save with 1s debounce, markdown preview, localStorage persistence
|
||||
- Main content adjusts width via padding transition when panel opens
|
||||
- **Global Thin Scrollbar Styling:**
|
||||
- 6px thin scrollbars site-wide (Firefox `scrollbar-width: thin` + WebKit pseudo-elements)
|
||||
- Theme-aware colors using CSS variables (`--border`, `--muted-foreground`)
|
||||
|
||||
### What's In Progress
|
||||
|
||||
@@ -180,7 +194,7 @@ patherly/
|
||||
│ │ ├── router.tsx
|
||||
│ │ ├── assets/brand/ # Brand logos (SVG)
|
||||
│ │ ├── api/ # Axios API client
|
||||
│ │ │ ├── client.ts # Axios instance with interceptors
|
||||
│ │ │ ├── client.ts # Axios instance with refresh queue interceptor
|
||||
│ │ │ ├── auth.ts
|
||||
│ │ │ ├── trees.ts
|
||||
│ │ │ └── sessions.ts
|
||||
@@ -196,7 +210,7 @@ patherly/
|
||||
│ │ │ ├── tree-editor/ # Tree editor components
|
||||
│ │ │ ├── tree-preview/ # Visual tree preview
|
||||
│ │ │ ├── step-library/ # Step library browser, forms, modals
|
||||
│ │ │ ├── session/ # Session modals, scratchpad sidebar
|
||||
│ │ │ ├── session/ # Session modals, scratchpad floating overlay
|
||||
│ │ │ └── ui/ # MarkdownContent
|
||||
│ │ ├── pages/
|
||||
│ │ │ ├── LoginPage.tsx
|
||||
@@ -508,6 +522,33 @@ Key state: `pendingStep`, `pendingContinuationNodeId`, `customBranchMode`, `bran
|
||||
Custom steps are stored in session JSONB (`custom_steps` field) and referenced by UUID in `pathTaken`.
|
||||
`findNode()` only searches tree structure -- use `findCustomStep()` for custom step UUIDs.
|
||||
|
||||
### Token Refresh: Match Frontend/Backend Contract
|
||||
|
||||
The refresh endpoint must accept tokens the same way the frontend sends them.
|
||||
|
||||
```python
|
||||
# WRONG - Expects bare string, but frontend sends Authorization header
|
||||
@router.post("/refresh")
|
||||
async def refresh_token(refresh_token: str):
|
||||
payload = decode_token(refresh_token)
|
||||
|
||||
# CORRECT - Use dependency that reads from Authorization header
|
||||
@router.post("/refresh")
|
||||
async def refresh_token(
|
||||
payload: Annotated[dict, Depends(get_refresh_token_payload)],
|
||||
):
|
||||
```
|
||||
|
||||
The frontend Axios interceptor sends `Authorization: Bearer <refresh_token>`. The backend must extract it from the header, not expect it as a query/body parameter.
|
||||
|
||||
### CORS Errors Can Mask Server 500s
|
||||
|
||||
When the backend returns a 500 Internal Server Error, CORS headers are not added to the response. The browser reports this as a CORS error, hiding the real cause. Always check backend logs first when debugging CORS issues locally.
|
||||
|
||||
### Run Migrations Before Local Testing
|
||||
|
||||
After cloning or pulling new changes, always run `alembic upgrade head` before starting the backend. Missing migrations cause 500 errors (e.g., `column does not exist`) that manifest as CORS errors in the browser.
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints Reference
|
||||
@@ -586,7 +627,7 @@ interface Decision {
|
||||
|
||||
### State Management
|
||||
|
||||
- **Auth:** `useAuthStore` - Zustand with localStorage persistence
|
||||
- **Auth:** `useAuthStore` - Zustand with localStorage persistence (includes `setTokens` for silent refresh sync)
|
||||
- **Theme:** `useThemeStore` - Dark/light/system preference
|
||||
- **Tree Editor:** `useTreeEditorStore` - Zustand + immer + zundo (undo/redo)
|
||||
- **User Preferences:** `useUserPreferencesStore` - Zustand with localStorage persistence (export format default)
|
||||
@@ -612,9 +653,28 @@ interface Decision {
|
||||
import api from '@/api/client'
|
||||
|
||||
// Token refresh handled automatically by interceptor
|
||||
// Concurrent 401s are queued — only one refresh request fires at a time
|
||||
// On refresh failure, user is logged out and redirected to /login
|
||||
const response = await api.get('/api/v1/trees')
|
||||
```
|
||||
|
||||
### Floating Overlay Pattern (Scratchpad)
|
||||
|
||||
The scratchpad uses `position: fixed` with an `onOpenChange` callback so the parent page can adjust layout:
|
||||
|
||||
```tsx
|
||||
// Child: ScratchpadSidebar.tsx
|
||||
onOpenChange?: (isOpen: boolean) => void
|
||||
// Fires when collapsed state changes, parent uses it to add/remove padding
|
||||
|
||||
// Parent: TreeNavigationPage.tsx
|
||||
const [scratchpadOpen, setScratchpadOpen] = useState(...)
|
||||
<div className={cn('...', scratchpadOpen && 'pr-[440px]')}>
|
||||
<div className="mx-auto max-w-4xl"> {/* centers in available space */}
|
||||
```
|
||||
|
||||
Position overlay at `right-2` (not `right-0`) so it sits inside the page scrollbar, and use full `rounded-lg` (not `rounded-l-lg`).
|
||||
|
||||
---
|
||||
|
||||
## Common Tasks
|
||||
|
||||
Reference in New Issue
Block a user