Commit Graph

146 Commits

Author SHA1 Message Date
Jeff Emmett 0753b31990 fix(spaces): correct API paths for member/invite operations in settings panel
All fetch calls in rstack-space-settings were missing the /api/spaces prefix,
causing 404s for add-by-username, invite-by-email, change-role, remove-member,
and load-invites. Also add color-scheme: light dark to fix native select
dropdowns rendering in light mode on dark theme.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 15:15:33 -07:00
Jeff Emmett 8071b620e1 fix(auth): throttle session validation, typed auth-change events, misc fixes
- rstack-identity.ts: throttle server session validation to every 5min,
  add reason detail to all auth-change events (signin/signout/revoked/
  refresh/persona-switch), remove redundant location.reload on signout
- shell.ts: skip UI side-effects on token refresh, only redirect home
  on genuine signout/revocation
- server.ts: add PUT to CORS allowMethods
- folk-inbox-client.ts: pass auth token on mailbox API fetch

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 12:21:26 -07:00
Jeff Emmett 06a58f1bba fix(ui): theme-adaptive rSocials buttons + device link textbox
- campaign-wizard.css: fix undefined --rs-surface-hover → --rs-bg-hover
- campaign-workflow.css: replace hardcoded blue colors with theme vars
  (--rs-primary-hover, --rs-bg-active, --rs-border, --rs-primary)
- rstack-identity.ts: device link URL input --rs-bg-inset → --rs-input-bg
  (--rs-bg-inset was never defined in theme.css, dark fallback always won)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 11:39:14 -07:00
Jeff Emmett 423d2612af fix(auth): preserve active tab through login on bare domain
The identity component's inline _getCurrentModule() assumed path-based
routing (/{space}/{moduleId}) for non-subdomain URLs, returning "rspace"
instead of the actual module. On bare domain (rspace.online/rnotes),
this caused login to redirect to the canvas instead of rnotes.

Add _isBareDomain() check so _getCurrentSpace() returns "demo" and
_getCurrentModule() reads parts[0] (the module) on the bare domain.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 11:27:30 -07:00
Jeff Emmett 22b5c00a13 fix(routing): prevent domain stacking and remove Try Demo button
- Fix server-side redirect to always use canonical rspace.online as base
  domain instead of deriving from hostClean (prevents demo.rspace.rspace.online)
- Fix bare-domain check to exact match instead of .includes() (prevents
  stacked subdomains from triggering bare-domain routing)
- Fix client-side rspaceNavUrl to guard against reserved subdomain names
  being used as space subdomains (e.g. rspace.rspace.online)
- Fix identity component _navUrl with same canonical domain guards
- Remove "Try Demo" header button from all shell rendering functions
- Remove demo-btn CSS styles
- Fix pre-existing SchemaType error in Gemini tool declarations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 11:16:46 -07:00
Jeff Emmett dd46905e12 feat(mi): agentic upgrade — multi-turn loop, LiteLLM, media gen, live data
Transform MI from single-shot chat into an agentic system:

- LiteLLM provider with Claude Sonnet/Haiku models via proxy
- Agentic loop (max 5 turns): stream → parse actions → execute server-side → feed results back
- Server-side media generation (fal.ai + Gemini) as first-class MI actions
- Module data queries (rnotes, rtasks, rcal) read directly from Automerge
- System prompt enriched with recent notes, open tasks, and calendar events
- Client handles new NDJSON types (turn, action-start, action-result)
- Extracted shared media helpers, refactored image/video endpoints

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 19:26:56 -07:00
Jeff Emmett 3596bb9d7c fix(ui): raise MI search z-index and unclip header overflow
Search dropdown was rendering behind overlays and getting clipped by
the header's overflow:hidden. Bump z-index to 10001 and set overflow
to visible.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 19:02:04 -07:00
Jeff Emmett efa5a5c315 fix(collab): read username from session.claims.username instead of session.username
The encryptid session stores username at claims.username, not at the
top level. Also falls back to the rspace-username localStorage key
that rstack-identity sets on login.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 18:03:01 -07:00
Jeff Emmett cefc1aa5b7 fix(collab): rename Solo/Share to Online/Offline, use EncryptID usernames
Swap toggle labels to Online/Offline (more intuitive). Resolve canvas
peer usernames from encryptid_session instead of rspace-username fallback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 17:45:55 -07:00
Jeff Emmett e70b40df9a fix(collab): theme-aware N-online badge and people panel
The collab overlay badge used --rs-bg-secondary (which doesn't exist in
the theme system), causing it to always fall back to dark hardcoded values.
Updated all Shadow DOM CSS to use proper theme variables (--rs-glass-bg,
--rs-glass-border, --rs-text-primary, etc.) with light-mode-safe fallbacks
so the badge and panel are readable in both dark and light themes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 17:39:40 -07:00
Jeff Emmett 41051715b9 feat(rtasks): ClickUp two-way sync integration
Add bidirectional sync between rTasks and ClickUp:
- API client with 100 req/min rate limiter
- OAuth2 + personal API token connection flows
- Import wizard (workspace → space → list picker)
- Outbound push queue (5s intervals, 10-item batches)
- Inbound webhook with HMAC-SHA256 validation
- Field-level conflict detection (rTasks wins)
- Source badges (purple CU) with sync status dots on task cards
- Sync status indicator in board header for connected boards

