- SidebarResizer: pointer-drag handle on the sidebar's right edge,
keyboard-adjustable (arrows, Home/End), double-click resets to the
16rem default; width clamped to 12-32rem
- AppLayout persists the width via usePersistentState (ui.sidebar.width)
and sets --sidebar-width inline on .app-body, so collapse/force-hide
keep animating from/to the chosen width
- localized aria-label (de+en), handle hidden while collapsed
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1
Enable the third sidebar sort mode — a freely defined order.
- api: `PATCH /pages/:id/position` (before/after neighbour) recomputes only
the moved page's fractional `sort_key`. Pure `sort-key.ts` helpers
(`nextKeyOrRebalance`, `evenlySpacedKeys`) decide between the cheap
single-key path and a full pond rebalance to evenly-spaced keys when a key
would exceed MAX_SORT_KEY_LENGTH or the client's neighbours are stale;
rebalance runs in one transaction. Order is server-authoritative.
- web: enable 'manual' in the sort-mode switch; in manual mode the owner can
reorder via native drag-and-drop (drop above/below by pointer half) or the
keyboard (per-row up/down buttons), each announced through an aria-live
region. Reordering is hidden while a label filter narrows the list. New
pages already append at the end (create uses generateKeyBetween(last, null)).
Pure `reorder.ts` neighbour helpers, unit-tested.
- i18n: manual sort mode + reorder strings (de + en).
- tests: sort-key property test (10.000 adversarial reorders never collide or
overflow — rebalance verified); reposition db test (persist, server-order,
sort-mode switch keeps manual order); reorder e2e pack (keyboard reorder
persists across reload + identical on a fresh read; aria-live announced).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGdhRiwU1WRL4XxJfZYipY
GET /ponds/:id/pages lists a pond's pages ordered by the pond's
persisted sidebarSort setting (alpha/created; manual arrives with #45).
The sidebar consumes it to show the page list with an active-page
highlight, an owner-only sort switch (persists via the existing
PATCH /ponds/:id), and an inline "new page" flow. The top bar gains a
pond switcher; a new /p/:pondSlug route gives it somewhere to land,
redirecting to the pond's first page once loaded. Sidebar collapse
gains a Ctrl/Cmd+\ shortcut and a slightly refined transition.
Closes#26
Translation resources live in packages/shared/i18n/<lang>/<ns>.json
(common, errors) and ship with de and en. The web app initializes
react-i18next with bundled resources (?lng= wins, then the browser
language); all shell components use useTranslation and the temporary
t() stub is gone. The api localizes its uniform error bodies via a
minimal i18next instance negotiated from Accept-Language. `pnpm
i18n:check` fails CI when any key is missing in any language, backed
by tested helpers in @dorfteich/shared.
Closes#5
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>