Updated documentation; added PERFORMANCE-HEALTH-CHECK.md

This commit is contained in:
Michael Chihlas
2026-02-04 21:46:32 -05:00
parent 2733a00253
commit d7c5c8c9ce
2 changed files with 697 additions and 3 deletions

View File

@@ -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