fix: switch all module shell themes from light to dark

The light theme was causing the header bar to render with a white
background, clashing with the dark-themed module content underneath.
All modules and both shell renderers now default to dark theme.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-02-24 19:42:33 -08:00
parent f84e45fc5a
commit 346b406b80
54 changed files with 1949 additions and 28 deletions

View File

@ -0,0 +1,8 @@
---
id: m-0
title: "Canvas → rSpace Migration"
---
## Description
Complete migration of all custom shapes and functionality from canvas-website (tldraw) to rspace-online (Web Components). rspace-online replaces canvas-website as the production platform.

View File

@ -0,0 +1,8 @@
---
id: m-1
title: "rSpace App Ecosystem"
---
## Description
Transform rSpace from a flat shape canvas into a composable app ecosystem with data pipes, event broadcasting, semantic grouping, shape nesting, and cross-app embedding from the r-ecosystem (rWallet, rVote, rMaps, etc.).

View File

@ -0,0 +1,44 @@
---
id: TASK-21
title: Offline-first support with IndexedDB persistence and Service Worker
status: Done
assignee: []
created_date: '2026-02-18 19:39'
labels:
- feature
- offline
- infrastructure
dependencies: []
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Added full offline-first capability to rSpace apps. Automerge documents and sync state now persist to IndexedDB via a new OfflineStore class, enabling instant load from cache, offline editing, and automatic incremental merge on reconnect. A Service Worker caches the app shell (HTML/JS/WASM) for loading without network.
## Changes
- **lib/offline-store.ts** (new): IndexedDB wrapper storing Automerge doc binary + SyncState per community slug, with debounced saves
- **lib/community-sync.ts**: Integrated offline persistence — initFromCache(), #scheduleSave(), #persistSyncState(), saveBeforeUnload(), infinite reconnect with capped backoff
- **website/sw.ts** (new): Service Worker — cache-first for hashed assets, network-first for HTML, skip WS/API
- **website/canvas.html**: SW registration, OfflineStore init, offline/online status UI, beforeunload save
- **vite.config.ts**: build-sw plugin to produce dist/sw.js without content hash
- **tsconfig.json**: Excluded sw.ts from main typecheck (WebWorker types)
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 IndexedDB persists Automerge doc binary per community slug
- [ ] #2 Cached content loads instantly on page refresh (before WebSocket connects)
- [ ] #3 Offline editing works — shapes can be created/moved/deleted without network
- [ ] #4 Changes merge automatically on reconnect via Automerge sync protocol
- [ ] #5 Service Worker caches HTML/JS/WASM for offline app shell loading
- [ ] #6 Reconnect retries indefinitely (30s max backoff) when offline store is present
- [ ] #7 beforeunload saves pending changes immediately
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Implemented offline-first support for rSpace. Created OfflineStore (IndexedDB wrapper) and Service Worker. Integrated into CommunitySync with debounced saves after every doc mutation, SyncState persistence for incremental reconnect, and infinite retry with capped backoff. Canvas UI shows offline/online status. Build produces dist/sw.js alongside main app. Pushed to Gitea main branch (6b06168).
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@ -0,0 +1,24 @@
---
id: TASK-22
title: Add offline-first section to rSpace landing page
status: Done
assignee: []
created_date: '2026-02-18 19:41'
labels:
- website
- content
dependencies: []
priority: low
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Added an "Offline-First" feature card to the hero grid and a dedicated content section on the rSpace.online landing page explaining local IndexedDB persistence, Automerge auto-merge, and incremental sync. Matches existing visual style with pillars and identity cards.
<!-- SECTION:DESCRIPTION:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Added 4th feature card (Offline-First) to hero grid, updated grid to 4 columns (2 on mobile), and added a new section with 3 pillars (Local Persistence, Auto-Merge, Incremental Sync) plus a "How it works" explanation card. Pushed to Gitea main (ea8f1b3).
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@ -0,0 +1,43 @@
---
id: TASK-23
title: 'Feature parity audit: 13 overlapping shapes'
status: To Do
assignee: []
created_date: '2026-02-18 19:49'
labels:
- audit
- phase-0
milestone: m-0
dependencies: []
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Compare each of the 13 shapes that exist in both canvas-website (tldraw) and rspace-online (folk-*) side-by-side. Document missing features in the rspace versions.
Pairs to audit:
1. ChatBox ↔ folk-chat
2. VideoChat ↔ folk-video-chat
3. Embed ↔ folk-embed
4. Markdown ↔ folk-markdown
5. Slide ↔ folk-slide
6. Prompt ↔ folk-prompt
7. ObsNote ↔ folk-obs-note
8. Transcription ↔ folk-transcription
9. ImageGen ↔ folk-image-gen
10. VideoGen ↔ folk-video-gen
11. GoogleItem ↔ folk-google-item
12. Map ↔ folk-map
13. WorkflowBlock ↔ folk-workflow-block
For each pair: read both implementations, note feature gaps, classify as critical/nice-to-have.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 All 13 shape pairs compared side-by-side
- [ ] #2 Feature gaps documented with severity (critical/nice-to-have)
- [ ] #3 Critical gaps identified for immediate fix
<!-- AC:END -->

View File

@ -0,0 +1,31 @@
---
id: TASK-24
title: Add infrastructure dependencies for shape migration
status: To Do
assignee: []
created_date: '2026-02-18 19:49'
labels:
- infrastructure
- phase-1
milestone: m-0
dependencies: []
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Install npm dependencies required by shapes being ported from canvas-website:
- h3-js (HolonShape geospatial)
- @xterm/xterm + @xterm/addon-fit (Multmux terminal)
- safe-apps-sdk or ethers (TransactionBuilder, if needed)
Also verify existing deps like perfect-freehand are sufficient for Drawfast.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 All required npm packages installed
- [ ] #2 No build errors after adding dependencies
- [ ] #3 WASM plugins configured if needed (h3-js)
<!-- AC:END -->

View File

