Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 28 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: oven-sh/setup-bun@v2

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Lint & format check
run: bun run check

- name: Type check
run: bun run check-types
2 changes: 0 additions & 2 deletions apps/editor/lib/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ function loadBuiltinsSync(): void {
if (isDev()) {
const kinds = Array.from(nodeRegistry.entries(), ([k]) => k)
if (typeof console !== 'undefined') {
// biome-ignore lint/suspicious/noConsole: dev-only verification log
console.info(
`[pascal:registry] loaded ${builtinPlugin.id} v${builtinPlugin.apiVersion} (${kinds.length} kinds: ${kinds.join(', ') || '∅'})`,
)
Expand Down Expand Up @@ -74,7 +73,6 @@ export async function loadExternalPlugins(): Promise<void> {
await loadPlugin(plugin)
}
if (isDev() && externals.length > 0 && typeof console !== 'undefined') {
// biome-ignore lint/suspicious/noConsole: dev-only verification log
console.info(`[pascal:registry] + ${externals.length} discovered plugin(s)`)
}
}
Expand Down
3 changes: 2 additions & 1 deletion biome.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@
"!**/build",
"!**/public",
"!**/components/ui",
"!**/next-env.d.ts"
"!**/next-env.d.ts",
"!packages/mcp"
]
},
"overrides": [
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/store/actions/node-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,9 @@ export const applyNodeChangesAction = (
return { nodes: nextNodes, rootNodeIds: resolvedRootIds, collections: nextCollections }
})

