@pascal-app/core 0.5.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/dist/events/bus.d.ts +74 -4
  2. package/dist/events/bus.d.ts.map +1 -1
  3. package/dist/events/bus.js +1 -1
  4. package/dist/hooks/scene-registry/scene-registry.d.ts +2 -0
  5. package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
  6. package/dist/hooks/scene-registry/scene-registry.js +2 -0
  7. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
  8. package/dist/hooks/spatial-grid/spatial-grid-manager.js +164 -6
  9. package/dist/hooks/spatial-grid/spatial-grid.d.ts +2 -0
  10. package/dist/hooks/spatial-grid/spatial-grid.d.ts.map +1 -1
  11. package/dist/hooks/spatial-grid/spatial-grid.js +43 -20
  12. package/dist/index.d.ts +9 -13
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +7 -11
  15. package/dist/lib/door-operation.d.ts +7 -0
  16. package/dist/lib/door-operation.d.ts.map +1 -0
  17. package/dist/lib/door-operation.js +25 -0
  18. package/dist/lib/polygon-geometry.d.ts +3 -0
  19. package/dist/lib/polygon-geometry.d.ts.map +1 -0
  20. package/dist/lib/polygon-geometry.js +90 -0
  21. package/dist/lib/slab-polygon.d.ts +3 -0
  22. package/dist/lib/slab-polygon.d.ts.map +1 -0
  23. package/dist/lib/slab-polygon.js +58 -0
  24. package/dist/lib/space-detection.d.ts +10 -17
  25. package/dist/lib/space-detection.d.ts.map +1 -1
  26. package/dist/lib/space-detection.js +666 -453
  27. package/dist/material-library.d.ts +20 -0
  28. package/dist/material-library.d.ts.map +1 -0
  29. package/dist/material-library.js +580 -0
  30. package/dist/schema/asset-url.d.ts +34 -0
  31. package/dist/schema/asset-url.d.ts.map +1 -0
  32. package/dist/schema/asset-url.js +79 -0
  33. package/dist/schema/asset-url.test.d.ts +2 -0
  34. package/dist/schema/asset-url.test.d.ts.map +1 -0
  35. package/dist/schema/asset-url.test.js +138 -0
  36. package/dist/schema/index.d.ts +14 -7
  37. package/dist/schema/index.d.ts.map +1 -1
  38. package/dist/schema/index.js +10 -7
  39. package/dist/schema/material.d.ts +112 -2
  40. package/dist/schema/material.d.ts.map +1 -1
  41. package/dist/schema/material.js +55 -1
  42. package/dist/schema/nodes/ceiling.d.ts +11 -1
  43. package/dist/schema/nodes/ceiling.d.ts.map +1 -1
  44. package/dist/schema/nodes/ceiling.js +6 -0
  45. package/dist/schema/nodes/column.d.ts +520 -0
  46. package/dist/schema/nodes/column.d.ts.map +1 -0
  47. package/dist/schema/nodes/column.js +385 -0
  48. package/dist/schema/nodes/door.d.ts +74 -1
  49. package/dist/schema/nodes/door.d.ts.map +1 -1
  50. package/dist/schema/nodes/door.js +39 -2
  51. package/dist/schema/nodes/fence.d.ts +34 -0
  52. package/dist/schema/nodes/fence.d.ts.map +1 -1
  53. package/dist/schema/nodes/fence.js +5 -0
  54. package/dist/schema/nodes/guide.d.ts +17 -0
  55. package/dist/schema/nodes/guide.d.ts.map +1 -1
  56. package/dist/schema/nodes/guide.js +11 -1
  57. package/dist/schema/nodes/item.d.ts +10 -2
  58. package/dist/schema/nodes/item.d.ts.map +1 -1
  59. package/dist/schema/nodes/item.js +18 -1
  60. package/dist/schema/nodes/level.d.ts +1 -1
  61. package/dist/schema/nodes/level.d.ts.map +1 -1
  62. package/dist/schema/nodes/level.js +6 -0
  63. package/dist/schema/nodes/roof-segment.d.ts +3 -1
  64. package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
  65. package/dist/schema/nodes/roof-segment.js +1 -0
  66. package/dist/schema/nodes/roof.d.ts +108 -0
  67. package/dist/schema/nodes/roof.d.ts.map +1 -1
  68. package/dist/schema/nodes/roof.js +58 -2
  69. package/dist/schema/nodes/scan.d.ts.map +1 -1
  70. package/dist/schema/nodes/scan.js +2 -1
  71. package/dist/schema/nodes/site.d.ts +2 -1
  72. package/dist/schema/nodes/site.d.ts.map +1 -1
  73. package/dist/schema/nodes/slab.d.ts +11 -1
  74. package/dist/schema/nodes/slab.d.ts.map +1 -1
  75. package/dist/schema/nodes/slab.js +7 -0
  76. package/dist/schema/nodes/spawn.d.ts +24 -0
  77. package/dist/schema/nodes/spawn.d.ts.map +1 -0
  78. package/dist/schema/nodes/spawn.js +8 -0
  79. package/dist/schema/nodes/stair-segment.d.ts +3 -1
  80. package/dist/schema/nodes/stair-segment.d.ts.map +1 -1
  81. package/dist/schema/nodes/stair-segment.js +1 -0
  82. package/dist/schema/nodes/stair.d.ts +122 -2
  83. package/dist/schema/nodes/stair.d.ts.map +1 -1
  84. package/dist/schema/nodes/stair.js +72 -2
  85. package/dist/schema/nodes/surface-hole-metadata.d.ts +10 -0
  86. package/dist/schema/nodes/surface-hole-metadata.d.ts.map +1 -0
  87. package/dist/schema/nodes/surface-hole-metadata.js +5 -0
  88. package/dist/schema/nodes/wall.d.ts +87 -1
  89. package/dist/schema/nodes/wall.d.ts.map +1 -1
  90. package/dist/schema/nodes/wall.js +45 -4
  91. package/dist/schema/nodes/window.d.ts +57 -1
  92. package/dist/schema/nodes/window.d.ts.map +1 -1
  93. package/dist/schema/nodes/window.js +29 -0
  94. package/dist/schema/types.d.ts +653 -12
  95. package/dist/schema/types.d.ts.map +1 -1
  96. package/dist/schema/types.js +4 -0
  97. package/dist/store/actions/node-actions.d.ts +1 -1
  98. package/dist/store/actions/node-actions.d.ts.map +1 -1
  99. package/dist/store/actions/node-actions.js +181 -5
  100. package/dist/store/history-control.d.ts +14 -0
  101. package/dist/store/history-control.d.ts.map +1 -0
  102. package/dist/store/history-control.js +22 -0
  103. package/dist/store/use-interactive.d.ts +43 -0
  104. package/dist/store/use-interactive.d.ts.map +1 -1
  105. package/dist/store/use-interactive.js +66 -0
  106. package/dist/store/use-scene.d.ts.map +1 -1
  107. package/dist/store/use-scene.js +307 -3
  108. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
  109. package/dist/systems/ceiling/ceiling-system.js +7 -0
  110. package/dist/systems/fence/fence-system.d.ts.map +1 -1
  111. package/dist/systems/fence/fence-system.js +106 -39
  112. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  113. package/dist/systems/roof/roof-system.js +31 -1
  114. package/dist/systems/slab/slab-system.d.ts.map +1 -1
  115. package/dist/systems/slab/slab-system.js +45 -8
  116. package/dist/systems/stair/stair-opening-sync.d.ts +6 -0
  117. package/dist/systems/stair/stair-opening-sync.d.ts.map +1 -0
  118. package/dist/systems/stair/stair-opening-sync.js +576 -0
  119. package/dist/systems/stair/stair-opening-sync.test.d.ts +2 -0
  120. package/dist/systems/stair/stair-opening-sync.test.d.ts.map +1 -0
  121. package/dist/systems/stair/stair-opening-sync.test.js +65 -0
  122. package/dist/systems/stair/stair-system.d.ts.map +1 -1
  123. package/dist/systems/stair/stair-system.js +119 -2
  124. package/dist/systems/wall/wall-curve.d.ts +43 -0
  125. package/dist/systems/wall/wall-curve.d.ts.map +1 -0
  126. package/dist/systems/wall/wall-curve.js +176 -0
  127. package/dist/systems/wall/wall-footprint.d.ts.map +1 -1
  128. package/dist/systems/wall/wall-footprint.js +16 -2
  129. package/dist/systems/wall/wall-mitering.d.ts +7 -0
  130. package/dist/systems/wall/wall-mitering.d.ts.map +1 -1
  131. package/dist/systems/wall/wall-mitering.js +76 -3
  132. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  133. package/dist/systems/wall/wall-system.js +202 -2
  134. package/package.json +33 -5
