- Remove separate BoardSettingsDropdown button from UI panel
- Add board protection toggle and editor management to existing settings dropdown
- Show protection section only for admins (board owner or global admin)
- Add ability to toggle view-only mode for protected boards
- Add editor management UI with invite and remove functionality
- Fix TypeScript type annotations for API responses
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added the board settings dropdown between ShareBoardButton and StarBoardButton.
Provides access to:
- Board protection toggle (view-only mode)
- Editor management for protected boards
- Admin request functionality
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
NEW PERMISSION MODEL:
- All users (including anonymous) can now EDIT by default
- Boards can be marked as "protected" by admin - only listed editors can edit
- Global admins (jeffemmett@gmail.com) have admin on ALL boards
- Added BoardSettingsDropdown with view-only toggle for admins
Backend changes:
- Added is_protected column to boards table
- Added global_admins table
- New getEffectivePermission logic prioritizes: token > global admin > owner > protection status
- New API endpoints: /auth/global-admin-status, /admin/request, /boards/:id/info, /boards/:id/editors
- Admin request sends email via Resend API
Frontend changes:
- BoardSettingsDropdown component with protection toggle and editor management
- Updated AuthContext and Board.tsx to default to 'edit' permission
- isReadOnly now only true for protected boards where user is not an editor
Task: task-052
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add "(you)" indicator on tooltip when hovering current user's node
- Ensure current user always appears in graph even with no connections
- Add new participants immediately to graph (no 30s delay)
- Implement "leave" message protocol for presence cleanup:
- Client sends leave message before disconnecting
- Server broadcasts leave to other clients on disconnect
- Clients remove presence records on receiving leave
- Generate consistent user colors from CryptID username (not session ID)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When the browser reloads without network connectivity, the canvas now
automatically loads from local IndexedDB storage and renders the last
known state.
Changes:
- Board.tsx: Updated render condition to allow rendering when offline
with local data (isOfflineWithLocalData flag)
- useAutomergeStoreV2: Added isNetworkOnline parameter and offline fast
path that immediately loads records from Automerge doc without waiting
for network patches
- useAutomergeSyncRepo: Passes isNetworkOnline to useAutomergeStoreV2
- ConnectionStatusIndicator: Updated messaging to clarify users are
viewing locally cached canvas when offline
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add dropdown menu when clicking user nodes in network graph with options:
- Connect with <username>
- Navigate to <username> (pan to cursor)
- Screenfollow <username> (follow camera)
- Open <username>'s profile
- Fix tool visibility for logged-in users (timing issue with read-only mode)
- Fix 401 errors by correcting localStorage key from 'cryptid_session' to 'canvas_auth_session'
- Remove "(anonymous)" suffix from usernames in tooltips
- Simplify node colors to use user's profile/presence color
- Clear permission cache on logout to prevent stale state
- Various UI improvements to auth components and network graph
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add useMaximize hook to all shapes using StandardizedToolWrapper:
- MapShapeUtil, MultmuxShapeUtil, MarkdownShapeUtil
- ObsNoteShapeUtil, ImageGenShapeUtil, VideoGenShapeUtil
- HolonShapeUtil, PromptShapeUtil, EmbedShapeUtil
- FathomMeetingsBrowserShapeUtil, FathomNoteShapeUtil
- HolonBrowserShapeUtil, ObsidianBrowserShapeUtil
- TranscriptionShapeUtil, VideoChatShapeUtil
All tools now have maximize/fullscreen functionality via the
standardized header bar.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add maximize/fullscreen button to standardized header bar
- Create useMaximize hook for shape utils to enable fullscreen
- Shape fills viewport when maximized, restores on Esc or toggle
- Implement on ChatBoxShapeUtil as example (other shapes can add easily)
- Button shows ⤢ for maximize, ⊡ for exit fullscreen
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove blocking await adapter.whenReady() that prevented offline mode
- Load from IndexedDB immediately without waiting for network
- Set handle and mark as ready BEFORE network sync for instant UI
- Background server sync with 5-second timeout
- Continue with local data if network is unavailable
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add InviteDialog component with tabbed interface for sharing boards
- Add ShareBoardButton component to toolbar
- Integrate qrcode.react for QR code generation
- Implement Web NFC API for NFC tag writing
- Add placeholder for audio connect feature (coming soon)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Created single grey oval container for all top-right menu items
- Order: CryptID -> Star -> Gear -> Question mark
- Added vertical separator lines between each menu item
- Consistent styling with rounded container and subtle shadow
- Removed separate styling for individual buttons
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Social Network Graph:
- Dark/black theme with semi-transparent background
- Arrow markers on edges showing connection direction
- Color-coded arrows: grey (default), yellow (connected), green (trusted)
- Updated header, stats, and icon button colors for dark theme
MI (Mycelial Intelligence) Bar:
- Responsive width: full width on mobile, percentage on narrow, fixed on desktop
- Position: moves to bottom on mobile (above toolbar), stays at top on desktop
- Smooth transitions when resizing
- Smaller max height on mobile (300px vs 400px)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Network Graph:
- Add graceful fallback when API returns 401 or other errors
- Falls back to showing room participants as nodes
- Prevents error spam in console for unauthenticated users
Map Shape (linter changes):
- Add isFetchingNearby state for loading indicator
- Improve addAnnotation to accept name/color options
- Add logging for Find Nearby debugging
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
UI Changes:
- Add CryptIDDropdown component with Google integration under Integrations
- Remove user presence avatars (moved to network graph)
- New top-right layout: CryptID -> Star -> Gear dropdown -> Question mark
- Settings gear shows dropdown with dark mode toggle + All Settings link
- Network graph label changed to "Social Network"
- Network graph shows for all users including anonymous
- Solo users see themselves as a lone node
Map Shape Fixes:
- Fix stale closure bug: tool clicks now work using activeToolRef
- Fix wheel scroll: native event listener prevents tldraw capture
- Add pointerEvents: 'auto' to map container for proper mouse interaction
Bug Fix:
- Add Map shape sanitization in AutomergeToTLStore for pinnedToView/isMinimized
- Prevents "Expected boolean, got undefined" errors on old Map data
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Wrap map component in StandardizedToolWrapper with header bar
- Add onPointerDown={stopPropagation} to all sidebar interactive elements
- Add handleMapWheel that forwards wheel zoom to map component
- Add pinnedToView, tags, isMinimized props for consistency
- Fix TypeScript type for stopPropagation handler
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix unused parameter errors (prefix with underscore)
- Fix TrustCircleManager API: add getTrustLevel/setTrustLevel methods
- Fix MyceliumNetwork method calls: addNode→createNode, addHypha→createHypha
- Fix createCommitment signature to use CommitmentParams object
- Fix GeohashCommitment type with proper geohash field
- Fix PRECISION_CELL_SIZE usage (returns {lat,lng} object)
- Add type assertions for fetch response data
- Fix MapCanvas attributionControl type
- Fix GPSCollaborationLayer markerStyle merge with defaults
- Update MapShapeUtil with better event handling:
- Raise z-index to 10000 for all map buttons
- Add pointerEvents: auto for button clickability
- Add handleWheel with preventDefault to enable map zoom
- Add capturePointerEvents for proper interaction
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Shows all collaborators currently on the canvas with their connection status:
- Green border: Trusted (edit access)
- Yellow border: Connected (view access)
- Grey border: Not connected
Users can:
- Add unconnected users as Connected or Trusted
- Upgrade Connected users to Trusted
- Downgrade Trusted users to Connected
- Remove connections
Also fixes TypeScript errors in networking module.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add permission fetching and state management in Board.tsx
- Fetch user's permission level when board loads
- Set tldraw to read-only mode when user has 'view' permission
- Show AnonymousViewerBanner for unauthenticated users
- Banner prompts CryptID sign-up with your specified messaging
- Update permission state when user authenticates
- Wire up permission API routes in worker/worker.ts
- GET /boards/:boardId/permission
- GET /boards/:boardId/permissions (admin)
- POST /boards/:boardId/permissions (admin)
- DELETE /boards/:boardId/permissions/:userId (admin)
- PATCH /boards/:boardId (admin)
- Add X-CryptID-PublicKey to CORS allowed headers
- Add PUT, PATCH, DELETE to CORS allowed methods
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 1 of user permissions feature:
- Add board permissions schema to D1 database
- boards table with owner, default_permission, is_public
- board_permissions table for per-user permissions
- Add permission types (PermissionLevel) to worker and client
- Implement permission API handlers in worker/boardPermissions.ts
- GET /boards/:boardId/permission - check user's permission
- GET /boards/:boardId/permissions - list all (admin only)
- POST /boards/:boardId/permissions - grant permission (admin)
- DELETE /boards/:boardId/permissions/:userId - revoke (admin)
- PATCH /boards/:boardId - update board settings (admin)
- Update AuthContext with permission fetching and caching
- fetchBoardPermission() - fetch and cache permission for a board
- canEdit() - check if user can edit current board
- isAdmin() - check if user is admin for current board
- Create AnonymousViewerBanner component with CryptID signup prompt
- Add CSS styles for anonymous viewer banner
- Fix automerge sync manager to flush saves on peer disconnect
Permission levels:
- view: Read-only, cannot create/edit/delete shapes
- edit: Can modify board contents
- admin: Full access + permission management
Next steps:
- Integrate with Board component for read-only mode
- Wire up permission checking in Automerge sync
- Add permission management UI for admins
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add src/open-mapping/** to tsconfig exclude (21K lines, to harden later)
- Delete MapShapeUtil.backup.tsx
- Fix ConnectionStatus type in OfflineIndicator
- Fix data type assertions in MapShapeUtil (routing/search)
- Fix GoogleDataService.authenticate() call with required param
- Add ts-expect-error for Automerge NetworkAdapter 'ready' event
- Add .wasm?module type declaration for Wrangler imports
- Include GPS location sharing enhancements in MapShapeUtil
TypeScript now compiles cleanly. Vite build needs NODE_OPTIONS for memory.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add Google Workspace integration directly in user dropdown (CustomPeopleMenu)
- Shows connection status (Connected/Not Connected)
- Connect button to trigger OAuth flow
- Browse Data button to open GoogleExportBrowser modal
- Add toggleable keyboard shortcuts panel (? icon)
- Shows full names of tools and actions with their shortcuts
- Organized by category: Tools, Custom Tools, Actions, Custom Actions
- Toggle on/off by clicking, closes when clicking outside
- Import GoogleExportBrowser component for data browsing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add VisibilityChangeModal for confirming visibility changes
- Add VisibilityChangeManager to handle events and drag detection
- GoogleItem shapes now dispatch visibility change events on badge click
- Support both local->shared and shared->local transitions
- Auto-detect when GoogleItems are dragged outside PrivateWorkspace
- Session storage for "don't ask again" preference
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Privacy-aware item shapes for Google Export data:
- GoogleItemShapeUtil: Custom shape for Google items with:
- Visual distinction: dashed border + shaded overlay for LOCAL items
- Solid border for SHARED items
- Privacy badge (🔒 local, 🌐 shared) in top-right corner
- Click badge to trigger visibility change (Phase 5)
- Service icon, title, preview, date display
- Optional thumbnail support for photos
- Dark mode support
- GoogleItemTool: Tool for creating GoogleItem shapes
- Updated ShareableItem type to include `service` and `thumbnailUrl`
- Updated usePrivateWorkspace hook to create GoogleItem shapes
instead of placeholder text shapes
Items added from GoogleExportBrowser now appear as proper
GoogleItem shapes with privacy indicators inside the workspace.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- PrivateWorkspaceShapeUtil: Frosted glass container shape with:
- Dashed indigo border for visual distinction
- Pin/collapse/close buttons in header
- Dark mode support
- Position/size persistence to localStorage
- Helper functions for zone detection
- PrivateWorkspaceTool: Tool for creating workspace zones
- usePrivateWorkspace hook:
- Creates/toggles workspace visibility
- Listens for 'add-google-items-to-canvas' events
- Places items inside the private zone
- Persists visibility state
- PrivateWorkspaceManager: Headless component that manages
workspace lifecycle inside Tldraw context
Items added from GoogleExportBrowser will now appear in the
Private Workspace zone as placeholder text shapes (Phase 4
will add proper GoogleItemShape with visual badges).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Rename component file and interface for consistent naming
- Update all imports and state variables in UserSettingsModal
- Better reflects the purpose as a data export browser
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Phase 2 of Data Sovereignty Zone implementation:
- Create GoogleDataBrowser component with service tabs (Gmail, Drive, Photos, Calendar)
- Searchable item list with checkboxes for multi-select
- Select All/Clear functionality
- Dark mode support with consistent styling
- "Add to Private Workspace" button
- Privacy note explaining local-only encryption
- Emits 'add-google-items-to-canvas' event for Board.tsx integration
Integration with UserSettingsModal:
- Import and render GoogleDataBrowser when "Open Data Browser" clicked
- Handler for adding selected items to canvas
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Phase 1 of Data Sovereignty Zone implementation:
- Add Google Workspace section to Settings > Integrations tab
- Show connection status, import counts (emails, files, photos, events)
- Connect/Disconnect Google account buttons
- "Open Data Browser" button (Phase 2 will implement the browser)
- Add getStoredCounts() and getInstance() to GoogleDataService
Privacy messaging: "Your data is encrypted with AES-256 and stored
only in your browser. Choose what to share to the board."
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Bring in all the latest changes from main including:
- Index validation and migration for tldraw shapes
- UserSettingsModal with integrations tab
- CryptID authentication updates
- AI services (image gen, video gen, mycelial intelligence)
- Automerge sync improvements
- Various UI improvements
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Core modules:
- encryption.ts: WebCrypto AES-256-GCM, HKDF key derivation, PKCE utilities
- database.ts: IndexedDB schema for gmail, drive, photos, calendar
- oauth.ts: OAuth 2.0 PKCE flow with encrypted token storage
- share.ts: Create tldraw shapes from encrypted data
- backup.ts: R2 backup service with encrypted manifest
Importers:
- gmail.ts: Gmail import with pagination and batch storage
- drive.ts: Drive import with folder navigation, Google Docs export
- photos.ts: Photos thumbnail import (403 issue pending investigation)
- calendar.ts: Calendar import with date range filtering
Test interface at /google route for debugging OAuth flow.
Known issue: Photos API returning 403 on some thumbnail URLs - needs
further investigation with proper OAuth consent screen setup.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Introduces a comprehensive mapping and routing layer for the canvas
that provides advanced route planning capabilities beyond Google Maps.
Built on open-source foundations:
- OpenStreetMap for base map data
- OSRM/Valhalla for routing engines
- MapLibre GL JS for map rendering
- VROOM for route optimization
- Y.js for real-time collaboration
Features planned:
- Multi-path routing with alternatives comparison
- Real-time collaborative waypoint editing
- Layer management (basemaps, overlays, custom GeoJSON)
- Calendar/scheduling integration
- Budget tracking per waypoint/route
- Offline tile caching via PWA
Includes:
- TypeScript types for routes, waypoints, layers
- React hooks for map instance, routing, collaboration
- Service abstractions for multiple routing providers
- Docker Compose config for backend deployment
- Setup script for OSRM data preparation
Backlog task: task-024
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The previous validation allowed "b1" which is invalid because 'b' prefix
expects 2-digit integers (10-99), not 1-digit. This caused ValidationError
when selecting old format content.
Now validates that:
- 'a' prefix: 1 digit (a0-a9)
- 'b' prefix: 2 digits (b10-b99)
- 'c' prefix: 3 digits (c100-c999)
- etc.
Invalid indices are converted to 'a1' as a safe default.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The index validation was incorrectly rejecting valid tldraw fractional
indices like "b1", "c10", etc. tldraw's fractional indexing uses:
- First letter (a-z) indicates integer part length (a=1 digit, b=2 digits)
- Followed by alphanumeric characters for value and jitter
This was causing ValidationError on production for Embed shapes with
index "b1". Fixed regex in all validation functions to accept any
lowercase letter prefix, not just 'a'.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add ResizeObserver for reliable resize detection
- Use requestAnimationFrame for smoother fit operations
- Apply full-size styles to xterm elements after fit
- Hide tags to maximize terminal area
- Fix flex layout for proper container sizing
- Add error handling for fit operations during rapid resize
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Allow passing full system prompts (>100 chars) or personality IDs
- Auto-detect prompt type based on length
- Pass custom prompts through provider chain with retry logic
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Increase default width from 500px to 650px to fit full toolbar
- Add fixed-position toggle button (top-right) that doesn't move between states
- Remove horizontal scrollbar with overflow: hidden
- Add right padding to toolbar for toggle button space
- Tighten toolbar spacing (gap: 1px, padding: 4px 6px)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- 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>
- 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>
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 @automerge/automerge-repo-storage-indexeddb for local persistence
- Create documentIdMapping utility to track roomId → documentId in IndexedDB
- Update useAutomergeSyncRepo with offline-first loading strategy:
- Load from IndexedDB first for instant access
- Sync with server in background when online
- Track connection status (online/offline/syncing)
- Add OfflineIndicator component to show connection state
- Integrate offline indicator into Board component
Documents are now cached locally and available offline. Automerge CRDT
handles conflict resolution when syncing back online.
🤖 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>
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>
- Add document ID coordination via server to ensure all clients sync to same document
- Add new endpoints GET/POST /room/:roomId/documentId for document ID management
- Store automergeDocumentId in Durable Object storage
- Add enhanced logging to CloudflareAdapter send() method for debugging
- Add sharePolicy to Automerge Repo to enable document sharing
- Fix TypeScript errors in useAutomergeSyncRepo
This fixes the issue where each client was creating its own Automerge document
with a unique ID, preventing real-time sync. Now all clients in a room use the
same document ID, enabling proper real-time collaboration.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive RunPod AI API integration including:
- New runpodApi.ts client for RunPod endpoint communication
- Image generation tool and shape utilities for AI-generated images
- Enhanced LLM utilities with RunPod support for text generation
- Updated Whisper transcription with improved error handling
- UI components for image generation tool
- Setup and testing documentation
This commit preserves work-in-progress RunPod integration before switching branches.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Rename CryptoLogin component to CryptID
- Update all imports and usages across the codebase
- Display 'CryptID: username' in user dropdown menu
- Update UI text to reference CryptID branding
- Update Profile component header to show CryptID
- Update component comments and documentation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Change from repo.find('automerge:patricia') to repo.create() because
Automerge requires proper UUID-based document IDs, not arbitrary strings.
Each client creates a local document, loads initial data from server,
and syncs via WebSocket. The server syncs documents by room ID, not
by Automerge document ID.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Cast handle.doc() to any to fix TypeScript error about missing 'store' property.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
repo.find() returns a Promise<DocHandle>, so we need to await it.
This fixes the TypeScript compilation errors in the build.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add console logs in production to debug why shapes aren't being saved to R2.
This will help identify if saves are:
- Being triggered
- Being deferred/skipped
- Successfully completing
Logs added:
- 💾 When persistence starts
- ✅ When persistence succeeds
- 🔍 When shape patches are detected
- 🚫 When saves are skipped (ephemeral/pinned changes)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove ImageGenShape import and references from useAutomergeStoreV2.ts
to fix TypeScript build error. ImageGen feature files are not yet committed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add manual sync triggering to broadcast document changes to other peers in real-time.
The Automerge Repo wasn't auto-broadcasting because the WebSocket setup doesn't use peer discovery.
Changes:
- Add triggerSync() helper function to manually trigger sync broadcasts
- Call triggerSync() after all document changes (position updates, eraser changes, regular changes)
- Pass Automerge document to patch handlers to prevent coordinate loss
- Add ImageGenShape support to schema
This fixes the issue where changes were being saved to Automerge locally but not
broadcast to other connected clients until page reload.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add case normalization for custom shape types to prevent validation errors when loading shapes with lowercase type names (e.g., "chatBox" → "ChatBox"). The TLDraw schema expects PascalCase type names for custom shapes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>