Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
3731eb3
Add roof surface placement support for items
sudhir9297 May 18, 2026
ed53bc2
Merge branch 'main' of github.com:pascalorg/editor
sudhir9297 May 20, 2026
fd8e02c
Merge branch 'main' of github.com:pascalorg/editor
sudhir9297 May 20, 2026
7c1e383
fixed conflict
sudhir9297 May 20, 2026
b3377da
Merge branch 'main' of github.com:pascalorg/editor
sudhir9297 May 20, 2026
f177a65
Merge branch 'main' of github.com:pascalorg/editor
sudhir9297 May 22, 2026
48b7f3e
wall: 2D floor-plan move, side arrows, drag-to-move endpoints
sudhir9297 May 22, 2026
e10294a
wall: 2D floor-plan move adopts 3D junction planner
sudhir9297 May 22, 2026
3376bc9
door/window: R flips side, E toggles open/close
sudhir9297 May 22, 2026
c7d8edf
door/window: keep 2D plan in sync during placement drag
sudhir9297 May 22, 2026
1d872d8
floor-plan: live wall-draft measurement + opening placement event order
sudhir9297 May 22, 2026
c3c94bd
wall: pass geometry as prop on move arrow handles
sudhir9297 May 22, 2026
21caac8
ifc-converter: regenerate next-env.d.ts after Next route path move
sudhir9297 May 22, 2026
03651da
Merge remote-tracking branch 'upstream/main' into fix/may-22-friday
sudhir9297 May 22, 2026
4382fb5
move: track which view finalised so split-view cleanups don't race
sudhir9297 May 22, 2026
36358da
wall: 2D drag publishes to useLiveNodeOverrides, zustand only on commit
sudhir9297 May 22, 2026
f68d0d9
wall: new in-world selection UI — side arrows, height handle, corner …
sudhir9297 May 23, 2026
bc08187
wall: smooth ground-menu side-flip with hysteresis + lerp
sudhir9297 May 23, 2026
12facb6
wall: raise ground-action menu to 10 cm so icons clear floor textures
sudhir9297 May 23, 2026
6024d52
wall: anchor 2D action menu and 3D height arrow at curve apex
sudhir9297 May 23, 2026
5bd2a12
door: in-world selection UI — side width arrows, height arrow, ground…
sudhir9297 May 23, 2026
be87821
door: 2D width arrows + world-relative move dot, deterministic commits
sudhir9297 May 23, 2026
4994675
window: in-world selection UI, 2D width arrows, deterministic commits
sudhir9297 May 23, 2026
4744d92
stair: in-world selection UI — side/length/height arrows, ground menu
sudhir9297 May 23, 2026
44344bf
stair: 2D move-revert fix, parent ground menu, curved/spiral in-world…
sudhir9297 May 25, 2026
a0b56ed
Merge branch 'main' into fix/may-22-friday
sudhir9297 May 25, 2026
f0a0c3d
editor: reuse measurement-bar geometry; fix two post-merge dangling refs
sudhir9297 May 25, 2026
8ec41a0
editor: pin building bbox center to cursor during move
sudhir9297 May 25, 2026
cc2be42
stair: 2D resize affordances + dispose curved/spiral geometry on swap
sudhir9297 May 25, 2026
cbb9970
editor: revert in-world ground menus to HTML floating; align grid to …
sudhir9297 May 25, 2026
7cfad53
handles: registry-driven in-world resize arrows
sudhir9297 May 25, 2026
5756f24
handles: migrate wall + parent stair to registry; add arc-resize
sudhir9297 May 25, 2026
a1bc6cb
handles: tap-action descriptor + EditorApi; finish wall + fence migra…
sudhir9297 May 25, 2026
0e207a7
wall: revert side-move arrows + corner pickers to legacy component
sudhir9297 May 25, 2026
0f9274c
editor: level naming helper + ambient floorplan render during buildin…
sudhir9297 May 25, 2026
a80942d
handles: 3D resize arrows publish to useLiveNodeOverrides, commit on …
sudhir9297 May 25, 2026
9f54a79
floating menu: enable Move icon for wall / door / window
sudhir9297 May 25, 2026
3815af5
handles: live drag, guide rings, dimension chips; floating-menu Move …
sudhir9297 May 26, 2026
071f7cf
fence + column: registry handles, brace spread arrows, per-style defa…
sudhir9297 May 26, 2026
0727875
elevator + column: registry handles, rotation gizmo with curved arrow
sudhir9297 May 26, 2026
713c19b
slab + ceiling + shelf: registry handles, live polygon preview, curso…
sudhir9297 May 26, 2026
c97f702
roof-segment: registry handles, live override path, analytical-normal…
sudhir9297 May 26, 2026
7ddb229
floorplan: resize/rotate arrows for column, shelf, elevator, fence; r…
sudhir9297 May 26, 2026
d1ed88d
registry: floorplanScope + movable capability; cursor focus under mouse
sudhir9297 May 27, 2026
e89a822
wall: corner billboard, perpendicular grid snap, drop 45° from move/d…
sudhir9297 May 27, 2026
fbb4841
fence: drop 45° angle snap from draft + endpoint move, Shift = fine step
sudhir9297 May 27, 2026
4474f97
floorplan menu: show Move button for selected walls
sudhir9297 May 27, 2026
3746d63
floorplan wall move: axis-lock to wall normal, match 3D MoveWallTool
sudhir9297 May 27, 2026
8132c8f
floorplan move overlay: commit at last pointermove, not pointer-up
sudhir9297 May 27, 2026
3cc004c
floorplan: door wall-hit placement, fence/stair move fixes, wall auto…
sudhir9297 May 27, 2026
c6bb46b
Merge remote-tracking branch 'upstream/main' into fix/may-22-friday
sudhir9297 May 27, 2026
eea1e30
open-pr skill: refresh existing PR description instead of bailing
sudhir9297 May 27, 2026
dd06577
floorplan: capability/hook dispatch, generic resize + endpoint state
sudhir9297 May 27, 2026
d6b74fb
3d wall measurement: hide label for selected walls
sudhir9297 May 27, 2026
b47359a
editor: set EDITOR_LAYER on arrow handles to exclude from thumbnails
open-pascal May 27, 2026
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
41 changes: 38 additions & 3 deletions .agents/skills/open-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,12 @@ git push -u origin HEAD
Check for an existing PR first:

```bash
gh pr view --json url 2>/dev/null
gh pr view --json number,url,title,body 2>/dev/null
```

If none exists, create it. Pass the body via HEREDOC to preserve markdown formatting:
### 3a. No existing PR → create one

Pass the body via HEREDOC to preserve markdown formatting:

```bash
gh pr create --title "short, scope-prefixed title" --body "$(cat <<'EOF'
Expand Down Expand Up @@ -85,7 +87,40 @@ EOF

Keep the title under ~70 characters. Use a scope prefix when there's an obvious one (`viewer:`, `core:`, `editor:`, `mcp:`).

If a PR already exists, print its URL and stop — don't recreate.
### 3b. PR already exists → update its description

Do **not** recreate the PR. Refresh the existing body so it reflects everything currently on the branch, while keeping the template structure and any reviewer-meaningful state the user already set.

1. Capture the existing body and the full branch history:

```bash
gh pr view --json number,body -q '.number, .body' > /tmp/existing-pr.txt
git log --oneline main..HEAD
git diff --stat main..HEAD
```

2. Reconstruct the body section-by-section. Keep the four template headings in the same order (`## What does this PR do?`, `## How to test`, `## Screenshots / screen recording`, `## Checklist`). For each section:

- **What does this PR do?** — rewrite from the *current* commits and diff on the branch, not from memory. Preserve any `Fixes #123` / `Refs #123` lines from the old body.
- **How to test** — regenerate concrete steps for the *current* behaviour. If a previous step is still valid, keep its wording; drop steps that no longer apply; add steps for new commits.
- **Screenshots / screen recording** — preserve the existing content verbatim (links, embedded images, "N/A — …"). Do not blank it out. Only change it if the user explicitly provided a new recording.
- **Checklist** — preserve the user's tick state (`[x]` vs `[ ]`) for every item that still exists in the template. Add any new template items as unchecked.