@@ -0,0 +1,79 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Scheme allowlist for asset-like URLs embedded in scene graphs.
4
+ *
5
+ * Phase 3 security audit: `scan.url`, `guide.url`, `material.texture.url`, and
6
+ * `item.asset.src` were previously bare `z.string()`. That meant an
7
+ * attacker-crafted scene loaded in the editor could beacon to arbitrary URLs
8
+ * (e.g. `javascript:`, `file:///etc/passwd`, `http://169.254.169.254/...`).
9
+ *
10
+ * This validator rejects URLs that don't match the scheme allowlist below.
11
+ */
12
+ const ALLOWED_SCHEMES = ['asset:', 'blob:', 'https:', 'data:image/'];
13
+ /**
14
+ * Optional environment variable that narrows which `https:` origins are
15
+ * accepted. Set to a comma-separated list (e.g. `https://cdn.pascal.app`).
16
+ * When unset, any `https:` origin is permitted.
17
+ */
18
+ export const ALLOWED_ORIGINS_ENV = 'PASCAL_ALLOWED_ASSET_ORIGINS';
19
+ // Narrow access to the environment variable without requiring @types/node in
20
+ // this package. The core package ships to both browser and Node contexts.
21
+ function readAllowedOrigins() {
22
+ const g = globalThis;
23
+ const value = g.process?.env?.[ALLOWED_ORIGINS_ENV];
24
+ if (!value)
25
+ return undefined;
26
+ const list = value
27
+ .split(',')
28
+ .map((s) => s.trim())
29
+ .filter((s) => s.length > 0);
30
+ return list.length > 0 ? list : undefined;
31
+ }
32
+ function isAllowedAssetUrl(url) {
33
+ if (typeof url !== 'string' || url.length === 0)
34
+ return false;
35
+ if (url.startsWith('asset://'))
36
+ return true; // internal handle
37
+ if (url.startsWith('blob:'))
38
+ return true; // in-memory reference
39
+ if (url.startsWith('data:image/'))
40
+ return true; // inline image only (never data:text/html)
41
+ if (url.startsWith('/'))
42
+ return true; // app-relative path
43
+ try {
44
+ const parsed = new URL(url);
45
+ if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:')
46
+ return false;
47
+ // http is only permitted for localhost development
48
+ if (parsed.protocol === 'http:' && !['localhost', '127.0.0.1'].includes(parsed.hostname)) {
49
+ return false;
50
+ }
51
+ // optional env-driven origin allowlist (only enforced for https URLs)
52
+ if (parsed.protocol === 'https:') {
53
+ const allowlist = readAllowedOrigins();
54
+ if (allowlist)
55
+ return allowlist.includes(parsed.origin);
56
+ }
57
+ return true;
58
+ }
59
+ catch {
60
+ return false;
61
+ }
62
+ }
63
+ /**
64
+ * Zod validator for asset-style URL fields. Accepts:
65
+ * - `asset://…` internal handles
66
+ * - `blob:…` in-memory references
67
+ * - `data:image/…` inline images (not `data:text/html` or other types)
68
+ * - `/…` app-relative paths
69
+ * - `https://…` public URLs (optionally narrowed to an env allowlist)
70
+ * - `http://localhost[:port]/…` or `http://127.0.0.1/…` for local dev
71
+ *
72
+ * Rejects every other scheme, including `javascript:`, `file:`, `ftp:`,
73
+ * and `data:text/html`, as well as empty strings and non-URL garbage.
74
+ */
75
+ export const AssetUrl = z.string().refine(isAllowedAssetUrl, {
76
+ message: 'URL must be asset://, blob:, data:image/, /path, or https://. http://localhost allowed for dev.',
77
+ });
78
+ // re-export the scheme allowlist for documentation / downstream validators
79
+ export { ALLOWED_SCHEMES };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=asset-url.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset-url.test.d.ts","sourceRoot":"","sources":["../../src/schema/asset-url.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,138 @@
1
+ // @ts-expect-error — bun:test is provided by the Bun runtime; core does not
2
+ // depend on @types/bun so the import type is unresolved at compile time.
3
+ // The tsconfig in packages/core still emits this file; the @ts-expect-error
4
+ // keeps the build green while letting `bun test` pick it up normally.
5
+ import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
6
+ import { ALLOWED_ORIGINS_ENV, AssetUrl } from './asset-url';
7
+ function isValid(url) {
8
+ return AssetUrl.safeParse(url).success;
9
+ }
10
+ describe('AssetUrl', () => {
11
+ describe('allowed URLs', () => {
12
+ const cases = [
13
+ ['asset://abc', 'internal asset handle'],
14
+ ['asset://catalog/items/chair-1', 'nested asset handle'],
15
+ ['blob:http://example.com/uuid-1234', 'blob URL with http inner'],
16
+ ['blob:https://example.com/uuid-5678', 'blob URL with https inner'],
17
+ ['https://cdn.example.com/a.glb', 'https CDN URL'],
18
+ ['https://cdn.example.com/models/chair.glb?v=2', 'https URL with query string'],
19
+ ['http://localhost:3000/x', 'http localhost with port'],
20
+ ['http://localhost/x', 'http localhost without port'],
21
+ ['http://127.0.0.1:8080/texture.png', 'http 127.0.0.1 loopback'],
22
+ ['/public/a.glb', 'app-relative path'],
23
+ ['/material/wood1/albedoMap_basecolor.jpg', 'relative path deep'],
24
+ ['data:image/png;base64,AAA', 'inline PNG data URL'],
25
+ ['data:image/jpeg;base64,/9j/', 'inline JPEG data URL'],
26
+ ['data:image/webp;base64,UklGR', 'inline WebP data URL'],
27
+ ['data:image/svg+xml,%3Csvg%3E', 'inline SVG data URL'],
28
+ ];
29
+ for (const [url, label] of cases) {
30
+ test(`accepts ${label}: ${url}`, () => {
31
+ expect(isValid(url)).toBe(true);
32
+ });
33
+ }
34
+ });
35
+ describe('rejected URLs', () => {
36
+ const cases = [
37
+ ['javascript:alert(1)', 'javascript scheme'],
38
+ ['JAVASCRIPT:alert(1)', 'javascript scheme uppercase'],
39
+ ['file:///etc/passwd', 'file scheme'],
40
+ ['file://C:/Windows/System32/config', 'file scheme Windows'],
41
+ ['http://evil.com/', 'non-loopback http'],
42
+ ['http://example.com:3000/x', 'http on non-loopback host'],
43
+ ['http://169.254.169.254/latest/meta-data/', 'http on link-local (cloud metadata)'],
44
+ ['data:text/html,<script>alert(1)</script>', 'data text/html'],
45
+ ['data:application/javascript,alert(1)', 'data application/javascript'],
46
+ ['data:text/plain,hi', 'data text/plain'],
47
+ ['ftp://a.b.com', 'ftp scheme'],
48
+ ['ws://example.com/', 'websocket scheme'],
49
+ ['vbscript:msgbox', 'vbscript scheme'],
50
+ ['', 'empty string'],
51
+ ['not a url at all', 'non-url string'],
52
+ ['://missing-scheme', 'malformed'],
53
+ ];
54
+ for (const [url, label] of cases) {
55
+ test(`rejects ${label}: ${url}`, () => {
56
+ expect(isValid(url)).toBe(false);
57
+ });
58
+ }
59
+ });
60
+ describe(`env allowlist via ${ALLOWED_ORIGINS_ENV}`, () => {
61
+ const g = globalThis;
62
+ const original = g.process?.env?.[ALLOWED_ORIGINS_ENV];
63
+ beforeEach(() => {
64
+ if (g.process?.env)
65
+ delete g.process.env[ALLOWED_ORIGINS_ENV];
66
+ });
67
+ afterEach(() => {
68
+ if (!g.process?.env)
69
+ return;
70
+ if (original === undefined) {
71
+ delete g.process.env[ALLOWED_ORIGINS_ENV];
72
+ }
73
+ else {
74
+ g.process.env[ALLOWED_ORIGINS_ENV] = original;
75
+ }
76
+ });
77
+ test('single origin allowlist accepts matching https URL', () => {
78
+ if (!g.process?.env)
79
+ return; // browser-only runtime
80
+ g.process.env[ALLOWED_ORIGINS_ENV] = 'https://cdn.pascal.app';
81
+ expect(isValid('https://cdn.pascal.app/a.glb')).toBe(true);
82
+ expect(isValid('https://cdn.pascal.app/deep/path?q=1')).toBe(true);
83
+ });
84
+ test('single origin allowlist rejects non-matching https URL', () => {
85
+ if (!g.process?.env)
86
+ return;
87
+ g.process.env[ALLOWED_ORIGINS_ENV] = 'https://cdn.pascal.app';
88
+ expect(isValid('https://cdn.other.com/a.glb')).toBe(false);
89
+ expect(isValid('https://attacker.example.com/x')).toBe(false);
90
+ });
91
+ test('multi-origin allowlist accepts any listed origin', () => {
92
+ if (!g.process?.env)
93
+ return;
94
+ g.process.env[ALLOWED_ORIGINS_ENV] = 'https://cdn.pascal.app, https://assets.pascal.app';
95
+ expect(isValid('https://cdn.pascal.app/a.glb')).toBe(true);
96
+ expect(isValid('https://assets.pascal.app/tex.webp')).toBe(true);
97
+ expect(isValid('https://third.example.com/x')).toBe(false);
98
+ });
99
+ test('allowlist ignores trailing / in URL path (origin match only)', () => {
100
+ if (!g.process?.env)
101
+ return;
102
+ g.process.env[ALLOWED_ORIGINS_ENV] = 'https://cdn.pascal.app';
103
+ expect(isValid('https://cdn.pascal.app/')).toBe(true);
104
+ expect(isValid('https://cdn.pascal.app')).toBe(true);
105
+ });
106
+ test('empty allowlist behaves like unset', () => {
107
+ if (!g.process?.env)
108
+ return;
109
+ g.process.env[ALLOWED_ORIGINS_ENV] = '';
110
+ expect(isValid('https://cdn.other.com/a.glb')).toBe(true);
111
+ });
112
+ test('allowlist does not restrict non-https schemes', () => {
113
+ if (!g.process?.env)
114
+ return;
115
+ g.process.env[ALLOWED_ORIGINS_ENV] = 'https://cdn.pascal.app';
116
+ // these should still pass because they match earlier scheme-based branches
117
+ expect(isValid('asset://x')).toBe(true);
118
+ expect(isValid('blob:https://example.com/abc')).toBe(true);
119
+ expect(isValid('data:image/png;base64,AAA')).toBe(true);
120
+ expect(isValid('/public/a.glb')).toBe(true);
121
+ expect(isValid('http://localhost:3000/x')).toBe(true);
122
+ });
123
+ test('allowlist rejects subdomain spoofing', () => {
124
+ if (!g.process?.env)
125
+ return;
126
+ g.process.env[ALLOWED_ORIGINS_ENV] = 'https://cdn.pascal.app';
127
+ expect(isValid('https://cdn.pascal.app.evil.com/x')).toBe(false);
128
+ expect(isValid('https://evil.com/cdn.pascal.app')).toBe(false);
129
+ });
130
+ test('allowlist respects ports', () => {
131
+ if (!g.process?.env)
132
+ return;
133
+ g.process.env[ALLOWED_ORIGINS_ENV] = 'https://cdn.pascal.app:8443';
134
+ expect(isValid('https://cdn.pascal.app:8443/x')).toBe(true);
135
+ expect(isValid('https://cdn.pascal.app/x')).toBe(false);
136
+ });
137
+ });
138
+ });
@@ -1,24 +1,31 @@
1
1
  export { BaseNode, generateId, Material, nodeType, objectId } from './base';
