Shopping lists:
- Multiple lists per tenant: picker uses checkboxes instead of
radios. Relay schema migrated to composite PK (tenant_id,
card_uid) with a position column for display ordering.
- Picker rows are drag-and-drop reorderable (grip handle +
accent drop indicator). Order persists in localStorage and is
pushed to the relay as position integers — phone renders lists
in that order (ORDER BY position, title).
- Each picker row has a goto-arrow that closes the popup,
switches to the card's bucket if needed, and opens the card.
- Relay DELETE /api/shopping removes deactivated lists. Client
proxy forwards DELETE. Deactivating a list in the picker
immediately removes it from the relay.
- Phone UI renders multiple lists as stacked boxes with headers.
Section headings (# h1–h6) get 14px top padding for breathing
room. CSS selectors fixed to match div-based headings inside
the new .shopping-list-box containers.
- Help text updated for multi-list wording.
- pullOne preserves the synced position so a pull doesn't cause
a spurious re-push of the same order.
Test fixes:
- Line-numbers gutter height tolerance bumped 50%→60% — OS text
shaping can push the divergence past 50%.
- Find/replace flaky: replaced sleep(200) with waitFor on the
confirm dialog so slow renders don't miss the OK click.
- Shopping picker test updated: checkboxes instead of radios.
Bumps to 1.9.25.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Restructure the shopping-list feature end to end:
- New public/plugins/shopping/ plugin with backend.php (proxy
routes registered via PluginManager::onApiRoute), shopping.js
(self-contained; injects the header Shopping button, owns the
picker dialog + sync loop via hyprcrd.hooks), plugin.json and a
placeholder css file. Core no longer knows about shopping —
index.php loses the static button and the proxy block, app.js
loses the initShopping wiring, the old js/modules/shopping.js
is gone.
- Phone view moves out of a standalone /shopping page and into
the relay UI as a regular `view-shopping` behind a nav-burger
entry (relay/ui/layout.php + app.js + style.css). Same session
auth as compose/chat/account — no separate login. Auto-refresh
every 4 s while the view is active; no manual refresh button.
Headings "# h1 … ###### h6" render between items for grouping.
Orphan section headings (no open items under them) are hidden.
- Sync: drop timestamp cursors (broke across SQLite-UTC / PHP
timezones and silently blocked pushes after a pull). Both sides
diff the (uid, title, body) triple instead — timezone-agnostic
and authoritative. Client ticks push-before-pull so fresh local
edits land on the relay before a stale pull can overwrite them.
Relay drops its "server newer" guard and stamps server times
with gmdate so anything left comparing to SQLite is UTC.
- Client also ticks on cards.reloaded, not just sync.tick, so
any edit path (editor save, checkbox toggle, context menu,
undo) pushes the update immediately.
- Tests: updated for the plugin (still uses the same selectors
because the plugin injects the same ids), plus a plugin-
registration check and "(no active list)" option assertion.
- Help section copy updated — references the Shopping header
button and the shopping tab in the relay UI rather than the
gone /shopping URL.
Both #einkauf and #shopping work as trigger tags; phone is
check-only, editing stays in hyprcrd. Version bumped to 1.9.21.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>