fix: zone grid snapping, shelf position panel, deselect on item placement (#394)
* fix: zone grid snapping, shelf position panel, deselect on item placement - Zone tool reads the editor's gridSnapStep (0.5/0.25/0.1/0.05) instead of a hardcoded 0.5 for both cursor move and click snapping. - Shelf inspector gains a Position group (vec3 X/Y/Z sliders), matching the item panel. - Item catalog clears the viewer selection before arming placement so shortcuts (rotate & co) don't hit both the ghost and the selected node. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor: move level display naming into @pascal-app/core getDefaultLevelName / getLevelDisplayName ("Ground Floor" / "Floor N" / "Basement N") lived in packages/editor's internal lib, so viewer-only surfaces couldn't reach them and fell back to hand-rolled "Level N" labels. The helpers are pure domain logic, so they move to core and export from its barrel; the editor package's seven call sites now import them from there. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
7539387ff5
commit
d36e8fcc78
@@ -61,6 +61,7 @@ export {
|
|||||||
isOperationDoorType,
|
isOperationDoorType,
|
||||||
SECTIONAL_GARAGE_RENDER_OPEN_SCALE,
|
SECTIONAL_GARAGE_RENDER_OPEN_SCALE,
|
||||||
} from './lib/door-operation'
|
} from './lib/door-operation'
|
||||||
|
export { getDefaultLevelName, getLevelDisplayName } from './lib/level-name'
|
||||||
export {
|
export {
|
||||||
type Point2D as PolygonPoint2D,
|
type Point2D as PolygonPoint2D,
|
||||||
pointInPolygon as pointInPolygon2D,
|
pointInPolygon as pointInPolygon2D,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { LevelNode } from '@pascal-app/core'
|
import type { LevelNode } from '../schema'
|
||||||
|
|
||||||
export function getDefaultLevelName(level: number): string {
|
export function getDefaultLevelName(level: number): string {
|
||||||
if (level === 0) return 'Ground Floor'
|
if (level === 0) return 'Ground Floor'
|
||||||
@@ -184,7 +184,7 @@ export const ZoneTool: React.FC = () => {
|
|||||||
const [gridX, gridZ] = snapWorldXZForActiveBuilding(
|
const [gridX, gridZ] = snapWorldXZForActiveBuilding(
|
||||||
event.position[0],
|
event.position[0],
|
||||||
event.position[2],
|
event.position[2],
|
||||||
0.5,
|
useEditor.getState().gridSnapStep,
|
||||||
).local
|
).local
|
||||||
cursorPosition = [gridX, gridZ]
|
cursorPosition = [gridX, gridZ]
|
||||||
levelYRef.current = event.localPosition[1]
|
levelYRef.current = event.localPosition[1]
|
||||||
@@ -217,7 +217,7 @@ export const ZoneTool: React.FC = () => {
|
|||||||
const [gridX, gridZ] = snapWorldXZForActiveBuilding(
|
const [gridX, gridZ] = snapWorldXZForActiveBuilding(
|
||||||
event.position[0],
|
event.position[0],
|
||||||
event.position[2],
|
event.position[2],
|
||||||
0.5,
|
useEditor.getState().gridSnapStep,
|
||||||
).local
|
).local
|
||||||
let clickPoint: [number, number] = [gridX, gridZ]
|
let clickPoint: [number, number] = [gridX, gridZ]
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { useViewer } from '@pascal-app/viewer'
|
|||||||
import { Check, ChevronDown, Eye, EyeOff, Layers2, Plus, Trash2 } from 'lucide-react'
|
import { Check, ChevronDown, Eye, EyeOff, Layers2, Plus, Trash2 } from 'lucide-react'
|
||||||
import { useCallback, useRef, useState } from 'react'
|
import { useCallback, useRef, useState } from 'react'
|
||||||
import { useShallow } from 'zustand/react/shallow'
|
import { useShallow } from 'zustand/react/shallow'
|
||||||
import { getLevelDisplayName } from '../../../lib/level-name'
|
import { getLevelDisplayName } from '@pascal-app/core'
|
||||||
import { createLocalGuideImage } from '../../../lib/local-guide-image'
|
import { createLocalGuideImage } from '../../../lib/local-guide-image'
|
||||||
import { cn } from '../../../lib/utils'
|
import { cn } from '../../../lib/utils'
|
||||||
import useEditor, { type GridSnapStep } from '../../../store/use-editor'
|
import useEditor, { type GridSnapStep } from '../../../store/use-editor'
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { useEffect, useState } from 'react'
|
|||||||
import { create } from 'zustand'
|
import { create } from 'zustand'
|
||||||
import { useShallow } from 'zustand/shallow'
|
import { useShallow } from 'zustand/shallow'
|
||||||
import { Dialog, DialogContent, DialogTitle } from './../../../components/ui/primitives/dialog'
|
import { Dialog, DialogContent, DialogTitle } from './../../../components/ui/primitives/dialog'
|
||||||
import { getLevelDisplayName } from '../../../lib/level-name'
|
import { getLevelDisplayName } from '@pascal-app/core'
|
||||||
import { useCommandRegistry } from '../../../store/use-command-registry'
|
import { useCommandRegistry } from '../../../store/use-command-registry'
|
||||||
import { usePaletteViewRegistry } from '../../../store/use-palette-view-registry'
|
import { usePaletteViewRegistry } from '../../../store/use-palette-view-registry'
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ import {
|
|||||||
buildLevelDuplicateCreateOps,
|
buildLevelDuplicateCreateOps,
|
||||||
type LevelDuplicatePreset,
|
type LevelDuplicatePreset,
|
||||||
} from '../../lib/level-duplication'
|
} from '../../lib/level-duplication'
|
||||||
import { getDefaultLevelName, getLevelDisplayName } from '../../lib/level-name'
|
import { getDefaultLevelName, getLevelDisplayName } from '@pascal-app/core'
|
||||||
import { deleteLevelWithFallbackSelection } from '../../lib/level-selection'
|
import { deleteLevelWithFallbackSelection } from '../../lib/level-selection'
|
||||||
import {
|
import {
|
||||||
getEditorClipboardSnapshot,
|
getEditorClipboardSnapshot,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import type { AssetInput } from '@pascal-app/core'
|
import type { AssetInput } from '@pascal-app/core'
|
||||||
import { resolveCdnUrl } from '@pascal-app/viewer'
|
import { resolveCdnUrl, useViewer } from '@pascal-app/viewer'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import {
|
import {
|
||||||
@@ -96,6 +96,10 @@ export function ItemCatalog({
|
|||||||
key={index}
|
key={index}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
triggerSFX('sfx:menu-click')
|
triggerSFX('sfx:menu-click')
|
||||||
|
// Drop the current selection before arming placement — keeping
|
||||||
|
// it would route shortcuts (rotate & co) to both the ghost and
|
||||||
|
// the selected node.
|
||||||
|
useViewer.getState().setSelection({ selectedIds: [], zoneId: null })
|
||||||
setSelectedItem(item)
|
setSelectedItem(item)
|
||||||
setTool('item')
|
setTool('item')
|
||||||
setMode('build')
|
setMode('build')
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import type { LevelNode } from '@pascal-app/core'
|
import type { LevelNode } from '@pascal-app/core'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import type { LevelDuplicatePreset } from '../../lib/level-duplication'
|
import type { LevelDuplicatePreset } from '../../lib/level-duplication'
|
||||||
import { getLevelDisplayName } from '../../lib/level-name'
|
import { getLevelDisplayName } from '@pascal-app/core'
|
||||||
import { cn } from '../../lib/utils'
|
import { cn } from '../../lib/utils'
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import {
|
|||||||
buildLevelDuplicateCreateOps,
|
buildLevelDuplicateCreateOps,
|
||||||
type LevelDuplicatePreset,
|
type LevelDuplicatePreset,
|
||||||
} from './../../../../../lib/level-duplication'
|
} from './../../../../../lib/level-duplication'
|
||||||
import { getDefaultLevelName } from './../../../../../lib/level-name'
|
import { getDefaultLevelName } from '@pascal-app/core'
|
||||||
import { deleteLevelWithFallbackSelection } from './../../../../../lib/level-selection'
|
import { deleteLevelWithFallbackSelection } from './../../../../../lib/level-selection'
|
||||||
import { createLocalGuideImage } from './../../../../../lib/local-guide-image'
|
import { createLocalGuideImage } from './../../../../../lib/local-guide-image'
|
||||||
import { cn } from './../../../../../lib/utils'
|
import { cn } from './../../../../../lib/utils'
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useViewer } from '@pascal-app/viewer'
|
|||||||
import { Layers } from 'lucide-react'
|
import { Layers } from 'lucide-react'
|
||||||
import { memo, useCallback, useState } from 'react'
|
import { memo, useCallback, useState } from 'react'
|
||||||
import { useShallow } from 'zustand/react/shallow'
|
import { useShallow } from 'zustand/react/shallow'
|
||||||
import { getDefaultLevelName } from '../../../../../lib/level-name'
|
import { getDefaultLevelName } from '@pascal-app/core'
|
||||||
import { InlineRenameInput } from './inline-rename-input'
|
import { InlineRenameInput } from './inline-rename-input'
|
||||||
import { focusTreeNode, TreeNode, TreeNodeWrapper } from './tree-node'
|
import { focusTreeNode, TreeNode, TreeNodeWrapper } from './tree-node'
|
||||||
import { TreeNodeActions } from './tree-node-actions'
|
import { TreeNodeActions } from './tree-node-actions'
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
type AnyNodeId,
|
type AnyNodeId,
|
||||||
type BuildingNode,
|
type BuildingNode,
|
||||||
emitter,
|
emitter,
|
||||||
|
getLevelDisplayName,
|
||||||
type LevelNode,
|
type LevelNode,
|
||||||
useScene,
|
useScene,
|
||||||
type ZoneNode,
|
type ZoneNode,
|
||||||
@@ -30,7 +31,6 @@ import {
|
|||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useShallow } from 'zustand/react/shallow'
|
import { useShallow } from 'zustand/react/shallow'
|
||||||
import { getLevelDisplayName } from '../lib/level-name'
|
|
||||||
import { cn } from '../lib/utils'
|
import { cn } from '../lib/utils'
|
||||||
import { ActionButton } from './ui/action-menu/action-button'
|
import { ActionButton } from './ui/action-menu/action-button'
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -76,5 +76,9 @@ export const shelfParametrics: ParametricDescriptor<ShelfNode> = {
|
|||||||
{ key: 'height', kind: 'number', unit: 'm', min: 0.05, max: 2.5, step: 0.05 },
|
{ key: 'height', kind: 'number', unit: 'm', min: 0.05, max: 2.5, step: 0.05 },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Position',
|
||||||
|
fields: [{ key: 'position', kind: 'vec3' }],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user