2
2
  export { CameraSchema } from './camera';
3
3
  export { type Collection, type CollectionId, generateCollectionId } from './collections';
4
- export { DEFAULT_MATERIALS, MaterialPreset, MaterialProperties, MaterialSchema, resolveMaterial, } from './material';
4
+ export type { MaterialMapProperties, MaterialMaps, MaterialPresetPayload, MaterialTarget as MaterialTargetValue, TextureWrapMode as TextureWrapModeValue, } from './material';
5
+ export { DEFAULT_MATERIALS, MaterialMapPropertiesSchema, MaterialMapsSchema, MaterialPreset, MaterialPresetPayloadSchema, MaterialProperties, MaterialSchema, MaterialTarget, resolveMaterial, TextureWrapMode, } from './material';
5
6
  export { BuildingNode } from './nodes/building';
6
7
  export { CeilingNode } from './nodes/ceiling';
8
+ export { COLUMN_PRESETS, ColumnBaseStyle, ColumnCapitalStyle, ColumnCarvingPlacement, ColumnCrossSection, ColumnNode, ColumnPanelShape, type ColumnPresetId, ColumnRingPlacement, ColumnShaftDetail, ColumnShaftProfile, ColumnStyle, } from './nodes/column';
7
9
  export { DoorNode, DoorSegment } from './nodes/door';
