* Tree: add isLast prop & draw connectors Introduce an optional isLast prop across all site panel tree node components and TreeNode so children know when they're the last sibling. TreeNodeWrapper now renders vertical and horizontal connector lines (adjusted by depth and isLast) and keeps the toggle button clickable (z-index). Children are passed isLast when mapped, so the UI can stop the vertical line for final siblings. Also bump several deps in apps/editor package.json and update the lockfile accordingly. * chore: clean up monorepo dependencies and update packages Move app/library dependencies out of root package.json into their respective workspaces. Root now only contains monorepo tooling (turbo, biome, supabase CLI, typescript, portless, ultracite). Updates: - next 16.1.0 → 16.1.6 - drizzle-orm ^0.39.0 → ^0.45.1 - drizzle-kit ^0.30.0 → ^0.31.9 - drizzle-zod ^0.5.1 → ^0.8.3 - three-bvh-csg ^0.0.17 → ^0.0.18 - supabase 2.75.3 → 2.76.15 - typescript 5.9.2 → 5.9.3 (all workspaces) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Use semantic theme tokens and enable dark mode Replace hardcoded zinc color classes with semantic design tokens (text-muted-foreground, text-foreground, border, bg-background, bg-border) across the action menu, camera controls, control modes, and view toggles for consistent theming. Update separators and borders to use bg-border/border-border. Add useEffect in the editor root to add/remove the 'dark' class on document.body and apply 'dark text-foreground' to the root container so the UI renders in dark mode; also import useEffect. * Persistable sidebar width & resizer Introduce a zustand store to persist sidebar width (key: `sidebar-preferences`) and track dragging state. Default width is 288px (18rem) and is clamped between 288 and 800px. Wire the store into SidebarProvider to drive the CSS variable for width and expose a data-dragging attribute. Add a SidebarResizer component that handles pointer events to resize the sidebar, toggles dragging state, updates cursor/user-select, and disables transitions while dragging. Small class and structure adjustments ensure the resizer is rendered and resizing behaves smoothly. * Editor: floating action menu, selection/phase tweaks Add a FloatingActionMenu (move/duplicate/delete) that attaches to selected 3D nodes and integrates with editor/viewer state; includes new cursor SVG and layout wrapper to use the custom cursor. Improve selection and phase behavior: auto-select first building+level on scene load (moved/centralized in project scene hook and editor init), add resolveBuildingId helper, and enhance SelectionManager to auto-switch between structure/furnish based on clicked nodes and to preserve building/level context when changing selection. Update sidebar tree node click handlers to switch phases for structural vs item nodes and stop hiding nodes by phase (keep tree items visible across phases). Add keyboard escape behavior to clear selections while keeping building/level context and reset selected reference. Misc: register unified event listeners for selection, position floating menu over objects using three.js Box3, and various small integration fixes. * Add SceneLoader, loading states & editor fixes Add a SceneLoader component and associated CSS loader styles and wire it into Editor, Viewer and page-level flows so a loading UI displays while project/scene data loads. Replace useLayoutEffect in editor page with a client-mounted useEffect guard to avoid hydration issues. Introduce isSceneLoading state and setter in the project store and set/clear it in useProjectScene; include sceneGraph when creating a new project and clear the scene beforehand. Other changes: type-guard moving/duplicating logic in FloatingActionMenu and remove duplicated id on clones; refactor auto-selection logic for site/building/level and auto-select build/wall for empty levels in editor index and useProjectScene; optimize ZoneSystem to run per-frame (useFrame) and reduce unnecessary updates to visibility/label positions; add animated active background for layer toggle (motion) in site panel; add keyboard shortcuts to jump between levels (Ctrl/Cmd+ArrowUp/Down); simplify community-hub project-created handler to navigate straight to editor. These changes improve UX during load, enforce safer node operations, and optimize runtime rendering. * Improve selection logic and refine UI components Multiple editor and viewer updates: - Selection: enhance selection-manager to support modifier (Meta/Ctrl) multi-select, compute next selection logic, track modifier keys, prevent grid deselect races, and add debug logs. Adapted selection handlers throughout scene/editor components to pass native events and modifier state. - Sidebar / Site panel: refactor site panel to inline a LevelReferences UI (replacing the removed references dialog). Add upload flow for scan (.glb/.gltf) and guide images, handling file size/type checks, project asset upload/delete calls, and per-level reference lists. Add MultiSelectionBadge for showing/clearing multi-selection and animated expand/collapse for levels. Several tree-node components now use a shared handleTreeSelection helper and propagate click events properly. - UI polish & icons: change dark theme color tokens for better contrast; increase button sizes and icon sizes across action menus; swap some lucide icons for image assets (rotate, topview, select, build, mesh, floorplan, level, etc.) and add new public icons. - PascalRadio: replace popover with a framer-motion expandable panel, add outside-click handling, animated transitions, and minor UI tweaks. - Other: minor viewer hook/selection-manager updates and tweaks to control modes and view toggles to support image icons and consistent sizing. These changes improve multi-select reliability, user interactions in the sidebar, and overall visual polish with new icons and animations. * Fix TypeScript build errors in selection-manager and scene-loader Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Community Feature
This directory contains the optional community features (cloud synchronization and authentication) for the Pascal Editor. This feature is specific to the Pascal platform and can be safely removed if you're using the editor standalone.
What This Does
The community feature provides:
- Authentication - Sign in with magic link via Better Auth
- Property Management - Create and manage properties with Google Maps address search
- Scene Loading - Automatically load property scenes from the database when a property is selected
- Auto-Save - Automatically save scene changes to the database (2-second debounce)
- Database Sync - Save and load editor state from a PostgreSQL database via Supabase
Architecture
features/community/
├── lib/
│ ├── auth/
│ │ ├── client.ts # Re-exports from @pascal-app/auth
│ │ ├── server.ts # Server-side session handling
│ │ └── hooks.ts # useAuth React hook
│ ├── properties/
│ │ ├── actions.ts # Server actions for CRUD operations
│ │ ├── types.ts # TypeScript types for properties
│ │ ├── hooks.ts # Property React hooks
│ │ └── store.ts # Zustand store for property state
│ ├── models/
│ │ ├── actions.ts # Scene model CRUD operations
│ │ └── hooks.ts # Scene loading and auto-save hooks
│ ├── database/
│ │ └── server.ts # Re-exports from @pascal-app/db
│ └── utils/
│ └── id-generator.ts # nanoid-based ID generation
├── components/
│ ├── cloud-save-button.tsx # Main UI entry point (top-right button)
│ ├── sign-in-dialog.tsx # Magic link sign-in dialog
│ ├── profile-dropdown.tsx # User profile menu
│ ├── property-dropdown.tsx # Property selector dropdown
│ ├── new-property-dialog.tsx # Create new property dialog
│ └── google-address-search.tsx # Google Maps autocomplete
└── README.md # This file
How It Works
Authentication Flow
- User clicks "Save to cloud" button
- Signs in with magic link (email-based, no password)
- Better Auth session is stored in cookies
- Server actions validate session using Better Auth API
Property Management
- User creates a property with a real-world address (Google Maps)
- Address and property are saved to PostgreSQL via Supabase
- Properties are associated with the authenticated user
- User can switch between properties
Scene Management
- When a property is selected, its scene is loaded from
properties_modelstable - If no scene exists, loads default empty scene
- Scene changes are auto-saved every 2 seconds (debounced)
- Updates existing model (highest version) instead of creating new ones
- Scene graph includes all nodes and hierarchy
Database Integration
- Uses Supabase (PostgreSQL) for database access
- Better Auth manages authentication tables directly
- Server actions use service role key to bypass RLS
- Permissions enforced by filtering on
owner_id - Tables:
users,sessions,properties,properties_addresses,properties_models
Required Environment Variables
# Database Connection
DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres
# Supabase Configuration
NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key_here
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key_here
# Better Auth
BETTER_AUTH_SECRET=<generate_with_openssl_rand_base64_32>
BETTER_AUTH_URL=http://localhost:3000
# Google Maps API Key (for address search)
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=your_google_maps_key_here
Generate BETTER_AUTH_SECRET:
openssl rand -base64 32
Dependencies
The community feature requires these packages:
{
"better-auth": "^1.4.18",
"@supabase/supabase-js": "^2.95.3",
"@react-google-maps/api": "^2.20.8",
"nanoid": "^5.1.6"
}
How to Remove (For Open Source Users)
If you want to use the editor without community features:
1. Delete this directory
rm -rf features/community
2. Remove the CloudSaveButton from the editor
Edit components/editor/index.tsx:
- import { CloudSaveButton } from '@/features/community/components/cloud-save-button'
export default function Editor() {
return (
<div className="w-full h-full">
<ActionMenu />
<PanelManager />
- <CloudSaveButton />
3. Remove dependencies (optional)
Edit package.json:
- "better-auth": "^1.4.18",
- "@supabase/supabase-js": "^2.95.3",
- "@react-google-maps/api": "^2.20.8",
- "nanoid": "^5.1.6"
4. Remove environment variables
Delete from .env.local and .env.example:
- NEXT_PUBLIC_API_URL=...
- NEXT_PUBLIC_SUPABASE_URL=...
- NEXT_PUBLIC_SUPABASE_ANON_KEY=...
- SUPABASE_SERVICE_ROLE_KEY=...
- NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=...
That's it! The editor will work as a standalone application without any cloud features.
Backend Requirements
This feature requires:
- Supabase local development instance
- PostgreSQL database with the following tables:
users- User accounts (Better Auth)sessions- Authentication sessions (Better Auth)verification_tokens- Magic link tokens (Better Auth)properties- Property recordsproperties_addresses- Property addressesproperties_models- Scene graph models
- Database migrations are managed in
packages/db/supabase/migrations/
Development
To work on this feature:
- Install dependencies:
bun install - Start Supabase local development:
bun db:start - Run database migrations:
bun db:reset - Configure all environment variables in
apps/editor/.env.local - Run the editor:
bun dev
The editor will be available at http://localhost:3000.
For detailed setup instructions, see SETUP.md in the root directory.
Notes
- This feature uses server actions (Next.js App Router) for all database operations
- Authentication is handled by Better Auth with magic link support
- Better Auth server is configured in
packages/authand mounted at/api/auth/* - The editor queries the database directly using Supabase with service role key
- IDs are generated using nanoid with custom alphabet
- Scene state is managed with a Zustand store for reliable property switching
- Scene changes are auto-saved with 2-second debouncing to the currently selected property