Commit Graph
12 Commits
Author SHA1 Message Date
thandClaude Opus 4.6 52da9abd44 chat: client-side rooms, disposable flag, history backfill
Extends the local chat cache to mirror rooms + membership from the
relay. chat_messages grows a room_uid column; peer is reused as
author-username for group rows so a single table covers both kinds.

New local tables:
  chat_rooms            — uid, name, owner, synced_at
  chat_room_members     — room_uid + username
  chat_thread_settings  — per-peer disposable flag

New / extended endpoints:
  GET/POST/PATCH/DELETE /api/chat/rooms[/{uid}] — proxy to relay
  POST /api/chat/backfill — pulls older messages from relay
  GET/POST /api/chat/thread-settings — disposable toggle
  /api/chat/conversations — mixed list of peers + rooms
  /api/chat/messages — accepts peer OR room_uid
  /api/chat/send — accepts peer OR room_uid; forwards disposable
  /api/chat/poll — mirrors rooms on every tick; sets direction
                   from `mine` flag so multi-device group sends
                   render as out-bubbles

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 20:15:38 +02:00
thandClaude Opus 4.6 81d9ce0e87 chat: peer-switch strip, back button moved to flyout header
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>
2026-04-15 19:30:38 +02:00
thandClaude Opus 4.6 1d6499e5dd extract sync orchestration into sync.js, unify on sync.tick heartbeat
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>
2026-04-15 19:02:44 +02:00
thandClaude Opus 4.6 f465306c1d global tooltip system, migrate all title attrs to data-tip/data-help
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>
2026-04-12 19:55:17 +02:00
thandClaude Opus 4.6 285e3b4abe chat: read receipts (delivered + seen) and persisted active bucket
Read receipts on chat messages — single check for delivered (recipient
polled), double check for seen (recipient opened the thread). Pending
state shows a clock until the relay acks the post.

Relay (relay/):
* bootstrap.php: ALTER TABLE chat_messages ADD COLUMN seen + a
  (sender_id, id) index for the sender-side state poll. Migration is
  idempotent via PRAGMA table_info.
* routes/chat.php: two new routes
  - GET  /api/chat/sent?since=N — returns the receipt state (id,
    recipient_id, fetched, seen, created_at) for messages the caller
    sent. No body — sender already has the plaintext locally.
  - POST /api/chat/seen { peer } — marks every message FROM peer TO
    caller as seen=1. Idempotent. Frontend calls this when opening
    a thread; sender's next /sent poll picks the new state up.

Plugin backend (public/plugins/chat/backend.php):
* schema migration: chat_messages gets `delivered` and `seen` columns
  (idempotent), only meaningful for direction='out'.
* /api/chat/send: stores the relay's autoincrement id as `relay_id`
  on the local outgoing row so the receipt poll can match against it.
* /api/chat/poll: after the normal incoming pull, also calls
  GET /api/chat/sent?since=last_sent_relay_id and updates local
  outgoing rows with delivered/seen via UPDATE WHERE relay_id. The
  cursor (last_sent_relay_id state value) only advances past rows
  whose state is final (seen=1); rows still in flight stay re-pollable.
  Response now carries `receipts: <count>` so the frontend can refresh
  the open thread when state changes without new incoming.
* /api/chat/read: after the local is_read=1 update, fires a POST to
  the relay's /api/chat/seen so the sender gets the double-check.
  Silent on failure — the user already saw the messages locally.
* /api/chat/messages: SELECT now includes delivered, seen.

Plugin frontend (public/plugins/chat/chat.js):
* renderThread() appends a receipt icon to outgoing bubbles:
  - seen=1                      → ph-checks (accent, double check)
  - delivered=1, seen=0         → ph-check  (muted, single check)
  - both 0                       → ph-clock  (dim, pending)
  Incoming messages get nothing.
* pollOnce() refreshes the open thread when receipts > 0 even with
  no new incoming, so the icons update in real time while the user
  is looking at the thread.

CSS (public/plugins/chat/chat.css):
* .chat-bubble-time becomes flex with gap so timestamp + receipt
  icon sit on one line.
* .chat-receipt.pending / .delivered / .seen color tiers.

board: persist active bucket across reloads

* state.js: state.activeBucket initializes from
  localStorage.getItem('hyprcrd-active-bucket') || 'all'. New
  setActiveBucket(value) helper writes through to localStorage; new
  ensureActiveBucketValid() helper falls back to 'all' if the
  persisted id no longer exists in state.buckets.
* buckets.js: loadBuckets() calls ensureActiveBucketValid() right
  after the API fetch and before renderBar(). All four production
  assignments (delete-keep, delete-all, popover entry click, regular
  tab click) replaced with setActiveBucket() so the persisted value
  stays in sync. Tests still mutate state.activeBucket directly so
  they don't touch localStorage.

chore: bump version to 0.9.6

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 22:43:28 +02:00
thandClaude Opus 4.6 a29708df85 chore: cursor-blink on empty-state placeholders
Adds the cursor-blink class to "no X" / "select a Y" placeholders across
core (calendar popup, board preview, search preview, detail split, notifs)
and plugins (chat, email, gitea, slideshow). Visual consistency with the
rest of the pixel-font UI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 18:49:25 +02:00
thandClaude Opus 4.6 2aec2d8f44 theme: exempt status-indicator animations from anim-speed override
Cursor blink, card-bar-pulse, sync spinner and the chat unread blink all
have deliberate cadences — letting body.anim-fast/anim-slow speed them up
turns them into unreadable strobes (or the sync icon spinning at 100ms).
Higher-specificity rules pin each one to its natural duration via
!important.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 18:48:54 +02:00
th f886ded054 fixes 2026-04-11 13:38:14 +02:00
th 22f73ab3b9 fixes + theme 2026-04-11 13:01:27 +02:00
th 3ae84ccdd7 chat fix 2026-04-11 01:28:49 +02:00
th 262b853c5b slideshow 2026-04-11 01:22:52 +02:00
th 988a5410f7 chat drin 2026-04-10 22:49:25 +02:00