The horizontal scrollbar on the bucket row was ugly and
inconsistent with the rest of the app's scrollbar-less panels. Hide
it, redirect vertical wheel over the bar to horizontal scroll, and
surface overflow with soft bg-coloured gradients on whichever side
has more content off-screen: right only while anchored at the
start, both in the middle, left only at the end.
Fade overlays live on #bucket-row (parent) as pseudo-elements with
pointer-events:none, rather than mask-image on the bar itself — a
mask would also clip super-bucket popovers that are intentionally
rendered inside #bucket-bar for the collab MutationObserver.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
An empty board on first load blurting "no cards found" was alarming
on a board the user knew had cards. Track a cardsLoaded flag that
flips on the first /api/cards (meta or full) response — or on a
cached-cards hydration — and show a centred spinner placeholder
while it's still false. The "no cards found" copy only shows once
we've actually heard back and the set is genuinely empty.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Author name above group bubbles was 8px dim-grey pixel font — visible
in theory, invisible in practice. Bump to 10px semibold accent,
left-aligned above the incoming bubble it labels.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extends chat.test.js to cover the new conversation shape (kind/key
fields), the /api/chat/rooms endpoint, the thread-settings
round-trip, and the group-creation pick-view toggle. Room-info view
DOM is structurally checked. Bumps to 1.9.4.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Switching conversations used to require bouncing back to the list view
and picking from there. Now the thread view has a horizontal peer
strip at the top: one click hops to another conversation, active tab
is highlighted and auto-scrolls into view, unread peers show a dot.
Scrollbar is hidden — vertical wheel delta is redirected to horizontal
scroll so the user can slide the strip with the mouse wheel when it
overflows.
In-thread header is gone; the back button moved next to "chat" in the
flyout header and only shows while a thread is open. Peer name stays
visible via the active tab.
Bumps to 1.9.3.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
caldav.js was carrying the core sync timer, autosync toggle, relay
settings and every poller hook alongside its actual CalDAV/calendar
config concerns. Split out:
- New sync.js owns the single background timer, header Sync button,
autosync pause toggle, configurable interval, and emits `sync.tick`
so plugins can fan out without their own setIntervals.
- collab-buckets + chat now subscribe to sync.tick instead of running
their own pollers, so the manual Sync button refreshes them too and
the Autosync pause actually pauses everything.
- Replace the number input for sync interval with a stepped slider
(5s..1h snap points) and a live label, bumped to 1.9.2.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- header Sync button now has a paired autosync toggle to its right —
reads as one button with no inner seam (outer border only). Dot inside
pulses once per actual sync (bursts on every backgroundSync /
triggerSync / collab pollOnce via a new sync.pulse hook) so the user
can see polls happen. Dot + data-autosync attribute persist the
enabled state in localStorage; paused state stops both the
CalDAV/relay background timer and the collab poll via a broadcast
autosync.changed hook.
- hover polish: Sync regains a full accent border (the missing inner
edge re-appears only on its hover); the toggle fills with solid
accent as hover treatment so the pair never shows a half-outline.
- sync interval moved out of hardcode into a settings field under
Settings → Relay: number input, 5s..3600s, default 15s (was 45s on
caldav / 30s on collab). Changes broadcast via sync.interval.changed
and restart both timers without reload.
- session cookie lifetime raised to 30 days with matching gc_maxlifetime
and SameSite=Lax (was 0 / Strict — some users got kicked to login on
every reload when gc cleaned up the /tmp/sess file or a cross-link
return blocked the cookie).
- search button's kbd element was pushing the button ~3px taller than
Sync; leading-none + tighter padding puts them on the same height.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Temporary diagnostic scaffolding — B-side logs every POST to the relay
(status + body snippet), A-side already console.debugs the GET result
via sync_debug. Lets us narrow down where the sender-side toast gets
lost.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- detail banner timeAgo: SQLite timestamps are UTC without tz marker,
JS was parsing them as local. Fixed by appending Z when absent —
same pattern chat uses. "2h ago" for a 30s-old update is gone.
- banner Reload button now re-fires detail.opened so the collab-buckets
plugin clears the red dot on the board card and calls mark-card-seen.
Dismiss (×) still leaves the dot — that's the acknowledgement that
nothing was actually read.
- instrument /api/relay/poll with a sync_debug field so the JS can
console.debug what happened on the sync-events sub-request (status,
count, error body). Helps diagnose "no toast" reports.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- replace chat-bot sync notifications with a dedicated relay_sync_events
queue: POST/GET /api/sync-events, drained by /api/relay/poll and
surfaced as stacking toasts via caldav.js. Old /api/chat/bot removed,
bot=1 rows purged on bootstrap, orange bot rendering dropped from
relay UI. Chat thread stays clean.
- collab pull: fetch attachments BEFORE applying the body so the moment
state.cards reflects the new reference the file is on disk. Fixes the
"preview shows broken/missing image, editor shows it" race where the
browser cached the 404 between attachment landing and user re-render.
- detail stale banner now compares title/body/color/tags in addition to
updated_at — robust against second-granularity timestamp collisions
and any other case where updated_at doesn't tick but content differs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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>
Fullscreen cluster dashboard view consuming the Oumta Go backend.
All data currently mocked — see plugin TODO.md for integration plan.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Inbox poll continues fetching but skips DOM updates when user is typing,
deleting, or confirming. Quiet mode diffs message IDs to avoid
unnecessary re-renders. Focus/blur tracking on all inputs auto-guards.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New tooltip.js module with styled tooltips (data-tip) and multi-line
inline help (data-help). Migrates all ~70 native title attributes
across index.php, JS modules, and all plugins. Adds inline help on
9 strategic UI elements (split, superview, zen, extract, assets, etc).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Clicking the super-bucket tab now activates the last-shown child
directly. Only the caret arrow on the right opens the dropdown popover.
Caret has its own hit area with accent hover highlight.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Gitea plugin: default_repo field per account (stored in DB, pre-fills
create issue form but stays editable). Editor toolbar: asset picker
flyout with 4-column thumbnail grid and live search filter. Clicking
a tile inserts the matching markdown.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add Electron desktop app section, document all 8 plugins, update
features list (assets, super-buckets, find/replace, arrow nav, PDF
upload), fix relay docs (chat shipped, not future), add missing API
endpoints, correct electron data storage paths and php-bin fetch info.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Set upload_max_filesize=64M and display_errors=0 for Electron PHP
- Import shows error dialog instead of silently failing
- Footer count repositions at 1450px instead of 1250px
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>