feat: backport monorepo PRs #297, #302, #306 — floorplan thumbnails + mobile editor UI

Port floorplan item thumbnails, full mobile editor UI, and
mobile polish from the private monorepo into the public editor.

Monorepo PR #297 (feat/item-admin-advanced):
- Add optional floorPlanUrl to item asset schema (core)
- Render 2D floor-plan images inside item footprints on floorplan
- Improve slider drag with modifier key re-anchoring

Monorepo PR #302 (feat/mobile-ui):
- Mobile editor layout with draggable bottom sheet
- Mobile tab bar, selection bar, and panel sheet
- Mobile-aware panel manager and panel wrapper
- useIsMobile rewrite (useSyncExternalStore, SSR-safe)
- Camera actions hideOrbit prop
- GridSnapControl and SecondaryToggles exports
- Action menu hides on mobile contextual tabs
- SidebarTab extended with mobileDefaultSnap/mobileIcon

Monorepo PR #306 (feat/mobile-ui-polish):
- Touch gesture mapping for camera controls (one/two/three finger)
- Snap ratio constants for bottom sheet
- Thumbnail generator WebGL2 fallback (bottom-up row flip)
- ErrorBoundary scope logging, viewer scene wrap
- GPUDeviceWatcher enhanced logging and uncaptured error handler
- WebGPURenderer init error handling and diagnostics
- Post-processing log improvements
- MergedOutlineNode WebGL2 FBO corruption fix

Excluded: apps/community/*, apps/editor/*, packages/community-*,
.cursor/*, .env.example (monorepo-only files)

Co-authored-by: Pascal <open@pascal.app>
This commit is contained in:
Aymeric Rabot
2026-04-28 06:36:08 -07:00
committed by GitHub
co-authored by Pascal
parent 010c874963
commit aa43bb6a43
27 changed files with 1710 additions and 386 deletions
@@ -1,15 +1,25 @@
import type { ErrorInfo, ReactNode } from 'react'
import { Component } from 'react'
export class ErrorBoundary extends Component<
{ children: ReactNode; fallback: ReactNode },
{ hasError: boolean }
> {
interface ErrorBoundaryProps {
children: ReactNode
fallback: ReactNode
/** Tag for log lines so we can tell which boundary swallowed an error. */
scope?: string
}
export class ErrorBoundary extends Component<ErrorBoundaryProps, { hasError: boolean }> {
state = { hasError: false }
static getDerivedStateFromError() {
return { hasError: true }
}
componentDidCatch(_e: Error, _i: ErrorInfo) {}
componentDidCatch(error: Error, info: ErrorInfo) {
console.error(
`[viewer] ErrorBoundary caught${this.props.scope ? ` (${this.props.scope})` : ''}:`,
error,
info.componentStack,
)
}
render() {
return this.state.hasError ? this.props.fallback : this.props.children
}