rspace-online/docs/MIGRATION-PLAN.md

5.1 KiB
Raw Blame History

Canvas-Website to FolkJS Migration Plan

Overview

This document outlines the transition from canvas-website (React + tldraw) to rspace-online (FolkJS web components). The goal is to simplify the architecture while preserving core functionality.

Architecture Comparison

Aspect canvas-website (tldraw) rspace-online (FolkJS)
Framework React 18 + tldraw v3 Web Components (Lit-style)
State tldraw store + Jotai atoms Reactive properties + Automerge
Sync Cloudflare Durable Objects WebSocket + Automerge CRDT
Shapes BaseBoxShapeUtil classes FolkShape subclasses
Styling CSS-in-JS / CSS modules CSS in template literals
Hooks React hooks (9 shared) Class methods + utilities

Key Simplifications

1. No React

  • Before: JSX, useState, useEffect, useCallback, useMemo
  • After: Template literals, reactive properties, event listeners

2. No tldraw Abstraction Layer

  • Before: BaseBoxShapeUtil, editor.updateShape(), shape migrations
  • After: FolkShape base class, direct property updates, Automerge patches

3. Simpler State Management

  • Before: tldraw store → Automerge → tldraw store (bidirectional sync)
  • After: FolkShape → Automerge → FolkShape (direct sync via CommunitySync)

4. Web-Native APIs

  • Before: React context providers for auth, notifications, file system
  • After: Custom events, localStorage, native browser APIs

Shape Porting Pattern

tldraw Shape (Before)

export class MyShapeUtil extends BaseBoxShapeUtil<MyShape> {
  static type = 'my-shape'

  getDefaultProps(): MyShape['props'] {
    return { w: 300, h: 200, content: '' }
  }

  component(shape: MyShape) {
    const { content } = shape.props
    const handleChange = useCallback((e) => {
      this.editor.updateShape({ id: shape.id, props: { content: e.target.value } })
    }, [shape.id])

    return (
      <StandardizedToolWrapper shape={shape}>
        <textarea value={content} onChange={handleChange} />
      </StandardizedToolWrapper>
    )
  }
}

FolkJS Shape (After)

export class FolkMyShape extends FolkShape {
  static tagName = 'folk-my-shape'

  #content = ''
  get content() { return this.#content }
  set content(value: string) {
    this.#content = value
    this.requestUpdate('content')
    this.dispatchEvent(new CustomEvent('content-change', { detail: { content: value } }))
  }

  override createRenderRoot() {
    const root = super.createRenderRoot()
    // Add UI to shadow DOM
    const wrapper = document.createElement('div')
    wrapper.innerHTML = `
      <div class="header" data-drag>
        <span>My Shape</span>
        <button class="close-btn">×</button>
      </div>
      <div class="content">
        <textarea></textarea>
      </div>
    `
    // Event listeners...
    return root
  }
}

Migration Phases

Phase 1: Foundation (4 shapes)

Status: To Do

  • folk-slide (passive display)
  • folk-chat (real-time chat)
  • folk-google-item (data display)
  • folk-piano (interactive music)

Phase 2: Core Data (4 shapes)

Status: To Do

  • folk-embed (URL embeds)
  • folk-markdown (already done!)
  • folk-calendar (date picker + events)
  • folk-map (Mapbox/MapLibre collaborative maps)

Phase 3: AI Integration (4 shapes)

Status: To Do

  • folk-image-gen (fal.ai Flux)
  • folk-video-gen (WAN 2.1)
  • folk-prompt (LLM execution)
  • folk-transcription (Whisper)

Phase 4: Advanced (5+ shapes)

Status: To Do

  • folk-video-chat (Daily.co)
  • folk-obs-note (Obsidian sync)
  • folk-holon (H3 geo-indexing)
  • folk-workflow-block (visual workflows)
  • folk-zine-generator (AI zine creation)

Shared Utilities to Port

React Hook FolkJS Equivalent Status
useMaximize maximizeShape() utility Done
usePinnedToView PinnedViewManager class Done
useCalendarEvents CalendarService class To Do
useWhisperTranscription WhisperService class To Do
useLiveImage LiveImageService class To Do

Already Completed

  • FolkShape base class with drag/resize/rotate
  • FolkMarkdown (markdown editing)
  • FolkWrapper (standardized header/actions)
  • FolkArrow (shape connections)
  • CommunitySync (Automerge integration)
  • Server with WebSocket sync
  • Subdomain routing for communities

External Dependencies

Keep (MCP or direct API)

  • fal.ai (image/video gen) - via MCP
  • Gemini (text gen) - via MCP
  • MapLibre GL (maps)
  • Daily.co (video chat)
  • Whisper API (transcription)

Replace/Simplify

  • MDXEditor → simpler markdown renderer (already done)
  • tldraw → FolkJS (in progress)
  • React contexts → custom events + services

Remove (tldraw-specific)

  • BaseBoxShapeUtil
  • Shape migrations
  • tldraw editor commands
  • tldraw store subscriptions

Success Criteria

  1. All 28 shapes ported to FolkJS web components
  2. Real-time collaboration working via Automerge
  3. Mobile touch support
  4. Presence cursors showing other users
  5. No React dependencies in runtime
  6. Bundle size < 500KB (vs ~2MB current)