Also fix 6 pre-existing TS errors across crowdsurf, rcal, rnotes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 17:37:01 -07:00
Jeff Emmett 13a7e44e24 feat(identity): replace device nudge toast with QR code for mobile linking
Instead of a "Set up now" button, the device nudge now generates a device
link token and displays a scannable QR code + copyable link URL directly
in the toast, making it easy to link a phone or tablet.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 17:06:29 -07:00
Jeff Emmett df8631360e feat(collab): unified presence system across all 27 rApps
Harmonize the two disconnected presence systems into one:
- New shared/collab-presence.ts utility (broadcastPresence, startPresenceHeartbeat)
- Collab overlay now listens to custom presence messages, shows module context in people panel
- Fixed Shadow DOM focus tracking using composedPath() for focus rings through shadow boundaries
- Replaced rNotes custom presence with shared utility (kept sidebar dots)
- Added presence heartbeat to all 27 rApp components with dynamic context strings
- Bumped cache versions in all modified mod.ts files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 16:56:06 -07:00
Jeff Emmett 50edf06900 feat(encryptid): periodic nudge toast for users without a second device
Shows a dismissible toast notification 3s after page load when the user
has only one passkey. Links directly to the device section in My Account.
Dismissal remembered for 7 days via localStorage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 16:14:59 -07:00
Jeff Emmett a7eda3c53f feat(encryptid): post-signup prompt recommending second device linking
After registration, users now see a welcome modal that prominently
recommends linking a second device via QR code before entering their
space. Provides backup access and cross-device sign-in awareness.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 16:05:50 -07:00
Jeff Emmett 772e5e4352 fix(shell): header button reliability — history, settings, comments
History panel: lazy doc acquisition from CommunitySync/offline runtime
on open + listen for community-sync-ready event for late connections.

Space settings: reposition panel after every re-render (async data loads
were destroying inline positioning styles), clamp max-height to viewport,
fix wrong global name (__rspaceCommunitySync → __communitySync).

Comment bell: render DOM once, update badge without innerHTML churn,
listen for community-sync-ready event, cache sync reference.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 16:04:11 -07:00
Jeff Emmett a83c714f5a fix(auth): username-first flow in rstack-identity sign-in modal
The actual login UI lives in rstack-identity.ts, not login-button.ts.
Added username input to the sign-in modal, pass allowCredentials from
server to WebAuthn so the browser auto-selects the matching passkey.
Shows "No account found" if username not recognized. Enter key support
and auto-focus on the username field.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 14:44:39 -07:00
Jeff Emmett 4536b5cab1 fix: remove rvote proposals from space dashboard + fix protocol in redirects
1. Remove all rvote/proposals fetching from rstack-user-dashboard.
   rApp-specific data (proposals) should stay within the rVote module,
   not leak into the space-level dashboard.

2. Fix url.protocol in bare-domain redirects — TLS is terminated by
   Cloudflare/Traefik so url.protocol is always http: internally.
   Use https: for production domains.

3. Rewrite /{space}/api/... paths internally on bare domain instead
   of redirecting to subdomain (avoids CORS + mixed content issues).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 12:29:15 -07:00
