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>
20 lines
721 B
TypeScript
20 lines
721 B
TypeScript
import * as React from 'react'
|
|
|
|
const MOBILE_BREAKPOINT = 768
|
|
|
|
const subscribe = (callback: () => void): (() => void) => {
|
|
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
|
|
mql.addEventListener('change', callback)
|
|
return () => mql.removeEventListener('change', callback)
|
|
}
|
|
|
|
const getClientSnapshot = (): boolean => window.innerWidth < MOBILE_BREAKPOINT
|
|
|
|
// Server can't know the viewport — assume desktop. React's useSyncExternalStore
|
|
// reconciles the SSR / client snapshots without a hydration mismatch warning.
|
|
const getServerSnapshot = (): boolean => false
|
|
|
|
export function useIsMobile(): boolean {
|
|
return React.useSyncExternalStore(subscribe, getClientSnapshot, getServerSnapshot)
|
|
}
|