If the old body contains extra sections the template doesn't have (e.g. a manual "Notes" block), keep them at the end.

3. Apply the update:

```bash
gh pr edit <number> --body "$(cat <<'EOF'
## What does this PR do?
EOF
)"
```

Use `gh pr edit --title` *only* if the branch's scope has clearly changed; otherwise leave the title alone.

4. Print the PR URL so the user can confirm the edit.

## 4. Report

Expand Down
9 changes: 8 additions & 1 deletion apps/editor/app/client-bootstrap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@
// `loaded` guard inside `../lib/bootstrap` keeps the side effect
// idempotent under HMR.
import '../lib/bootstrap'
import type { ReactNode } from 'react'
import { type ReactNode, useEffect } from 'react'

export function ClientBootstrap({ children }: { children: ReactNode }) {
useEffect(() => {
if (process.env.NODE_ENV !== 'development') return
// Loaded here (not via a `<Script>` tag in <head>) to avoid React's
// "script inside a React component" hydration warning. The package
// is already a direct dep, so we don't need the CDN auto-global.
import('react-scan').then(({ scan }) => scan({ enabled: true }))
}, [])
return children
}
2 changes: 1 addition & 1 deletion apps/ifc-converter/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import './.next/types/routes.d.ts'
import "./.next/dev/types/routes.d.ts";

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
34 changes: 34 additions & 0 deletions packages/core/src/hooks/spatial-grid/spatial-grid-sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,40 @@ export function resolveLevelId(node: AnyNode, nodes: Record<string, AnyNode>): s
return 'default' // fallback for orphaned items
}

/**
* Returns the building id that contains the given level, or `null` if
* the level is unparented or no enclosing building exists.
*
* Most scenes record the relationship via `level.parentId →
* building.id`, but older serialisations occasionally drop `parentId`
* even though the building's `children` array still references the
* level. The fallback scan covers that case.
*
* Used by `FloorplanRegistryLayer` to discover building-scoped kinds
* (`def.floorplanScope === 'building'`) without hardcoding any kind
* name in the editor layer.
*/
export function resolveBuildingForLevel(
levelId: AnyNodeId,
nodes: Record<AnyNodeId, AnyNode>,
): AnyNodeId | null {
const level = nodes[levelId] as AnyNode | undefined
if (!level) return null
const directParent = (level as { parentId?: AnyNodeId | null }).parentId ?? null
if (directParent) {
const candidate = nodes[directParent]
if (candidate?.type === 'building') return candidate.id as AnyNodeId
}
for (const candidate of Object.values(nodes)) {
if (candidate?.type !== 'building') continue
const children = (candidate as { children?: AnyNodeId[] }).children
if (Array.isArray(children) && children.includes(levelId)) {
return candidate.id as AnyNodeId
}
}
return null
}

