Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 0 additions & 38 deletions i18n/literal-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -197,18 +197,6 @@
"packages/editor/src/components/editor/join-walls-pill.tsx": [
"Join walls"
],
"packages/editor/src/components/editor/node-action-menu.tsx": [
"Curve",
"Cut out",
"Duplicate",
"Edit mesh",
"Find in catalog",
"Group selection",
"Move",
"Rotate left",
"Rotate right",
"Ungroup selection"
],
"packages/editor/src/components/editor/open-wall-ends-3d-layer.tsx": [
"Wall end not joined"
],
Expand All @@ -222,14 +210,6 @@
"Pinned",
"{?} {?} measurements"
],
"packages/editor/src/components/editor/riser-diagram-panel.tsx": [
"DWV plumbing isometric",
"DWV riser diagram",
"No drain, waste, or vent pipes yet. Draw plumbing to see the riser diagram.",
"Riser Diagram",
"Vent",
"Waste"
],
"packages/editor/src/components/editor/room-controls.tsx": [
"Add stairs",
"Divide room"
Expand Down Expand Up @@ -353,24 +333,6 @@
"Undo anyway",
"You've changed the scene since. Undoing also removes those changes."
],
"packages/editor/src/components/procedural-items/inspector.tsx": [
"Adjust design",
"Back",
"Edit parts",
"Invalid attachment",
"Invalid edit",
"Invalid placement",
"Invalid position",
"Invalid reset",
"Position",
"Reset this part",
"pos"
],
"packages/editor/src/components/tools/distribution/distribution-run-tool.tsx": [
"Length:",
"Tab: direction",
"type a length"
],
"packages/editor/src/components/ui/command-palette/editor-commands.tsx": [
"Add Level",
"Ceiling Tool",
Expand Down
14 changes: 14 additions & 0 deletions i18n/pseudo-surfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,20 @@ export const surfaces: PseudoSurface[] = [
scope: '[data-editor-inspector]',
data: furnishedData,
},
{
name: 'node-action-menu',
path: furnishedPath,
steps: async (page) => {
await openLayer(page, 'Door')
await page
.locator('[data-node-action-menu] button')
.last()
.hover()
.catch(() => undefined)
await page.waitForTimeout(600)
},
scope: '[data-node-action-menu], [role="tooltip"]',
},
{
name: 'icon-rail',
path: apartmentPath,
Expand Down
5 changes: 4 additions & 1 deletion packages/editor/src/components/editor/action-menu-button.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
'use client'

import { usePascalI18n } from '@pascal-app/core'
import type { MouseEventHandler, ReactNode } from 'react'
import { toolHintKey } from '../../i18n/node-messages'
import { cn } from '../../lib/utils'
import { shortcutDisplayValue } from '../ui/primitives/shortcut-token'
import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/primitives/tooltip'
Expand Down Expand Up @@ -37,6 +39,7 @@ export function ActionMenuButton({
pressed?: boolean
children: ReactNode
}) {
const i18n = usePascalI18n()
return (
<Tooltip>
<TooltipTrigger asChild>
Expand Down Expand Up @@ -72,7 +75,7 @@ export function ActionMenuButton({
className="rounded border border-background/30 px-1 font-mono text-[10px] leading-4 opacity-80"
key={key}
>
{shortcutDisplayValue(key)}
{toolHintKey(shortcutDisplayValue(key), i18n)}
</kbd>
))}
</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

import { type AlignmentGuide, sceneRegistry } from '@pascal-app/core'
import { useViewer } from '@pascal-app/viewer'
import { Html } from '@react-three/drei'
import { useFrame } from '@react-three/fiber'
import { memo, useMemo, useRef } from 'react'
import { BoxGeometry, CircleGeometry, type Group, Vector3 } from 'three'
import { MeshBasicNodeMaterial } from 'three/webgpu'
import { EDITOR_LAYER } from '../../lib/constants'
import useAlignmentGuides from '../../store/use-alignment-guides'
import { Html } from './localized-html'
import { formatMeasurement } from './measurement-pill'

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

import { sceneRegistry } from '@pascal-app/core'
import { useViewer } from '@pascal-app/viewer'
import { Html } from '@react-three/drei'
import { useFrame } from '@react-three/fiber'
import { memo, useMemo, useRef } from 'react'
import { BoxGeometry, CircleGeometry, type Group } from 'three'
import { MeshBasicNodeMaterial } from 'three/webgpu'
import { EDITOR_LAYER } from '../../lib/constants'
import useElevationGuides from '../../store/use-elevation-guides'
import { Html } from './localized-html'
import { formatMeasurement } from './measurement-pill'

const LINE_COLOR = 0x81_8c_f8
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import {
type WallNode,
} from '@pascal-app/core'
import { useViewer } from '@pascal-app/viewer'
import { Html } from '@react-three/drei'
import { useFrame } from '@react-three/fiber'
import { useCallback, useMemo, useRef } from 'react'
import * as THREE from 'three'
Expand All @@ -54,6 +53,7 @@ import useInteractionScope, {
} from '../../store/use-interaction-scope'
import { IconRefGlyph } from '../ui/icon-ref'
import { duplicateNodeAndPickUp } from './duplicate-node'
import { Html } from './localized-html'
import { formatMeasurement, MeasurementPill } from './measurement-pill'
import { NodeActionMenu } from './node-action-menu'
import { startZoneRoomTransform } from './room-controls'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

import { type BuildingNode, sceneRegistry, useScene } from '@pascal-app/core'
import { useViewer } from '@pascal-app/viewer'
import { Html } from '@react-three/drei'
import { useFrame } from '@react-three/fiber'
import { useCallback, useRef } from 'react'
import * as THREE from 'three'
import { sfxEmitter } from '../../lib/sfx-bus'
import useEditor from '../../store/use-editor'
import { Html } from './localized-html'
import { NodeActionMenu } from './node-action-menu'

export function FloatingBuildingActionMenu() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
useScene,
} from '@pascal-app/core'
import { useViewer } from '@pascal-app/viewer'
import { Html } from '@react-three/drei'
import { type ThreeEvent, useFrame, useThree } from '@react-three/fiber'
import { useRef, useState } from 'react'
import { type Group, Plane, Vector2, Vector3 } from 'three'
Expand All @@ -26,6 +25,7 @@ import { runRoomHandleDrag } from '../../lib/room-handle-drag'
import useEditor, { isGridSnapActive } from '../../store/use-editor'
import useInteractionScope from '../../store/use-interaction-scope'
import { suppressBoxSelectForPointer } from '../tools/select/box-select-state'
import { Html } from './localized-html'
import { ElevationArrows, outlineCentroid, useHandleScale } from './room-handles'

export const FOOTPRINT_HEIGHT_DRAG_LABEL = 'footprint-height'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import { type AnyNodeId, useScene } from '@pascal-app/core'
import { useViewer } from '@pascal-app/viewer'
import { Html } from '@react-three/drei'
import { useFrame } from '@react-three/fiber'
import { useCallback, useMemo, useRef } from 'react'
import * as THREE from 'three'
Expand All @@ -21,6 +20,7 @@ import {
computeGroupPlanBox,
levelFrame,
} from './group-transform-shared'
import { Html } from './localized-html'
import { NodeActionMenu } from './node-action-menu'
import { useMeshSettleEpoch } from './use-mesh-settle-epoch'

Expand Down
22 changes: 22 additions & 0 deletions packages/editor/src/components/editor/localized-html.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use client'

import { PascalI18nProvider, usePascalI18n } from '@pascal-app/core'
import { Html as DreiHtml } from '@react-three/drei'
import { type ComponentProps, forwardRef } from 'react'

/**
* drei's `Html`, keeping the editor's i18n scope. `Html` renders its children
* in a separate React root, which sees none of the surrounding context, so a
* component inside it would translate with the default (English) scope instead
* of the editor's `?lang=` or `<Editor locale>` one.
*/
export const Html = forwardRef<HTMLDivElement, Omit<ComponentProps<typeof DreiHtml>, 'ref'>>(
function Html({ children, ...props }, ref) {
const i18n = usePascalI18n()
return (
<DreiHtml {...props} ref={ref}>
<PascalI18nProvider i18n={i18n}>{children}</PascalI18nProvider>
</DreiHtml>
)
},
)
58 changes: 46 additions & 12 deletions packages/editor/src/components/editor/node-action-menu.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client'

import { Icon } from '@iconify/react'
import { usePascalI18n } from '@pascal-app/core'
import {
Copy,
Group,
Expand Down Expand Up @@ -65,70 +66,103 @@ export function NodeActionMenu({
onPointerEnter,
onPointerLeave,
children,
deleteLabel = 'Delete',
deleteLabel,
deleteDisabledReason,
}: NodeActionMenuProps) {
const i18n = usePascalI18n()
return (
<div
className="pointer-events-auto flex items-center gap-1 rounded-lg border border-border bg-background/95 p-1 shadow-xl backdrop-blur-md"
data-node-action-menu
onPointerDown={onPointerDown}
onPointerEnter={onPointerEnter}
onPointerLeave={onPointerLeave}
onPointerUp={onPointerUp}
>
{onFind && (
<ActionMenuButton label="Find in catalog" onClick={onFind}>
<ActionMenuButton
label={i18n._({ id: 'editor.nodeActionMenu.findInCatalog', message: 'Find in catalog' })}
onClick={onFind}
>
<Search className="h-4 w-4" />
</ActionMenuButton>
)}
{onMove && (
<ActionMenuButton label="Move" onClick={onMove}>
<ActionMenuButton
label={i18n._({ id: 'editor.nodeActionMenu.move', message: 'Move' })}
onClick={onMove}
>
<Move className="h-4 w-4" />
</ActionMenuButton>
)}
{onEditMesh && (
<ActionMenuButton label="Edit mesh" onClick={onEditMesh}>
<ActionMenuButton
label={i18n._({ id: 'editor.nodeActionMenu.editMesh', message: 'Edit mesh' })}
onClick={onEditMesh}
>
<PencilRuler className="h-4 w-4" />
</ActionMenuButton>
)}
{onGroup && (
<ActionMenuButton keys={['Cmd/Ctrl', 'G']} label="Group selection" onClick={onGroup}>
<ActionMenuButton
keys={['Cmd/Ctrl', 'G']}
label={i18n._({ id: 'editor.nodeActionMenu.groupSelection', message: 'Group selection' })}
onClick={onGroup}
>
<Group className="h-4 w-4" />
</ActionMenuButton>
)}
{onUngroup && (
<ActionMenuButton
keys={['Cmd/Ctrl', 'Shift', 'G']}
label="Ungroup selection"
label={i18n._({
id: 'editor.nodeActionMenu.ungroupSelection',
message: 'Ungroup selection',
})}
onClick={onUngroup}
>
<Ungroup className="h-4 w-4" />
</ActionMenuButton>
)}
{onCurve && (
<ActionMenuButton label="Curve" onClick={onCurve}>
<ActionMenuButton
label={i18n._({ id: 'editor.nodeActionMenu.curve', message: 'Curve' })}
onClick={onCurve}
>
<Spline className="h-4 w-4" />
</ActionMenuButton>
)}
{onRotateLeft && (
<ActionMenuButton label="Rotate left" onClick={onRotateLeft}>
<ActionMenuButton
label={i18n._({ id: 'editor.nodeActionMenu.rotateLeft', message: 'Rotate left' })}
onClick={onRotateLeft}
>
<RotateCcw className="h-4 w-4" />
</ActionMenuButton>
)}
{onRotateRight && (
<ActionMenuButton label="Rotate right" onClick={onRotateRight}>
<ActionMenuButton
label={i18n._({ id: 'editor.nodeActionMenu.rotateRight', message: 'Rotate right' })}
onClick={onRotateRight}
>
<RotateCw className="h-4 w-4" />
</ActionMenuButton>
)}
<RegistryActionContributions />
{children}
{onDuplicate && (
<ActionMenuButton label="Duplicate" onClick={onDuplicate}>
<ActionMenuButton
label={i18n._({ id: 'editor.nodeActionMenu.duplicate', message: 'Duplicate' })}
onClick={onDuplicate}
>
<Copy className="h-4 w-4" />
</ActionMenuButton>
)}
{onAddHole && (
<ActionMenuButton label="Cut out" onClick={onAddHole}>
<ActionMenuButton
label={i18n._({ id: 'editor.nodeActionMenu.cutOut', message: 'Cut out' })}
onClick={onAddHole}
>
<Icon height={16} icon="carbon:cut-out" width={16} />
</ActionMenuButton>
)}
Expand All @@ -138,7 +172,7 @@ export function NodeActionMenu({
disabled={!!deleteDisabledReason}
disabledReason={deleteDisabledReason}
keys={['Delete / Backspace']}
label={deleteLabel}
label={deleteLabel ?? i18n._({ id: 'editor.nodeActionMenu.delete', message: 'Delete' })}
onClick={onDelete}
>
<Trash2 className="h-4 w-4" />
Expand Down
3 changes: 1 addition & 2 deletions packages/editor/src/components/editor/node-arrow-handles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
useScene,
} from '@pascal-app/core'
import { useViewer } from '@pascal-app/viewer'
import { Html } from '@react-three/drei'
import { createPortal, type ThreeEvent, useFrame, useThree } from '@react-three/fiber'
import { useEffect, useMemo, useRef, useState } from 'react'
import {
Expand All @@ -41,7 +40,6 @@ import {
Vector3,
} from 'three'
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'

import { MeshBasicNodeMaterial } from 'three/webgpu'
import { useCeilingEditCeilingId } from '../../lib/ceiling-edit-session'
import { EDITOR_LAYER } from '../../lib/constants'
Expand Down Expand Up @@ -78,6 +76,7 @@ import {
import { createLinearResizeDragBinding, linearResizeFactor } from './handles/linear-resize-drag'
import { resolveResizeSnapValue } from './handles/resize-snap'
import { type HandleDragControls, useHandleDrag } from './handles/use-handle-drag'
import { Html } from './localized-html'

// Pooled scratch for the handle rig's world-relative pose mapping.
const _rigRelative = new Matrix4()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import { type OpenWallEnd, sceneRegistry } from '@pascal-app/core'
import { useViewer } from '@pascal-app/viewer'
import { Html } from '@react-three/drei'
import { useFrame } from '@react-three/fiber'
import { memo, useEffect, useMemo, useRef } from 'react'
import { BoxGeometry, CircleGeometry, type Group, RingGeometry } from 'three'
Expand All @@ -19,6 +18,7 @@ import { cn } from '../../lib/utils'
import useEditor from '../../store/use-editor'
import { isViewVisible, VIEW_3D, visibleScene } from '../../store/view-layout'
import { JoinWallsPill } from './join-walls-pill'
import { Html } from './localized-html'
import { OpenWallEndsHint } from './open-wall-ends-hint'

/**
Expand Down
Loading
Loading