nodesToMarkDirty.forEach((id) => get().markDirty(id))
nodesToMarkDirty.forEach((id) => {
get().markDirty(id)
})
parentsToMarkDirty.forEach((id) => {
get().markDirty(id)
const parent = get().nodes[id]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,9 @@ export function buildFirstPersonColliderWorldFromRegistry(): FirstPersonCollider
}

const mergedGeometry = mergeGeometries(geometries, false)
geometries.forEach((geometry) => geometry.dispose())
geometries.forEach((geometry) => {
geometry.dispose()
})

if (!mergedGeometry || mergedGeometry.getAttribute('position') == null) {
mergedGeometry?.dispose()
Expand Down
8 changes: 6 additions & 2 deletions packages/nodes/src/ceiling/tool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,12 @@ export const CeilingTool: React.FC = () => {
if (points.length === 0) {
mainLineRef.current.visible = false
closingLineRef.current.visible = false
groundMainLineRef.current && (groundMainLineRef.current.visible = false)
groundClosingLineRef.current && (groundClosingLineRef.current.visible = false)
if (groundMainLineRef.current) {
groundMainLineRef.current.visible = false
}
if (groundClosingLineRef.current) {
groundClosingLineRef.current.visible = false
}
return
}
const ceilingY = levelY + CEILING_HEIGHT
Expand Down
30 changes: 15 additions & 15 deletions packages/nodes/src/chimney/panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,21 @@ export default function ChimneyPanel() {
}
}, [selectedId, node, deleteNode, setSelection])

// Match the current store node against the preset table so the
// segmented control highlights "the preset you'd land on if you
// applied X again". Compare against the store node, not the live-
// override-merged `node`, so the highlight is stable across slider
// drags. Null means the user has tweaked away from any preset; the
// segmented control will then render with no segment selected.
const activePreset = useMemo(() => detectActiveChimneyPreset(storeNode), [storeNode])
const applyPreset = useCallback(
(key: ChimneyPresetKey) => {
commitProp(chimneyPresets[key] as Partial<ChimneyNode>)
triggerSFX('sfx:item-pick')
},
[commitProp],
)

if (!(node && node.type === 'chimney' && selectedId)) return null

const scenestate = useScene.getState()
Expand Down Expand Up @@ -321,21 +336,6 @@ export default function ChimneyPanel() {
commitProp({ rotation: newWorldRot - ancestorWorldY })
}

// Match the current store node against the preset table so the
// segmented control highlights "the preset you'd land on if you
// applied X again". Compare against the store node, not the live-
// override-merged `node`, so the highlight is stable across slider
// drags. Null means the user has tweaked away from any preset; the
// segmented control will then render with no segment selected.
const activePreset = useMemo(() => detectActiveChimneyPreset(storeNode), [storeNode])
const applyPreset = useCallback(
(key: ChimneyPresetKey) => {
commitProp(chimneyPresets[key] as Partial<ChimneyNode>)
triggerSFX('sfx:item-pick')
},
[commitProp],
)

return (
<PanelWrapper
icon="/icons/roof.png"
Expand Down
3 changes: 0 additions & 3 deletions packages/nodes/src/dormer/csg-geometry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,6 @@ export function generateDormerGeometry(

const dormerBrushes = getRoofSegmentBrushes(virtualSegment)
if (!dormerBrushes) {
// biome-ignore lint/suspicious/noConsole: keep diagnostic — fallback path.
console.warn('[dormer] getRoofSegmentBrushes returned null; using fallback silhouette.')
return buildDormerFallbackGeometry(dormer)
}
Expand Down Expand Up @@ -476,7 +475,6 @@ export function generateDormerGeometry(
remapRoofShellFaces(resultGeo, virtualSegment)
splitDormerGableMaterial(resultGeo, dormer.height, DORMER_GABLE_MATERIAL_INDEX)
} catch (e) {
// biome-ignore lint/suspicious/noConsole: dormer CSG can throw; keep diagnostic.
console.error('[dormer] CSG failed, falling back to silhouette:', e)
if (dormerSolid) {
try {
Expand All @@ -496,7 +494,6 @@ export function generateDormerGeometry(
// dormer is at least visible.
const triCount = resultGeo.getIndex()?.count ?? resultGeo.getAttribute('position')?.count ?? 0
if (triCount === 0) {
// biome-ignore lint/suspicious/noConsole: keep diagnostic — empty CSG.
console.warn('[dormer] CSG produced empty geometry; using fallback silhouette.')
return buildDormerFallbackGeometry(dormer)
}
Expand Down
1 change: 0 additions & 1 deletion packages/nodes/src/dormer/move-tool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ const MoveDormerTool = ({ node }: { node: DormerNode }) => {
})
}
}
// biome-ignore lint/correctness/useExhaustiveDependencies: capture-on-mount; meta is intentionally not re-read on changes.
}, [node.id, isNew])

const { activeBuildingId, segmentXform, hitLocal, ghostRotation } = useDormerPlacement({
Expand Down
1 change: 0 additions & 1 deletion packages/nodes/src/dormer/panel-position-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ export function DormerPositionSection({
if (Number.isFinite(lo_x)) bounds = { minX: lo_x, maxX: hi_x, minZ: lo_z, maxZ: hi_z }
}
return { worldX, worldZ, worldRotation, bounds }
// biome-ignore lint/correctness/useExhaustiveDependencies: roofChildrenKey is the stable signature of `roof.children`; intentionally omitting `roof` (object identity) in favor of the joined ids.
}, [selectedId, px, py, pz, nodeRotation, segmentId, roofChildrenKey])

const worldX_now = worldXform.worldX
Expand Down
2 changes: 1 addition & 1 deletion packages/nodes/src/dormer/panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export default function DormerPanel() {
}, [node, selectedId, setMovingNode, setSelection])

const handleDuplicate = useCallback(() => {
if (!(node && node.roofSegmentId)) return
if (!node?.roofSegmentId) return
triggerSFX('sfx:item-pick')
// Deep clone and strip the id so the move tool's onClick branch
// (`isNew || !node.id`) takes the "create fresh" path. Setting
Expand Down
2 changes: 0 additions & 2 deletions packages/nodes/src/dormer/window-assembly.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ const DormerWindowAssembly = ({
{winGeo.glassPanes.map((pane, i) => (
<mesh
geometry={pane.geo}
// biome-ignore lint/suspicious/noArrayIndexKey: glass panes are derived from grid indices, no stable id.
key={`${keyPrefix}-glass-${i}`}
material={glassMaterial}
name={`dormer-glass-${keyPrefix}-${i}`}
Expand All @@ -155,7 +154,6 @@ const DormerWindowAssembly = ({
<mesh
castShadow
geometry={bar.geo}
// biome-ignore lint/suspicious/noArrayIndexKey: frame bars are derived from grid indices, no stable id.
key={`${keyPrefix}-bar-${i}`}
material={frameMaterial}
name={`dormer-frame-${keyPrefix}-${i}`}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,6 @@ type RenderableNode = AnyNode & {
export const ParametricNodeRenderer = ({ node }: { node: AnyNode }) => {
const ref = useRef<Group>(null!)
const n = node as RenderableNode
// biome-ignore lint/suspicious/noExplicitAny: useNodeEvents is keyed by
// literal kind; the registry path passes a runtime kind union. Routing
// through the type cast is safer than widening the hook signature.
const handlers = useNodeEvents(node as any, node.type as any)
const liveTransform = useLiveTransforms((s) => s.get(node.id as AnyNodeId))
// Registry arrow handles (rotation gizmo, position-affecting patches)
Expand Down
3 changes: 0 additions & 3 deletions packages/viewer/src/r3f.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,21 +78,18 @@ interface ThreeJSXElements {
}

declare module 'react' {
// biome-ignore lint/style/noNamespace: Required for JSX module augmentation
namespace JSX {
interface IntrinsicElements extends ThreeJSXElements {}
}
}

declare module 'react/jsx-runtime' {
// biome-ignore lint/style/noNamespace: Required for JSX module augmentation
namespace JSX {
interface IntrinsicElements extends ThreeJSXElements {}
}
}

declare module 'react/jsx-dev-runtime' {
// biome-ignore lint/style/noNamespace: Required for JSX module augmentation
namespace JSX {
interface IntrinsicElements extends ThreeJSXElements {}
}
Expand Down
2 changes: 1 addition & 1 deletion turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"dependsOn": ["^lint"]
},
"check-types": {
"dependsOn": ["^check-types"]
"dependsOn": ["^build", "^check-types"]
},
"dev": {
"dependsOn": ["^build"],
Expand Down
Loading