// Call this once at app initialization. Returns an unsubscribe function that
// detaches the scene-store listener (useful when the editor is unmounted so
// the spatial grid singleton does not hold stale references to old scenes).
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export {
export { pointInPolygon, spatialGridManager } from './hooks/spatial-grid/spatial-grid-manager'
export {
initSpatialGridSync,
resolveBuildingForLevel,
resolveLevelId,
} from './hooks/spatial-grid/spatial-grid-sync'
export { useSpatialQuery } from './hooks/spatial-grid/use-spatial-query'
Expand All @@ -53,11 +54,13 @@ export {
} from './lib/door-operation'
export { getRenderableSlabPolygon } from './lib/slab-polygon'
export {
type AutoCeilingSyncPlan,
type AutoSlabSyncPlan,
detectSpacesForLevel,
initSpaceDetectionSync,
isSpaceDetectionPaused,
pauseSpaceDetection,
planAutoCeilingsForLevel,
planAutoSlabsForLevel,
resumeSpaceDetection,
type Space,
Expand Down Expand Up @@ -100,6 +103,7 @@ export {
export {
default as useLiveNodeOverrides,
type LiveNodeOverrides,
getEffectiveNode,
} from './store/use-live-node-overrides'
export { default as useLiveTransforms, type LiveTransform } from './store/use-live-transforms'
export { clearSceneHistory, default as useScene } from './store/use-scene'
Expand Down Expand Up @@ -172,11 +176,14 @@ export {
} from './systems/wall/wall-mitering'
export {
constrainWallMoveDeltaToAxis,
getLinkedWallUpdates,
getPerpendicularWallMoveAxis,
getPlannedLinkedWallUpdates,
planWallMoveJunctions,
type WallMoveAxis,
type WallMoveBridgePlan,
type WallMoveJunctionPlan,
type WallMoveLinkedWallTargetPlan,
type WallPlanPoint,
} from './systems/wall/wall-move'
export type { SceneGraph } from './utils/clone-scene-graph'
Expand Down
39 changes: 29 additions & 10 deletions packages/core/src/lib/space-detection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ export type AutoSlabSyncPlan = {
delete: Array<SlabNodeType['id']>
}

export type AutoCeilingSyncPlan = {
create: CeilingNodeType[]
update: Array<{ id: CeilingNodeType['id']; data: Partial<CeilingNodeType> }>
delete: Array<CeilingNodeType['id']>
}

const DEFAULT_AUTO_SLAB_ELEVATION = 0.05
const DEFAULT_AUTO_CEILING_HEIGHT = 2.5
const ROOM_CURVE_TOLERANCE = 0.04
Expand Down Expand Up @@ -650,12 +656,10 @@ function syncAutoSlabsForLevel(
}
}

function syncAutoCeilingsForLevel(
levelId: string,
export function planAutoCeilingsForLevel(
roomPolygons: Point2D[][],
existingCeilings: CeilingNodeType[],
sceneStore: any,
) {
): AutoCeilingSyncPlan {
const manualCeilings = existingCeilings.filter((ceiling) => !ceiling.autoFromWalls)
const manualSignatures = new Set(
manualCeilings.map((ceiling) => polygonSignature(ceiling.polygon.map(pointFromTuple))),
Expand Down Expand Up @@ -782,16 +786,31 @@ function syncAutoCeilingsForLevel(
)
}

if (ceilingsToDelete.length > 0) {
sceneStore.getState().deleteNodes(ceilingsToDelete)
return {
create: ceilingsToCreate,
update: ceilingsToUpdate,
delete: ceilingsToDelete,
}
}

if (ceilingsToUpdate.length > 0) {
sceneStore.getState().updateNodes(ceilingsToUpdate)
function syncAutoCeilingsForLevel(
levelId: string,
roomPolygons: Point2D[][],
existingCeilings: CeilingNodeType[],
sceneStore: any,
) {
const plan = planAutoCeilingsForLevel(roomPolygons, existingCeilings)

if (plan.delete.length > 0) {
sceneStore.getState().deleteNodes(plan.delete)
}

if (ceilingsToCreate.length > 0) {
sceneStore.getState().createNodes(ceilingsToCreate.map((node) => ({ node, parentId: levelId })))
if (plan.update.length > 0) {
sceneStore.getState().updateNodes(plan.update)
}

if (plan.create.length > 0) {
sceneStore.getState().createNodes(plan.create.map((node) => ({ node, parentId: levelId })))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ function buildFixture() {
function makeScene(nodes: Record<string, AnyNode>): SceneApi {
return {
get: ((nid: AnyNodeId) => nodes[nid as string]) as SceneApi['get'],
nodes: () => nodes as Readonly<Record<AnyNodeId, AnyNode>>,
update: () => {},
upsert: () => ID(''),
delete: () => {},
Expand Down
Loading
Loading