Jeff Emmett 8f1ad52557 feat(collab): unify header & tab-row — shared people-panel across all rApps
Canvas now uses the same rstack-collab-overlay component as all other
rApps instead of its own custom #people-online-badge. Header restructured
to match renderShell() layout (history/settings in dropdown-wraps on left).
Bridge API (updatePeer/removePeer/setConnState/clearPeers) lets canvas
feed CommunitySync peers into the shared component.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 11:46:26 -07:00
Jeff Emmett f9767191e8 fix(auth): reload page on logout and redirect logged-in users from space grid
After logout, reload the page so the server re-renders the current rApp
in logged-out/demo mode instead of showing a blank screen. Cross-tab
logout also triggers a reload. Space dashboard now redirects logged-in
users to the rSpace canvas instead of showing the rApp grid.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 10:54:35 -07:00
Jeff Emmett 4722aca065 fix(auth): wire cross-session logout in rstack-identity + encryptid profile
rstack-identity is the actual sign-out component used in production.
clearSession() now calls /api/session/logout, and connectedCallback
validates the session with the server to detect revocation. Also
updated the auth.rspace.online profile page handleLogout().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 10:46:44 -07:00
Jeff Emmett aa4a200f32 feat(history): add "Revert to this point" button in Time Machine panel
Forward Automerge change that overwrites content fields with snapshot data,
preserving meta and full history. Also fixes pre-existing TS errors in
folk-flow-river (undefined exitX) and test-full-loop (StepResult type).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 19:08:15 -07:00
Jeff Emmett 0c46e33148 fix(collab): embed online badge in tab row instead of fixed overlay
The "N online" collab badge was position:fixed at top:8px, overlapping
the header login area. Move it into the tab bar slot (main shell) or
header-right section (standalone shells) so it flows inline with other
chrome elements.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 18:19:29 -07:00
Jeff Emmett 1282ba5325 feat(history): humanize activity entries with icons and timestamps
Replace raw Automerge change messages (e.g. "Update shape abc-123-uuid")
with human-readable text and contextual icons. Add per-entry timestamps
for clearer chronology within author groups.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 17:56:13 -07:00
Jeff Emmett 30f037c2a0 feat(dashboard): redesign space dashboard with members, activity, votes
Replace the global "Your Spaces" grid with a space-centric dashboard showing
members, previously open tools, recent activity, active votes, and quick
actions. Fix layout cut-off by positioning dashboard fixed below header+tab
row (top: 92px) with sidebar-push support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 16:57:54 -07:00
Jeff Emmett 389dc70e77 fix(settings): position dropdown below and to the right of gear icon
The panel was right-aligned relative to the button which pushed it
off-screen since the gear is on the left side of the header. Now
left-aligns with the button and clamps to stay within viewport.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 16:37:25 -07:00
Jeff Emmett afd2a5a40b fix(canvas): persistent drawing tools + online/offline label + drag fixes
Drawing/shape tools (pencil, line, rect, circle) now stay active for
multiple strokes instead of resetting to selector after each use.
Renamed collab overlay badge from "Solo/Share" to "Offline/Online".
Prevent canvas drag when interacting with image-gen and prompt content.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 16:03:15 -07:00
Jeff Emmett f5de97c60c feat(ux): move comment button to header bar with unresolved badge
Move the "Leave Comment" button from the bottom canvas toolbar to the
top header bar as <rstack-comment-bell>, positioned left of the
notification bell. Shows a red badge with unresolved comment pin count.
Wires canvas via comment-pin-activate/comment-pins-changed custom events.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 14:02:27 -07:00
Jeff Emmett b91233092b fix(ux): move people-online badge into sub-tab header bar
- Move people badge + panel from fixed position to inline in .rstack-tab-row
- Badge sits right of the layer toggle icon with a subtle separator
- Panel drops down from badge position instead of floating fixed
- Online/Offline toggle replaces Solo/Multi labels for clarity
- Badge shows "Offline" with gray dot when in offline mode
- Mobile: hide text label, show dots only
- Tab bar gets flex:1 + min-width:0 to share row space

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 13:29:45 -07:00
Jeff Emmett d458a00550 fix(ux): instant bulk-delete dialog + post-login space routing
Bulk delete dialog: switch from click to pointerdown for instant
touch response, raise z-index above all overlays, add hover/active
feedback and keyboard support.