@ -0,0 +1,38 @@
---
id: TASK-25
title: Add server API proxy endpoints for new shapes
status: To Do
assignee: []
created_date: '2026-02-18 19:49'
labels:
- infrastructure
- phase-1
- server
milestone: m-0
dependencies: []
references:
- rspace-online/server/index.ts
- canvas-website/src/shapes/ImageGenShapeUtil.tsx (API pattern reference)
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Add API routes to server/index.ts for shapes that call external services:
- POST /api/blender-gen — Proxy to Blender generation service
- POST /api/mycrozine — Proxy for zine generation LLM calls
- POST /api/fathom/* — Proxy to Fathom API (meeting transcripts)
- POST /api/obsidian/* — Local Obsidian vault file operations
- POST /api/holon/* — HoloSphere network queries
- WebSocket endpoint for terminal sessions (Multmux)
Follow existing pattern from /api/image-gen endpoint.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 All proxy endpoints return expected responses
- [ ] #2 WebSocket terminal endpoint accepts connections
- [ ] #3 Error handling and auth middleware applied
<!-- AC:END -->

View File

@ -0,0 +1,46 @@
---
id: TASK-26
title: Port folk-blender-gen shape (3D procedural generation)
status: To Do
assignee: []
created_date: '2026-02-18 19:49'
labels:
- shape-port
- phase-2
- ai
milestone: m-0
dependencies:
- TASK-24
- TASK-25
references:
- canvas-website/src/shapes/BlenderGenShapeUtil.tsx
- rspace-online/lib/folk-image-gen.ts (pattern reference)
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Port BlenderGenShapeUtil from canvas-website to rspace-online as a FolkShape Web Component.
Source: canvas-website/src/shapes/BlenderGenShapeUtil.tsx (693 lines)
Target: rspace-online/lib/folk-blender-gen.ts
Features to implement:
- Blender script editor textarea
- LLM code generation (prompt → Blender Python script)
- 3D preview iframe/canvas
- Model download button
- Loading/error states
Follow folk-image-gen.ts pattern for API-calling shape with loading/result states.
Needs /api/blender-gen server endpoint (TASK-25).
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Shape renders with script editor and 3D preview
- [ ] #2 LLM code generation produces valid Blender scripts
- [ ] #3 Results sync across clients via Automerge
- [ ] #4 Toolbar button added to canvas.html
<!-- AC:END -->

View File

@ -0,0 +1,36 @@
---
id: TASK-27
title: Port folk-mycrozine-template shape (zine template picker)
status: To Do
assignee: []
created_date: '2026-02-18 19:50'
labels:
- shape-port
- phase-2
- ai
milestone: m-0
dependencies:
- TASK-24
references:
- canvas-website/src/shapes/MycrozineTemplateShapeUtil.tsx
priority: low
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Port MycrozineTemplateShapeUtil from canvas-website to rspace-online.
Source: canvas-website/src/shapes/MycrozineTemplateShapeUtil.tsx (80 lines)
Target: rspace-online/lib/folk-mycrozine-template.ts
Features: Template gallery picker, preview panel, template selection that launches MycroZineGenerator.
Small shape — mostly a launcher UI for the generator.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Template gallery displays available templates
- [ ] #2 Selecting a template creates/configures a MycroZineGenerator shape
- [ ] #3 Toolbar button added to canvas.html
<!-- AC:END -->

View File

@ -0,0 +1,45 @@
---
id: TASK-28
title: Port folk-mycrozine-gen shape (AI zine generator)
status: To Do
assignee: []
created_date: '2026-02-18 19:50'
labels:
- shape-port
- phase-2
- ai
milestone: m-0
dependencies:
- TASK-24
- TASK-25
references:
- canvas-website/src/shapes/MycroZineGeneratorShapeUtil.tsx
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Port MycroZineGeneratorShapeUtil from canvas-website to rspace-online.
Source: canvas-website/src/shapes/MycroZineGeneratorShapeUtil.tsx (1,222 lines)
Target: rspace-online/lib/folk-mycrozine-gen.ts
Features to implement:
- Prompt input for zine content
- Style and layout settings UI
- AI-powered multi-page zine generation via LLM
- Page preview/navigation
- Export/download functionality
Largest AI shape to port. Needs /api/mycrozine server endpoint (TASK-25).
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Prompt input generates zine pages via LLM
- [ ] #2 Multi-page navigation works
- [ ] #3 Style/layout settings affect output
- [ ] #4 Results sync across clients via Automerge
- [ ] #5 Toolbar button added to canvas.html
<!-- AC:END -->

View File

@ -0,0 +1,41 @@
---
id: TASK-29
title: Port folk-drawfast shape (collaborative drawing/gesture recognition)
status: To Do
assignee: []
created_date: '2026-02-18 19:50'
labels:
- shape-port
- phase-2
- creative
milestone: m-0
dependencies:
- TASK-24
references:
- canvas-website/src/shapes/DrawfastShapeUtil.tsx
priority: low
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Port DrawfastShapeUtil from canvas-website to rspace-online.
Source: canvas-website/src/shapes/DrawfastShapeUtil.tsx (652 lines)
Target: rspace-online/lib/folk-drawfast.ts
Features to implement:
- Freehand sketch input canvas
- Gesture recognition (circles, lines, rectangles, arrows)
- Shape detection and conversion
- Real-time collaborative drawing
- May use perfect-freehand (already in rspace deps)
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Freehand drawing works with pointer/touch input
- [ ] #2 Gesture recognition detects basic shapes
- [ ] #3 Drawing state syncs across clients
- [ ] #4 Toolbar button added to canvas.html
<!-- AC:END -->

View File

@ -0,0 +1,47 @@
---
id: TASK-30
title: Port folk-holon shape (H3 geospatial hex hierarchy)
status: To Do
assignee: []
created_date: '2026-02-18 19:50'
labels:
- shape-port
- phase-3
- data-integration
milestone: m-0
dependencies:
- TASK-24
- TASK-25
references:
- canvas-website/src/shapes/HolonShapeUtil.tsx
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Port HolonShapeUtil from canvas-website to rspace-online.
Source: canvas-website/src/shapes/HolonShapeUtil.tsx (1,216 lines)
Target: rspace-online/lib/folk-holon.ts
Features to implement:
- H3 geospatial hexagonal hierarchy visualization
- HoloSphere network connection (real-time)
- Multi-lens data viewing (switch between data perspectives)
- In-place editing with auto-resize
- Location props: latitude, longitude, resolution
- Content props: name, description, holonId
- State: isConnected, isEditing, selectedLens, data, connections
Dependencies: h3-js (TASK-24), /api/holon/* endpoints (TASK-25)
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 H3 hex hierarchy renders correctly
- [ ] #2 HoloSphere network connection works
- [ ] #3 Multi-lens data switching functional
- [ ] #4 Geospatial props sync across clients
- [ ] #5 Toolbar button added to canvas.html
<!-- AC:END -->

View File

@ -0,0 +1,36 @@
---
id: TASK-31
title: Port folk-holon-browser shape (Holon network browser)
status: To Do
assignee: []
created_date: '2026-02-18 19:50'
labels:
- shape-port
- phase-3
- data-integration
milestone: m-0
dependencies:
- TASK-30
references:
- canvas-website/src/shapes/HolonBrowserShapeUtil.tsx
priority: low
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Port HolonBrowserShapeUtil from canvas-website to rspace-online.
Source: canvas-website/src/shapes/HolonBrowserShapeUtil.tsx (202 lines)
Target: rspace-online/lib/folk-holon-browser.ts
Features: Network visualization, search, filtering through Holon data. Companion to folk-holon — shares HoloSphere service client.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Browse/search Holon network data
- [ ] #2 Visualization renders network graph
- [ ] #3 Can open individual Holons from browser
- [ ] #4 Toolbar button added to canvas.html
<!-- AC:END -->

View File

@ -0,0 +1,47 @@
---
id: TASK-32
title: Port folk-obsidian-browser shape (Obsidian vault explorer)
status: To Do
assignee: []
created_date: '2026-02-18 19:50'
labels:
- shape-port
- phase-3
- data-integration
milestone: m-0
dependencies:
- TASK-25
references:
- canvas-website/src/shapes/ObsidianBrowserShapeUtil.tsx
- canvas-website/src/components/ObsidianVaultBrowser.tsx
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Port ObsidianBrowserShapeUtil + ObsidianVaultBrowser component from canvas-website to rspace-online.
Source: canvas-website/src/shapes/ObsidianBrowserShapeUtil.tsx (413 lines) + canvas-website/src/components/ObsidianVaultBrowser.tsx (1,694 lines)
Target: rspace-online/lib/folk-obsidian-browser.ts
Total: 2,107 lines — one of the largest ports.
Features to implement:
- Obsidian vault file tree navigation
- Full-text search across vault
- Backlink preview and navigation
- Note opening (creates folk-obs-note shapes)
- Vault metadata display
Needs /api/obsidian/* server endpoints for local vault file operations (TASK-25).
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 File tree renders vault directory structure
- [ ] #2 Full-text search returns matching notes
- [ ] #3 Backlink preview displays on hover/click
- [ ] #4 Selecting a note creates folk-obs-note shape
- [ ] #5 Toolbar button added to canvas.html
<!-- AC:END -->

View File

@ -0,0 +1,44 @@
---
id: TASK-33
title: Port folk-fathom-browser shape (Fathom meetings integration)
status: To Do
assignee: []
created_date: '2026-02-18 19:50'
labels:
- shape-port
- phase-3
- data-integration
milestone: m-0
dependencies:
- TASK-25
references:
- canvas-website/src/shapes/FathomMeetingsBrowserShapeUtil.tsx
- canvas-website/src/components/FathomMeetingsPanel.tsx
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Port FathomMeetingsBrowserShapeUtil + FathomMeetingsPanel from canvas-website to rspace-online.
Source: canvas-website/src/shapes/FathomMeetingsBrowserShapeUtil.tsx (549 lines) + canvas-website/src/components/FathomMeetingsPanel.tsx (705 lines)
Target: rspace-online/lib/folk-fathom-browser.ts
Features to implement:
- Fathom API integration (meeting list, transcripts)
- Meeting list with search/filter
- Transcript search with speaker identification
- Open individual meetings as folk-fathom-note shapes
Needs /api/fathom/* proxy endpoints (TASK-25).
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Fathom API returns meeting list
- [ ] #2 Search/filter meetings works
- [ ] #3 Speaker identification displayed
- [ ] #4 Selecting meeting creates folk-fathom-note
- [ ] #5 Toolbar button added to canvas.html
<!-- AC:END -->

View File

@ -0,0 +1,42 @@
---
id: TASK-34
title: Port folk-fathom-note shape (individual meeting note)
status: To Do
assignee: []
created_date: '2026-02-18 19:50'
labels:
- shape-port
- phase-3
- data-integration
milestone: m-0
dependencies:
- TASK-33
references:
- canvas-website/src/shapes/FathomNoteShapeUtil.tsx
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Port FathomNoteShapeUtil from canvas-website to rspace-online.
Source: canvas-website/src/shapes/FathomNoteShapeUtil.tsx (667 lines)
Target: rspace-online/lib/folk-fathom-note.ts
Features to implement:
- Individual meeting note display
- Speaker clips with timestamps
- Timestamp linking (click to jump)
- Note editing and annotation
- Created by folk-fathom-browser when user selects a meeting
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Meeting note renders with transcript content
- [ ] #2 Speaker clips display correctly
- [ ] #3 Timestamp links navigate within transcript
- [ ] #4 Note editing syncs across clients
- [ ] #5 Toolbar button added to canvas.html
<!-- AC:END -->

View File

@ -0,0 +1,46 @@
---
id: TASK-35
title: Port folk-multmux shape (xterm.js terminal emulator)
status: To Do
assignee: []
created_date: '2026-02-18 19:50'
labels:
- shape-port
- phase-4
- dev-tools
milestone: m-0
dependencies:
- TASK-24
- TASK-25
references:
- canvas-website/src/shapes/MultmuxShapeUtil.tsx
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Port MultmuxShapeUtil from canvas-website to rspace-online.
Source: canvas-website/src/shapes/MultmuxShapeUtil.tsx (850 lines)
Target: rspace-online/lib/folk-multmux.ts
Features to implement:
- xterm.js terminal emulator in a shape
- WebSocket session management with auto-reconnect
- Session naming and persistence
- Fit addon for responsive terminal sizing
- Shape migration support (versioning)
Dependencies: @xterm/xterm, @xterm/addon-fit (TASK-24)
Needs WebSocket terminal endpoint on server (TASK-25).
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Terminal renders with xterm.js
- [ ] #2 WebSocket connection to terminal session works
- [ ] #3 Auto-reconnect on disconnect
- [ ] #4 Session state persists across page reloads
- [ ] #5 Toolbar button added to canvas.html
<!-- AC:END -->

View File

@ -0,0 +1,41 @@
---
id: TASK-36
title: Port folk-private-workspace shape (data sovereignty zone)
status: To Do
assignee: []
created_date: '2026-02-18 19:50'
labels:
- shape-port
- phase-4
- privacy
milestone: m-0
dependencies:
- TASK-24
references:
- canvas-website/src/shapes/PrivateWorkspaceShapeUtil.tsx
priority: low
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Port PrivateWorkspaceShapeUtil from canvas-website to rspace-online.
Source: canvas-website/src/shapes/PrivateWorkspaceShapeUtil.tsx (370 lines)
Target: rspace-online/lib/folk-private-workspace.ts
Features to implement:
- Data sovereignty container zone
- Visibility badges (public/private indicators)
- Private data compartmentalization
- Works with existing folk-google-item shape
- Drag-in/drag-out items to change privacy scope
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Workspace zone renders with privacy boundary
- [ ] #2 Visibility badges display correctly
- [ ] #3 Items inside zone respect privacy scope
- [ ] #4 Toolbar button added to canvas.html
<!-- AC:END -->

View File

@ -0,0 +1,47 @@
---
id: TASK-37
title: Port folk-transaction-builder shape (Safe multisig)
status: To Do
assignee: []
created_date: '2026-02-18 19:51'
labels:
- shape-port
- phase-4
- web3
milestone: m-0
dependencies:
- TASK-24
references:
- canvas-website/src/shapes/TransactionBuilderShapeUtil.tsx
- canvas-website/src/components/safe/TransactionComposer.tsx
- canvas-website/src/components/safe/PendingTransactions.tsx
- canvas-website/src/components/safe/TransactionHistory.tsx
priority: low
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Port TransactionBuilderShapeUtil + Safe components from canvas-website to rspace-online.
Source: canvas-website/src/shapes/TransactionBuilderShapeUtil.tsx (157 lines) + canvas-website/src/components/safe/ (585 lines total: SafeHeader, TransactionComposer, PendingTransactions, TransactionHistory)
Target: rspace-online/lib/folk-transaction-builder.ts
Features to implement:
- Transaction composition UI (select recipient, amount, data)
- Pending transaction queue display
- Transaction history view
- Mode switching: compose/pending/history
- Safe wallet integration
May need safe-apps-sdk or ethers.js dependency (TASK-24).
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Transaction composer creates valid transactions
- [ ] #2 Pending queue displays waiting transactions
- [ ] #3 History view shows past transactions
- [ ] #4 Mode switching works (compose/pending/history)
- [ ] #5 Toolbar button added to canvas.html
<!-- AC:END -->

View File

@ -0,0 +1,44 @@
---
id: TASK-38
title: Port folk-calendar-event shape (calendar event sub-shape)
status: To Do
assignee: []
created_date: '2026-02-18 19:51'
labels:
- shape-port
- phase-4
milestone: m-0
dependencies:
- TASK-24
references:
- canvas-website/src/shapes/CalendarEventShapeUtil.tsx
- rspace-online/lib/folk-calendar.ts
priority: low
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Port CalendarEventShapeUtil from canvas-website to rspace-online.
Source: canvas-website/src/shapes/CalendarEventShapeUtil.tsx (457 lines)
Target: rspace-online/lib/folk-calendar-event.ts
Features to implement:
- Individual calendar event display
- Time/date formatting and display
- Recurrence info visualization
- Color coding by event type/calendar
- Created by existing folk-calendar when user drags out an event
Companion to existing folk-calendar shape.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Event renders with title, time, and color
- [ ] #2 Recurrence info displays correctly
- [ ] #3 folk-calendar can create calendar-event shapes
- [ ] #4 Event data syncs across clients
- [ ] #5 Toolbar button added to canvas.html
<!-- AC:END -->

View File

@ -0,0 +1,50 @@
---
id: TASK-39
title: Port MycelialIntelligence system (global AI bar + shape)
status: To Do
assignee: []
created_date: '2026-02-18 19:51'
labels:
- shape-port
- phase-5
- ai
- infrastructure
milestone: m-0
dependencies:
- TASK-25
references:
- canvas-website/src/ui/MycelialIntelligenceBar.tsx
- canvas-website/src/shapes/MycelialIntelligenceShapeUtil.tsx
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Port the MycelialIntelligence system from canvas-website to rspace-online. This is a GLOBAL UI element (not just a shape).
Sources:
- canvas-website/src/ui/MycelialIntelligenceBar.tsx (2,231 lines) — the main AI bar
- canvas-website/src/shapes/MycelialIntelligenceShapeUtil.tsx (69 lines) — backward-compat shape
Target: rspace-online/lib/mycelial-intelligence-bar.ts (Web Component) + rspace-online/lib/folk-mycelial-intelligence.ts (shape)
This is the largest single migration item. Implement in phases:
Phase A: Basic chat UI bar (fixed bottom bar with prompt input + response display)
Phase B: Canvas context awareness (knows selected shapes, viewport contents)
Phase C: Shape creation/modification via AI commands (create shapes, edit properties)
Phase D: Full tool integration (all AI capabilities available through bar)
The bar should be added as a persistent element in canvas.html, independent of the shape system.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 AI bar renders as persistent bottom UI element
- [ ] #2 Chat prompt sends to LLM and displays responses
- [ ] #3 Bar is context-aware of selected shapes and canvas state
- [ ] #4 Can create/modify shapes via AI commands
- [ ] #5 Backward-compat folk-mycelial-intelligence shape exists
- [ ] #6 Toolbar button toggles bar visibility
<!-- AC:END -->

View File

@ -0,0 +1,53 @@
---
id: TASK-40
title: Port workflow engine (propagators + execution)
status: To Do
assignee: []
created_date: '2026-02-18 19:51'
labels:
- infrastructure
- phase-6
- workflow
milestone: m-0
dependencies:
- TASK-24
references:
- canvas-website/src/lib/workflow/
- canvas-website/src/propagators/WorkflowPropagator.ts
- canvas-website/src/propagators/ScopedPropagators.ts
- rspace-online/lib/folk-workflow-block.ts
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Port the workflow execution engine from canvas-website to rspace-online. This powers the existing folk-workflow-block shape with actual data flow and execution.
Source files (3,676 lines total):
- canvas-website/src/lib/workflow/types.ts (159 lines) — type definitions
- canvas-website/src/lib/workflow/blockRegistry.ts (438 lines) — block definition registry
- canvas-website/src/lib/workflow/executor.ts (548 lines) — block execution engine
- canvas-website/src/lib/workflow/portBindings.ts (464 lines) — port connection logic
- canvas-website/src/lib/workflow/validation.ts (417 lines) — graph validation
- canvas-website/src/lib/workflow/serialization.ts (571 lines) — save/load workflows
- canvas-website/src/propagators/WorkflowPropagator.ts (326 lines) — real-time data flow
Target: rspace-online/lib/workflow/ directory
Key adaptation needed: canvas-website uses tldraw store for state; rspace-online uses Automerge. The execution engine needs to read/write shape data through CommunitySync instead of tldraw's store.
Also port relevant propagator concepts:
- canvas-website/src/propagators/ScopedPropagators.ts (314 lines)
- canvas-website/src/propagators/SpatialIndex.ts (164 lines)
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Block registry loads available block types
- [ ] #2 Port connections transfer data between blocks
- [ ] #3 Execution engine runs blocks in correct order
- [ ] #4 Validation catches invalid graph configurations
- [ ] #5 Workflows serialize/deserialize through Automerge
- [ ] #6 Real-time propagation updates connected blocks
<!-- AC:END -->

View File

@ -0,0 +1,41 @@
---
id: TASK-41
title: Build dynamic Shape Registry to replace hardcoded switch statements
status: To Do
assignee: []
created_date: '2026-02-18 20:06'
labels:
- infrastructure
- phase-0
- ecosystem
milestone: m-1
dependencies: []
references:
- rspace-online/lib/folk-shape.ts
- rspace-online/website/canvas.html
- rspace-online/lib/community-sync.ts
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Replace the 170-line switch statement in canvas.html's `createShapeElement()` and the 100-line type-switch in community-sync.ts's `#updateShapeElement()` with a dynamic ShapeRegistry.
Create lib/shape-registry.ts with:
- ShapeRegistration interface (tagName, elementClass, defaults, category, portDescriptors, eventDescriptors)
- ShapeRegistry class with register(), createElement(), updateElement(), listAll(), getByCategory()
- Each folk-*.ts gets a static `registration` property and static `fromData()` method
This is the prerequisite for all other ecosystem features (pipes, events, groups, nesting, embedding).
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 ShapeRegistry class created with register/createElement/updateElement methods
- [ ] #2 All 30+ folk-*.ts shapes have static registration property
- [ ] #3 canvas.html switch statement replaced with registry.createElement()
- [ ] #4 community-sync.ts type-switch replaced with registry.updateElement()
- [ ] #5 All existing shapes still create and sync correctly
- [ ] #6 No regression in shape creation or remote sync
<!-- AC:END -->

View File

@ -0,0 +1,62 @@
---
id: TASK-42
title: 'Implement Data Pipes: typed data flow through arrows'
status: To Do
assignee: []
created_date: '2026-02-18 20:06'
labels:
- feature
- phase-1
- ecosystem
milestone: m-1
dependencies:
- TASK-41
references:
- rspace-online/lib/folk-arrow.ts
- rspace-online/lib/folk-shape.ts
- rspace-online/lib/folk-image-gen.ts
- rspace-online/lib/folk-prompt.ts
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Transform folk-arrow from visual-only connector into a typed data conduit between shapes.
New file lib/data-types.ts:
- DataType enum: string, number, boolean, image-url, video-url, text, json, trigger, any
- Type compatibility matrix and isCompatible() function
Add port mixin to FolkShape:
- ports map, getPort(), setPortValue(), onPortValueChanged()
- Port values stored in Automerge: doc.shapes[id].ports[name].value
- 100ms debounce on port propagation to prevent keystroke thrashing
Enhance folk-arrow:
- sourcePort/targetPort fields referencing named ports
- Listen for port-value-changed on source, push to target
- Type compatibility check before pushing
- Visual: arrows tinted by data type, flow animation when active
- Port handle UI during connect mode
Add port descriptors to AI shapes:
- folk-image-gen: input "prompt" (text), output "image" (image-url)
- folk-video-gen: input "prompt" (text), input "image" (image-url), output "video" (video-url)
- folk-prompt: input "context" (text), output "response" (text)
- folk-transcription: output "transcript" (text)
Example pipeline: Transcription →[text]→ Prompt →[text]→ ImageGen →[image-url]→ VideoGen
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 DataType system with compatibility matrix works
- [ ] #2 Shapes can declare input/output ports via registration
- [ ] #3 setPortValue() writes to Automerge and dispatches event
- [ ] #4 folk-arrow pipes data from source port to target port
- [ ] #5 Type incompatible connections show warning
- [ ] #6 Arrows visually indicate data type and active flow
- [ ] #7 Port values sync to remote clients via Automerge
- [ ] #8 100ms debounce prevents thrashing on rapid changes
<!-- AC:END -->

View File

@ -0,0 +1,48 @@
---
id: TASK-43
title: 'Implement Event Broadcasting: canvas-wide pub/sub system'
status: To Do
assignee: []
created_date: '2026-02-18 20:06'
labels:
- feature
- phase-2
- ecosystem
milestone: m-1
dependencies:
- TASK-41
references:
- rspace-online/lib/community-sync.ts
- rspace-online/server/community-store.ts
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Add a pub/sub event system so shapes can broadcast and subscribe to named events across the canvas.
New file lib/event-bus.ts:
- CanvasEventBus class with emit(), subscribe(), unsubscribe(), getSubscribers()
- Events written to CRDT doc.eventLog (bounded ring buffer, last 100 entries)
- Remote users see events replayed via Automerge patch application
- Re-entrancy guard kills chains after 10 levels to prevent infinite loops
Automerge schema additions:
- doc.eventLog: EventEntry[] (id, channel, sourceShapeId, payload, timestamp)
- shapes[id].subscriptions: string[] (channel names)
Shapes opt in with onEventReceived(channel, payload) method.
Example: Timer emits "timer:done" → all subscribed Budget shapes recalculate.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 CanvasEventBus emits events to CRDT eventLog
- [ ] #2 Shapes can subscribe to channels and receive events
- [ ] #3 Events sync to remote users via Automerge
- [ ] #4 Ring buffer bounded at 100 entries with GC
- [ ] #5 Re-entrancy guard prevents infinite event loops
- [ ] #6 Works offline (events queued in CRDT, replayed on reconnect)
<!-- AC:END -->

View File

@ -0,0 +1,58 @@
---
id: TASK-44
title: 'Implement Semantic Grouping: named shape clusters with templates'
status: To Do
assignee: []
created_date: '2026-02-18 20:06'
labels:
- feature
- phase-3
- ecosystem
milestone: m-1
dependencies:
- TASK-41
references:
- rspace-online/lib/DOMRectTransform.ts
- rspace-online/lib/community-sync.ts
- rspace-online/server/community-store.ts
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Add the ability to group shapes into named clusters that can be collapsed, moved together, and saved as reusable templates.
New file lib/group-manager.ts:
- GroupManager class: createGroup(), dissolveGroup(), addToGroup(), removeFromGroup()
- collapseGroup() / expandGroup() — hide members, show summary card
- moveGroup(groupId, dx, dy) — moves all members by delta
- saveAsTemplate() / instantiateTemplate() — serialize group as reusable JSON template
Automerge schema additions:
- doc.groups: { [groupId]: GroupData } — top-level CRDT entity
- GroupData: id, name, color, icon, memberShapeIds[], collapsed, x, y, width, height
- shapes[id].groupId: string — which group a shape belongs to
Visual rendering:
- folk-group-frame — lightweight overlay element (NOT a FolkShape), dashed border + header bar
- Recalculates bounds from member shapes on each animation frame
- Uses existing DOMRectTransform.ts for rotated shape bounding boxes
- Collapse button, group name, color indicator
Canvas.html additions:
- Rubber-band or shift-click multi-select → right-click "Group"
- Group context menu (rename, change color, collapse, save as template, dissolve)
- Template library panel for saved templates
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 GroupManager creates/dissolves groups stored in Automerge
- [ ] #2 Moving group header moves all member shapes together
- [ ] #3 Collapse hides members and shows summary card
- [ ] #4 Expand restores all members to original positions
- [ ] #5 Groups sync to remote users via Automerge
- [ ] #6 Save as template serializes group + internal arrows as JSON
- [ ] #7 Instantiate template creates new shapes from template
<!-- AC:END -->

View File

@ -0,0 +1,57 @@
---
id: TASK-45
title: 'Implement Shape Nesting: shapes containing shapes + recursive canvas'
status: To Do
assignee: []
created_date: '2026-02-18 20:06'
labels:
- feature
- phase-4
- ecosystem
milestone: m-1
dependencies:
- TASK-44
references:
- rspace-online/lib/folk-shape.ts
- rspace-online/lib/community-sync.ts
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Allow shapes to contain other shapes, including a recursive canvas shape.
Automerge schema additions (flat references, NOT nested objects):
- shapes[id].parentId: string — nested inside this shape
- shapes[id].childIds: string[] — contains these shapes
New shape lib/folk-canvas.ts:
- A shape that IS a canvas — renders child shapes inside scrollable/zoomable container
- Optional linkedCommunitySlug to show shapes from another community (cross-canvas embedding)
- Own zoom/pan controls within the mini-canvas
Coordinate system:
- Children store ABSOLUTE canvas coordinates in CRDT (simplifies sync, prevents jitter)
- folk-canvas applies CSS transform offset so children appear inside it
- When parent moves, nesting manager applies delta to all children
- Nesting is a render-time concern, not a data-model concern
FolkShape additions:
- parentShape getter, childShapes getter
- addChild(), removeChild()
- toParentCoords(), toCanvasCoords() for coordinate transforms
Canvas.html: drag-drop shape onto folk-canvas to nest it.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Shapes can be nested inside folk-canvas via drag-drop
- [ ] #2 Nested shapes move with parent when parent is moved
- [ ] #3 folk-canvas has its own zoom/pan controls
- [ ] #4 parentId/childIds sync correctly via Automerge
- [ ] #5 Un-nesting a shape restores it to top-level canvas
- [ ] #6 No coordinate jitter when two users move parent and child simultaneously
- [ ] #7 Optional cross-canvas linking via linkedCommunitySlug
<!-- AC:END -->

View File

@ -0,0 +1,66 @@
---
id: TASK-46
title: 'Implement Cross-App Embedding: r-ecosystem apps in rSpace canvases'
status: To Do
assignee: []
created_date: '2026-02-18 20:07'
labels:
- feature
- phase-5
- ecosystem
milestone: m-1
dependencies:
- TASK-41
- TASK-42
references:
- rspace-online/lib/shape-registry.ts
- rspace-online/server/index.ts
- rspace-online/website/canvas.html
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Allow r-ecosystem apps (rWallet, rVote, rMaps, etc.) to embed their live UI into rSpace canvases via dynamically loaded Web Components.
Ecosystem App Manifest Protocol:
- Each app hosts /.well-known/rspace-manifest.json
- Manifest declares: appId, name, icon, moduleUrl, shapes[] (tagName, defaults, portDescriptors, eventDescriptors)
New file lib/ecosystem-bridge.ts:
- EcosystemBridge class: loadManifest(), loadModule(), createSandboxedEmbed()
- Two embedding modes:
1. Trusted (Web Component): dynamic import(), shares CRDT directly, full port/event access
2. Sandboxed (iframe): postMessage bridge for untrusted apps, limited API
New Automerge shape type:
- type: "ecosystem-embed", appId, moduleUrl, config, sandboxed boolean
Server additions:
- GET /api/ecosystem/:appId/manifest — proxy to avoid CORS
- Server pre-fetches and caches ecosystem manifests
Canvas.html additions:
- Dynamic toolbar section for ecosystem apps (loaded from manifests)
- Lazy module loading on first use
- Service Worker caches modules for offline
Runtime:
1. Server fetches ecosystem manifests → toolbar shows app buttons
2. User adds ecosystem shape → module import()-ed → Web Component registered → shape created
3. Remote sync: create-shape for ecosystem-embed triggers lazy module load on other clients
4. Embedded components participate in port/event system like native shapes
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Ecosystem manifest protocol defined and documented
- [ ] #2 EcosystemBridge loads manifests and dynamic imports modules
- [ ] #3 Trusted Web Components share CRDT and port/event system
- [ ] #4 Sandboxed iframe mode works with postMessage bridge
- [ ] #5 Server proxy avoids CORS for manifest/module loading
- [ ] #6 Toolbar dynamically shows ecosystem app buttons
- [ ] #7 Remote clients lazy-load modules when ecosystem shapes appear
- [ ] #8 Service Worker caches ecosystem modules for offline
<!-- AC:END -->

View File

@ -0,0 +1,32 @@
---
id: TASK-48
title: Fix TypeScript build errors blocking deployment
status: Done
assignee: []
created_date: '2026-02-24 03:51'
labels:
- bugfix
- typescript
- build
dependencies: []
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Resolved all TypeScript compilation errors that were preventing `npm run build` from succeeding on the server. The build command `tsc --noEmit && vite build` was failing with ~50 errors across multiple modules.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 tsc --noEmit passes with zero errors
- [ ] #2 vite build completes successfully
- [ ] #3 Docker build and deploy succeeds on Netcup
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Fixed ~50 TypeScript errors across the codebase:\n\n- **9 modules** (cal, cart, forum, inbox, notes, providers, trips, vote, work): Changed `unknown[]``any[]` for `sql.unsafe()` parameter arrays. The postgres.js `ParameterOrJSON<never>` type was too restrictive when no custom types are defined.\n- **website/sw.ts**: Excluded from tsconfig — service worker needs WebWorker types, not DOM types, and is built separately by Vite.\n- **src/lib/demo-sync.ts**: Excluded from tsconfig — React hook for external consumers, React not a project dependency.\n- **modules/splat**: Fixed Hono context `c.get()` typing via cast, `instanceof File` via `unknown` cast, and inline sql.unsafe arrays.\n- **modules/swag/folk-swag-designer.ts**: Renamed `private title``private designTitle` to avoid collision with `HTMLElement.title`.\n- **types/gaussian-splats-3d.d.ts**: Added type declaration stub for CDN-loaded package.\n\nCommit: b2f1beb on main. Deployed to Netcup successfully.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@ -0,0 +1,32 @@
---
id: TASK-49
title: Add admin dashboard at /admin with space overview
status: Done
assignee:
- '@claude'
created_date: '2026-02-24 23:29'
labels:
- feature
- admin
- frontend
dependencies: []
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Added an admin dashboard page at /admin that shows all rSpace spaces with detailed stats including shape count, member count, file size on disk, visibility, creation date, and owner DID. Includes search, filter by visibility, and sort controls. Also added /api/spaces/admin API endpoint returning all space data.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Admin page accessible at /admin
- [ ] #2 Shows all spaces with shape count, member count, file size
- [ ] #3 Search bar filters by name/slug/owner
- [ ] #4 Visibility filter buttons work
- [ ] #5 Sort dropdown works (date, name, shapes, size)
- [ ] #6 API endpoint at /api/spaces/admin returns detailed space data
- [ ] #7 Vite build includes admin.html
- [ ] #8 Consistent styling with existing rSpace dark theme
<!-- AC:END -->

View File

@ -0,0 +1,87 @@
---
id: TASK-50
title: Implement nested spaces architecture with permission cascade
status: Done
assignee:
- '@claude'
created_date: '2026-02-25 02:27'
updated_date: '2026-02-25 02:43'
labels:
- architecture
- spaces
- permissions
- encryptid
dependencies: []
references:
- server/community-store.ts
- server/spaces.ts
- src/encryptid/server.ts
- lib/community-sync.ts
documentation:
- docs/SPACE-ARCHITECTURE.md
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Spaces are now nestable — any space can embed references to other spaces via SpaceRef, with a permission cascade model (most-restrictive-wins at each nesting boundary). Every EncryptID registration auto-provisions a sovereign space at &lt;username&gt;.rspace.online with consent-based nesting controls.
Core principle: a space is a space is a space. No type field distinguishing personal vs community. The "personal" quality emerges from ownership + permissions, not a schema distinction.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 NestPolicy type with consent levels (open/members/approval/closed)
- [ ] #2 SpaceRef CRUD endpoints on /api/spaces/:slug/nest
- [ ] #3 Permission cascade via intersection (most-restrictive-wins)
- [ ] #4 Approval flow for nest requests with admin review
- [ ] #5 Source space admins can always revoke nestings (sovereignty guarantee)
- [ ] #6 Auto-provision <username>.rspace.online on EncryptID registration
- [ ] #7 defaultPermissions ceiling caps requested permissions
- [ ] #8 Allowlist/blocklist per space
- [ ] #9 Reverse lookup (nested-in) endpoint
- [ ] #10 Client-side types for nested space rendering
- [ ] #11 TypeScript compiles clean
- [ ] #12 Full architecture spec at docs/SPACE-ARCHITECTURE.md
<!-- AC:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Phase 3-5 implemented and pushed to dev:
- Phase 3: folk-canvas nested space shape with live WS, auto-scaling, collapsed/expanded views
- Phase 4: WS cascade enforcement — nest filter on broadcasts, addShapes/deleteShapes checks
- Phase 5: AES-256-GCM at-rest encryption with transparent encrypt/decrypt and API endpoints
- All phases type-check clean (npx tsc --noEmit)
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
## Phase 1+2 Implementation Complete
### Schema changes (community-store.ts)
- New types: NestPermissions, NestNotifications, NestPolicy, SpaceRef, SpaceRefFilter, PendingNestRequest
- Default policies: DEFAULT_USER_NEST_POLICY (approval consent) and DEFAULT_COMMUNITY_NEST_POLICY (members consent)
- Updated CommunityMeta with enabledModules, description, avatar, nestPolicy, encrypted fields
- Updated CommunityDoc with nestedSpaces map
- CRUD: addNestedSpace, updateNestedSpace, removeNestedSpace, getNestPolicy, updateNestPolicy, setEnabledModules
- Permission logic: capPermissions (ceiling), cascadePermissions (intersection)
- Reverse lookup: findNestedIn
### REST API (spaces.ts)
- GET/PATCH /:slug/nest-policy
- GET/POST /:slug/nest (with full consent flow)
- GET/PATCH/DELETE /:slug/nest/:refId
- GET /:slug/nested-in
- GET/PATCH /:slug/nest-requests (approval flow)
### Auto-provisioning (encryptid/server.ts)
- After registration, creates &lt;username&gt;.rspace.online with members_only visibility, user nest policy, default modules
### Remaining phases
- Phase 3: folk-canvas shape renderer for SpaceRef entries
- Phase 4: Full cascade enforcement on WebSocket writes
- Phase 5: Encryption integration
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@ -223,7 +223,7 @@ routes.get("/", async (c) => {
<folk-book-shelf id="shelf"></folk-book-shelf>
`,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
head: `<link rel="stylesheet" href="/modules/books/books.css">`,
scripts: `
<script type="module">

View File

@ -380,7 +380,7 @@ routes.get("/", (c) => {
moduleId: "cal",
spaceSlug: space,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: `<link rel="stylesheet" href="/modules/cal/cal.css">`,
body: `<folk-calendar-view space="${space}"></folk-calendar-view>`,
scripts: `<script type="module" src="/modules/cal/folk-calendar-view.js"></script>`,

View File

@ -42,7 +42,7 @@ routes.get("/", async (c) => {
spaceSlug,
body: canvasBody,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
scripts: `<script type="module" src="/canvas-module.js"></script>`,
});

View File

@ -446,7 +446,7 @@ routes.get("/", (c) => {
moduleId: "cart",
spaceSlug: space,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: `<link rel="stylesheet" href="/modules/cart/cart.css">`,
body: `<folk-cart-shop></folk-cart-shop>`,
scripts: `<script type="module" src="/modules/cart/folk-cart-shop.js"></script>`,

View File

@ -53,7 +53,7 @@ routes.get("/", (c) => {
moduleId: "choices",
spaceSlug,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: `<link rel="stylesheet" href="/modules/choices/choices.css">`,
body: `<folk-choices-dashboard space="${spaceSlug}"></folk-choices-dashboard>`,
scripts: `<script type="module" src="/modules/choices/folk-choices-dashboard.js"></script>`,

View File

@ -125,7 +125,7 @@ routes.get("/", (c) => {
moduleId: "data",
spaceSlug: space,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: `<link rel="stylesheet" href="/modules/data/data.css">`,
body: `<folk-analytics-view space="${space}"></folk-analytics-view>`,
scripts: `<script type="module" src="/modules/data/folk-analytics-view.js"></script>`,

View File

@ -371,7 +371,7 @@ routes.get("/", (c) => {
moduleId: "files",
spaceSlug,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: `<link rel="stylesheet" href="/modules/files/files.css">`,
body: `<folk-file-browser space="${spaceSlug}"></folk-file-browser>`,
scripts: `<script type="module" src="/modules/files/folk-file-browser.js"></script>`,

View File

@ -162,7 +162,7 @@ routes.get("/", (c) => {
moduleId: "forum",
spaceSlug,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: `<link rel="stylesheet" href="/modules/forum/forum.css">`,
body: `<folk-forum-dashboard space="${spaceSlug}"></folk-forum-dashboard>`,
scripts: `<script type="module" src="/modules/forum/folk-forum-dashboard.js"></script>`,

View File

@ -201,7 +201,7 @@ routes.get("/", (c) => {
moduleId: "funds",
spaceSlug,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: fundsStyles,
body: `<folk-funds-app space="${spaceSlug}"></folk-funds-app>`,
scripts: fundsScripts,
@ -216,7 +216,7 @@ routes.get("/demo", (c) => {
moduleId: "funds",
spaceSlug,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: fundsStyles,
body: `<folk-funds-app space="${spaceSlug}" mode="demo"></folk-funds-app>`,
scripts: fundsScripts,
@ -232,7 +232,7 @@ routes.get("/flow/:flowId", (c) => {
moduleId: "funds",
spaceSlug,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: fundsStyles,
body: `<folk-funds-app space="${spaceSlug}" flow-id="${flowId}"></folk-funds-app>`,
scripts: fundsScripts,

View File

@ -535,7 +535,7 @@ routes.get("/", (c) => {
moduleId: "inbox",
spaceSlug: space,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: `<link rel="stylesheet" href="/modules/inbox/inbox.css">`,
body: `<folk-inbox-client space="${space}"></folk-inbox-client>`,
scripts: `<script type="module" src="/modules/inbox/folk-inbox-client.js"></script>`,

View File

@ -138,7 +138,7 @@ routes.get("/", (c) => {
moduleId: "maps",
spaceSlug: space,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: `<link rel="stylesheet" href="/modules/maps/maps.css">`,
body: `<folk-map-viewer space="${space}"></folk-map-viewer>`,
scripts: `<script type="module" src="/modules/maps/folk-map-viewer.js"></script>`,
@ -154,7 +154,7 @@ routes.get("/:room", (c) => {
moduleId: "maps",
spaceSlug: space,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: `<link rel="stylesheet" href="/modules/maps/maps.css">`,
body: `<folk-map-viewer space="${space}" room="${room}"></folk-map-viewer>`,
scripts: `<script type="module" src="/modules/maps/folk-map-viewer.js"></script>`,

View File

@ -221,7 +221,7 @@ routes.get("/", (c) => {
moduleId: "network",
spaceSlug: space,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: `<link rel="stylesheet" href="/modules/network/network.css">`,
body: `<folk-graph-viewer space="${space}"></folk-graph-viewer>`,
scripts: `<script type="module" src="/modules/network/folk-graph-viewer.js"></script>`,

View File

@ -366,7 +366,7 @@ routes.get("/", (c) => {
moduleId: "notes",
spaceSlug: space,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: `<link rel="stylesheet" href="/modules/notes/notes.css">`,
body: `<folk-notes-app space="${space}"></folk-notes-app>`,
scripts: `<script type="module" src="/modules/notes/folk-notes-app.js"></script>`,

View File

@ -353,7 +353,7 @@ routes.get("/", (c) => {
moduleId: "providers",
spaceSlug: space,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: `<link rel="stylesheet" href="/modules/providers/providers.css">`,
body: `<folk-provider-directory></folk-provider-directory>`,
scripts: `<script type="module" src="/modules/providers/folk-provider-directory.js"></script>`,

View File

@ -332,7 +332,7 @@ routes.get("/", async (c) => {
<folk-pubs-editor id="editor"></folk-pubs-editor>
`,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
head: `<link rel="stylesheet" href="/modules/pubs/pubs.css">`,
scripts: `
<script type="module">

View File

@ -233,7 +233,7 @@ routes.get("/", (c) => {
moduleId: "swag",
spaceSlug: space,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: `<link rel="stylesheet" href="/modules/swag/swag.css">`,
body: `<folk-swag-designer></folk-swag-designer>`,
scripts: `<script type="module" src="/modules/swag/folk-swag-designer.js"></script>`,

View File

@ -243,7 +243,7 @@ routes.get("/routes", (c) => {
moduleId: "trips",
spaceSlug: space,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: `<link rel="stylesheet" href="/modules/trips/route-planner.css">`,
body: `<folk-route-planner></folk-route-planner>`,
scripts: `<script type="module" src="/modules/trips/folk-route-planner.js"></script>`,
@ -258,7 +258,7 @@ routes.get("/", (c) => {
moduleId: "trips",
spaceSlug: space,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: `<link rel="stylesheet" href="/modules/trips/trips.css">`,
body: `<folk-trips-planner space="${space}"></folk-trips-planner>`,
scripts: `<script type="module" src="/modules/trips/folk-trips-planner.js"></script>`,

View File

@ -196,7 +196,7 @@ routes.get("/", (c) => {
moduleId: "tube",
spaceSlug: space,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: `<link rel="stylesheet" href="/modules/tube/tube.css">`,
body: `<folk-video-player space="${space}"></folk-video-player>`,
scripts: `<script type="module" src="/modules/tube/folk-video-player.js"></script>`,

View File

@ -332,7 +332,7 @@ routes.get("/", (c) => {
moduleId: "vote",
spaceSlug: space,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: `<link rel="stylesheet" href="/modules/vote/vote.css">`,
body: `<folk-vote-dashboard space="${space}"></folk-vote-dashboard>`,
scripts: `<script type="module" src="/modules/vote/folk-vote-dashboard.js"></script>`,

View File

@ -99,7 +99,7 @@ routes.get("/", (c) => {
moduleId: "wallet",
spaceSlug,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: `<link rel="stylesheet" href="/modules/wallet/wallet.css">`,
body: `<folk-wallet-viewer space="${spaceSlug}"></folk-wallet-viewer>`,
scripts: `<script type="module" src="/modules/wallet/folk-wallet-viewer.js"></script>`,

View File

@ -222,7 +222,7 @@ routes.get("/", (c) => {
moduleId: "work",
spaceSlug: space,
modules: getModuleInfoList(),
theme: "light",
theme: "dark",
styles: `<link rel="stylesheet" href="/modules/work/work.css">`,
body: `<folk-work-board space="${space}"></folk-work-board>`,
scripts: `<script type="module" src="/modules/work/folk-work-board.js"></script>`,

579
package-lock.json generated
View File

@ -12,6 +12,8 @@
"@aws-sdk/client-s3": "^3.700.0",
"@encryptid/sdk": "file:../encryptid-sdk",
"@lit/reactive-element": "^2.0.4",
"@x402/core": "^2.3.1",
"@x402/evm": "^2.3.1",
"hono": "^4.11.7",
"imapflow": "^1.0.170",
"mailparser": "^3.7.2",
@ -58,6 +60,13 @@
}
}
},
"node_modules/@adraffy/ens-normalize": {
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz",
"integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==",
"license": "MIT",
"peer": true
},
"node_modules/@automerge/automerge": {
"version": "2.2.9",
"resolved": "https://registry.npmjs.org/@automerge/automerge/-/automerge-2.2.9.tgz",
@ -1845,6 +1854,43 @@
"@lit-labs/ssr-dom-shim": "^1.5.0"
}
},
"node_modules/@noble/ciphers": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz",
"integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==",
"license": "MIT",
"engines": {
"node": "^14.21.3 || >=16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@noble/curves": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@noble/hashes": "1.3.2"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@noble/hashes": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
"license": "MIT",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@pinojs/redact": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz",
@ -2219,6 +2265,81 @@
"win32"
]
},
"node_modules/@scure/base": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz",
"integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==",
"license": "MIT",
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@scure/bip32": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz",
"integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==",
"license": "MIT",
"dependencies": {
"@noble/curves": "~1.9.0",
"@noble/hashes": "~1.8.0",
"@scure/base": "~1.2.5"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@scure/bip32/node_modules/@noble/curves": {
"version": "1.9.7",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz",
"integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==",
"license": "MIT",
"dependencies": {
"@noble/hashes": "1.8.0"
},
"engines": {
"node": "^14.21.3 || >=16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@scure/bip32/node_modules/@noble/hashes": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
"license": "MIT",
"engines": {
"node": "^14.21.3 || >=16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@scure/bip39": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz",
"integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==",
"license": "MIT",
"dependencies": {
"@noble/hashes": "~1.8.0",
"@scure/base": "~1.2.5"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@scure/bip39/node_modules/@noble/hashes": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
"license": "MIT",
"engines": {
"node": "^14.21.3 || >=16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@selderee/plugin-htmlparser2": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz",
@ -2964,6 +3085,49 @@
"node": ">=18.0.0"
}
},
"node_modules/@spruceid/siwe-parser": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/@spruceid/siwe-parser/-/siwe-parser-2.1.2.tgz",
"integrity": "sha512-d/r3S1LwJyMaRAKQ0awmo9whfXeE88Qt00vRj91q5uv5ATtWIQEGJ67Yr5eSZw5zp1/fZCXZYuEckt8lSkereQ==",
"license": "Apache-2.0",
"dependencies": {
"@noble/hashes": "^1.1.2",
"apg-js": "^4.3.0",
"uri-js": "^4.4.1",
"valid-url": "^1.0.9"
}
},
"node_modules/@stablelib/binary": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz",
"integrity": "sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==",
"license": "MIT",
"dependencies": {
"@stablelib/int": "^1.0.1"
}
},
"node_modules/@stablelib/int": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz",
"integrity": "sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==",
"license": "MIT"
},
"node_modules/@stablelib/random": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@stablelib/random/-/random-1.0.2.tgz",
"integrity": "sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==",
"license": "MIT",
"dependencies": {
"@stablelib/binary": "^1.0.1",
"@stablelib/wipe": "^1.0.1"
}
},
"node_modules/@stablelib/wipe": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz",
"integrity": "sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==",
"license": "MIT"
},
"node_modules/@swc/core": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.11.tgz",
@ -3235,6 +3399,42 @@
"@types/node": "*"
}
},
"node_modules/@x402/core": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@x402/core/-/core-2.4.0.tgz",
"integrity": "sha512-g4K5dAVjevQftxCcpFlUDjO2AHE43FkO43VxwLCQ8ET3ki4aj2fzCcgvnXEj2eloJoocFS/Evt4pSTnP/4cFJw==",
"license": "Apache-2.0",
"dependencies": {
"zod": "^3.24.2"
}
},
"node_modules/@x402/evm": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@x402/evm/-/evm-2.4.0.tgz",
"integrity": "sha512-k9qIEhJ5m8jZLPA44hcLEP9I1WC8nF375A7pX/3XGPA+H2UPUoY8fzBaQA2U+4lMv/eIyfz05klSj/8LzP1saA==",
"license": "Apache-2.0",
"dependencies": {
"@x402/core": "~2.4.0",
"@x402/extensions": "~2.4.0",
"viem": "^2.39.3",
"zod": "^3.24.2"
}
},
"node_modules/@x402/extensions": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@x402/extensions/-/extensions-2.4.0.tgz",
"integrity": "sha512-tg/mSAS+NgwUaOdjt8agSjVkO1s9NYy+kSPubVlZf/Fy884qu7dSW81Ixu1NRYEz+vBk0rtz6b+eEvS0v72tMQ==",
"license": "Apache-2.0",
"dependencies": {
"@scure/base": "^1.2.6",
"@x402/core": "~2.4.0",
"ajv": "^8.17.1",
"siwe": "^2.3.2",
"tweetnacl": "^1.0.3",
"viem": "^2.43.5",
"zod": "^3.24.2"
}
},
"node_modules/@zone-eu/mailsplit": {
"version": "5.4.8",
"resolved": "https://registry.npmjs.org/@zone-eu/mailsplit/-/mailsplit-5.4.8.tgz",
@ -3246,6 +3446,56 @@
"libqp": "2.1.1"
}
},
"node_modules/abitype": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/abitype/-/abitype-1.2.3.tgz",
"integrity": "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/wevm"
},
"peerDependencies": {
"typescript": ">=5.0.4",
"zod": "^3.22.0 || ^4.0.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
},
"zod": {
"optional": true
}
}
},
"node_modules/aes-js": {
"version": "4.0.0-beta.5",
"resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz",
"integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==",
"license": "MIT",
"peer": true
},
"node_modules/ajv": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/apg-js": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/apg-js/-/apg-js-4.4.0.tgz",
"integrity": "sha512-fefmXFknJmtgtNEXfPwZKYkMFX4Fyeyz+fNF6JWp87biGOPslJbCBVU158zvKRZfHBKnJDy8CMM40oLFGkXT8Q==",
"license": "BSD-2-Clause"
},
"node_modules/atomic-sleep": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
@ -3448,6 +3698,87 @@
"@esbuild/win32-x64": "0.25.12"
}
},
"node_modules/ethers": {
"version": "6.16.0",
"resolved": "https://registry.npmjs.org/ethers/-/ethers-6.16.0.tgz",
"integrity": "sha512-U1wulmetNymijEhpSEQ7Ct/P/Jw9/e7R1j5XIbPRydgV2DjLVMsULDlNksq3RQnFgKoLlZf88ijYtWEXcPa07A==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/ethers-io/"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"@adraffy/ens-normalize": "1.10.1",
"@noble/curves": "1.2.0",
"@noble/hashes": "1.3.2",
"@types/node": "22.7.5",
"aes-js": "4.0.0-beta.5",
"tslib": "2.7.0",
"ws": "8.17.1"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/ethers/node_modules/@types/node": {
"version": "22.7.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz",
"integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"undici-types": "~6.19.2"
}
},
"node_modules/ethers/node_modules/tslib": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
"integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==",
"license": "0BSD",
"peer": true
},
"node_modules/ethers/node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"license": "MIT",
"peer": true
},
"node_modules/eventemitter3": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
"license": "MIT"
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"license": "MIT"
},
"node_modules/fast-uri": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
"integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"license": "BSD-3-Clause"
},
"node_modules/fast-xml-parser": {
"version": "5.3.6",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.6.tgz",
@ -3621,6 +3952,27 @@
"integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==",
"license": "MIT"
},
"node_modules/isows": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz",
"integrity": "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/wevm"
}
],
"license": "MIT",
"peerDependencies": {
"ws": "*"
}
},
"node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
},
"node_modules/leac": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz",
@ -3743,6 +4095,69 @@
"node": ">=14.0.0"
}
},
"node_modules/ox": {
"version": "0.12.4",
"resolved": "https://registry.npmjs.org/ox/-/ox-0.12.4.tgz",
"integrity": "sha512-+P+C7QzuwPV8lu79dOwjBKfB2CbnbEXe/hfyyrff1drrO1nOOj3Hc87svHfcW1yneRr3WXaKr6nz11nq+/DF9Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/wevm"
}
],
"license": "MIT",
"dependencies": {
"@adraffy/ens-normalize": "^1.11.0",
"@noble/ciphers": "^1.3.0",
"@noble/curves": "1.9.1",
"@noble/hashes": "^1.8.0",
"@scure/bip32": "^1.7.0",
"@scure/bip39": "^1.6.0",
"abitype": "^1.2.3",
"eventemitter3": "5.0.1"
},
"peerDependencies": {
"typescript": ">=5.4.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/ox/node_modules/@adraffy/ens-normalize": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz",
"integrity": "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==",
"license": "MIT"
},
"node_modules/ox/node_modules/@noble/curves": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz",
"integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==",
"license": "MIT",
"dependencies": {
"@noble/hashes": "1.8.0"
},
"engines": {
"node": "^14.21.3 || >=16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/ox/node_modules/@noble/hashes": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
"license": "MIT",
"engines": {
"node": "^14.21.3 || >=16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/parseley": {
"version": "0.12.1",
"resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz",
@ -3895,6 +4310,15 @@
],
"license": "MIT"
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/punycode.js": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
@ -3919,6 +4343,15 @@
"node": ">= 12.13.0"
}
},
"node_modules/require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/rollup": {
"version": "4.58.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.58.0.tgz",
@ -4051,6 +4484,21 @@
"is-arrayish": "^0.3.1"
}
},
"node_modules/siwe": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/siwe/-/siwe-2.3.2.tgz",
"integrity": "sha512-aSf+6+Latyttbj5nMu6GF3doMfv2UYj83hhwZgUF20ky6fTS83uVhkQABdIVnEuS8y1bBdk7p6ltb9SmlhTTlA==",
"license": "Apache-2.0",
"dependencies": {
"@spruceid/siwe-parser": "^2.1.2",
"@stablelib/random": "^1.0.1",
"uri-js": "^4.4.1",
"valid-url": "^1.0.9"
},
"peerDependencies": {
"ethers": "^5.6.8 || ^6.0.8"
}
},
"node_modules/smart-buffer": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
@ -4159,11 +4607,17 @@
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/tweetnacl": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==",
"license": "Unlicense"
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"devOptional": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
@ -4186,6 +4640,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"license": "BSD-2-Clause",
"dependencies": {
"punycode": "^2.1.0"
}
},
"node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
@ -4199,6 +4662,89 @@
"uuid": "dist/bin/uuid"
}
},
"node_modules/valid-url": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz",
"integrity": "sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA=="
},
"node_modules/viem": {
"version": "2.46.3",
"resolved": "https://registry.npmjs.org/viem/-/viem-2.46.3.tgz",
"integrity": "sha512-2LJS+Hyh2sYjHXQtzfv1kU9pZx9dxFzvoU/ZKIcn0FNtOU0HQuIICuYdWtUDFHaGXbAdVo8J1eCvmjkL9JVGwg==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/wevm"
}
],
"license": "MIT",
"dependencies": {
"@noble/curves": "1.9.1",
"@noble/hashes": "1.8.0",
"@scure/bip32": "1.7.0",
"@scure/bip39": "1.6.0",
"abitype": "1.2.3",
"isows": "1.0.7",
"ox": "0.12.4",
"ws": "8.18.3"
},
"peerDependencies": {
"typescript": ">=5.0.4"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/viem/node_modules/@noble/curves": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz",
"integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==",
"license": "MIT",
"dependencies": {
"@noble/hashes": "1.8.0"
},
"engines": {
"node": "^14.21.3 || >=16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/viem/node_modules/@noble/hashes": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
"license": "MIT",
"engines": {
"node": "^14.21.3 || >=16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/viem/node_modules/ws": {
"version": "8.18.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/vite": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
@ -4313,6 +4859,37 @@
"peerDependencies": {
"vite": "^2 || ^3 || ^4 || ^5 || ^6 || ^7"
}
},
"node_modules/ws": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/zod": {
"version": "3.25.76",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
}
}
}

View File

@ -43,7 +43,7 @@ export function renderShell(opts: ShellOptions): string {
scripts = "",
styles = "",
modules,
theme = "light",
theme = "dark",
head = "",
} = opts;
@ -92,7 +92,7 @@ export function renderStandaloneShell(opts: {
theme?: "dark" | "light";
head?: string;
}): string {
const { title, body, scripts = "", styles = "", theme = "light", head = "" } = opts;
const { title, body, scripts = "", styles = "", theme = "dark", head = "" } = opts;
return `<!DOCTYPE html>
<html lang="en">