shopping: multi-list, DnD ordering, phone layout; fix flaky tests

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>
This commit is contained in:
thandClaude Opus 4.6 committed 2026-04-16 17:26:50 +02:00
1 parent 4c3954d989
commit be7eb1523e
12 files changed
+370 -192

No files matched your search

+51 -14
View File
@@ -276,26 +276,63 @@ label.checkbox input[type="checkbox"] { accent-color:var(--accent); cursor:point
}
.shopping-items li:active { transform: scale(0.98); background: var(--raised, var(--surface)); }
.shopping-items li.checked { opacity: 0.35; text-decoration: line-through; }
.shopping-items .empty { text-align: center; padding: 40px 16px; color: var(--dim); background: transparent; border: none; cursor: default; }
.shopping-items .empty { text-align: center; padding: 40px 16px; color: var(--dim); background: transparent; border: none; cursor: default; list-style: none; }
.shopping-items .empty:active { transform: none; }
/* Section headings between items — "# Obst", "## Backwaren", etc. */
.shopping-items li.shopping-heading {
background: transparent;
border: none;
padding: 18px 4px 4px;
/* Multi-list stacked boxes */
.shopping-list-box {
list-style: none;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 10px;
display: flex;
flex-direction: column;
gap: 6px;
}
.shopping-list-box:active { transform: none; }
.shopping-list-header {
font-weight: 600;
font-size: 13px;
color: var(--bright);
padding: 4px 6px 8px;
border-bottom: 1px solid var(--border);
}
.shopping-list-done {
text-align: center;
padding: 16px 8px;
color: var(--dim);
font-size: 14px;
}
.shopping-item {
background: var(--raised, var(--surface));
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 12px 14px;
font-size: 15px;
cursor: pointer;
user-select: none;
-webkit-tap-highlight-color: transparent;
transition: transform 0.12s, opacity 0.3s, background 0.12s;
}
.shopping-item:active { transform: scale(0.98); }
.shopping-item.checked { opacity: 0.35; text-decoration: line-through; }
/* Section headings between items — "# Obst", "## Backwaren", etc.
Adds breathing room above so grouped items feel like sections. */
.shopping-heading {
padding: 14px 6px 2px;
cursor: default;
font-weight: 600;
letter-spacing: 0.02em;
}
.shopping-items li.shopping-heading:active { transform: none; background: transparent; opacity: 1; }
.shopping-items li.shopping-heading:first-child { padding-top: 2px; }
.shopping-items li.shopping-heading.h1 { font-size: 18px; color: var(--bright); }
.shopping-items li.shopping-heading.h2 { font-size: 15px; color: var(--accent); }
.shopping-items li.shopping-heading.h3,
.shopping-items li.shopping-heading.h4,
.shopping-items li.shopping-heading.h5,
.shopping-items li.shopping-heading.h6 { font-size: 13px; color: var(--muted); }
.shopping-heading:first-child { padding-top: 2px; }
.shopping-heading.h1 { font-size: 17px; color: var(--bright); }
.shopping-heading.h2 { font-size: 14px; color: var(--accent); }
.shopping-heading.h3,
.shopping-heading.h4,
.shopping-heading.h5,
.shopping-heading.h6 { font-size: 12px; color: var(--muted); }
.shopping-err { background: var(--danger-bg, #7f1d1d); color: var(--danger-fg, #fecaca); padding: 8px 12px; border-radius: 6px; font-size: 13px; }
.shopping-err.hidden { display: none; }