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.
114 lines
3.2 KiB
TypeScript
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>
|
|
)
|
|
}
|