Commit Graph

314 Commits

Author SHA1 Message Date
Jeff Emmett 1c338529bf fix(presence): relay presence-leave messages and clean up listener on disconnect
- Server now relays presence-leave alongside presence messages for immediate peer removal
- Overlay properly unsubscribes leave listener on disconnectedCallback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 16:42:49 -07:00
Jeff Emmett 2f8ce08e3c feat: add rDesign module metadata, collab presence-leave cleanup
- Register rDesign in folk-rapp MODULE_META and shell FAVICON_BADGE_MAP
- Handle explicit presence-leave messages for immediate peer cleanup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 16:35:10 -07:00
Jeff Emmett 6b81f808f5 feat(canvas): mobile toolbar flush, zoom icon toggle, SW update banner
- Mobile toolbar collapsed position now flush with bottom-toolbar (bottom: 8px)
- Toolbar defaults to collapsed on mobile (<768px)
- Zoom expand/minimize uses distinct icons (magnifier +/-) instead of CSS rotation
- SW update banner on all pages: "New version available — Tap to update"
  - Detects controllerchange + updatefound events
  - Purple gradient bar, dismissible, reloads on tap
  - Added to both shell.ts (module pages) and canvas.html (standalone)
- folk-rapp: filter picker/switcher by enabled modules
- server/shell: react to modules-changed event for runtime module toggling
- collab-presence: minor overlay updates

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 16:34:48 -07:00
Jeff Emmett 395623af66 feat(rdesign): deploy KiCad & FreeCAD MCP as Docker sidecars
Switch from broken StdioClientTransport (child process) to
SSEClientTransport (HTTP to sidecar containers via supergateway).
Both sidecars share rspace-files volume so generated CAD files
(STEP, STL, Gerber, SVG) are directly servable without copying.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 16:25:23 -07:00
Jeff Emmett 55b973ebc2 fix(blender): use correct Ollama model (qwen2.5-coder:7b)
qwen2.5:14b doesn't exist on the server, causing silent 404 from
Ollama and 502 to the client. Also added error logging for non-ok
Ollama responses.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 16:10:11 -07:00
Jeff Emmett aa23108f5f fix(invites): redirect to invited space, improve invite emails
- Fix invite accept fetch URL in shell.ts (was missing /api/spaces prefix)
- After accepting invite, redirect to the invited space instead of reloading
- Notification actionUrls now point to the space subdomain (https://slug.rspace.online)
- Direct-add email includes inviter name, role, and space description
- Identity invite email includes space name/role context when inviting to a space

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 15:39:58 -07:00
Jeff Emmett 2f3a4a13dc fix(blender): make RunPod optional, fix script-only generation
RunPod was hard-gated — returned 503 when RUNPOD_API_KEY missing,
blocking even LLM script generation. Now generates script via Ollama
regardless, only attempts RunPod render if key is configured. Health
check returns warnings (non-blocking) vs issues (blocking). Default
model switched to qwen2.5:14b (available on server). Regex also
handles non-python-tagged code blocks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 15:38:49 -07:00
Jeff Emmett 59d2cc9933 feat(spaces): add visibility/description to settings panel, merge invite sources
- Space Settings section in dropdown with visibility (public/permissioned/private)
  and description fields, matching the full edit space modal
- GET /api/spaces/:slug now includes description field
- listSpaceInvites merges both space_invites and identity_invites tables
  so email invites appear in Pending Invites
- revokeSpaceInvite falls through to identity_invites table

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 15:29:33 -07:00
Jeff Emmett 32be9d7b94 fix(ui): bump subnav sticky top to avoid header overlap
Increase .rapp-subnav top from 92px to 93px so pill buttons
don't clip under the fixed header + tab-row border.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 11:54:23 -07:00
Jeff Emmett c2d7e8b238 fix(shell): prevent infinite loop when rapidly switching tabs
Abort previous in-flight fetch when switchTo() is called again, and
guard the shell's fallback navigation against stale callbacks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 11:44:38 -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 a5c7eef466 fix(startup): move space alias provisioning after loadAllDocs
The IIFE raced with doc loading so listCommunities returned empty.
Moved into the .then() callback to ensure all docs are loaded first.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 19:06:06 -07:00
Jeff Emmett 193588443e fix(routing): eliminate /{space} from paths on subdomain routing
On subdomain routing (e.g. demo.rspace.online), the space slug belongs
only in the subdomain — never in the URL path. This fixes all server
and client code that was generating /{space}/module paths on subdomains.

Server fixes:
- index.ts: notification actionUrls, template/disabled-module redirects,
  subdomain passthrough (now redirects HTML, rewrites API), WS notifications
- output-list.ts: subdomain-aware path prefix for hrefs and fetch URLs
- shell.ts: dashboard pushState URL

Client fixes (basePath getter pattern):
- folk-book-shelf.ts, folk-splat-viewer.ts: _basePath getter
- folk-campaign-wizard.ts: basePath subdomain check
- folk-trips-planner.ts: use __rspaceNavUrl for canvas export
- rschedule/landing.ts: onclick handlers use __rspaceNavUrl
- rsplat/mod.ts: legacy view redirect subdomain check

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 18:45:16 -07:00
Jeff Emmett 75ad3f8194 feat(rinbox): per-space email forwarding via Mailcow aliases
Provision {space}@rspace.online forwarding aliases that route to
opted-in members' personal emails. Admins/mods opted in by default;
regular members can opt in via PUT /api/spaces/:slug/email-forwarding/me.

New: space-alias-service.ts, schema tables, 8 DB functions, 6 API routes.
Hooks: rinbox onSpaceCreate/Delete, spaces.ts member lifecycle, startup migration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 18:40:29 -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 677a69645e debug(shell): add console logging to trace tab-switch failure for rspace
Adds diagnostic console.log to layer-switch handler, TabCache.switchTo,
fetchAndInject, and reconcileRemoteLayers to identify why clicking the
rspace tab closes all tabs and shows the dashboard.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 16:32:57 -07:00
Jeff Emmett 433833da0c fix(shell): prevent remote sync from wiping all tabs and showing dashboard
reconcileRemoteLayers() could wipe all local tabs when Automerge sync
or BroadcastChannel delivered empty layer data (CRDT initial state,
sync race). This caused clicking tabs to show an infinite-loading
dashboard. Now: empty remote layers are rejected when an active module
exists, and the current module always stays in the tab list.

Also adds 10s fetch timeout to TabCache.fetchAndInject() to prevent
infinite loading spinners on slow/failed module fetches.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 16:23:45 -07:00
Jeff Emmett 3655632e0f fix(auth): check cross-subdomain cookie in access gate and dashboard redirects
The access gate and space dashboard redirect scripts checked only
localStorage, which is per-origin. When navigating between subdomains
(e.g. demo → jeff), the session wasn't found. Now both scripts also
check the eid_token cross-subdomain cookie and sync it to localStorage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 14:24:30 -07:00
Jeff Emmett bf0661fab2 feat(cad): LLM-orchestrated MCP tool-calling for KiCad and FreeCAD
Add Gemini Flash agentic loop that converts natural language prompts
into real MCP tool call sequences for PCB design (KiCad) and parametric
CAD (FreeCAD). Dynamic schema conversion from MCP tools to Gemini
function declarations, 8-turn/60s loop with real execution and result
feedback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 14:07:19 -07:00
Jeff Emmett d8736ba341 fix(rcal): add post-load migration for tags field on existing events
Automerge docs created before the tags schema don't have the field.
The migration runs 5s after startup (after loadAllDocs completes),
patches missing tags onto events, and assigns known demo event tags.
Also removes debug endpoint and tracing logs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 14:01:01 -07:00
Jeff Emmett 9ae88e14b7 debug: add tags tracing logs 2026-03-24 13:56:22 -07:00
Jeff Emmett 2dd5e764cd feat(rcal): MI calendar awareness, tags, saved views, MCP server
Phase 1: MI now knows the current date/time and upcoming events (14-day
lookahead, 5 events max) via direct Automerge read — no HTTP overhead.

Phase 2: Tags (string[] | null) on CalendarEvent for first-class filtering.
Saved views (named filter presets) with full CRUD API. Tag filter on
GET /api/events via comma-separated AND logic. Demo events seeded with tags.

Phase 3: Calendar MCP server (5 tools: cal_list_events, cal_get_event,
cal_list_sources, cal_list_views, cal_create_event) registered in .mcp.json.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 13:28:02 -07:00
Jeff Emmett f2c3245240 fix: ensure demo community visibility is always public on startup
The demo Automerge doc had visibility: "private" from initial creation.
ensureDemoCommunity now forces visibility to "public" on every startup
if it drifted.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 13:21:59 -07:00
Jeff Emmett 7f327eb07a fix: whitelist rvote GET API as public + guard campaign wizard auth client-side
1. Add GET /rvote/api/* to public endpoint whitelist so proposal
   listings work on private/permissioned spaces without auth.
2. Campaign wizard now checks for auth token before POSTing,
   showing "Please sign in" instead of a cryptic 401.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 12:58:21 -07:00
Jeff Emmett 7ad5666b9a fix: enforce enabledModules check on module root path
The sub-path middleware (/:space/:moduleId/*) already blocked disabled
modules, but the root path (/:space/:moduleId) didn't. Now both paths
consistently check enabledModules before allowing access.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 12:38:01 -07:00
Jeff Emmett 6e4d1436e0 fix: mixed content on bare-domain API calls + SSE stream error handling
Two fixes:
1. Bare-domain routing used url.protocol (always http: behind TLS
   termination) for redirects, causing mixed-content blocks. Added
   proto helper that uses https: on production domains. Also rewrite
   /{space}/api/... calls internally instead of redirecting to the
   subdomain.
2. rDesign SSE stream reader now catches QUIC protocol errors on
   stream close gracefully.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 12:20:20 -07:00
Jeff Emmett 724c0e16ba fix(auth): redirect logged-out visitors from private spaces to module landing
Non-demo space dashboards now redirect logged-out visitors to
rspace.online/ instead of showing another user's rApp grid. Private
space module pages redirect to rspace.online/{moduleId} instead of
showing the sign-in gate overlay.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 11:39:39 -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 21b38e7297 fix(tabs): server-authoritative tab sync across browser sessions
Changed syncTabsFromServer to replace local tabs with server tabs
instead of merging (union). This prevents tabs closed in browser A
from being resurrected when browser B refreshes. Also added server
sync to the iframe module landing path which was localStorage-only.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 10:53:24 -07:00
Jeff Emmett f70972cfb7 fix(rdesign): add design-agent API to public endpoint exemptions
The demo space auth middleware was blocking POST requests with 403.
Add /rdesign/api/ to isPublicEndpoint list, matching rwallet pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 10:32:18 -07:00
Jeff Emmett 77b7aba893 feat(rdesign): Scribus noVNC + AI design agent + CRDT sync
Replace Affine wrapper with full Scribus DTP stack:
- Docker container: Scribus 1.5 + Xvfb + x11vnc + noVNC + Python bridge
- Bridge API: Flask server (port 8765) proxying to Scribus Python API via Unix socket
- Design agent: Gemini tool-calling loop drives Scribus headlessly from text briefs
- CRDT sync: Automerge schema v2 with pages/frames, bidirectional SLA bridge
- Canvas tool: folk-design-agent shape + create_design_agent in canvas-tools registry
- Module UI: inline text prompt + step log + SVG layout preview (no iframe)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 10:06:04 -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 245d2ec3b4 feat(rwallet): HyperSwitch full-loop fiat↔CRDT payment integration
- /api/internal/mint-crdt: on-ramp webhook → cUSDC mint (idempotent)
- /api/internal/escrow-burn: off-ramp escrow with two-step confirm/reverse
- $MYCO bonding curve (server/bonding-curve.ts): quadratic price curve,
  buy/sell/quote/settlement-state endpoints in rwallet
- BFT token renamed to $MYCO (6 decimals) in seed data
- LedgerEntry schema extended with offRampId, status for escrow tracking
- burnTokens, burnTokensEscrow, confirmBurn, reverseBurn in token-service
- Wallet UI: Buy cUSDC, $MYCO Swap, Withdraw sections with live quotes
- scripts/test-full-loop.ts for end-to-end verification

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 17:34:48 -07:00
Jeff Emmett b455e639d7 fix(rwallet): exempt wallet API endpoints from private space access gate
Balance queries, Safe detection, and chain analysis are blockchain
reads that should work for any authenticated user regardless of
space membership. The route handlers enforce their own auth.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 17:29:37 -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 8649598c26 fix(tab-cache): add Accept header so tab fetch works on private spaces
The TabCache fetchAndInject() was sending fetch() without Accept: text/html,
causing the server to treat it as an API request and return 401 on private
spaces. Also adds .catch() fallbacks to shell tab handlers for resilience.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 16:00:51 -07:00
Jeff Emmett cdfba02b03 fix(rcart): exempt payment endpoints from private space access gate
Payment creation, QR codes, and pay pages should be accessible to any
authenticated user regardless of space visibility, since the payment
goes to the creator's wallet. The route handlers enforce their own auth.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 14:30:55 -07:00
Jeff Emmett 67f1927eb5 fix(rflows): exempt public on-ramp endpoints from space auth middleware
Space visibility defaults to "private", blocking unauthenticated API calls.
The on-ramp and webhook endpoints are designed for unauthenticated users,
so they need to bypass the space-level auth check.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 14:23:44 -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 023a9e7fbd fix(notifications): use full URL pathname for proxy path
c.req.path in Hono returns the full path, not the relative path
within the sub-router, causing the /api/notifications prefix to
be doubled.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 13:36:51 -07:00
Jeff Emmett cb784b8102 fix(notifications): proxy to encryptid instead of direct DB access
The rspace container cannot resolve encryptid-db hostname, causing
/api/notifications/count to 524 timeout on every 30s poll. Rewrites
notification-routes.ts as an HTTP proxy to encryptid (which has DB
access), adds notification API endpoints to encryptid server, and
wraps BroadcastChannel.postMessage in try/catch to prevent uncaught
errors during navigation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 13:30:58 -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 07e3c7348c feat(canvas): add Figma-style comment pin system
Overlay markers at canvas or shape-relative coords with threaded
comments, @mention notifications, and rSchedule reminder integration.
Toolbar "Leave Comment" button (/) next to Note tool.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 18:47:03 -07:00
Jeff Emmett cebad27b38 fix(auth): pass JWT secret to all SDK verify calls + add auth header to rpubs generate
evaluateSpaceAccess and authenticateWSUpgrade were calling the SDK
without the secret option, forcing remote verification. Also adds
auth header to rpubs editor generate fetch (was getting 401 on
private spaces).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 18:15:17 -07:00
Jeff Emmett 999502464f feat(prompt): add canvas tool use via Gemini function calling
folk-prompt can now spawn shapes on the canvas when Tools mode is
enabled. Gemini calls functions (create_map, create_note, create_embed,
create_image, create_bookmark, create_image_gen) and the client
executes them via window.__canvasApi. Multi-turn loop on server (max 5
rounds) with synthetic success responses. Extensible via registerCanvasTool().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 16:42:08 -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 4c1cd21b8c fix(spaces): ensure demo space is always public in access checks
getSpaceConfig() read stored visibility without the demo override,
so the client-side access gate blocked unauthenticated users from
accessing the demo space.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 15:47:00 -07:00
Jeff Emmett 692c75ee5c fix(shell): escape apostrophe in access gate to prevent script parse failure
The single quote in "don't" broke the JS string literal inside the
inline script, causing a SyntaxError that killed the entire script
block including tab bar and header initialization.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 15:39:45 -07:00