Files
editor/packages/nodes/src/hvac-equipment/definition.ts
T
Wassim SAMADandClaude Opus 4.8 267dac7b1a perf(editor): convert UI icons from PNG to WebP
The editor's /icons assets were ~10MB of oversized PNGs (a single
toolbar icon up to 1.4MB). Convert every non-PWA icon to WebP (quality
92) and repoint all /icons/*.png references to .webp across
packages/editor, packages/nodes, and apps/editor. PWA/platform icons
(apple-touch-icon, icon-192, icon-512) stay PNG.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 10:41:50 -04:00

107 lines
3.0 KiB
TypeScript

import type { NodeDefinition } from '@pascal-app/core'
import { buildHvacEquipmentFloorplan } from './floorplan'
import { buildHvacEquipmentGeometry } from './geometry'
import { hvacEquipmentParametrics } from './parametrics'
import { getHvacEquipmentPorts } from './ports'
import { HvacEquipmentNode } from './schema'
/**
* Phase 3 of the HVAC node system — equipment cabinets (furnace /
* air handler / condenser). Furnaces and air handlers expose supply +
* return ports, giving duct runs a real origin: the duct and fitting
* tools snap onto these collars like any other port.
*
* Composition: `def.geometry` only. Yaw-only rotation, so the editor's
* default R-rotate works on a selected unit without custom actions.
*/
export const hvacEquipmentDefinition: NodeDefinition<typeof HvacEquipmentNode> = {
kind: 'hvac-equipment',
schemaVersion: 1,
schema: HvacEquipmentNode,
category: 'utility',
distributionRole: 'equipment',
defaults: () => ({
object: 'node',
parentId: null,
visible: true,
metadata: {},
position: [0, 0, 0],
rotation: 0,
equipmentType: 'furnace',
width: 0.56,
depth: 0.71,
height: 1.1,
supplyShape: 'round',
returnShape: 'round',
supplyDiameter: 8,
returnDiameter: 8,
supplyWidth: 12,
supplyHeight: 8,
returnWidth: 14,
returnHeight: 8,
}),
capabilities: {
selectable: { hitVolume: 'bbox' },
movable: { axes: ['x', 'z'], gridSnap: true },
rotatable: { axes: ['y'], snapAngles: [Math.PI / 4] },
duplicable: true,
deletable: true,
floorPlaced: {
footprint: (node) => {
const n = node as HvacEquipmentNode
return {
dimensions: [n.width, n.height, n.depth],
rotation: [0, n.rotation, 0],
}
},
},
},
parametrics: hvacEquipmentParametrics,
geometry: buildHvacEquipmentGeometry,
geometryKey: (n) =>
JSON.stringify([
n.equipmentType,
n.width,
n.depth,
n.height,
n.supplyShape,
n.returnShape,
n.supplyDiameter,
n.returnDiameter,
n.supplyWidth,
n.supplyHeight,
n.returnWidth,
n.returnHeight,
]),
ports: getHvacEquipmentPorts,
floorplan: buildHvacEquipmentFloorplan,
tool: () => import('./tool'),
toolHints: [
{ key: 'Click', label: 'Place unit' },
{ key: 'R / T', label: 'Rotate ±45°' },
{ key: 'Shift', label: 'Smooth (no grid snap)' },
{ key: 'Esc', label: 'Exit' },
],
presentation: {
label: 'HVAC Unit',
description:
'Furnace, air handler, or condenser — duct runs connect to its supply/return collars.',
icon: { kind: 'url', src: '/icons/HVAC.webp' },
paletteSection: 'structure',
paletteOrder: 92,
},
mcp: {
description:
'HVAC equipment cabinet (furnace, air handler, or condenser). Furnaces and air handlers have supply/return duct ports; every unit also has a refrigerant service port that a lineset run connects to. Position is level-local meters; rotation is yaw radians.',
},
}