Skip to content

editor: integrate local plugins, surface stairs and deck railings - #1001

Open
sudhir9297 wants to merge 20 commits into
pascalorg:mainfrom
sudhir9297:t3code/link-local-plugins
Open

sudhir9297 wants to merge 20 commits into
pascalorg:mainfrom
sudhir9297:t3code/link-local-plugins

Conversation

@sudhir9297

@sudhir9297 sudhir9297 commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

  • Wire Landscape and Bath Space discovery and host panels into the standalone editor, pin portable Pascal cluster package revisions for Bath Space, Landscape, Pool, Streetscape and WebXR while supporting optional local development links. Add an install helper for package-directory links and shared peer dependencies, plus a Turbopack root that includes those packages.
  • Connect native straight stairs to registered top-surface boundaries with matching placement and movement in 2D and 3D, live flight previews, inspector controls and curved-boundary transitions. Respect magnetic snap mode and use SceneApi for floor-plan commits.
  • Draw fences as deck railings with inward edge/corner snapping, deck-top elevation and active-floor tracking. Remap stair/fence support references during cloning, strip preset host references and clear deleted hosts in the same scene update.
  • Preserve wall-mounted object grab offsets and face placement, support alpha-cutout selection outlines, and let primary inspector extensions supply the card content without Landscape-specific dispatch.
  • Add plugin connected-move hooks and room-boundary opt-outs, align the Saved scenes control at the canvas top, and integrate pool section messaging and plugin styling.

The companion Landscape/WebXR fixes are published in sudhir9297/pascal-cluster on fix/editor-ci-1001, with immutable package snapshots referenced by this PR. Local plugin development is opt-in through PASCAL_LOCAL_PLUGINS_ROOT in the ignored .env.local; Clean CI and portable installs use the pinned packages; SETUP.md explains restoring them before a portable build from a locally linked checkout. See SETUP.md for the local workflow.

How to test

  1. Run bun install --frozen-lockfile in a clean checkout. No sibling repository is required. For local plugin development, configure PASCAL_LOCAL_PLUGINS_ROOT in .env.local as documented in SETUP.md and run bun install.
  2. Run the editor on port 3004. Confirm the editor canvas loads, plugin panels are available, and Saved scenes is centered at the top of the canvas. The local server was run and returned HTTP 200 with a rendered canvas.
  3. On a raised deck on the active floor, draw a fence from its corners/edges. Confirm snap dots and the fence sit on the deck top, slightly inside its perimeter, and changing floors does not continue the previous drawing chain.
  4. Place and move a straight stair beside a supported surface in 2D and 3D. With magnetic snapping enabled, confirm its high end, rise and flight dimensions follow the surface; with snapping off or Alt held, confirm raw placement. Commit and undo the movement as one gesture.
  5. Duplicate a level containing the surface and its fence/stair. Confirm support IDs point to the copied host; delete a host and undo, confirming surviving references clear and restore. Check wall-mounted movement and selection outlines on alpha-cutout geometry.

Checks run: a clean checkout passed frozen dependency installation and CI=1 bun run ci with Bun 1.3.14 (lint, skill validation, typechecks, existing test suites and production builds). MCP Node smoke and the packed CLI/runtime smoke passed, including installation, digest verification and a scene round-trip. Lint reports warnings but no errors. The integrated plugin model/texture catalogs make the runtime archive 182.9 MB; its size budget is now 220 MB. The local editor on port 3004 loads with all plugin menu entries. Steps 3–5 remain reviewer checks.

Screenshots / screen recording

Not added yet.

Checklist

  • I've tested this locally with bun dev
  • My code follows the existing code style (run bun check to verify)
  • I've updated relevant documentation (if applicable)
  • This PR targets the main branch

Note

Medium Risk
Large dependency and bootstrap surface area plus new spatial snapping, reference remapping, and move/commit paths that affect scene graph integrity and undo behavior.

Overview
Integrates Landscape, Bath Space, Pool, and Streetscape Lab into the standalone editor via pinned GitHub packages, dynamic bootstrap discovery, Tailwind @source paths, and a raised portable runtime archive budget (~220 MB).

Straight stairs magnetically attach to plugin walking surfaces (landscapeSurfaceId, boundary-based snap in 2D/3D), with flight preview/commit and inspector hooks; fences can draw as deck railings with edge/corner snap and supportSurfaceNodeId lift. Core adds surface-support clone/delete handling, optional metadata.roomBoundary exclusions, connectedMove for port graphs, and inspector primaryWhen extensions.

