- Replace useMemo with useState + MutationObserver for isDarkMode detection
- Add MDXEditor's built-in 'dark-theme' class for proper toolbar/icon theming
- Theme now switches instantly when user toggles dark/light mode
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Traefik cannot auto-link routers when multiple services are defined.
Fixed by using a single service (canvas) that both routers explicitly
reference via the .service label.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add router rules for jeffemmett.com and www.jeffemmett.com
- Keep staging.jeffemmett.com for testing
- Preparing for migration from Cloudflare Pages to Docker deployment
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add pin functionality to ImageGen and VideoGen shapes
- Refactor ImageGen to use StandardizedToolWrapper with tags support
- Update StandardizedToolWrapper: grey tags, fix button overlap, improve header drag
- Fix index validation in AutomergeToTLStore for old format indices
- Update wrangler.toml with latest compatibility date and RunPod endpoint docs
- Refactor VideoGen to use captured editor reference for consistency
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add index sanitization in Board.tsx to fix "Expected an index key"
validation errors when selecting shapes with old format indices
- Improve RunPod error handling to properly display status messages
(IN_PROGRESS, IN_QUEUE, FAILED) instead of generic errors
- Update wrangler.toml with current compatibility date and document
RunPod endpoint configuration for reference
- Add sanitizeIndex helper function to convert invalid indices like
"b1" to valid tldraw fractional indices like "a1"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Video generation on RunPod can take significant time:
- GPU cold start: 30-120 seconds
- Model loading: 30-60 seconds
- Generation: 60-180 seconds
Increased polling timeout from 4 to 6 minutes and updated UI
to set proper expectations for users.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
All RunPod API functions now have hardcoded fallback values so
every user can access AI features without needing their own keys:
- Image Generation: Automatic1111 endpoint (tzf1j3sc3zufsy)
- Video Generation: Wan2.2 endpoint (4jql4l7l0yw0f3)
- Text Generation: vLLM endpoint (03g5hz3hlo8gr2)
- Transcription: Whisper endpoint (lrtisuv8ixbtub)
- Ollama: Netcup AI Orchestrator (ai.jeffemmett.com)
This ensures ImageGen, VideoGen, Mycelial Intelligence, and
transcription work for all users of the canvas out of the box.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Hardcoded fallback values for Ollama and RunPod text endpoints so
that all users have access to AI features without needing to
configure their own API keys:
- Ollama: defaults to https://ai.jeffemmett.com (Netcup AI Orchestrator)
- RunPod Text: defaults to pre-configured vLLM endpoint
This ensures Mycelial Intelligence works for everyone out of the box.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The FathomNote shape was being created by FathomMeetingsBrowserShape
but wasn't registered with tldraw, causing "No shape util found for
type FathomNote" errors when loading canvases with Fathom notes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added isValidTldrawIndex() function to properly validate tldraw
fractional indices (e.g., "a1", "a1V" are valid, "b1", "c1" are not)
- Apply migration to IndexedDB data as well as server data
- This fixes ValidationError when loading old data with invalid indices
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Sharp 0.33.5 has prebuilt binaries for linux-x64 while the older
0.32.x version in @xenova/transformers requires native compilation.
Using npm overrides to force 0.33.5 throughout the dependency tree.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added omit=optional to .npmrc to prevent @xenova/transformers from
trying to compile sharp with native dependencies. Sharp is only used
for server-side image processing which isn't needed in the browser.
Also added override for sharp version in package.json as fallback.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds migrateStoreData() function to fix ValidationError when loading
old data with invalid index keys (e.g., 'b1' instead of fractional
indices like 'a1V'). The migration detects invalid indices and
regenerates valid ones using tldraw's getIndexAbove().
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add cleanup routine on editor mount to remove corrupted draw/line shapes
that have no points/segments (these cause geometry errors)
- Add global error handler to suppress geometry errors from tldraw
instead of crashing the entire app
- Both fixes ensure old JSON data with corrupted shapes loads gracefully
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Fix undefined 'result' variable reference in useWhisperTranscriptionSimple.ts
- Add type guards for array checks in ImageGenShapeUtil.tsx output handling
- Add Record<string, any> type assertions for response.json() calls in llmUtils.ts
- Remove unused 'isDark' parameter from MicrophoneIcon component
- Remove unused 'index' parameter in components.tsx map callback
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Holon Shape Improvements:
- Add H3 cell ID validation before connecting to Holosphere
- Extract coordinates and resolution from H3 cell IDs automatically
- Improve data rendering with proper lens/item structure display
- Add "Generate H3 Cell" button for quick cell ID creation
- Update placeholders and error messages for H3 format
- Fix HolonBrowser validation and placeholder text
Geometry Error Fix:
- Add try-catch in ClickPropagator.eventHandler for shapes with invalid paths
- Add try-catch in CmdK for getShapesAtPoint geometry errors
- Prevents "No nearest point found" crashes from corrupted draw/line shapes
Offline Persistence:
- Add IndexedDB storage adapter for Automerge documents
- Implement document ID mapping for room persistence
- Merge local and server data on reconnection
- Support offline editing with automatic sync
Other Changes:
- Update .env.example with Ollama and RunPod configuration
- Add multmux Docker configuration files
- UI styling improvements for toolbar and share zone
- Remove auto-creation of MycelialIntelligence shape (now permanent UI bar)
- Various shape utility minor fixes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Mycelial Intelligence UI refactor:
- Created permanent floating bar at top of screen (MycelialIntelligenceBar.tsx)
- Bar stays fixed and doesn't zoom with canvas
- Collapses when clicking outside
- Removed from toolbar tool menu
- Added deprecated shape stub for backwards compatibility with old boards
- ImageGen RunPod fix:
- Changed from async /run to sync /runsync endpoint
- Fixed output parsing for output.images array format with base64
- Other updates:
- Added FocusLockIndicator and UserSettingsModal UI components
- mulTmux server and shape updates
- Automerge sync and store improvements
- Various CSS and UI refinements
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Combines RunPod AI integration (ImageGen, VideoGen) with mulTmux
collaborative terminal functionality.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updates to collaborative terminal integration and various shape
improvements across the canvas.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add RunPod config helpers for image, video, text, whisper endpoints
- Update VideoGenShapeUtil to call RunPod video endpoint directly
- Add Ollama URL config for local LLM support
- Remove dependency on AI orchestrator backend (not yet built)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add VideoGenShapeUtil with StandardizedToolWrapper for consistent UI
- Add VideoGenTool for canvas video generation
- Add AI Orchestrator client library for smart routing to RS 8000/RunPod
- Register new shapes and tools in Board.tsx
- Add deployment guides and migration documentation
- Ollama deployed on Netcup RS 8000 at 159.195.32.209:11434
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add mulTmux as an integrated workspace in canvas-website project:
- Node.js/TypeScript backend with tmux session management
- CLI client with blessed-based terminal UI
- WebSocket-based real-time collaboration
- Token-based authentication with invite links
- Session management (create, join, list)
- PM2 deployment scripts for AI server
- nginx reverse proxy configuration
- Workspace integration with npm scripts
Usage:
- npm run multmux:build - Build server and CLI
- npm run multmux:start - Start production server
- multmux create <name> - Create collaborative session
- multmux join <token> - Join existing session
See MULTMUX_INTEGRATION.md for full documentation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add Git hook and management scripts for automatic worktree creation when branching from main.
## Features
**Automatic Worktree Creation:**
- Post-checkout Git hook automatically creates worktrees for new branches
- Creates worktrees at `../repo-name-branch-name`
- Only activates when branching from main/master
- Smart detection to avoid duplicate worktrees
**Worktree Manager Script:**
- `list` - List all worktrees with branches
- `create <branch>` - Manually create worktree
- `remove <branch>` - Remove worktree
- `clean` - Remove all worktrees except main
- `goto <branch>` - Get path to worktree (for cd)
- `status` - Show git status of all worktrees
## Benefits
- Work on multiple branches simultaneously
- No need to stash when switching branches
- Run dev servers on different branches in parallel
- Compare code across branches easily
- Keep main branch clean
## Files Added
- `.git/hooks/post-checkout` - Auto-creates worktrees on branch creation
- `scripts/worktree-manager.sh` - Manual worktree management CLI
- `WORKTREE_SETUP.md` - Complete documentation and usage guide
## Usage
**Automatic (when branching from main):**
```bash
git checkout -b feature/new-feature
# Worktree automatically created at ../canvas-website-feature-new-feature
```
**Manual:**
```bash
./scripts/worktree-manager.sh create feature/my-feature
./scripts/worktree-manager.sh list
cd $(./scripts/worktree-manager.sh goto feature/my-feature)
```
See WORKTREE_SETUP.md for complete documentation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Geo shapes saved before the tldraw schema change have props.text which is
no longer valid. This causes ValidationError on page reload when shapes are
loaded from Automerge:
"ValidationError: At shape(type = geo).props.text: Unexpected property"
The migration logic was only in JSON import (CustomMainMenu.tsx), but shapes
loading from Automerge also need migration.
This fix adds the props.text → props.richText migration to the sanitizeRecord
function in AutomergeToTLStore.ts, ensuring geo shapes are properly migrated
when loaded from Automerge, matching the behavior during JSON import.
The original text is preserved in meta.text for backward compatibility with
search and other features that reference it.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Fix coordinate collapse bug where shapes were resetting to (0,0)
- Convert geo shape props.text to props.richText (tldraw schema change)
- Preserve text in meta.text for backward compatibility
- Add .nvmrc to enforce Node 20
- Update package.json to require Node >=20.0.0
- Add debug logging for sync and import operations
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed TypeScript error by changing from useState to useMemo for repo and
adapter initialization. This properly exposes the repo and adapter objects
instead of returning a state setter function.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added await for adapter.whenReady() to ensure WebSocket connection is
established before creating the Automerge document. This should enable
the Automerge Repo to properly send binary sync messages when document
changes occur.
Changes:
- Extract adapter from repo initialization to access it
- Wait for adapter.whenReady() before creating document
- Update useEffect dependencies to include adapter and workerUrl
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Reverted to the proven approach from commit 90605be where each client
creates its own Automerge document with repo.create(). The Automerge
binary sync protocol handles synchronization between clients through
the WebSocket network adapter, without requiring shared document IDs.
Key changes:
- Each client calls repo.create() to get a unique document
- Initial content loaded from server via HTTP/R2
- Binary sync messages broadcast between clients keep documents in sync
- No need for shared document ID storage/retrieval
This fixes the "Document unavailable" errors and enables real-time
collaboration across multiple board instances.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed document ID storage/retrieval logic that was causing "Document unavailable"
errors. Each client now creates its own Automerge document handle and syncs content
via WebSocket binary protocol. This allows multiple boards to load the same room
simultaneously without conflicts.
- Removed /room/:roomId/documentId endpoints usage
- Each client creates document with repo.create()
- Content syncs via Automerge's native binary sync protocol
- Initial content still loaded from server via HTTP
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
repo.find() returns a Promise<DocHandle>, not DocHandle directly.
Added missing await keyword to fix TypeScript build error.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When multiple clients try to load the same room simultaneously, repo.find()
throws "Document unavailable" error if the document isn't in the repo yet.
Wrapped repo.find() in try-catch to create a new handle when document isn't
available, allowing multiple boards to load the same page concurrently.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The issue was that repo.find() was creating a NEW document instead of
waiting for the existing one to sync from the network.
Changes:
- Use 'automerge:{documentId}' URL format for repo.find()
- Remove try-catch that was creating new documents
- Let repo.find() properly wait for network sync
This ensures all clients use the SAME document ID and can sync in real-time.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When repo.find() is called for a document that exists on the server but not
locally, it throws 'Document unavailable' error. This fix:
- Wraps repo.find() in try-catch block
- Falls back to creating new handle if document not found
- Allows sync adapter to merge with server state via network
This handles the case where clients join existing rooms and need to sync
documents from the network.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>