ai-chatbot/packages/editor/src/components/ui/level-duplicate-dialog.tsx
Wawa e618175bba Reset core/viewer/editor/mcp packages and apps/editor from private-editor
Wholesale swap of packages/{core,viewer,editor,mcp} and apps/editor with the
versions from the private editor repo, which is the production source of truth.

Setup changes:
- packages/{core,viewer,editor} versions held at 0.7.0 baseline (matching
  the most recent published release) so a bump=minor publishes 0.8.0
- packages/mcp held at 0.1.1 (never published; first publish will go through
  the new release.yml flow)
- peerDependencies and devDependencies for inter-package @pascal-app/*
  references pinned to ^0.7.0 instead of '*' / 'workspace:*' so they are
  valid for npm consumers
- Root package.json: TypeScript bumped to 6.0.2, added overrides for
  @types/react, @types/react-dom, @types/three to prevent JSX namespace
  fragmentation across the workspace
- release.yml extended to also publish editor and mcp; 'both' option renamed
  to 'all'; added a sync step that updates inter-package peerDeps/devDeps to
  match the new versions on every bump (so viewer/editor/mcp tarballs always
  reference the version of core they were built against)
- Root scripts gained release:editor and release:mcp shortcuts

Verification:
- bun install --frozen-lockfile is consistent
- packages/{core,viewer,mcp} build cleanly, dist/index.d.ts emitted
- packages/editor check-types reports 21 pre-existing errors, identical to
  what private-editor currently reports

Open PRs against editor-v2 will need rebasing/conflict resolution.
2026-05-09 20:52:26 +00:00

114 lines
3.2 KiB
TypeScript

'use client'
import type { LevelNode } from '@pascal-app/core'
import { useEffect, useState } from 'react'
import type { LevelDuplicatePreset } from '../../lib/level-duplication'
import { cn } from '../../lib/utils'
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from './primitives/dialog'
const DUPLICATE_PRESETS: Array<{
id: LevelDuplicatePreset
label: string
description: string
}> = [
{
id: 'everything',
label: 'Everything',
description: 'Structure, materials, furniture, and references.',
},
{
id: 'structure',
label: 'Structure only',
description: 'Walls, slabs, roofs, stairs, windows, and doors without finishes.',
},
{
id: 'structure-materials',
label: 'Structure + materials',
description: 'Structure with the current material and finish assignments.',
},
{
id: 'structure-furniture',
label: 'Structure + furniture',
description: 'Structure, finishes, and placed items, without guide references.',
},
]
function getLevelLabel(level: LevelNode | null) {
if (!level) return 'this level'
return level.name || `Level ${level.level}`
}
export function LevelDuplicateDialog({
open,
level,
onConfirm,
onOpenChange,
}: {
open: boolean
level: LevelNode | null
onConfirm: (preset: LevelDuplicatePreset) => void
onOpenChange: (open: boolean) => void
}) {
const [preset, setPreset] = useState<LevelDuplicatePreset>('everything')
useEffect(() => {
if (open) {
setPreset('everything')
}
}, [open])
return (
<Dialog onOpenChange={onOpenChange} open={open}>
<DialogContent className="sm:max-w-md" showCloseButton={false}>
<DialogHeader>
<DialogTitle>Duplicate Level</DialogTitle>
<DialogDescription>Choose what to copy from {getLevelLabel(level)}.</DialogDescription>
</DialogHeader>
<div className="grid gap-2">
{DUPLICATE_PRESETS.map((option) => (
<button
className={cn(
'cursor-pointer rounded-xl border px-3 py-3 text-left transition-colors',
preset === option.id
? 'border-primary bg-primary/10 text-foreground'
: 'border-border bg-background hover:bg-accent/40',
)}
key={option.id}
onClick={() => setPreset(option.id)}
type="button"
>
<div className="font-medium text-sm">{option.label}</div>
<div className="mt-1 text-muted-foreground text-xs">{option.description}</div>
</button>
))}
</div>
<DialogFooter>
<button
className="cursor-pointer rounded-md px-4 py-2 text-muted-foreground text-sm transition-colors hover:bg-accent"
onClick={() => onOpenChange(false)}
type="button"
>
Cancel
</button>
<button
className="cursor-pointer rounded-md bg-primary px-4 py-2 text-primary-foreground text-sm transition-opacity hover:opacity-90"
onClick={() => onConfirm(preset)}
type="button"
>
Duplicate
</button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}