8
10
  export { FenceBaseStyle, FenceNode, FenceStyle } from './nodes/fence';
9
- export { GuideNode } from './nodes/guide';
11
+ export { GuideNode, GuideScaleReference } from './nodes/guide';
10
12
  export type { AnimationEffect, Asset, AssetInput, Control, Effect, Interactive, LightEffect, SliderControl, TemperatureControl, ToggleControl, } from './nodes/item';
11
- export { getScaledDimensions, ItemNode } from './nodes/item';
13
+ export { getScaledDimensions, ItemNode, isLowProfileItemSurface, LOW_PROFILE_ITEM_SURFACE_MAX_HEIGHT, } from './nodes/item';
12
14
  export { LevelNode } from './nodes/level';
13
- export { RoofNode } from './nodes/roof';
15
+ export type { RoofSurfaceMaterialRole, RoofSurfaceMaterialSpec } from './nodes/roof';
16
+ export { getEffectiveRoofSurfaceMaterial, RoofNode } from './nodes/roof';
14
17
  export { RoofSegmentNode, RoofType } from './nodes/roof-segment';
15
18
  export { ScanNode } from './nodes/scan';
16
19
  export { SiteNode } from './nodes/site';
17
20
  export { SlabNode } from './nodes/slab';
18
- export { StairNode, StairRailingMode, StairTopLandingMode, StairType } from './nodes/stair';
21
+ export { SpawnNode } from './nodes/spawn';
22
+ export type { StairSurfaceMaterialRole, StairSurfaceMaterialSpec } from './nodes/stair';
23
+ export { getEffectiveStairSurfaceMaterial, StairNode, StairRailingMode, StairSlabOpeningMode, StairTopLandingMode, StairType, } from './nodes/stair';
19
24
  export { AttachmentSide, StairSegmentNode, StairSegmentType } from './nodes/stair-segment';
20
- export { WallNode } from './nodes/wall';
21
- export { WindowNode } from './nodes/window';
25
+ export { SurfaceHoleMetadata } from './nodes/surface-hole-metadata';
26
+ export type { WallSurfaceMaterialSpec, WallSurfaceSide } from './nodes/wall';
27
+ export { getEffectiveWallSurfaceMaterial, getWallSurfaceMaterialSignature, WallNode, } from './nodes/wall';
28
+ export { WindowNode, WindowType } from './nodes/window';
22
29
  export { ZoneNode } from './nodes/zone';
23
30
  export type { AnyNodeId, AnyNodeType } from './types';
24
31
  export { AnyNode } from './types';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAE3E,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAEvC,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AAExF,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,eAAe,GAChB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AACpD,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AACrE,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACzC,YAAY,EACV,eAAe,EACf,KAAK,EACL,UAAU,EACV,OAAO,EACP,MAAM,EACN,WAAW,EACX,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,aAAa,GACd,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAEvC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAC3F,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAC1F,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAErD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"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"}
@@ -5,24 +5,27 @@ export { CameraSchema } from './camera';
5
5
  // Collections
6
6
  export { generateCollectionId } from './collections';
7
7
  // Material
8
- export { DEFAULT_MATERIALS, MaterialPreset, MaterialProperties, MaterialSchema, resolveMaterial, } from './material';
8
+ export { DEFAULT_MATERIALS, MaterialMapPropertiesSchema, MaterialMapsSchema, MaterialPreset, MaterialPresetPayloadSchema, MaterialProperties, MaterialSchema, MaterialTarget, resolveMaterial, TextureWrapMode, } from './material';
9
9
  export { BuildingNode } from './nodes/building';
10
10
  export { CeilingNode } from './nodes/ceiling';
11
+ export { COLUMN_PRESETS, ColumnBaseStyle, ColumnCapitalStyle, ColumnCarvingPlacement, ColumnCrossSection, ColumnNode, ColumnPanelShape, ColumnRingPlacement, ColumnShaftDetail, ColumnShaftProfile, ColumnStyle, } from './nodes/column';
11
12
  export { DoorNode, DoorSegment } from './nodes/door';
12
13
  export { FenceBaseStyle, FenceNode, FenceStyle } from './nodes/fence';
13
- export { GuideNode } from './nodes/guide';
14
- export { getScaledDimensions, ItemNode } from './nodes/item';
14
+ export { GuideNode, GuideScaleReference } from './nodes/guide';
15
+ export { getScaledDimensions, ItemNode, isLowProfileItemSurface, LOW_PROFILE_ITEM_SURFACE_MAX_HEIGHT, } from './nodes/item';
15
16
  export { LevelNode } from './nodes/level';
16
- export { RoofNode } from './nodes/roof';
17
+ export { getEffectiveRoofSurfaceMaterial, RoofNode } from './nodes/roof';
17
18
  export { RoofSegmentNode, RoofType } from './nodes/roof-segment';
18
19
  export { ScanNode } from './nodes/scan';
19
20
  // Nodes
20
21
  export { SiteNode } from './nodes/site';
21
22
  export { SlabNode } from './nodes/slab';
22
- export { StairNode, StairRailingMode, StairTopLandingMode, StairType } from './nodes/stair';
23
+ export { SpawnNode } from './nodes/spawn';
24
+ export { getEffectiveStairSurfaceMaterial, StairNode, StairRailingMode, StairSlabOpeningMode, StairTopLandingMode, StairType, } from './nodes/stair';
23
25
  export { AttachmentSide, StairSegmentNode, StairSegmentType } from './nodes/stair-segment';
24
- export { WallNode } from './nodes/wall';
25
- export { WindowNode } from './nodes/window';
26
+ export { SurfaceHoleMetadata } from './nodes/surface-hole-metadata';
27
+ export { getEffectiveWallSurfaceMaterial, getWallSurfaceMaterialSignature, WallNode, } from './nodes/wall';
28
+ export { WindowNode, WindowType } from './nodes/window';
26
29
  export { ZoneNode } from './nodes/zone';
27
30
  // Union types
28
31
  export { AnyNode } from './types';
@@ -1,6 +1,5 @@
1
1
  import { z } from 'zod';
2
2
  export declare const MaterialPreset: z.ZodEnum<{
3
- custom: "custom";
4
3
  white: "white";
5
4
  brick: "brick";
6
5
  concrete: "concrete";
@@ -10,6 +9,7 @@ export declare const MaterialPreset: z.ZodEnum<{
10
9
  plaster: "plaster";
11
10
  tile: "tile";
12
11
  marble: "marble";
12
+ custom: "custom";
13
13
  }>;
14
14
  export type MaterialPreset = z.infer<typeof MaterialPreset>;
15
15
  export declare const MaterialProperties: z.ZodObject<{
@@ -26,8 +26,8 @@ export declare const MaterialProperties: z.ZodObject<{
26
26
  }, z.core.$strip>;
27
27
  export type MaterialProperties = z.infer<typeof MaterialProperties>;
28
28
  export declare const MaterialSchema: z.ZodObject<{
29
+ id: z.ZodOptional<z.ZodString>;
29
30
  preset: z.ZodOptional<z.ZodEnum<{
30
- custom: "custom";
31
31
  white: "white";
32
32
  brick: "brick";
33
33
  concrete: "concrete";
@@ -37,6 +37,7 @@ export declare const MaterialSchema: z.ZodObject<{
37
37
  plaster: "plaster";
38
38
  tile: "tile";
39
39
  marble: "marble";
40
+ custom: "custom";
40
41
  }>>;
41
42
  properties: z.ZodOptional<z.ZodObject<{
42
43
  color: z.ZodDefault<z.ZodString>;
@@ -57,6 +58,115 @@ export declare const MaterialSchema: z.ZodObject<{
57
58
  }, z.core.$strip>>;
58
59
  }, z.core.$strip>;
59
60
  export type MaterialSchema = z.infer<typeof MaterialSchema>;
61
+ export declare const MaterialTarget: z.ZodEnum<{
62
+ wall: "wall";
63
+ roof: "roof";
64
+ "roof-segment": "roof-segment";
65
+ stair: "stair";
66
+ "stair-segment": "stair-segment";
67
+ fence: "fence";
68
+ column: "column";
69
+ slab: "slab";
70
+ ceiling: "ceiling";
71
+ door: "door";
72
+ window: "window";
73
+ }>;
74
+ export type MaterialTarget = z.infer<typeof MaterialTarget>;
75
+ export declare const TextureWrapMode: z.ZodEnum<{
76
+ Repeat: "Repeat";
77
+ ClampToEdge: "ClampToEdge";
78
+ MirroredRepeat: "MirroredRepeat";
79
+ }>;
80
+ export type TextureWrapMode = z.infer<typeof TextureWrapMode>;
81
+ export declare const MaterialMapsSchema: z.ZodObject<{
82
+ albedoMap: z.ZodOptional<z.ZodString>;
83
+ metalnessMap: z.ZodOptional<z.ZodString>;
84
+ roughnessMap: z.ZodOptional<z.ZodString>;
85
+ normalMap: z.ZodOptional<z.ZodString>;
86
+ displacementMap: z.ZodOptional<z.ZodString>;
87
+ aoMap: z.ZodOptional<z.ZodString>;
88
+ emissiveMap: z.ZodOptional<z.ZodString>;
89
+ bumpMap: z.ZodOptional<z.ZodString>;
90
+ alphaMap: z.ZodOptional<z.ZodString>;
91
+ lightMap: z.ZodOptional<z.ZodString>;
92
+ }, z.core.$strip>;
93
+ export type MaterialMaps = z.infer<typeof MaterialMapsSchema>;
94
+ export declare const MaterialMapPropertiesSchema: z.ZodObject<{
95
+ color: z.ZodDefault<z.ZodString>;
96
+ roughness: z.ZodDefault<z.ZodNumber>;
97
+ metalness: z.ZodDefault<z.ZodNumber>;
98
+ repeatX: z.ZodDefault<z.ZodNumber>;
99
+ repeatY: z.ZodDefault<z.ZodNumber>;
100
+ rotation: z.ZodDefault<z.ZodNumber>;
101
+ wrapS: z.ZodDefault<z.ZodEnum<{
102
+ Repeat: "Repeat";
103
+ ClampToEdge: "ClampToEdge";
104
+ MirroredRepeat: "MirroredRepeat";
105
+ }>>;
106
+ wrapT: z.ZodDefault<z.ZodEnum<{
107
+ Repeat: "Repeat";
108
+ ClampToEdge: "ClampToEdge";
109
+ MirroredRepeat: "MirroredRepeat";
110
+ }>>;
111
+ normalScaleX: z.ZodDefault<z.ZodNumber>;
112
+ normalScaleY: z.ZodDefault<z.ZodNumber>;
113
+ emissiveIntensity: z.ZodDefault<z.ZodNumber>;
114
+ displacementScale: z.ZodDefault<z.ZodNumber>;
115
+ transparent: z.ZodDefault<z.ZodBoolean>;
116
+ flipY: z.ZodDefault<z.ZodBoolean>;
117
+ bumpScale: z.ZodDefault<z.ZodNumber>;
118
+ emissiveColor: z.ZodDefault<z.ZodString>;
119
+ aoMapIntensity: z.ZodDefault<z.ZodNumber>;
120
+ side: z.ZodDefault<z.ZodNumber>;
121
+ opacity: z.ZodDefault<z.ZodNumber>;
122
+ lightMapIntensity: z.ZodDefault<z.ZodNumber>;
123
+ }, z.core.$strip>;
124
+ export type MaterialMapProperties = z.infer<typeof MaterialMapPropertiesSchema>;
125
+ export declare const MaterialPresetPayloadSchema: z.ZodObject<{
126
+ maps: z.ZodObject<{
127
+ albedoMap: z.ZodOptional<z.ZodString>;
128
+ metalnessMap: z.ZodOptional<z.ZodString>;
129
+ roughnessMap: z.ZodOptional<z.ZodString>;
130
+ normalMap: z.ZodOptional<z.ZodString>;
131
+ displacementMap: z.ZodOptional<z.ZodString>;
132
+ aoMap: z.ZodOptional<z.ZodString>;
133
+ emissiveMap: z.ZodOptional<z.ZodString>;
134
+ bumpMap: z.ZodOptional<z.ZodString>;
135
+ alphaMap: z.ZodOptional<z.ZodString>;
136
+ lightMap: z.ZodOptional<z.ZodString>;
137
+ }, z.core.$strip>;
138
+ mapProperties: z.ZodObject<{
139
+ color: z.ZodDefault<z.ZodString>;
140
+ roughness: z.ZodDefault<z.ZodNumber>;
141
+ metalness: z.ZodDefault<z.ZodNumber>;
142
+ repeatX: z.ZodDefault<z.ZodNumber>;
143
+ repeatY: z.ZodDefault<z.ZodNumber>;
144
+ rotation: z.ZodDefault<z.ZodNumber>;
145
+ wrapS: z.ZodDefault<z.ZodEnum<{
146
+ Repeat: "Repeat";
147
+ ClampToEdge: "ClampToEdge";
148
+ MirroredRepeat: "MirroredRepeat";
149
+ }>>;
150
+ wrapT: z.ZodDefault<z.ZodEnum<{
151
+ Repeat: "Repeat";
152
+ ClampToEdge: "ClampToEdge";
153
+ MirroredRepeat: "MirroredRepeat";
154
+ }>>;
155
+ normalScaleX: z.ZodDefault<z.ZodNumber>;
156
+ normalScaleY: z.ZodDefault<z.ZodNumber>;
157
+ emissiveIntensity: z.ZodDefault<z.ZodNumber>;
158
+ displacementScale: z.ZodDefault<z.ZodNumber>;
159
+ transparent: z.ZodDefault<z.ZodBoolean>;
160
+ flipY: z.ZodDefault<z.ZodBoolean>;
161
+ bumpScale: z.ZodDefault<z.ZodNumber>;
162
+ emissiveColor: z.ZodDefault<z.ZodString>;
163
+ aoMapIntensity: z.ZodDefault<z.ZodNumber>;
164
+ side: z.ZodDefault<z.ZodNumber>;
165
+ opacity: z.ZodDefault<z.ZodNumber>;
166
+ lightMapIntensity: z.ZodDefault<z.ZodNumber>;
167
+ }, z.core.$strip>;
168
+ }, z.core.$strip>;
169
+ export type MaterialPresetPayload = z.infer<typeof MaterialPresetPayloadSchema>;
60
170
  export declare const DEFAULT_MATERIALS: Record<MaterialPreset, MaterialProperties>;
61
171
  export declare function resolveMaterial(material?: MaterialSchema): MaterialProperties;
62
172
  //# sourceMappingURL=material.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"material.d.ts","sourceRoot":"","sources":["../../src/schema/material.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,cAAc;;;;;;;;;;;EAWzB,CAAA;AACF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAA;AAE3D,eAAO,MAAM,kBAAkB;;;;;;;;;;;iBAO7B,CAAA;AACF,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAA;AAEnE,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAUzB,CAAA;AACF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAA;AAE3D,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,cAAc,EAAE,kBAAkB,CAiFxE,CAAA;AAED,wBAAgB,eAAe,CAAC,QAAQ,CAAC,EAAE,cAAc,GAAG,kBAAkB,CAiB7E"}
1
+ {"version":3,"file":"material.d.ts","sourceRoot":"","sources":["../../src/schema/material.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,cAAc;;;;;;;;;;;EAWzB,CAAA;AACF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAA;AAE3D,eAAO,MAAM,kBAAkB;;;;;;;;;;;iBAO7B,CAAA;AACF,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAA;AAEnE,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAWzB,CAAA;AACF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAA;AAE3D,eAAO,MAAM,cAAc;;;;;;;;;;;;EAYzB,CAAA;AACF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAA;AAE3D,eAAO,MAAM,eAAe;;;;EAAsD,CAAA;AAClF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAA;AAE7D,eAAO,MAAM,kBAAkB;;;;;;;;;;;iBAW7B,CAAA;AACF,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAA;AAE7D,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqBtC,CAAA;AACF,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAA;AAE/E,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAGtC,CAAA;AACF,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAA;AAE/E,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,cAAc,EAAE,kBAAkB,CAiFxE,CAAA;AAED,wBAAgB,eAAe,CAAC,QAAQ,CAAC,EAAE,cAAc,GAAG,kBAAkB,CAiB7E"}
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { AssetUrl } from './asset-url';
2
3
  export const MaterialPreset = z.enum([
3
4
  'white',
4
5
  'brick',
@@ -20,16 +21,69 @@ export const MaterialProperties = z.object({
20
21
  side: z.enum(['front', 'back', 'double']).default('front'),
21
22
  });
22
23
  export const MaterialSchema = z.object({
24
+ id: z.string().optional(),
23
25
  preset: MaterialPreset.optional(),
24
26
  properties: MaterialProperties.optional(),
25
27
  texture: z
26
28
  .object({
27
- url: z.string(),
29
+ url: AssetUrl,
28
30
  repeat: z.tuple([z.number(), z.number()]).optional(),
29
31
  scale: z.number().optional(),
30
32
  })
31
33
  .optional(),
32
34
  });
35
+ export const MaterialTarget = z.enum([
36
+ 'wall',
37
+ 'roof',
38
+ 'roof-segment',
39
+ 'stair',
40
+ 'stair-segment',
41
+ 'fence',
42
+ 'column',
43
+ 'slab',
44
+ 'ceiling',
45
+ 'door',
46
+ 'window',
47
+ ]);
48
+ export const TextureWrapMode = z.enum(['Repeat', 'ClampToEdge', 'MirroredRepeat']);
49
+ export const MaterialMapsSchema = z.object({
50
+ albedoMap: AssetUrl.optional(),
51
+ metalnessMap: AssetUrl.optional(),
52
+ roughnessMap: AssetUrl.optional(),
53
+ normalMap: AssetUrl.optional(),
54
+ displacementMap: AssetUrl.optional(),
55
+ aoMap: AssetUrl.optional(),
56
+ emissiveMap: AssetUrl.optional(),
57
+ bumpMap: AssetUrl.optional(),
58
+ alphaMap: AssetUrl.optional(),
59
+ lightMap: AssetUrl.optional(),
60
+ });
61
+ export const MaterialMapPropertiesSchema = z.object({
62
+ color: z.string().default('#ffffff'),
63
+ roughness: z.number().min(0).max(1).default(0.5),
64
+ metalness: z.number().min(0).max(1).default(0),
65
+ repeatX: z.number().default(1),
66
+ repeatY: z.number().default(1),
67
+ rotation: z.number().default(0),
68
+ wrapS: TextureWrapMode.default('Repeat'),
69
+ wrapT: TextureWrapMode.default('Repeat'),
70
+ normalScaleX: z.number().default(1),
71
+ normalScaleY: z.number().default(1),
72
+ emissiveIntensity: z.number().default(1),
73
+ displacementScale: z.number().default(0.02),
74
+ transparent: z.boolean().default(false),
75
+ flipY: z.boolean().default(true),
76
+ bumpScale: z.number().default(1),
77
+ emissiveColor: z.string().default('#000000'),
78
+ aoMapIntensity: z.number().default(1),
79
+ side: z.number().default(0),
80
+ opacity: z.number().min(0).max(1).default(1),
81
+ lightMapIntensity: z.number().default(1),
82
+ });
83
+ export const MaterialPresetPayloadSchema = z.object({
84
+ maps: MaterialMapsSchema,
85
+ mapProperties: MaterialMapPropertiesSchema,
86
+ });
33
87
  export const DEFAULT_MATERIALS = {
34
88
  white: {
35
89
  color: '#ffffff',
@@ -19,8 +19,8 @@ export declare const CeilingNode: z.ZodObject<{
19
19
  type: z.ZodDefault<z.ZodLiteral<"ceiling">>;
20
20
  children: z.ZodDefault<z.ZodArray<z.ZodDefault<z.ZodTemplateLiteral<`item_${string}`>>>>;
21
21
  material: z.ZodOptional<z.ZodObject<{
22
+ id: z.ZodOptional<z.ZodString>;
22
23
  preset: z.ZodOptional<z.ZodEnum<{
23
- custom: "custom";
24
24
  white: "white";
25
25
  brick: "brick";
26
26
  concrete: "concrete";
@@ -30,6 +30,7 @@ export declare const CeilingNode: z.ZodObject<{
30
30
  plaster: "plaster";
31
31
  tile: "tile";
32
32
  marble: "marble";
33
+ custom: "custom";
33
34
  }>>;
34
35
  properties: z.ZodOptional<z.ZodObject<{
35
36
  color: z.ZodDefault<z.ZodString>;
@@ -49,9 +50,18 @@ export declare const CeilingNode: z.ZodObject<{
49
50
  scale: z.ZodOptional<z.ZodNumber>;
50
51
  }, z.core.$strip>>;
51
52
  }, z.core.$strip>>;
53
+ materialPreset: z.ZodOptional<z.ZodString>;
52
54
  polygon: z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>;
53
55
  holes: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>>>;
56
+ holeMetadata: z.ZodDefault<z.ZodArray<z.ZodObject<{
57
+ source: z.ZodDefault<z.ZodEnum<{
58
+ stair: "stair";
59
+ manual: "manual";
60
+ }>>;
61
+ stairId: z.ZodOptional<z.ZodString>;
62
+ }, z.core.$strip>>>;
54
63
  height: z.ZodDefault<z.ZodNumber>;
64
+ autoFromWalls: z.ZodDefault<z.ZodBoolean>;
55
65
  }, z.core.$strip>;
56
66
  export type CeilingNode = z.infer<typeof CeilingNode>;
57
67
  //# sourceMappingURL=ceiling.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ceiling.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/ceiling.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAKvB,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAcvB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAA"}
1
+ {"version":3,"file":"ceiling.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/ceiling.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAMvB,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmBvB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAA"}
@@ -3,16 +3,22 @@ import { z } from 'zod';
3
3
  import { BaseNode, nodeType, objectId } from '../base';
4
4
  import { MaterialSchema } from '../material';
5
5
  import { ItemNode } from './item';
6
+ import { SurfaceHoleMetadata } from './surface-hole-metadata';
6
7
  export const CeilingNode = BaseNode.extend({
7
8
  id: objectId('ceiling'),
8
9
  type: nodeType('ceiling'),
9
10
  children: z.array(ItemNode.shape.id).default([]),
10
11
  material: MaterialSchema.optional(),
12
+ materialPreset: z.string().optional(),
11
13
  polygon: z.array(z.tuple([z.number(), z.number()])),
12
14
  holes: z.array(z.array(z.tuple([z.number(), z.number()]))).default([]),
15
+ holeMetadata: z.array(SurfaceHoleMetadata).default([]),
13
16
  height: z.number().default(2.5), // Height in meters
17
+ autoFromWalls: z.boolean().default(false),
14
18
  }).describe(dedent `
15
19
  Ceiling node - used to represent a ceiling in the building
16
20
  - polygon: array of [x, z] points defining the ceiling boundary
17
21
  - holes: array of polygons representing holes in the ceiling
22
+ - holeMetadata: metadata parallel to holes, used to preserve manual and stair-managed cutouts
23
+ - autoFromWalls: whether the ceiling is automatically generated from a closed wall loop
18
24
  `);