diff --git a/.agents/skills/review-architecture/SKILL.md b/.agents/skills/review-architecture/SKILL.md index 923c36da5a..df2ff85755 100644 --- a/.agents/skills/review-architecture/SKILL.md +++ b/.agents/skills/review-architecture/SKILL.md @@ -24,6 +24,7 @@ Required on every review. Read the remaining pages on demand when the diff touch - `wiki/architecture/scene-registry.md` - `wiki/architecture/spatial-queries.md` - `wiki/architecture/node-schemas.md` +- `wiki/architecture/inspector-field-limits.md` — no arbitrary `min`/`max` on dimension fields. Read whenever the diff adds or edits `parametrics.ts`, a kind `panel.tsx`, or `` bounds. - `wiki/architecture/events.md` - `wiki/architecture/interaction-scope.md` — the interaction state machine + the unified snapping/modifier convention. Read whenever the diff touches a tool, a `move-tool` / `selection` / endpoint / reshape file, `lib/interaction/**`, `lib/snapping-mode.ts`, or `use-interaction-scope`. diff --git a/packages/editor/src/components/ui/panels/multi-parametric-inspector.tsx b/packages/editor/src/components/ui/panels/multi-parametric-inspector.tsx index 2a509b7e4d..7246aab6f4 100644 --- a/packages/editor/src/components/ui/panels/multi-parametric-inspector.tsx +++ b/packages/editor/src/components/ui/panels/multi-parametric-inspector.tsx @@ -240,8 +240,6 @@ function MultiVec3Field({ previewMultiNodeFields(patchAxis(next))} onCommit={(next) => { diff --git a/packages/editor/src/components/ui/panels/parametric-field-control.tsx b/packages/editor/src/components/ui/panels/parametric-field-control.tsx index 995b0ee804..4defaeeccf 100644 --- a/packages/editor/src/components/ui/panels/parametric-field-control.tsx +++ b/packages/editor/src/components/ui/panels/parametric-field-control.tsx @@ -152,8 +152,6 @@ export function ParametricFieldControl({ onChange(apply(next))} onCommit={onCommit ? (next) => onCommit(apply(next)) : undefined} diff --git a/packages/nodes/src/block/panel.tsx b/packages/nodes/src/block/panel.tsx index 2f9438f341..0e0de80149 100644 --- a/packages/nodes/src/block/panel.tsx +++ b/packages/nodes/src/block/panel.tsx @@ -277,8 +277,6 @@ export default function BlockPanel() { previewProp({ width: v })} onCommit={(v) => handleUpdate({ width: v })} @@ -197,7 +197,7 @@ export default function BoxVentPanel() { /> previewProp({ depth: v })} onCommit={(v) => handleUpdate({ depth: v })} @@ -209,7 +209,7 @@ export default function BoxVentPanel() { /> previewProp({ height: v })} onCommit={(v) => handleUpdate({ height: v })} diff --git a/packages/nodes/src/box-vent/parametrics.ts b/packages/nodes/src/box-vent/parametrics.ts index 95e5d1d206..b9d1d16563 100644 --- a/packages/nodes/src/box-vent/parametrics.ts +++ b/packages/nodes/src/box-vent/parametrics.ts @@ -28,9 +28,9 @@ export const boxVentParametrics: ParametricDescriptor = { { label: 'Dimensions', fields: [ - { key: 'width', kind: 'number', unit: 'm', min: 0.15, max: 0.8, step: 0.01 }, - { key: 'depth', kind: 'number', unit: 'm', min: 0.15, max: 0.8, step: 0.01 }, - { key: 'height', kind: 'number', unit: 'm', min: 0.05, max: 0.4, step: 0.01 }, + { key: 'width', kind: 'number', unit: 'm', min: 0.15, max: 1000, step: 0.01 }, + { key: 'depth', kind: 'number', unit: 'm', min: 0.15, max: 1000, step: 0.01 }, + { key: 'height', kind: 'number', unit: 'm', min: 0.05, max: 1000, step: 0.01 }, { key: 'hoodOverhang', kind: 'number', unit: 'm', min: 0, max: 0.12, step: 0.005 }, ], }, diff --git a/packages/nodes/src/ceiling/panel.tsx b/packages/nodes/src/ceiling/panel.tsx index c3cb2cb809..1f8772954d 100644 --- a/packages/nodes/src/ceiling/panel.tsx +++ b/packages/nodes/src/ceiling/panel.tsx @@ -260,7 +260,7 @@ export function CeilingPanel() { ) : ( = { groups: [ { label: 'Dimensions', - fields: [{ key: 'height', kind: 'number', unit: 'm', min: 1.5, max: 20, step: 0.05 }], + fields: [{ key: 'height', kind: 'number', unit: 'm', min: 0.1, max: 1000, step: 0.05 }], }, ], customPanel: () => import('./panel'), diff --git a/packages/nodes/src/chimney/panel.tsx b/packages/nodes/src/chimney/panel.tsx index bb8dc31062..d706dbd5e8 100644 --- a/packages/nodes/src/chimney/panel.tsx +++ b/packages/nodes/src/chimney/panel.tsx @@ -369,7 +369,7 @@ export default function ChimneyPanel() { /> previewProp({ width: v })} onCommit={(v) => commitProp({ width: v })} @@ -382,7 +382,7 @@ export default function ChimneyPanel() { {(node.bodyShape ?? 'square') !== 'round' && ( previewProp({ depth: v })} onCommit={(v) => commitProp({ depth: v })} @@ -436,7 +436,7 @@ export default function ChimneyPanel() { previewProp({ heightAboveRidge: v })} onCommit={(v) => commitProp({ heightAboveRidge: v })} diff --git a/packages/nodes/src/chimney/parametrics.ts b/packages/nodes/src/chimney/parametrics.ts index acd5acdc19..fafe128386 100644 --- a/packages/nodes/src/chimney/parametrics.ts +++ b/packages/nodes/src/chimney/parametrics.ts @@ -18,17 +18,17 @@ export const chimneyParametrics: ParametricDescriptor = { options: ['square', 'round'], display: 'segmented', }, - { key: 'width', kind: 'number', unit: 'm', min: 0.2, max: 2, step: 0.05 }, + { key: 'width', kind: 'number', unit: 'm', min: 0.2, max: 1000, step: 0.05 }, { key: 'depth', kind: 'number', unit: 'm', min: 0.2, - max: 2, + max: 1000, step: 0.05, visibleIf: (n) => n.bodyShape === 'square', }, - { key: 'heightAboveRidge', kind: 'number', unit: 'm', min: 0.2, max: 3, step: 0.05 }, + { key: 'heightAboveRidge', kind: 'number', unit: 'm', min: 0.2, max: 1000, step: 0.05 }, { key: 'cornerBevel', kind: 'number', diff --git a/packages/nodes/src/column/panel.tsx b/packages/nodes/src/column/panel.tsx index 4bcf674266..519c5474b6 100644 --- a/packages/nodes/src/column/panel.tsx +++ b/packages/nodes/src/column/panel.tsx @@ -585,7 +585,7 @@ export default function ColumnPanel() { {!managedByLeanTo && ( handleUpdate({ height: value })} precision={2} @@ -606,7 +606,7 @@ export default function ColumnPanel() { supportStyle === 'box-frame') && ( handleUpdate({ @@ -667,7 +667,7 @@ export default function ColumnPanel() { <> handleUpdate({ @@ -684,7 +684,7 @@ export default function ColumnPanel() { {node.crossSection === 'rectangular' && ( handleUpdate({ depth: value })} precision={2} diff --git a/packages/nodes/src/column/parametrics.ts b/packages/nodes/src/column/parametrics.ts index 340c58f6f9..8b9622bd8b 100644 --- a/packages/nodes/src/column/parametrics.ts +++ b/packages/nodes/src/column/parametrics.ts @@ -14,9 +14,9 @@ export const columnParametrics: ParametricDescriptor = { { label: 'Dimensions', fields: [ - { key: 'height', kind: 'number', unit: 'm', min: 0.5, max: 20, step: 0.05 }, - { key: 'width', kind: 'number', unit: 'm', min: 0.1, max: 2, step: 0.01 }, - { key: 'depth', kind: 'number', unit: 'm', min: 0.1, max: 2, step: 0.01 }, + { key: 'height', kind: 'number', unit: 'm', min: 0.5, max: 1000, step: 0.05 }, + { key: 'width', kind: 'number', unit: 'm', min: 0.1, max: 1000, step: 0.01 }, + { key: 'depth', kind: 'number', unit: 'm', min: 0.1, max: 1000, step: 0.01 }, ], }, ], diff --git a/packages/nodes/src/cupola/panel.tsx b/packages/nodes/src/cupola/panel.tsx index ec6f68af0d..2d5913a341 100644 --- a/packages/nodes/src/cupola/panel.tsx +++ b/packages/nodes/src/cupola/panel.tsx @@ -165,7 +165,7 @@ export default function CupolaPanel() { previewProp({ width: v })} onCommit={(v) => handleUpdate({ width: v })} @@ -177,7 +177,7 @@ export default function CupolaPanel() { /> previewProp({ depth: v })} onCommit={(v) => handleUpdate({ depth: v })} @@ -189,7 +189,7 @@ export default function CupolaPanel() { /> previewProp({ height: v })} onCommit={(v) => handleUpdate({ height: v })} diff --git a/packages/nodes/src/cupola/parametrics.ts b/packages/nodes/src/cupola/parametrics.ts index 7ea4cf9fe3..238ad3a275 100644 --- a/packages/nodes/src/cupola/parametrics.ts +++ b/packages/nodes/src/cupola/parametrics.ts @@ -24,9 +24,9 @@ export const cupolaParametrics: ParametricDescriptor = { { label: 'Dimensions', fields: [ - { key: 'width', kind: 'number', unit: 'm', min: 0.3, max: 2, step: 0.05 }, - { key: 'depth', kind: 'number', unit: 'm', min: 0.3, max: 2, step: 0.05 }, - { key: 'height', kind: 'number', unit: 'm', min: 0.4, max: 2.5, step: 0.05 }, + { key: 'width', kind: 'number', unit: 'm', min: 0.3, max: 1000, step: 0.05 }, + { key: 'depth', kind: 'number', unit: 'm', min: 0.3, max: 1000, step: 0.05 }, + { key: 'height', kind: 'number', unit: 'm', min: 0.4, max: 1000, step: 0.05 }, ], }, ], diff --git a/packages/nodes/src/door/panel.tsx b/packages/nodes/src/door/panel.tsx index 984230231d..12608e108b 100644 --- a/packages/nodes/src/door/panel.tsx +++ b/packages/nodes/src/door/panel.tsx @@ -731,7 +731,7 @@ export default function DoorPanel() { /> handleUpdate({ diff --git a/packages/nodes/src/door/parametrics.ts b/packages/nodes/src/door/parametrics.ts index a924571b45..bdf0bee6ab 100644 --- a/packages/nodes/src/door/parametrics.ts +++ b/packages/nodes/src/door/parametrics.ts @@ -14,8 +14,8 @@ export const doorParametrics: ParametricDescriptor = { { label: 'Dimensions', fields: [ - { key: 'width', kind: 'number', unit: 'm', min: 0.5, max: 6, step: 0.05 }, - { key: 'height', kind: 'number', unit: 'm', min: 1.0, max: 4, step: 0.05 }, + { key: 'width', kind: 'number', unit: 'm', min: 0.5, max: 1000, step: 0.05 }, + { key: 'height', kind: 'number', unit: 'm', min: 1.0, max: 1000, step: 0.05 }, ], }, { diff --git a/packages/nodes/src/dormer/panel.tsx b/packages/nodes/src/dormer/panel.tsx index dd19c16dfa..219fbcec87 100644 --- a/packages/nodes/src/dormer/panel.tsx +++ b/packages/nodes/src/dormer/panel.tsx @@ -202,7 +202,7 @@ export default function DormerPanel() { previewProp({ width: v })} onCommit={(v) => commitProp({ width: v })} @@ -214,7 +214,7 @@ export default function DormerPanel() { /> previewProp({ depth: v })} onCommit={(v) => commitProp({ depth: v })} @@ -226,7 +226,7 @@ export default function DormerPanel() { /> previewProp({ height: v })} onCommit={(v) => commitProp({ height: v })} diff --git a/packages/nodes/src/dormer/parametrics.ts b/packages/nodes/src/dormer/parametrics.ts index d8a46cccf1..f11ee87442 100644 --- a/packages/nodes/src/dormer/parametrics.ts +++ b/packages/nodes/src/dormer/parametrics.ts @@ -11,9 +11,9 @@ export const dormerParametrics: ParametricDescriptor = { { label: 'Dormer', fields: [ - { key: 'width', kind: 'number', unit: 'm', min: 0.5, max: 4, step: 0.05 }, - { key: 'depth', kind: 'number', unit: 'm', min: 0.5, max: 5, step: 0.05 }, - { key: 'height', kind: 'number', unit: 'm', min: 0, max: 5, step: 0.05 }, + { key: 'width', kind: 'number', unit: 'm', min: 0.5, max: 1000, step: 0.05 }, + { key: 'depth', kind: 'number', unit: 'm', min: 0.5, max: 1000, step: 0.05 }, + { key: 'height', kind: 'number', unit: 'm', min: 0, max: 1000, step: 0.05 }, ], }, { @@ -30,7 +30,9 @@ export const dormerParametrics: ParametricDescriptor = { }, { label: 'Hung wall', - fields: [{ key: 'wallSkirtHeight', kind: 'number', unit: 'm', min: 0.2, max: 6, step: 0.05 }], + fields: [ + { key: 'wallSkirtHeight', kind: 'number', unit: 'm', min: 0.2, max: 1000, step: 0.05 }, + ], }, { label: 'Window opening', diff --git a/packages/nodes/src/downspout/parametrics.ts b/packages/nodes/src/downspout/parametrics.ts index b66f1e69cc..2ab301768b 100644 --- a/packages/nodes/src/downspout/parametrics.ts +++ b/packages/nodes/src/downspout/parametrics.ts @@ -8,7 +8,7 @@ export const downspoutParametrics: ParametricDescriptor = { { label: 'Dimensions', fields: [ - { key: 'length', kind: 'number', unit: 'm', min: 0.1, max: 8, step: 0.05 }, + { key: 'length', kind: 'number', unit: 'm', min: 0.1, max: 1000, step: 0.05 }, { key: 'diameter', kind: 'number', unit: 'm', min: 0.02, max: 0.15, step: 0.005 }, // Cross-section: follow the gutter profile, or force round / rect. { diff --git a/packages/nodes/src/duct-terminal/parametrics.ts b/packages/nodes/src/duct-terminal/parametrics.ts index 88a02c1e50..4df7760ef7 100644 --- a/packages/nodes/src/duct-terminal/parametrics.ts +++ b/packages/nodes/src/duct-terminal/parametrics.ts @@ -22,8 +22,8 @@ export const ductTerminalParametrics: ParametricDescriptor = { { label: 'Face', fields: [ - { key: 'width', kind: 'number', unit: 'm', min: 0.1, max: 1.5, step: 0.05 }, - { key: 'depth', kind: 'number', unit: 'm', min: 0.05, max: 1.5, step: 0.05 }, + { key: 'width', kind: 'number', unit: 'm', min: 0.1, max: 1000, step: 0.05 }, + { key: 'depth', kind: 'number', unit: 'm', min: 0.05, max: 1000, step: 0.05 }, ], }, { diff --git a/packages/nodes/src/elevator/panel.tsx b/packages/nodes/src/elevator/panel.tsx index 09ac7cc9ce..f716e1b3c9 100644 --- a/packages/nodes/src/elevator/panel.tsx +++ b/packages/nodes/src/elevator/panel.tsx @@ -496,8 +496,6 @@ export default function ElevatorPanel() { { const position = getSupportedPosition(value, displayPosition[2]) previewTransform(position, displayRotation) @@ -514,8 +512,6 @@ export default function ElevatorPanel() { /> { const position: ElevatorNode['position'] = [ displayPosition[0], @@ -540,8 +536,6 @@ export default function ElevatorPanel() { /> { const position = getSupportedPosition(displayPosition[0], value) previewTransform(position, displayRotation) diff --git a/packages/nodes/src/eyebrow-vent/panel.tsx b/packages/nodes/src/eyebrow-vent/panel.tsx index 44934767dc..5ced7043e8 100644 --- a/packages/nodes/src/eyebrow-vent/panel.tsx +++ b/packages/nodes/src/eyebrow-vent/panel.tsx @@ -184,7 +184,7 @@ export default function EyebrowVentPanel() { previewProp({ width: v })} onCommit={(v) => handleUpdate({ width: v })} @@ -196,7 +196,7 @@ export default function EyebrowVentPanel() { /> previewProp({ depth: v })} onCommit={(v) => handleUpdate({ depth: v })} @@ -208,7 +208,7 @@ export default function EyebrowVentPanel() { /> previewProp({ height: v })} onCommit={(v) => handleUpdate({ height: v })} diff --git a/packages/nodes/src/eyebrow-vent/parametrics.ts b/packages/nodes/src/eyebrow-vent/parametrics.ts index 02fa2fe0ca..041e421fc1 100644 --- a/packages/nodes/src/eyebrow-vent/parametrics.ts +++ b/packages/nodes/src/eyebrow-vent/parametrics.ts @@ -25,9 +25,9 @@ export const eyebrowVentParametrics: ParametricDescriptor = { { label: 'Dimensions', fields: [ - { key: 'width', kind: 'number', unit: 'm', min: 0.3, max: 2, step: 0.05 }, - { key: 'depth', kind: 'number', unit: 'm', min: 0.2, max: 1.5, step: 0.05 }, - { key: 'height', kind: 'number', unit: 'm', min: 0.08, max: 1, step: 0.02 }, + { key: 'width', kind: 'number', unit: 'm', min: 0.3, max: 1000, step: 0.05 }, + { key: 'depth', kind: 'number', unit: 'm', min: 0.2, max: 1000, step: 0.05 }, + { key: 'height', kind: 'number', unit: 'm', min: 0.08, max: 1000, step: 0.02 }, ], }, ], diff --git a/packages/nodes/src/fence/parametrics.ts b/packages/nodes/src/fence/parametrics.ts index 8d5f67a308..c26f529d83 100644 --- a/packages/nodes/src/fence/parametrics.ts +++ b/packages/nodes/src/fence/parametrics.ts @@ -51,8 +51,8 @@ export const fenceParametrics: ParametricDescriptor = { component: FenceCurveEditor, visibleIf: (n) => !isSplineFence(n), }, - { key: 'height', kind: 'number', unit: 'm', min: 0.4, max: 4, step: 0.05 }, - { key: 'thickness', kind: 'number', unit: 'm', min: 0.03, max: 0.5, step: 0.005 }, + { key: 'height', kind: 'number', unit: 'm', min: 0.4, max: 1000, step: 0.05 }, + { key: 'thickness', kind: 'number', unit: 'm', min: 0.03, max: 1000, step: 0.005 }, ], }, { @@ -60,7 +60,7 @@ export const fenceParametrics: ParametricDescriptor = { fields: [ { key: 'baseHeight', kind: 'number', unit: 'm', min: 0.04, max: 1, step: 0.01 }, { key: 'topRailHeight', kind: 'number', unit: 'm', min: 0.01, max: 0.25, step: 0.005 }, - { key: 'postSpacing', kind: 'number', unit: 'm', min: 0.05, max: 5, step: 0.01 }, + { key: 'postSpacing', kind: 'number', unit: 'm', min: 0.05, max: 1000, step: 0.01 }, { key: 'postSize', kind: 'number', unit: 'm', min: 0.01, max: 0.4, step: 0.005 }, { // Dropdown (not segmented) so the inspector renders its "Post Cap" diff --git a/packages/nodes/src/gutter/parametrics.ts b/packages/nodes/src/gutter/parametrics.ts index cf1f68b377..622d390ccb 100644 --- a/packages/nodes/src/gutter/parametrics.ts +++ b/packages/nodes/src/gutter/parametrics.ts @@ -17,7 +17,7 @@ export const gutterParametrics: ParametricDescriptor = { { label: 'Dimensions', fields: [ - { key: 'length', kind: 'number', unit: 'm', min: 0.2, max: 12, step: 0.05 }, + { key: 'length', kind: 'number', unit: 'm', min: 0.2, max: 1000, step: 0.05 }, { key: 'size', kind: 'number', unit: 'm', min: 0.05, max: 0.3, step: 0.005 }, { key: 'thickness', diff --git a/packages/nodes/src/hvac-equipment/parametrics.ts b/packages/nodes/src/hvac-equipment/parametrics.ts index 569fe82930..9130f9ed0a 100644 --- a/packages/nodes/src/hvac-equipment/parametrics.ts +++ b/packages/nodes/src/hvac-equipment/parametrics.ts @@ -17,9 +17,9 @@ export const hvacEquipmentParametrics: ParametricDescriptor = { label: 'Cabinet', fields: [ - { key: 'width', kind: 'number', unit: 'm', min: 0.3, max: 2, step: 0.05 }, - { key: 'depth', kind: 'number', unit: 'm', min: 0.3, max: 2, step: 0.05 }, - { key: 'height', kind: 'number', unit: 'm', min: 0.4, max: 2.5, step: 0.05 }, + { key: 'width', kind: 'number', unit: 'm', min: 0.3, max: 1000, step: 0.05 }, + { key: 'depth', kind: 'number', unit: 'm', min: 0.3, max: 1000, step: 0.05 }, + { key: 'height', kind: 'number', unit: 'm', min: 0.4, max: 1000, step: 0.05 }, ], }, { diff --git a/packages/nodes/src/lean-to-extension/definition.ts b/packages/nodes/src/lean-to-extension/definition.ts index af914d0f72..bcdd0f2648 100644 --- a/packages/nodes/src/lean-to-extension/definition.ts +++ b/packages/nodes/src/lean-to-extension/definition.ts @@ -47,7 +47,7 @@ function highEdgeHeightHandle(): HandleDescriptor { anchor: 'min', shape: 'tracker', min: 0.8, - max: 10, + max: 1000, currentValue: (node) => node.highEdgeHeight, magneticSnap: (node, newValue, sceneApi) => { const wall = resolveHostWall(node, sceneApi) @@ -199,7 +199,7 @@ function spanHandle(side: 'left' | 'right'): HandleDescriptor node.span, apply: (node, span) => spanPatch(node, span, side), previewOverrides: (node, span, sceneApi) => @@ -225,7 +225,7 @@ leanToExtensionHandles.push({ axis: 'z', anchor: 'min', min: 0.5, - max: 10, + max: 1000, currentValue: (node) => node.projection, apply: (node, projection) => ({ projection, diff --git a/packages/nodes/src/lean-to-extension/parametrics.ts b/packages/nodes/src/lean-to-extension/parametrics.ts index b8061445af..cc28550c14 100644 --- a/packages/nodes/src/lean-to-extension/parametrics.ts +++ b/packages/nodes/src/lean-to-extension/parametrics.ts @@ -89,7 +89,7 @@ export const leanToExtensionParametrics: ParametricDescriptor node.connectionMode === 'manual' || !node.hostRoofSegmentId, }, @@ -178,7 +178,7 @@ export const leanToExtensionParametrics: ParametricDescriptor node.postLayoutMode === 'target-spacing', }, @@ -280,7 +280,7 @@ export const leanToExtensionParametrics: ParametricDescriptor node.connectionMode === 'manual' || !node.hostRoofSegmentId, }, diff --git a/packages/nodes/src/ridge-vent/panel.tsx b/packages/nodes/src/ridge-vent/panel.tsx index 957c779525..fb8e41b373 100644 --- a/packages/nodes/src/ridge-vent/panel.tsx +++ b/packages/nodes/src/ridge-vent/panel.tsx @@ -157,7 +157,7 @@ export default function RidgeVentPanel() { handleUpdate({ length: v })} onCommit={(v) => handleUpdate({ length: v })} @@ -216,8 +216,6 @@ export default function RidgeVentPanel() { /> handleUpdate({ position: [node.position[0] ?? 0, v, node.position[2] ?? 0], diff --git a/packages/nodes/src/ridge-vent/parametrics.ts b/packages/nodes/src/ridge-vent/parametrics.ts index 1816553a7c..583cee381b 100644 --- a/packages/nodes/src/ridge-vent/parametrics.ts +++ b/packages/nodes/src/ridge-vent/parametrics.ts @@ -22,7 +22,7 @@ export const ridgeVentParametrics: ParametricDescriptor = { { label: 'Dimensions', fields: [ - { key: 'length', kind: 'number', unit: 'm', min: 0.5, max: 8, step: 0.05 }, + { key: 'length', kind: 'number', unit: 'm', min: 0.5, max: 1000, step: 0.05 }, { key: 'width', kind: 'number', unit: 'm', min: 0.1, max: 0.6, step: 0.01 }, { key: 'height', kind: 'number', unit: 'm', min: 0.03, max: 0.2, step: 0.005 }, ], diff --git a/packages/nodes/src/roof-segment/panel.tsx b/packages/nodes/src/roof-segment/panel.tsx index 0f5ee3c4e9..559b520b35 100644 --- a/packages/nodes/src/roof-segment/panel.tsx +++ b/packages/nodes/src/roof-segment/panel.tsx @@ -353,7 +353,7 @@ export default function RoofSegmentPanel() { handleUpdate({ width: v })} precision={2} @@ -363,7 +363,7 @@ export default function RoofSegmentPanel() { /> handleUpdate({ depth: v })} precision={2} @@ -376,7 +376,7 @@ export default function RoofSegmentPanel() { handleUpdate({ wallHeight: v })} precision={2} @@ -569,8 +569,6 @@ export default function RoofSegmentPanel() { { const pos = [...node.position] as [number, number, number] pos[0] = v @@ -583,8 +581,6 @@ export default function RoofSegmentPanel() { /> { const pos = [...node.position] as [number, number, number] pos[1] = v @@ -597,8 +593,6 @@ export default function RoofSegmentPanel() { /> { const pos = [...node.position] as [number, number, number] pos[2] = v diff --git a/packages/nodes/src/roof/panel.tsx b/packages/nodes/src/roof/panel.tsx index a14400bc43..ac07a728d1 100644 --- a/packages/nodes/src/roof/panel.tsx +++ b/packages/nodes/src/roof/panel.tsx @@ -273,8 +273,6 @@ export default function RoofPanel() { { const pos = [...node.position] as [number, number, number] pos[0] = v @@ -287,8 +285,6 @@ export default function RoofPanel() { /> { const pos = [...node.position] as [number, number, number] pos[1] = v @@ -301,8 +297,6 @@ export default function RoofPanel() { /> { const pos = [...node.position] as [number, number, number] pos[2] = v diff --git a/packages/nodes/src/scan/parametrics.ts b/packages/nodes/src/scan/parametrics.ts index 6e2f389aa2..1511ed3e41 100644 --- a/packages/nodes/src/scan/parametrics.ts +++ b/packages/nodes/src/scan/parametrics.ts @@ -6,7 +6,7 @@ export const scanParametrics: ParametricDescriptor = { label: 'Transform', fields: [ { key: 'position', kind: 'vec3' }, - { key: 'scale', kind: 'number', min: 0.01, max: 10, step: 0.1 }, + { key: 'scale', kind: 'number', min: 0.01, max: 1000, step: 0.1 }, ], }, { diff --git a/packages/nodes/src/shelf/parametrics.ts b/packages/nodes/src/shelf/parametrics.ts index 0f4fdb629d..5da33dc80a 100644 --- a/packages/nodes/src/shelf/parametrics.ts +++ b/packages/nodes/src/shelf/parametrics.ts @@ -70,10 +70,10 @@ export const shelfParametrics: ParametricDescriptor = { { label: 'Dimensions', fields: [ - { key: 'width', kind: 'number', unit: 'm', min: 0.3, max: 3.0, step: 0.05 }, - { key: 'depth', kind: 'number', unit: 'm', min: 0.1, max: 1.0, step: 0.05 }, - { key: 'thickness', kind: 'number', unit: 'm', min: 0.01, max: 0.1, step: 0.005 }, - { key: 'height', kind: 'number', unit: 'm', min: 0.05, max: 2.5, step: 0.05 }, + { key: 'width', kind: 'number', unit: 'm', min: 0.3, max: 1000, step: 0.05 }, + { key: 'depth', kind: 'number', unit: 'm', min: 0.1, max: 1000, step: 0.05 }, + { key: 'thickness', kind: 'number', unit: 'm', min: 0.01, max: 1000, step: 0.005 }, + { key: 'height', kind: 'number', unit: 'm', min: 0.05, max: 1000, step: 0.05 }, ], }, { diff --git a/packages/nodes/src/skylight/panel.tsx b/packages/nodes/src/skylight/panel.tsx index 1b54bba86b..8630dfba9b 100644 --- a/packages/nodes/src/skylight/panel.tsx +++ b/packages/nodes/src/skylight/panel.tsx @@ -428,7 +428,7 @@ export default function SkylightPanel() { previewProp({ width: v })} onCommit={(v) => commitProp({ width: v })} @@ -440,7 +440,7 @@ export default function SkylightPanel() { /> previewProp({ height: v })} onCommit={(v) => commitProp({ height: v })} diff --git a/packages/nodes/src/skylight/parametrics.ts b/packages/nodes/src/skylight/parametrics.ts index ca19a83700..90607694e0 100644 --- a/packages/nodes/src/skylight/parametrics.ts +++ b/packages/nodes/src/skylight/parametrics.ts @@ -18,8 +18,8 @@ export const skylightParametrics: ParametricDescriptor = { { label: 'Dimensions', fields: [ - { key: 'width', kind: 'number', unit: 'm', min: 0.3, max: 3, step: 0.05 }, - { key: 'height', kind: 'number', unit: 'm', min: 0.3, max: 3, step: 0.05 }, + { key: 'width', kind: 'number', unit: 'm', min: 0.3, max: 1000, step: 0.05 }, + { key: 'height', kind: 'number', unit: 'm', min: 0.3, max: 1000, step: 0.05 }, { key: 'frameThickness', kind: 'number', unit: 'm', min: 0.02, max: 0.15, step: 0.005 }, { key: 'frameDepth', kind: 'number', unit: 'm', min: 0.02, max: 0.2, step: 0.005 }, { key: 'glassThickness', kind: 'number', unit: 'm', min: 0.005, max: 0.05, step: 0.001 }, diff --git a/packages/nodes/src/slab/panel.tsx b/packages/nodes/src/slab/panel.tsx index b582b3ca97..22a2eefc13 100644 --- a/packages/nodes/src/slab/panel.tsx +++ b/packages/nodes/src/slab/panel.tsx @@ -306,7 +306,7 @@ export function SlabPanel() { {node.recessed ? ( = { kind: 'number', unit: 'm', min: MIN_SLAB_THICKNESS, - max: 0.5, + max: 1000, step: 0.01, visibleIf: (n) => !n.recessed, }, diff --git a/packages/nodes/src/solar-panel/panel.tsx b/packages/nodes/src/solar-panel/panel.tsx index 92351bc1be..aa7d7c913d 100644 --- a/packages/nodes/src/solar-panel/panel.tsx +++ b/packages/nodes/src/solar-panel/panel.tsx @@ -278,7 +278,7 @@ export default function SolarPanelPanel() { previewProp({ panelWidth: v })} onCommit={(v) => commitProp({ panelWidth: v })} @@ -290,7 +290,7 @@ export default function SolarPanelPanel() { /> previewProp({ panelHeight: v })} onCommit={(v) => commitProp({ panelHeight: v })} diff --git a/packages/nodes/src/solar-panel/parametrics.ts b/packages/nodes/src/solar-panel/parametrics.ts index 8c09d8eb3d..28a6aa0ea0 100644 --- a/packages/nodes/src/solar-panel/parametrics.ts +++ b/packages/nodes/src/solar-panel/parametrics.ts @@ -17,8 +17,8 @@ export const solarPanelParametrics: ParametricDescriptor = { { label: 'Panel dimensions', fields: [ - { key: 'panelWidth', kind: 'number', unit: 'm', min: 0.4, max: 2, step: 0.01 }, - { key: 'panelHeight', kind: 'number', unit: 'm', min: 0.4, max: 2.5, step: 0.01 }, + { key: 'panelWidth', kind: 'number', unit: 'm', min: 0.4, max: 1000, step: 0.01 }, + { key: 'panelHeight', kind: 'number', unit: 'm', min: 0.4, max: 1000, step: 0.01 }, { key: 'gapX', kind: 'number', unit: 'm', min: 0, max: 0.2, step: 0.005 }, { key: 'gapY', kind: 'number', unit: 'm', min: 0, max: 0.2, step: 0.005 }, ], diff --git a/packages/nodes/src/stair-segment/panel.tsx b/packages/nodes/src/stair-segment/panel.tsx index 0c7a75b0d3..daad0fa4b2 100644 --- a/packages/nodes/src/stair-segment/panel.tsx +++ b/packages/nodes/src/stair-segment/panel.tsx @@ -160,7 +160,7 @@ export default function StairSegmentPanel() { handleUpdate({ width: v })} precision={2} @@ -170,7 +170,7 @@ export default function StairSegmentPanel() { /> handleUpdate({ length: v })} precision={2} @@ -182,7 +182,7 @@ export default function StairSegmentPanel() { <> handleUpdate({ height: v })} precision={2} @@ -214,7 +214,7 @@ export default function StairSegmentPanel() { {!node.fillToFloor && ( handleUpdate({ thickness: v })} precision={2} @@ -229,8 +229,6 @@ export default function StairSegmentPanel() { { const pos = [...node.position] as [number, number, number] pos[0] = v @@ -243,8 +241,6 @@ export default function StairSegmentPanel() { /> { const pos = [...node.position] as [number, number, number] pos[1] = v @@ -257,8 +253,6 @@ export default function StairSegmentPanel() { /> { const pos = [...node.position] as [number, number, number] pos[2] = v diff --git a/packages/nodes/src/stair/panel.tsx b/packages/nodes/src/stair/panel.tsx index fae2b5ac5c..9500bbce80 100644 --- a/packages/nodes/src/stair/panel.tsx +++ b/packages/nodes/src/stair/panel.tsx @@ -540,8 +540,6 @@ export default function StairPanel() { { const pos = [...node.position] as [number, number, number] pos[0] = v @@ -554,8 +552,6 @@ export default function StairPanel() { /> { const pos = [...node.position] as [number, number, number] pos[1] = v @@ -568,8 +564,6 @@ export default function StairPanel() { /> { const pos = [...node.position] as [number, number, number] pos[2] = v diff --git a/packages/nodes/src/turbine-vent/panel.tsx b/packages/nodes/src/turbine-vent/panel.tsx index 9588668a97..fc23df3142 100644 --- a/packages/nodes/src/turbine-vent/panel.tsx +++ b/packages/nodes/src/turbine-vent/panel.tsx @@ -181,7 +181,7 @@ export default function TurbineVentPanel() { previewProp({ diameter: v })} onCommit={(v) => handleUpdate({ diameter: v })} @@ -193,7 +193,7 @@ export default function TurbineVentPanel() { /> previewProp({ height: v })} onCommit={(v) => handleUpdate({ height: v })} diff --git a/packages/nodes/src/turbine-vent/parametrics.ts b/packages/nodes/src/turbine-vent/parametrics.ts index 04cfcbfa1a..6804207c16 100644 --- a/packages/nodes/src/turbine-vent/parametrics.ts +++ b/packages/nodes/src/turbine-vent/parametrics.ts @@ -23,8 +23,8 @@ export const turbineVentParametrics: ParametricDescriptor = { { label: 'Dimensions', fields: [ - { key: 'diameter', kind: 'number', unit: 'm', min: 0.15, max: 0.7, step: 0.01 }, - { key: 'height', kind: 'number', unit: 'm', min: 0.2, max: 0.9, step: 0.01 }, + { key: 'diameter', kind: 'number', unit: 'm', min: 0.15, max: 1000, step: 0.01 }, + { key: 'height', kind: 'number', unit: 'm', min: 0.2, max: 1000, step: 0.01 }, { key: 'neckHeight', kind: 'number', unit: 'm', min: 0.02, max: 0.3, step: 0.01 }, { key: 'vaneCount', kind: 'number', unit: '', min: 6, max: 36, step: 1 }, ], diff --git a/packages/nodes/src/wall/panel.tsx b/packages/nodes/src/wall/panel.tsx index 0d2dc398e6..754d563406 100644 --- a/packages/nodes/src/wall/panel.tsx +++ b/packages/nodes/src/wall/panel.tsx @@ -257,11 +257,11 @@ export default function WallPanel() { handleUpdateLength( - linearControlValueToMeters(value, unit, { maxMeters: 20, minMeters: 0.1 }), + linearControlValueToMeters(value, unit, { maxMeters: 1000, minMeters: 0.1 }), ) } precision={2} @@ -287,11 +287,11 @@ export default function WallPanel() { ) : ( handleUpdate({ - height: linearControlValueToMeters(v, unit, { maxMeters: 20, minMeters: 0.1 }), + height: linearControlValueToMeters(v, unit, { maxMeters: 1000, minMeters: 0.1 }), }) } precision={2} @@ -318,12 +318,12 @@ export default function WallPanel() { )} handleUpdate({ thickness: linearControlValueToMeters(v, unit, { - maxMeters: 1, + maxMeters: 1000, minMeters: 0.05, }), }) diff --git a/packages/nodes/src/wall/parametrics.ts b/packages/nodes/src/wall/parametrics.ts index 4c721ac3b0..411013bc00 100644 --- a/packages/nodes/src/wall/parametrics.ts +++ b/packages/nodes/src/wall/parametrics.ts @@ -18,10 +18,10 @@ export const wallParametrics: ParametricDescriptor = { { label: 'Dimensions', fields: [ - { key: 'thickness', kind: 'number', unit: 'm', min: 0.05, max: 0.6, step: 0.01 }, + { key: 'thickness', kind: 'number', unit: 'm', min: 0.05, max: 1000, step: 0.01 }, // `height` may be absent (plane-bound top); the custom panel owns the // Follows storey / Custom height mode switch, so this is metadata only. - { key: 'height', kind: 'number', unit: 'm', min: 1.5, max: 20, step: 0.05 }, + { key: 'height', kind: 'number', unit: 'm', min: 0.1, max: 1000, step: 0.05 }, { key: 'curveOffset', kind: 'number', unit: 'm', min: -3, max: 3, step: 0.05 }, ], }, diff --git a/packages/nodes/src/window/parametrics.ts b/packages/nodes/src/window/parametrics.ts index 4aa23abae0..785a94a2be 100644 --- a/packages/nodes/src/window/parametrics.ts +++ b/packages/nodes/src/window/parametrics.ts @@ -13,8 +13,8 @@ export const windowParametrics: ParametricDescriptor = { { label: 'Dimensions', fields: [ - { key: 'width', kind: 'number', unit: 'm', min: 0.3, max: 4, step: 0.05 }, - { key: 'height', kind: 'number', unit: 'm', min: 0.3, max: 4, step: 0.05 }, + { key: 'width', kind: 'number', unit: 'm', min: 0.3, max: 1000, step: 0.05 }, + { key: 'height', kind: 'number', unit: 'm', min: 0.3, max: 1000, step: 0.05 }, ], }, ], diff --git a/wiki/architecture/README.md b/wiki/architecture/README.md index 0ef72a8255..d4ed804517 100644 --- a/wiki/architecture/README.md +++ b/wiki/architecture/README.md @@ -23,6 +23,7 @@ Canonical rules for code that touches `packages/core`, `packages/viewer`, `packa | [scene-registry](scene-registry.md) | Global node ID → Object3D map and `useRegistry` | | [spatial-queries](spatial-queries.md) | Placement validation (`canPlaceOnFloor`/`Wall`/`Ceiling`) for tools | | [node-schemas](node-schemas.md) | Zod schema pattern for node types, `createNode`, `updateNode` | +| [inspector-field-limits](inspector-field-limits.md) | When a numeric inspector field may and may not have `min`/`max` — no arbitrary caps on dimensions | | [vertical-model](vertical-model.md) | Stored level heights, plane-bound wall/ceiling tops, slab placement + thickness, support hosts, clamp rules, and the load migration | | [events](events.md) | Typed event bus — emitting and listening to node and grid events | | [creating-rules](creating-rules.md) | How to add or update a page in this folder | diff --git a/wiki/architecture/inspector-field-limits.md b/wiki/architecture/inspector-field-limits.md new file mode 100644 index 0000000000..c209aae340 --- /dev/null +++ b/wiki/architecture/inspector-field-limits.md @@ -0,0 +1,26 @@ +# Inspector Field Limits + +*When a numeric inspector field may and may not have `min`/`max`.* + +Applies to: `packages/nodes/src/**/parametrics.ts`, `packages/nodes/src/**/panel.tsx`, and any `` usage. + +`SliderControl` is a scrubby number input, not a range slider: dragging applies a step-based delta (`dx/4 × step`), and the wheel/arrow keys step likewise. `min`/`max` play no role in the interaction — they are pure clamps, defaulting to ±Infinity, and a typed value beyond them is clamped **silently**. A max therefore never "smooths" anything; it only blocks users, and blocking reads as "the app ignored me". Sweep of 2026-08: all arbitrary maxes were lifted (editor PR for `chore/field-limit-sweep`). + +## Rules + +- **Never cap a physical dimension at its "typical" size.** Wall length is not 20 m, roof spans are not 25 m. For dimension fields (width / height / depth / length / span / thickness / spacing / diameter in meters) use `max: 1000` — a value nobody legitimately reaches that still catches a pasted or fat-fingered number before it produces degenerate geometry (spatial grid, shadows, bake). If even a typo is harmless (see positions below), omit `max` entirely. +- **Positions and offsets get no static bounds.** Omit `min`/`max`. Never feed a scrub window (`value ± N`) into `min`/`max` — that turns a UI convenience into a hidden clamp on typed input. +- **Mins are validity only.** Dimensions need a small positive floor (typically 0.01–0.1 m) so zero/negative geometry can't exist. A min must never encode "typical" (the old wall `min: 1.5` blocked parapets and garden walls). +- **Dynamic geometric bounds are encouraged.** Limits derived from the node or its host encode real validity and stay: door width ≤ host wall (`maxDoorWidth`), curve sagitta ≤ chord, roof-accessory positions within their segment face, cabinet carcass ≥ tallest module. +- **Keep bounds that are not dimensions:** counts (rows, posts, steps, louvers — they multiply generated geometry, so the cap is a perf guard), percentages and 0–1 fractions, angle ranges (pitch, tilt, opening), rotation −180..180. + +## Deliberate exemptions + +- **MEP inch fields** (duct, pipe, lineset, HVAC collars): bounds mirror real trade sizes and carry domain meaning. +- **Cabinet run width (3 m):** width drives auto-generated carcass modules, so the max is a geometry-count guard, not taste. +- **Item dimensions:** the 30 m envelope ties into the studio item-builder and bake caps; change it there, not here. +- **Detail knobs** (bevels, insets, overhangs, flanges, rails, sills, trim): bounded ranges are fine — they parameterize a shape, and extreme values produce self-intersecting geometry rather than a bigger valid object. + +Lengths that generate periodic children (gutter hangers, fence posts, downspout straps) scale instance counts with the value. That cost is user-visible and undoable — it is not a reason to reintroduce a cap. + +New kinds and new fields follow these rules; PR review should reject static maxes on dimension fields.