Post-login routing: reload page when logging in on a non-demo space
so access gates clear and CRDT sync reconnects with auth. Silently
provisions personal space in background.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 13:21:03 -07:00
Jeff Emmett 1930d7dab0 feat(identity): local persona switcher for client-side multi-account
Store known personas in localStorage, auto-register on login. Dropdown
shows other personas with one-click switch (triggers passkey re-auth),
add/remove persona buttons, and cross-tab sync.

Also fix pre-existing TS errors: non-null assert on filtered functionCall,
add optional VerifyOptions param to authenticateWSUpgrade type declaration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 12:10:55 -07:00
Jeff Emmett 73cc1d1cc4 feat(spaces): add Create Space modal with member invites and invite links
Adds an intermediate modal when creating a space: name/slug editing with
availability check, description, visibility radio cards, discoverable
toggle, member search with @username lookup and email invites, and a
shareable invite link generated post-creation.

Server: adds discoverable field to CommunityMeta, extends PATCH /:slug,
adds POST /:slug/invites for generic invite token creation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 12:04:27 -07:00
Jeff Emmett 7618433498 refactor(auth): replace @encryptid/sdk imports with local auth module
Consolidates token verification into server/auth.ts, removing the
external SDK dependency. All modules now import from the local module.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 16:41:59 -07:00
Jeff Emmett 29c4f48634 feat(identity): store verified email on profile + show in account modal
Email verification wrote to the `email` column but account status read
from `profile_email` — now setUserEmail writes both. Account modal email
section displays the verified address when collapsed. Tour finale step
triggers identity setup on completion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 15:49:09 -07:00
Jeff Emmett 2de61cd7ad feat(rcal): move zoom/legend below calendar, add multi-day event spans
- Reorder layout: nav → calendar → legend → zoom bars → bottom bar
- Add "Calendar Legend" heading above source badges
- Fix getEventsForDate() to support multi-day range checking
- Render colored span bars across day cells for multi-day events
- Make showAccountModal/isSignedIn public on rstack-identity
- Tour final step triggers identity setup flow

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 15:10:39 -07:00
Jeff Emmett 466a0305fd fix(spaces): dedup space switcher + restructure into sections
Personal space rename only applies when isPersonal flag is set (not all
private spaces). Demo forced to public visibility. Public spaces no
longer filtered out. Sections: Private → Permissioned → Public →
✦ Discover → Create.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 14:35:07 -07:00
Jeff Emmett fe7157ffe1 fix(shell): position settings/history panels below their buttons
Both panels now use position:fixed with JS-computed coordinates from
getBoundingClientRect(), ensuring they open directly below their
respective header buttons and right-aligned to the button edge.
History panel clamps left edge to prevent off-screen overflow.

Also adds missing click handlers for settings-btn and history-btn
in canvas.html (previously only wired in shell.ts module pages).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 13:59:44 -07:00
Jeff Emmett 63c1b7c1e7 refactor(shell): convert settings & history panels to dropdown menus
Replace full-height slide-in panels with compact dropdowns that appear
beneath header icons. Both icons now grouped in header-right with
mutual exclusion and click-outside-to-close behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 23:17:17 -07:00
Jeff Emmett 6491681e3e fix(auth): force platform authenticator + redirect disabled modules
Force authenticatorAttachment: 'platform' across all WebAuthn registration
flows to prevent USB security key prompts. Redirect browser navigations to
space root when accessing disabled modules instead of returning JSON error.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 22:31:13 -07:00
Jeff Emmett cc504d4a86 fix(spaces): make DID resolve endpoint public (no auth needed for profile data)
The resolve-dids endpoint was returning 401 because unsigned fallback tokens
fail HS256 verification. Since username/displayName is public profile data,
remove auth requirement from the endpoint and client call.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 18:06:36 -07:00
Jeff Emmett 524356d233 feat(tours+solo): add tours to remaining modules and solo mode toggle
Add guided tours to 6 modules that were missing them:
- Shadow DOM: rsocials dashboard, crowdsurf, rdata (TourEngine)
- Light DOM: rsplat, rbnb, rvnb (new LightTourEngine class)

Add solo mode toggle to collab overlay — click the presence badge
to hide your cursor/presence from others. Persists via localStorage,
dispatches event to canvas PresenceManager.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 17:26:57 -07:00
Jeff Emmett 64f9603e39 fix(spaces): stop module settings selects from polluting scope overrides
Module settings selects (notebook-id, select types) shared the .scope-select
CSS class with actual data-scope selects, causing their values to be sent as
scopeOverrides — triggering "Invalid scope '' for rnotes" on save.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 16:49:17 -07:00
Jeff Emmett acafe15c4b feat(spaces): resolve member DIDs to usernames in space settings
Add POST /api/users/resolve-dids batch endpoint in EncryptID, proxy
/api/users/* through rspace server, and batch-resolve missing displayNames
in the space settings panel so owners and members show usernames not DIDs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 16:39:48 -07:00
Jeff Emmett a415d6c308 fix(mi): only minimize on Escape when input empty, fix mobile transform
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 16:25:06 -07:00
Jeff Emmett b7aadf66cd feat(sync): proxy /api/user/* to EncryptID for cross-session tab state
Adds catch-all proxy route so client tab sync requests (saveTabs/syncTabsFromServer)
reach the EncryptID container. Also fixes rstack-mi panel positioning and
Shadow DOM click-outside handling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 16:22:37 -07:00
Jeff Emmett b679fc9f1f feat(spaces): bridge email invites with EncryptID identity system
New users get sent to /join for passkey registration + auto-space-join.
Existing users are directly added with in-app + email notification.
Add-by-username now also sends email notification if email is on file.

- Add id to /api/users/lookup response
- Enhance /api/internal/user-email/:userId with recovery + profile email
- Add GET /api/internal/user-by-email for email→DID resolution
- Rewrite POST /:slug/invite to use identity invite flow
- Add email notification to POST /:slug/members/add
- Add success/error feedback to space settings invite UI

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 16:04:22 -07:00
Jeff Emmett ad2120f4fb fix(spaces): keep create-space dropdown open during interaction
The dropdown closed on every click (radio buttons, input focus, etc.)
because a global document click listener removed the "open" class.

- Add menu-level stopPropagation so clicks inside the dropdown don't
  close it
- Only close on clicks outside both trigger and menu
- Preserve form state (name, visibility, open/closed) across
  close/reopen so content isn't lost when accidentally clicking away

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 15:59:31 -07:00
Jeff Emmett 4793f9c117 feat(crowdsurf): restore module and add Elo pairwise ranking layer
Restore CrowdSurf as standalone module with full integration (server,
app-switcher, shell favicon, rchoices tab, vite build). Add sortition-
based pairwise Elo ranking: users compare two activities head-to-head,
updating Elo ratings. Includes API endpoints (/api/crowdsurf/pair,
/api/crowdsurf/compare), Rank tab with leaderboard, and Elo badges.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 23:51:24 -07:00
Jeff Emmett d99b85046c feat(rswag): full feature parity — POD clients, dithering, AI gen, fulfillment
8-phase implementation bringing rSwag module to parity with standalone rswag.online:
- Printful v2 + Prodigi v4 API clients with sandbox mode
- 11 dithering algorithms + screen-print color separations
- Gemini AI design generation + user artwork upload
- ~15 new API routes (designs, mockups, storefront, fulfillment, admin)
- 4-tab frontend UI (Browse, Create, HitherDither, Orders)
- Interactive revenue Sankey diagram on landing page
- Fulfillment bridge routing orders to nearest POD provider

Also includes: rChats module scaffold, rVote enhancements, crowdsurf removal,
rchoices cleanup, rwallet tweaks, app-switcher updates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 23:21:53 -07:00
Jeff Emmett df77c9c903 feat(shell): cross-tab sync via BroadcastChannel + keyboard shortcuts & swipe gestures
Add BroadcastChannel for instant same-browser tab sync — opening/closing
tabs in one window propagates immediately to sibling tabs. Extract
reconcileRemoteLayers() helper shared by BroadcastChannel and Automerge,
which cleans up cached DOM panes on remote removal and handles
active-tab-closed scenarios.

Also adds configurable rApp shortcuts (Ctrl/Alt+1-9), header swipe
gestures for rApp cycling, and body data-module-id attr for swipe context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 13:04:31 -07:00