The home/scene editors use a viewerBanner (Saved scenes + lazy pool section bar), global thin scrollbars, and fence draft behavior respects Alt for angle lock. Stair placement moves to the nodes package (def.tool); legacy StairTool is removed from the tool manager. Move/placement polish includes level-preserved drags, wall grab tracking, and screen-sized floorplan handles.

Reviewed by Cursor Bugbot for commit a64250f. Bugbot is set up for automated code reviews on this repo. Configure here.

@pascal

pascal Bot commented Oct 6, 2026

Copy link
Copy Markdown

What this PR does

This wires five Pascal cluster plugins (Bath Space, Landscape, Pool, Streetscape, WebXR) into the standalone editor and adds two building features on top of the core/editor packages: straight stairs that attach to registered top surfaces, and fences drawn as deck railings that snap inside a deck's perimeter and track the active floor. Supporting work in core covers support references (remapping on clone, clearing on host delete), port connectivity, and a new stair-surface snap service with tests. The author marks this a local development draft: the five plugin dependencies in apps/editor/package.json point at file:/Users/sudhir/Desktop/work/pascal-cluster/packages, and the PR body says portable dependency configuration and a committed companion plugin revision are needed before it can merge.

Changed files

File Change What changed
apps/editor/package.json modified Pool, Streetscape (renamed to -lab), WebXR switched from GitHub refs to local file: paths; Bath Space and Landscape added the same way
bun.lock modified Lockfile follows the file: dependency switch, pulls in maplibre, tanstack virtual and other new transitive deps
package.json modified Root manifest tweak (1 line)
apps/editor/next.config.ts modified Derives a Turbopack root from the file: dependency paths; transpile list updated for the new/renamed plugins
apps/editor/lib/bootstrap.ts modified Registers Landscape and Bath Space plugins and host panels; Streetscape import points at plugin-streetscape-lab
apps/editor/app/globals.css modified Tailwind @source entries for the local plugins, plus global thin-scrollbar styling
apps/editor/app/page.tsx modified Replaces the inline local-editor notice with a viewerBanner holding a "Saved scenes" link and the pool banner
apps/editor/components/pool-section-banner.tsx added Thin wrapper rendering PoolSectionBar from the pool plugin
apps/editor/components/scene-loader.tsx modified Passes the pool banner through viewerBanner
packages/core/src/services/stair-surface-snap.ts (+ .test.ts) added New snap service for connecting stairs to top surfaces, with unit tests
packages/core/src/lib/surface-support-references.ts added Helper for surface support references
packages/core/src/services/port-connectivity.ts (+ .test.ts) modified Connectivity handling for the new support references, with added tests
packages/core/src/registry/types.ts, scene-api.ts, subtree.ts modified Registry types and SceneApi surface for support hosts and connected moves
packages/core/src/schema/nodes/stair.ts (+ stair.test.ts), fence.ts modified / added Schema fields for stair and fence support references
packages/core/src/utils/clone-scene-graph.ts, store/actions/node-actions.ts, hooks/spatial-grid/support-host-patch.ts, contracts/reference-inventory.ts modified Remap support refs on clone, clear refs when a host is deleted, inventory entries
packages/core/src/lib/room-graph.ts, room-topology-index.ts, structure-kernel.ts, space-detection.test.ts modified Room-boundary opt-out plumbing and a test for it
packages/editor/src/components/tools/stair/stair-tool.tsx modified Largest editor change: surface-aware placement and movement, flight preview, snap-mode handling
packages/editor/src/store/use-stair-build-preview.ts modified Preview state follows the snapped surface
packages/editor/src/components/tools/fence/fence-drafting.ts modified Deck-top elevation, inward edge/corner snapping, floor-change chain reset
packages/nodes/src/fence/lift.ts, definition.ts, geometry.ts, packages/nodes/package.json modified Fence lift/geometry support for deck railings
packages/editor/src/components/tools/registry/move-registry-node-tool.tsx, item-surface-move.ts, item/use-placement-coordinator.tsx modified Connected-move hooks and wall-mounted grab offset / face placement handling
packages/editor/src/lib/wall-placement-pointer.ts added Pointer helper for wall placement
packages/editor/src/components/ui/panels/panel-wrapper.tsx, editor/floorplan-panel.tsx, src/index.tsx modified Inspector extensions supply card content instead of Landscape-specific dispatch
packages/editor/src/components/editor-2d/*, lib/direct-manipulation.ts, fresh-planar-placement.ts, glb-export.ts, selection-routing.ts, use-floorplan-background-placement.ts modified 2D overlay/layer updates, alpha-cutout selection outlines, export and routing follow-ups
20 further files — Remainder of the 70 changed files, not shown in the truncated diff listing

Where to start

Start with apps/editor/package.json and next.config.ts to see the dependency setup this draft depends on, then read packages/core/src/services/stair-surface-snap.ts with its test and packages/editor/src/components/tools/stair/stair-tool.tsx, which carry most of the behaviour change.

@sudhir9297
sudhir9297 force-pushed the t3code/link-local-plugins branch from 3761c31 to 94c1dad Compare October 6, 2026 09:36
@sudhir9297
sudhir9297 marked this pull request as ready for review October 6, 2026 09:49

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/editor/src/lib/wall-placement-pointer.ts
Comment thread packages/editor/src/components/tools/fence/fence-drafting.ts Outdated
Comment thread packages/editor/src/components/tools/stair/stair-tool.tsx Outdated

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/nodes/src/stair/tool.tsx
Comment thread packages/editor/src/lib/wall-placement-pointer.ts

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/core/src/services/stair-surface-snap.ts
Comment thread packages/editor/src/components/tools/item/use-placement-coordinator.tsx Outdated

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/editor/src/components/editor/floorplan-panel.tsx
Comment thread packages/nodes/src/fence/drafting.ts
@sudhir9297
sudhir9297 force-pushed the t3code/link-local-plugins branch from 25f199d to 1c453b0 Compare October 7, 2026 05:21

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/nodes/src/stair/tool.tsx
Comment thread packages/editor/src/components/editor/floorplan-panel.tsx

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/editor/src/lib/wall-placement-pointer.ts
@ovurrsl

ovurrsl commented Oct 7, 2026

Copy link
Copy Markdown

Architectural Review: PR #1001

Thank you for the ambitious contribution connecting deck railings, landscape snapping, and stair surface workflows!

While the surface attachment mechanics and landscape transition math are valuable additions, this pull request introduces critical architectural boundary regressions, violates our unified modifier and snapping invariants, and breaks CI compilation for the host editor.

Review Summary

Verdict: REQUEST_CHANGES
Findings: 4 Blockers, 1 Suggestion, 1 Nit
Primary Files to Address:

  1. apps/editor/package.json & apps/editor/lib/bootstrap.ts
  2. packages/editor/src/components/tools/stair/stair-tool.tsx
  3. packages/nodes/src/fence/tool.tsx
  4. packages/nodes/src/stair/floorplan-move.ts

Findings

Blocker 1: Broken Typecheck and CI from External Git Packages

  • Where: apps/editor/lib/bootstrap.ts:12,19,21, apps/editor/components/pool-section-banner.tsx:3, apps/editor/package.json:24-27
  • Offending Code:
    // apps/editor/lib/bootstrap.ts:12,19,21
    import { bathSpaceHostPanel, bathSpacePlugin } from '@pascal-app/plugin-bath-space'
    import { landscapeHostPanel, landscapePlugin } from '@pascal-app/plugin-landscape'
    import { streetscapeHostPanel, streetscapePlugin } from '@pascal-app/plugin-streetscape-lab'
    
    // apps/editor/components/pool-section-banner.tsx:3
    import { PoolSectionBar } from '@pascal-app/plugin-pool'
  • Rule Violated: AGENTS.md Working Agreements ("bun run ci is exactly what CI runs"), DECISIONS.md E-014 (Plugin contract v1), and wiki/architecture/plugin-authoring.md.
  • Why: Running bun run check-types currently fails with fatal compilation errors:
    editor:check-types: components/pool-section-banner.tsx(3,10): error TS2305: Module '"@pascal-app/plugin-pool"' has no exported member 'PoolSectionBar'.
    editor:check-types: lib/bootstrap.ts(12,53): error TS2307: Cannot find module '@pascal-app/plugin-bath-space' or its corresponding type declarations.
    editor:check-types: lib/bootstrap.ts(19,53): error TS2307: Cannot find module '@pascal-app/plugin-landscape' or its corresponding type declarations.
    editor:check-types: lib/bootstrap.ts(21,57): error TS2307: Cannot find module '@pascal-app/plugin-streetscape-lab' or its corresponding type declarations.
    
    apps/editor/package.json points to personal git monorepo branches (github:sudhir9297/pascal-cluster#...) that fail type resolution in standard environments. Furthermore, scripts/link-local-plugins.ts and apps/editor/local-plugins.ts expect local disk checkouts (PASCAL_LOCAL_PLUGINS_ROOT). The host editor application cannot depend on unexported types from personal external git repositories.
  • Proposed Fix: Plugin discovery and registration must follow discoverPlugins() / setPluginDiscovery(). External plugins must either be published packages with valid exported TypeScript definitions or loaded dynamically via the standard plugin lifecycle. Remove broken static imports from bootstrap.ts and pool-section-banner.tsx.

Blocker 2: Legacy File Location & Registry Dispatch Regression

  • Where: packages/editor/src/components/tools/stair/stair-tool.tsx:615-674
  • Offending Code:
    // packages/editor/src/components/tools/stair/stair-tool.tsx:660-673
    const snap =
      event.nativeEvent?.altKey || !isMagneticSnapActive()
        ? null
        : resolveStairSurfaceSnap(
            draft,
            nodes,
            candidate,
            planStairSizing(previewRiseRef.current).length,
            baseElevation,
            useScene.getState().installedPlugins,
          )
    landscapeSnapRef.current = snap
    if (snap) return snap.position
    return candidate
  • Rule Violated: DECISIONS.md E-002 (Registry-driven composition: "A node kind is one NodeDefinition in packages/nodes/src// ... no per-kind files in legacy locations") and .agents/skills/review-architecture/SKILL.md §3 / §4.A / §4.B ("Does the file mention a specific kind by name? ... Then it belongs in packages/nodes/src//, NOT under packages/editor/src/components/tools//").
  • Why: Phase 6 systematically eliminated kind-specific tools from packages/editor/src/components/tools/<kind>/. Built-in and third-party plugins must participate equally through NodeDefinition:
    1. Authoring tool logic belongs on stairDefinition.tool in packages/nodes/src/stair/tool.tsx.
    2. Continuing to maintain and expand packages/editor/src/components/tools/stair/stair-tool.tsx creates a "special-cased" host file that violates plugin isolation.
  • Proposed Fix: Move all stair placement, preview, and surface snap interaction logic into packages/nodes/src/stair/tool.tsx exposed via stairDefinition.tool. Retire packages/editor/src/components/tools/stair/stair-tool.tsx.

Blocker 3: Alt-Key Misused as Snap and Alignment Bypass in Fence Drafting

  • Where: packages/nodes/src/fence/tool.tsx:738, 791, 821, 854, 1204
  • Offending Code:
    // packages/nodes/src/fence/tool.tsx:730-740
    const snappedLocal = alignPoint(
      snapFenceDraftPoint({
        point: localPoint,
        walls,
        fences,
        start: angleLocked ? [startingPoint.current.x, startingPoint.current.z] : undefined,
        angleSnap: angleLocked,
        magnetic: isMagneticSnapActive(),
        bypassSnap: event.nativeEvent.altKey,
      }),
      { applySnap: !angleLocked },
    )
    and
    // packages/nodes/src/fence/tool.tsx:1200-1205
    const point = snapPoint(
      [
        pointed?.localPoint?.[0] ?? event.localPosition[0],
        pointed?.localPoint?.[2] ?? event.localPosition[2],
      ],
      event.nativeEvent.altKey,
    )
  • Rule Violated: DECISIONS.md E-004 ("Snapping is a visible per-context mode. Shift tap cycles the mode; Alt hold forces the raw cursor past snapping and collisions. No held-Shift bypass; never read event.shiftKey for snapping ... event.altKey is not an alignment bypass") and review-architecture SKILL §4.F ("Already migrated — do not regress: wall + fence drafting (both modifier patterns)").
  • Why: In Pascal, snapping is strictly mode-driven (isGridSnapActive(), isMagneticSnapActive(), isAngleSnapActive()). Held modifier keys are undiscoverable and do not translate to 2D touch or accessible viewports. Alt is reserved exclusively for forcing placement/movement past physical collisions. Reading event.nativeEvent.altKey as bypassSnap directly bypasses magnetic snap and grid quantize, regressing the fence tool which was already migrated to the unified snapping model.
  • Proposed Fix: Remove bypassSnap: event.nativeEvent.altKey from all call sites in StraightFenceTool and SplineFenceDraft. Alignment and snapping must be gated purely on isMagneticSnapActive() and isGridSnapActive().

Blocker 4: Alt-Key Misused as Snap/Alignment Bypass in Stair Floorplan Move & 3D Placement

  • Where: packages/nodes/src/stair/floorplan-move.ts:59, 71, 84-86 and packages/editor/src/components/tools/stair/stair-tool.tsx:627-635, 660-662
  • Offending Code:
    // packages/nodes/src/stair/floorplan-move.ts:59-71
    const step = !modifiers.altKey && isGridSnapActive() ? getSegmentGridStep() : 0
    ...
    applyFloorplanAlignment(
      movingAnchors,
      candidates,
      { applySnap: !modifiers.altKey && isMagneticSnapActive() },
    )
    and
    // packages/nodes/src/stair/floorplan-move.ts:84-86
    landscapeSnap =
      modifiers.altKey || !isMagneticSnapActive()
        ? null
        : resolveStairSurfaceSnap(...)
  • Rule Violated: DECISIONS.md E-004 & review-architecture SKILL §4.F ("event.altKey is not an alignment bypass. A drafting/preview path that reads event.altKey to suppress Figma-alignment is a blocker in any new or touched tool — alignment follows the magnetic snap mode (bypass: !isMagneticSnapActive())").
  • Why: Checking !modifiers.altKey && isGridSnapActive() and !modifiers.altKey && isMagneticSnapActive() couples modifier state to snap suppression. If a user holds Alt to force free placement, it should not arbitrarily disable magnetic alignment or surface snapping via custom flags.
  • Proposed Fix:
    In packages/nodes/src/stair/floorplan-move.ts:
    const step = isGridSnapActive() ? getSegmentGridStep() : 0
    ...
    applyFloorplanAlignment(
      movingAnchors,
      candidates,
      { applySnap: isMagneticSnapActive() },
    )
    ...
    landscapeSnap = !isMagneticSnapActive() ? null : resolveStairSurfaceSnap(...)
    Remove all checks for modifiers.altKey and event.nativeEvent.altKey.

Suggestion 1: Schema Redundancy — supportSurfaceId vs Existing supportSurfaceNodeId in FenceNode

  • Where: packages/core/src/schema/nodes/fence.ts:103, packages/core/src/contracts/reference-inventory.ts:234-239, packages/nodes/src/fence/geometry.ts:18
  • Offending Code:
    // packages/core/src/schema/nodes/fence.ts:98-103
    supportSurfaceNodeId: z.string().optional(),
    supportSurfaceId: z.string().optional(),
    // packages/nodes/src/fence/geometry.ts:18
    const surfaceId = (node.supportSurfaceNodeId ?? node.supportSurfaceId) as AnyNodeId | undefined
  • Rule Violated: DECISIONS.md E-003 (Old scenes always load; clear schema evolution) and wiki/architecture/node-schemas.md.
  • Why: FenceNode already had supportSurfaceNodeId: z.string().optional() which represents the exact same host reference relationship. Introducing supportSurfaceId alongside it creates schema duplication and requires fallbacks like node.supportSurfaceNodeId ?? node.supportSurfaceId. Furthermore, fenceDefinition.capabilities.hostRefFields in packages/nodes/src/fence/definition.ts:387 only lists ['supportSurfaceId'], causing older scenes saved with supportSurfaceNodeId to miss host updates.
  • Proposed Fix: Consolidate onto a single field. Reuse supportSurfaceNodeId directly. If renaming to supportSurfaceId, implement a migration in packages/core/src/store/use-scene.ts (migrateNodes) that rewrites legacy scenes and strips supportSurfaceNodeId.

Nit 1: Comment Formatting in FenceNode Schema

  • Where: packages/core/src/schema/nodes/fence.ts:101-106
  • Offending Code:
    // Manual vertical offset from the elected slab, shaped surface, or level base.
    // Registered top surface supporting a drawn railing on the same level.
    supportSurfaceId: z.string().optional(),
    // Manual vertical offset from the elected slab or level support. This moves
    // the complete fence body without changing its height.
    supportOffset: z.number().finite().optional(),
  • Why: The description comment for supportOffset was duplicated and pasted above supportSurfaceId.
  • Proposed Fix: Clean up the JSDoc comments so each field has a distinct, accurate description.

Summary

  • Blockers: 4
  • Suggestions: 1
  • Nits: 1
  • Verdict: REQUEST_CHANGES

Addressing the package isolation in apps/editor, migrating the stair tool to packages/nodes/src/stair/tool.tsx, and adhering strictly to the mode-driven snapping convention (isMagneticSnapActive / isGridSnapActive without Alt bypass) will bring this PR into full alignment with Pascal's architecture.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/nodes/src/stair/floorplan-move.ts

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/nodes/src/stair/floorplan-move.ts
Comment thread packages/nodes/src/stair/tool.tsx
Comment thread apps/editor/app/globals.css

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

There are 3 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 61f3148. Configure here.

Comment thread packages/nodes/src/stair/tool.tsx
Comment thread packages/nodes/src/fence/tool.tsx
@sudhir9297
sudhir9297 force-pushed the t3code/link-local-plugins branch from 61f3148 to a64250f Compare October 9, 2026 18:13

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants