Monorepo for Aesthetic.Computer aesthetic.computer
core kidlisp.com LIVE-RELOAD-PLAN.md
8.3 kB

KidLisp.com Live Reload Implementation Plan #

Context Analysis #

The aesthetic computer stack has three main interop methods for live communication:

  • How it works: WebSocket-based pub/sub through session-server
  • Location: system/public/aesthetic.computer/lib/disk.mjs (line 2377)
  • Flow:
    • Editor subscribes to a unique channel via code.channel(channelId)
    • Server broadcasts code updates to all subscribers on that channel
    • Clients receive type: "code" messages with {piece, source, codeChannel}
    • Socket handler in lib/socket.mjs triggers reload automatically
  • Used by: VSCode extension for live piece editing
  • Pros:
    • Already built into the system
    • Handles multiple concurrent editors/viewers
    • Server-side filtering and routing
    • Persistent connections with auto-reconnect
  • Cons:
    • Requires WebSocket connection (already used by preview iframe)
    • Need to coordinate channel IDs

2. Window postMessage (Simple but Limited - ACTUALLY VIABLE!) #

  • How it works: Direct iframe ↔ parent communication
  • Flow:
    • Parent: iframe.contentWindow.postMessage({type, data}, '*')
    • Iframe: window.addEventListener('message', handler)
  • Pros:
    • ✅ No server/networking required - completely client-side!
    • ✅ Synchronous, immediate (no network latency)
    • ✅ Simple implementation
    • ✅ Works offline
    • ✅ No WebSocket connection overhead
  • Cons:
    • Only works for single parent-child relationships
    • No pub/sub or multi-viewer support
    • Security concerns with '*' origin (mitigated by origin checking)

UPDATE: This is actually the better choice for kidlisp.com because:

  1. We have direct parent→child iframe relationship
  2. No need for multi-viewer sync (single user editing)
  3. Zero network dependency = faster, more reliable
  4. Simpler architecture

3. Session Server Reload Endpoint (Development Only) #

  • How it works: HTTP POST to /reload endpoint
  • Location: session-server/session.mjs (line 197)
  • Flow:
    • POST to https://localhost:8889/reload with {piece, source}
    • Server broadcasts to all connected clients
    • Used by chokidar file watchers
  • Pros:
    • Works without maintaining WebSocket in editor
    • Simple HTTP request
  • Cons:
    • Development mode only (if (dev))
    • Broadcasts to ALL connected clients (no targeting)
    • Requires CORS configuration
    • Not suitable for production

Why postMessage is better for kidlisp.com:

  • ✅ No networking = instant updates
  • ✅ Works offline
  • ✅ Simpler implementation (no WebSocket management)
  • ✅ No server dependency
  • ✅ Lower latency
  • ✅ We only need single-editor-to-single-preview communication

When to use Code Channel instead:

  • Multiple viewers need to sync (like collaborative editing)
  • Remote/distributed scenarios
  • Need persistence across page reloads
  • Building a "watch party" feature

For kidlisp.com's use case (single user, local editing), postMessage is the clear winner.

Alternative: Code Channel System (for reference) #

Architecture #

┌─────────────────────────┐
│  kidlisp.com Editor     │
│  (Monaco)               │
│                         │
│  WebSocket Connection   │
│  wss://aesthetic.computer
└───────────┬─────────────┘
            │
            │ 1. Subscribe to channel
            │    code-channel:sub
            │
            ▼
┌─────────────────────────┐
│  Session Server         │
│  (WebSocket Hub)        │
│                         │
│  Manages subscriptions  │
└───────────┬─────────────┘
            │
            │ 2. Broadcast code updates
            │    type: "code"
            │
            ▼
┌─────────────────────────┐
│  Preview Iframe         │
│  aesthetic.computer     │
│                         │
│  Receives & auto-reloads│
└─────────────────────────┘

Implementation Steps #

Phase 1: Setup WebSocket Connection in Editor #

  1. Create WebSocket connection to wss://aesthetic.computer (or localhost:8889 for dev)
  2. Generate unique channel ID: kidlisp-editor-${Date.now()}-${Math.random()}
  3. Subscribe to channel: Send {type: "code-channel:sub", content: channelId}
  4. Store channel ID in preview iframe URL as query param

Phase 2: Modify Preview Iframe #

  1. Add channel ID to iframe URL: ?channel=${channelId}
  2. Iframe auto-subscribes to same channel on load
  3. Iframe receives code updates via existing socket infrastructure
  4. Code updates trigger piece reload without full page refresh

Phase 3: Send Code Updates from Editor #

  1. On "run" button or Cmd+Enter, send code via WebSocket:
    ws.send(JSON.stringify({
      type: "code",
      content: JSON.stringify({
        piece: encodedCode,
        source: editorCode,
        codeChannel: channelId
      })
    }))
    
  2. Session server routes to all channel subscribers
  3. Preview iframe receives and executes new code

Phase 4: Handle Edge Cases #

  1. Reconnection logic if WebSocket drops
  2. Channel cleanup on page unload
  3. Error handling for malformed code
  4. Visual feedback in editor (connection status indicator)

Code Changes Required #

kidlisp.com/index.html #

  • Add WebSocket connection setup
  • Generate and store channel ID
  • Send code updates through WebSocket
  • Add connection status indicator

Preview Iframe URL #

  • Add ?channel=${channelId} parameter
  • Iframe will auto-subscribe via existing code.channel() system

Session Server (Already Works!) #

  • ✅ Code channel subscription handling exists
  • ✅ Message routing exists
  • ✅ Broadcast to channel subscribers exists
  • No changes needed!

Alternative: Hybrid Approach #

If code-channel feels too heavy, could use postMessage for immediate feedback + code-channel for multi-viewer sync:

  1. Editor sends code via postMessage for instant local preview
  2. Also broadcasts via code-channel for other viewers
  3. Best of both worlds: speed + flexibility

Actually, postMessage DOES Work Here! #

Correction: The aesthetic.computer piece structure is:

kidlisp.com (parent)
  └─ iframe src="aesthetic.computer/[code]?nogap=true" (direct child)
       └─ piece renders directly in this iframe

It's only ONE iframe layer! We have direct access:

// In kidlisp.com:
const iframe = document.getElementById('preview-iframe');
iframe.contentWindow.postMessage({
  type: 'reload-kidlisp',
  code: editorCode
}, 'https://aesthetic.computer');

The iframe can listen and reload without full page refresh!

Why NOT HTTP Polling? #

  • Inefficient (repeated requests)
  • Higher latency
  • More server load
  • Worse UX than WebSocket push

Conclusion #

Use postMessage because:

  • ✅ Zero network overhead = instant feedback
  • ✅ Simpler implementation (< 20 lines of code)
  • ✅ No WebSocket management/reconnection logic
  • ✅ Works offline
  • ✅ Direct parent-child communication (no layers to traverse)
  • ✅ Perfect for single-user editing scenario

Implementation (Super Simple!) #

In kidlisp.com editor:

const iframe = document.getElementById('preview-iframe');

function updatePreview() {
  const code = editor.getValue();
  iframe.contentWindow.postMessage({
    type: 'kidlisp-update',
    code: code
  }, 'https://aesthetic.computer');
}

In aesthetic.computer (would need to add listener):

window.addEventListener('message', (event) => {
  if (event.origin !== 'https://kidlisp.com') return; // Security
  if (event.data.type === 'kidlisp-update') {
    // Reload piece with new code without full page refresh
    reloadPiece(event.data.code);
  }
});

Estimated implementation time: 30 minutes including testing.

Future enhancement: Could add code-channel later for multi-viewer "watch party" feature.