collab per-card unread, detail stale banner, image sync race fix

- per-card unread dot on board (collab-buckets): backend tracks
  last_seen_at per card, poll exposes unread_cards, frontend decorates
  matching board cards; cleared on any open path via detail.opened hook
- detail.opened + cards.reloaded hooks on the hyprcrd event bus
- stale banner in detail view with manual Reload when the currently
  viewed card got updated by a peer — no content swap under the user
- collab image sync: push attachments BEFORE card (race fix) + pull-side
  self-heal scans every collab card for missing uploads each poll
- relay user deletion now purges chat_messages sent by deleted user so
  ghost threads stop appearing for their peers
- calendar header nav buttons styled as square bordered buttons that
  honor --ui-radius-button; title bar widened to 280px so font variants
  don't wrap
- .toolbar-btn now uses var(--ui-radius-button) instead of hardcoded 3px

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
thandClaude Opus 4.6 committed 2026-04-14 22:39:32 +02:00
1 parent 39e94b8015
commit 26f95ba079
10 files changed
+324 -35

No files matched your search

+31 -1
View File
@@ -932,7 +932,7 @@ body.anim-slow .board-card.card-pulse-local {
#tag-bar { border-top: 1px dashed color-mix(in srgb, var(--t-accent) 30%, transparent); }
/* Toolbar */
.toolbar-btn { color: var(--t-muted); padding: 4px 6px; border-radius: 3px; transition: color 0.1s, background 0.1s; cursor: pointer; background: none; border: none; font-family: inherit; }
.toolbar-btn { color: var(--t-muted); padding: 4px 6px; border-radius: var(--ui-radius-button); transition: color 0.1s, background 0.1s; cursor: pointer; background: none; border: none; font-family: inherit; }
.toolbar-btn:hover { color: var(--t-bright); background: var(--t-raised); }
/* Editor */
@@ -1194,3 +1194,33 @@ body.anim-slow .board-card.card-pulse-local {
#detail-body .detail-reading-resize.resizing .detail-font-dragline::before {
border-color: var(--t-accent);
}
/* Remote-edit banner in the detail view.
Shown when a foreign sync replaced the card we're currently reading — the
user gets a manual Reload button instead of having the content change
under them mid-read. */
.detail-stale-banner {
display: flex; align-items: center; gap: 10px;
padding: 6px 12px;
background: color-mix(in srgb, var(--t-accent2) 10%, transparent);
border-bottom: 1px solid var(--t-accent2);
font-size: 11px;
color: var(--t-muted);
}
.detail-stale-banner i.ph-info { color: var(--t-accent2); flex-shrink: 0; font-size: 14px; }
.detail-stale-banner span { flex: 1; }
.detail-stale-banner button {
display: inline-flex; align-items: center; gap: 4px;
font-family: var(--font-pixel, 'Silkscreen', cursive);
font-size: 9px;
padding: 3px 8px;
background: transparent;
border: 1px solid var(--t-accent2);
color: var(--t-accent2);
cursor: pointer;
border-radius: var(--ui-radius-button);
transition: all 0.15s;
}
.detail-stale-banner button:hover { background: var(--t-accent2); color: var(--t-bg); }
.detail-stale-banner .detail-stale-dismiss { border-color: var(--t-border); color: var(--t-dim); padding: 3px 5px; }
.detail-stale-banner .detail-stale-dismiss:hover { border-color: var(--t-danger); color: var(--t-danger); background: transparent; }