@treasuryspatial/viewer-ui-kit 0.1.41 → 0.1.56
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.
- package/dist/AdminLayout.js +1 -1
- package/dist/AdminShell.d.ts +6 -1
- package/dist/AdminShell.d.ts.map +1 -1
- package/dist/AdminShell.js +45 -3
- package/dist/ChooserActionCard.d.ts +10 -0
- package/dist/ChooserActionCard.d.ts.map +1 -0
- package/dist/ChooserActionCard.js +39 -0
- package/dist/ComposerRightRail.d.ts +3 -1
- package/dist/ComposerRightRail.d.ts.map +1 -1
- package/dist/ComposerRightRail.js +11 -43
- package/dist/ConfiguratorShell.js +1 -1
- package/dist/LandingShell.d.ts +2 -1
- package/dist/LandingShell.d.ts.map +1 -1
- package/dist/LandingShell.js +162 -32
- package/dist/LoginForm.js +2 -2
- package/dist/LoginShell.d.ts +2 -1
- package/dist/LoginShell.d.ts.map +1 -1
- package/dist/LoginShell.js +38 -15
- package/dist/ManifestTopBar.d.ts +7 -1
- package/dist/ManifestTopBar.d.ts.map +1 -1
- package/dist/ManifestTopBar.js +249 -38
- package/dist/MetricsPanel.d.ts +1 -1
- package/dist/MetricsPanel.d.ts.map +1 -1
- package/dist/MetricsPanel.js +19 -29
- package/dist/MetricsPanelContent.d.ts +38 -17
- package/dist/MetricsPanelContent.d.ts.map +1 -1
- package/dist/MetricsPanelContent.js +84 -90
- package/dist/ModeBar.d.ts +6 -2
- package/dist/ModeBar.d.ts.map +1 -1
- package/dist/ModeBar.js +49 -82
- package/dist/ModuleSelectorPanel.d.ts +2 -1
- package/dist/ModuleSelectorPanel.d.ts.map +1 -1
- package/dist/ModuleSelectorPanel.js +47 -49
- package/dist/PanelSkin.d.ts.map +1 -1
- package/dist/PanelSkin.js +1598 -312
- package/dist/PanelSystem.d.ts +45 -0
- package/dist/PanelSystem.d.ts.map +1 -0
- package/dist/PanelSystem.js +450 -0
- package/dist/PanelTabs.d.ts.map +1 -1
- package/dist/PanelTabs.js +8 -34
- package/dist/PanelToggleDock.d.ts +10 -0
- package/dist/PanelToggleDock.d.ts.map +1 -0
- package/dist/PanelToggleDock.js +40 -0
- package/dist/PromptPackChooserPanel.d.ts +12 -11
- package/dist/PromptPackChooserPanel.d.ts.map +1 -1
- package/dist/PromptPackChooserPanel.js +103 -63
- package/dist/SceneInspectorPanel.d.ts +42 -0
- package/dist/SceneInspectorPanel.d.ts.map +1 -0
- package/dist/SceneInspectorPanel.js +135 -0
- package/dist/ScienceDataPanelContent.d.ts +16 -0
- package/dist/ScienceDataPanelContent.d.ts.map +1 -0
- package/dist/ScienceDataPanelContent.js +31 -0
- package/dist/ScienceMetricsPanelContent.d.ts +53 -0
- package/dist/ScienceMetricsPanelContent.d.ts.map +1 -0
- package/dist/ScienceMetricsPanelContent.js +415 -0
- package/dist/SpatialHud.d.ts +18 -0
- package/dist/SpatialHud.d.ts.map +1 -0
- package/dist/SpatialHud.js +120 -0
- package/dist/StreetviewModeSurface.d.ts +40 -0
- package/dist/StreetviewModeSurface.d.ts.map +1 -0
- package/dist/StreetviewModeSurface.js +358 -0
- package/dist/SurfaceSwitcher.d.ts +11 -0
- package/dist/SurfaceSwitcher.d.ts.map +1 -0
- package/dist/SurfaceSwitcher.js +46 -0
- package/dist/TopBar.d.ts +2 -0
- package/dist/TopBar.d.ts.map +1 -1
- package/dist/TopBar.js +3 -1
- package/dist/UnknownModeSurface.d.ts +6 -0
- package/dist/UnknownModeSurface.d.ts.map +1 -0
- package/dist/UnknownModeSurface.js +41 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -0
- package/dist/landingTokens.d.ts +7 -0
- package/dist/landingTokens.d.ts.map +1 -0
- package/dist/landingTokens.js +6 -0
- package/dist/layout.d.ts +27 -27
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +33 -27
- package/dist/mapMetrics.d.ts +88 -0
- package/dist/mapMetrics.d.ts.map +1 -0
- package/dist/mapMetrics.js +1 -0
- package/dist/panelPrimitives.d.ts +11 -0
- package/dist/panelPrimitives.d.ts.map +1 -0
- package/dist/panelPrimitives.js +41 -0
- package/dist/topbarLogoPolicy.d.ts +14 -0
- package/dist/topbarLogoPolicy.d.ts.map +1 -0
- package/dist/topbarLogoPolicy.js +41 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +18 -5
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useMemo, useState } from 'react';
|
|
4
|
+
import styled from 'styled-components';
|
|
5
|
+
import { panelPillButtonCss, panelPillRowCss, } from './panelPrimitives.js';
|
|
6
|
+
const SCIENCE_OUTPUT_LABELS = [
|
|
7
|
+
{ id: 'attention', label: 'attention' },
|
|
8
|
+
{ id: 'navigation', label: 'navigation' },
|
|
9
|
+
{ id: 'visual-perception', label: 'visual perception' },
|
|
10
|
+
{ id: 'locomotion', label: 'locomotion' },
|
|
11
|
+
{ id: 'agency', label: 'agency' },
|
|
12
|
+
{ id: 'object-perception', label: 'object perception' },
|
|
13
|
+
{ id: 'tactile-perception', label: 'tactile perception' },
|
|
14
|
+
{ id: 'affect', label: 'affect' },
|
|
15
|
+
{ id: 'social-cognition', label: 'social cognition' },
|
|
16
|
+
{ id: 'social-interaction', label: 'social interaction' },
|
|
17
|
+
{ id: 'goal-directed-behavior', label: 'goal-directed behavior' },
|
|
18
|
+
{ id: 'auditory-perception', label: 'auditory perception' },
|
|
19
|
+
];
|
|
20
|
+
const CEILING_TYPE_IDS = {
|
|
21
|
+
flat: 0,
|
|
22
|
+
vault: 1,
|
|
23
|
+
dome: 2,
|
|
24
|
+
pyramid: 3,
|
|
25
|
+
inverted_dome: 4,
|
|
26
|
+
shed: 5,
|
|
27
|
+
};
|
|
28
|
+
const Root = styled.div.attrs({ className: 'viewer-ui-panel-sections' }) ``;
|
|
29
|
+
const TabRow = styled.div `
|
|
30
|
+
${panelPillRowCss}
|
|
31
|
+
`;
|
|
32
|
+
const TabButton = styled.button `
|
|
33
|
+
${panelPillButtonCss}
|
|
34
|
+
`;
|
|
35
|
+
const Section = styled.section.attrs({ className: 'panel-section' }) ``;
|
|
36
|
+
const SectionHeader = styled.div.attrs({ className: 'panel-section-header' }) ``;
|
|
37
|
+
const SectionHeaderCopy = styled.div `
|
|
38
|
+
min-width: 0;
|
|
39
|
+
`;
|
|
40
|
+
const SectionList = styled.div.attrs({ className: 'panel-section-list' }) ``;
|
|
41
|
+
const MetricRow = styled.div.attrs({ className: 'panel-section-row' }) ``;
|
|
42
|
+
const MetricMeta = styled.div.attrs({ className: 'panel-section-row-copy' }) ``;
|
|
43
|
+
const MetricLabel = styled.span.attrs({ className: 'panel-section-row-label' }) ``;
|
|
44
|
+
const MetricHelp = styled.span.attrs({ className: 'panel-section-row-help' }) ``;
|
|
45
|
+
const MetricValue = styled.span.attrs({ className: 'panel-section-row-value' }) ``;
|
|
46
|
+
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
47
|
+
const resolvePathValue = (root, path) => {
|
|
48
|
+
if (!root || !path.trim())
|
|
49
|
+
return null;
|
|
50
|
+
const parts = path.split('.').map((part) => part.trim()).filter(Boolean);
|
|
51
|
+
let current = root;
|
|
52
|
+
for (const part of parts) {
|
|
53
|
+
if (!isRecord(current))
|
|
54
|
+
return null;
|
|
55
|
+
current = current[part];
|
|
56
|
+
}
|
|
57
|
+
return current ?? null;
|
|
58
|
+
};
|
|
59
|
+
const asFiniteNumber = (value) => {
|
|
60
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
61
|
+
return value;
|
|
62
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
63
|
+
const parsed = Number(value);
|
|
64
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
};
|
|
68
|
+
const asBoolean = (value) => {
|
|
69
|
+
if (typeof value === 'boolean')
|
|
70
|
+
return value;
|
|
71
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
72
|
+
return value !== 0;
|
|
73
|
+
if (typeof value === 'string') {
|
|
74
|
+
const normalized = value.trim().toLowerCase();
|
|
75
|
+
if (['true', 'yes', 'on', '1', 'enabled'].includes(normalized))
|
|
76
|
+
return true;
|
|
77
|
+
if (['false', 'no', 'off', '0', 'disabled'].includes(normalized))
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
};
|
|
82
|
+
const asText = (value) => {
|
|
83
|
+
if (typeof value === 'string' && value.trim())
|
|
84
|
+
return value.trim();
|
|
85
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
86
|
+
return String(value);
|
|
87
|
+
return null;
|
|
88
|
+
};
|
|
89
|
+
const firstResolvedValue = (root, paths) => {
|
|
90
|
+
if (!root)
|
|
91
|
+
return null;
|
|
92
|
+
for (const path of paths) {
|
|
93
|
+
const value = resolvePathValue(root, path);
|
|
94
|
+
if (value !== null && value !== undefined && value !== '')
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
};
|
|
99
|
+
const firstNumber = (root, paths, fallback = null) => {
|
|
100
|
+
const value = asFiniteNumber(firstResolvedValue(root, paths));
|
|
101
|
+
return value === null ? fallback : value;
|
|
102
|
+
};
|
|
103
|
+
const firstBoolean = (root, paths, fallback = null) => {
|
|
104
|
+
const value = asBoolean(firstResolvedValue(root, paths));
|
|
105
|
+
return value === null ? fallback : value;
|
|
106
|
+
};
|
|
107
|
+
const firstText = (root, paths, fallback = null) => {
|
|
108
|
+
const value = asText(firstResolvedValue(root, paths));
|
|
109
|
+
return value === null ? fallback : value;
|
|
110
|
+
};
|
|
111
|
+
const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
112
|
+
const clamp01 = (value) => clamp(value, 0, 1);
|
|
113
|
+
const smoothRatio = (value, min, max) => clamp01((value - min) / Math.max(0.0001, max - min));
|
|
114
|
+
const bellRatio = (value, target, spread = 0.85) => value > 0 && target > 0 ? clamp01(Math.exp(-0.5 * Math.pow(Math.log(value / target) / spread, 2))) : 0;
|
|
115
|
+
const formatDecimal = (value, digits = 2) => value === null || !Number.isFinite(value) ? 'not emitted' : value.toFixed(digits);
|
|
116
|
+
const formatArea = (value) => value === null || !Number.isFinite(value) ? 'not emitted' : `${value.toFixed(2)} sqm`;
|
|
117
|
+
const formatMeters = (value, digits = 2) => value === null || !Number.isFinite(value) ? 'not emitted' : `${value.toFixed(digits)} m`;
|
|
118
|
+
const formatCount = (value) => String(Math.max(0, Math.round(value)));
|
|
119
|
+
const formatYesNo = (value) => (value ? 'yes' : 'no');
|
|
120
|
+
const arrayCount = (value) => (Array.isArray(value) && value.length > 0 ? value.length : null);
|
|
121
|
+
const mmToMeters = (value) => value === null || !Number.isFinite(value) ? null : value / 1000;
|
|
122
|
+
const ellipsePerimeterMeters = (majorRadius, minorRadius) => {
|
|
123
|
+
if (majorRadius === null || minorRadius === null || majorRadius <= 0 || minorRadius <= 0)
|
|
124
|
+
return null;
|
|
125
|
+
const h = Math.pow(majorRadius - minorRadius, 2) / Math.pow(majorRadius + minorRadius, 2);
|
|
126
|
+
return Math.PI * (majorRadius + minorRadius) * (1 + (3 * h) / (10 + Math.sqrt(4 - 3 * h)));
|
|
127
|
+
};
|
|
128
|
+
const normalizeKey = (value, fallback) => (value || fallback).trim().toLowerCase();
|
|
129
|
+
const sideSpanCount = (parameters, side) => {
|
|
130
|
+
const normalized = side.trim().toLowerCase();
|
|
131
|
+
if (normalized === 'north')
|
|
132
|
+
return Math.max(1, Math.round(firstNumber(parameters, ['rectNorthSegments'], 1) ?? 1));
|
|
133
|
+
if (normalized === 'east')
|
|
134
|
+
return Math.max(1, Math.round(firstNumber(parameters, ['rectEastSegments'], 1) ?? 1));
|
|
135
|
+
if (normalized === 'south')
|
|
136
|
+
return Math.max(1, Math.round(firstNumber(parameters, ['rectSouthSegments'], 1) ?? 1));
|
|
137
|
+
if (normalized === 'west')
|
|
138
|
+
return Math.max(1, Math.round(firstNumber(parameters, ['rectWestSegments'], 1) ?? 1));
|
|
139
|
+
return 1;
|
|
140
|
+
};
|
|
141
|
+
const estimateWallTargetCount = (parameters, baseType, targetSpec, groundSpans) => {
|
|
142
|
+
const specs = targetSpec
|
|
143
|
+
.split(',')
|
|
144
|
+
.map((part) => part.trim().toLowerCase())
|
|
145
|
+
.filter(Boolean);
|
|
146
|
+
if (!specs.length)
|
|
147
|
+
return Math.max(1, groundSpans);
|
|
148
|
+
return specs.reduce((sum, spec) => {
|
|
149
|
+
if (baseType === 'elliptical' && spec.startsWith('arc.*')) {
|
|
150
|
+
return sum + Math.max(1, Math.round(firstNumber(parameters, ['ellipseSectorCount'], groundSpans) ?? groundSpans));
|
|
151
|
+
}
|
|
152
|
+
if (baseType === 'ribbon' && (spec.startsWith('run.*') || spec.startsWith('ribbon.*'))) {
|
|
153
|
+
return sum + Math.max(1, Math.round(firstNumber(parameters, ['ribbonSpanCount', 'ribbonStationCount'], groundSpans) ?? groundSpans));
|
|
154
|
+
}
|
|
155
|
+
if (spec.startsWith('*.vband.'))
|
|
156
|
+
return sum + Math.max(1, groundSpans);
|
|
157
|
+
const sideMatch = spec.match(/^(north|east|south|west)\.\*/);
|
|
158
|
+
if (sideMatch)
|
|
159
|
+
return sum + sideSpanCount(parameters, sideMatch[1] ?? '');
|
|
160
|
+
return sum + 1;
|
|
161
|
+
}, 0);
|
|
162
|
+
};
|
|
163
|
+
const resolveScienceInputs = (parameters) => {
|
|
164
|
+
const baseType = normalizeKey(firstText(parameters, ['baseType', 'room.baseType']), 'rectilinear');
|
|
165
|
+
const shellClass = normalizeKey(firstText(parameters, ['shellClass', 'room.shellClass']), 'basic-structure');
|
|
166
|
+
const roomHeight = mmToMeters(firstNumber(parameters, ['heightMm'])) ??
|
|
167
|
+
firstNumber(parameters, ['roomHeight', 'ceilingHeight', 'room.shared.height', 'room.height', 'height']);
|
|
168
|
+
const rectLength = mmToMeters(firstNumber(parameters, ['rectLengthMm']));
|
|
169
|
+
const rectDepth = mmToMeters(firstNumber(parameters, ['rectDepthMm']));
|
|
170
|
+
const ellipseMajor = mmToMeters(firstNumber(parameters, ['ellipseMajorRadiusMm']));
|
|
171
|
+
const ellipseMinor = mmToMeters(firstNumber(parameters, ['ellipseMinorRadiusMm']));
|
|
172
|
+
const ribbonLength = mmToMeters(firstNumber(parameters, ['ribbonLengthMm']));
|
|
173
|
+
const ribbonWidth = mmToMeters(firstNumber(parameters, ['ribbonWidthMm']));
|
|
174
|
+
const rectSpans = Math.max(1, Math.round((firstNumber(parameters, ['rectNorthSegments'], 1) ?? 1) +
|
|
175
|
+
(firstNumber(parameters, ['rectEastSegments'], 1) ?? 1) +
|
|
176
|
+
(firstNumber(parameters, ['rectSouthSegments'], 1) ?? 1) +
|
|
177
|
+
(firstNumber(parameters, ['rectWestSegments'], 1) ?? 1)));
|
|
178
|
+
const ellipseSectors = Math.max(1, Math.round(firstNumber(parameters, ['ellipseSectorCount'], 1) ?? 1));
|
|
179
|
+
const ribbonSpans = Math.max(1, Math.round(firstNumber(parameters, ['ribbonSpanCount', 'ribbonStationCount'], 1) ?? 1));
|
|
180
|
+
const verticalBands = Math.max(1, Math.round(firstNumber(parameters, ['verticalBandCount'], 1) ?? 1));
|
|
181
|
+
const length = baseType === 'elliptical'
|
|
182
|
+
? ellipseMajor !== null
|
|
183
|
+
? ellipseMajor * 2
|
|
184
|
+
: null
|
|
185
|
+
: baseType === 'ribbon'
|
|
186
|
+
? ribbonLength
|
|
187
|
+
: rectLength;
|
|
188
|
+
const width = baseType === 'elliptical'
|
|
189
|
+
? ellipseMinor !== null
|
|
190
|
+
? ellipseMinor * 2
|
|
191
|
+
: null
|
|
192
|
+
: baseType === 'ribbon'
|
|
193
|
+
? ribbonWidth
|
|
194
|
+
: rectDepth;
|
|
195
|
+
const floorArea = firstNumber(parameters, ['metrics.floorArea', 'diagnostics.floorArea', 'room.floorArea', 'floorArea']) ??
|
|
196
|
+
(baseType === 'elliptical' && ellipseMajor !== null && ellipseMinor !== null
|
|
197
|
+
? Math.PI * ellipseMajor * ellipseMinor
|
|
198
|
+
: length !== null && width !== null
|
|
199
|
+
? length * width
|
|
200
|
+
: null);
|
|
201
|
+
const perimeter = firstNumber(parameters, ['metrics.perimeter', 'diagnostics.perimeter', 'room.perimeter', 'perimeter']) ??
|
|
202
|
+
(baseType === 'elliptical'
|
|
203
|
+
? ellipsePerimeterMeters(ellipseMajor, ellipseMinor)
|
|
204
|
+
: length !== null && width !== null
|
|
205
|
+
? 2 * (length + width)
|
|
206
|
+
: null);
|
|
207
|
+
const wallCount = baseType === 'rectilinear' ? 4 : 1;
|
|
208
|
+
const groundSpans = baseType === 'elliptical' ? ellipseSectors : baseType === 'ribbon' ? ribbonSpans : rectSpans;
|
|
209
|
+
const curvature = baseType === 'rectilinear' ? 0 : baseType === 'elliptical' ? 0.72 : 0.9;
|
|
210
|
+
const wallAperturesEnabled = firstBoolean(parameters, ['wallAperturesEnabled', 'enableWindows'], false) ?? false;
|
|
211
|
+
const roofAperturesEnabled = firstBoolean(parameters, ['roofAperturesEnabled', 'enableSkylights'], false) ?? false;
|
|
212
|
+
const wallCutsPerTarget = Math.max(0, Math.round(firstNumber(parameters, ['wallApertureCount', 'windowCount', 'apertures.windows.count'], 0) ?? 0));
|
|
213
|
+
const wallTargetSpec = firstText(parameters, ['wallApertureTargetSpec'], '') ?? '';
|
|
214
|
+
const wallTargetCount = wallAperturesEnabled
|
|
215
|
+
? estimateWallTargetCount(parameters, baseType, wallTargetSpec, groundSpans)
|
|
216
|
+
: 0;
|
|
217
|
+
const generatedWindowCount = arrayCount(resolvePathValue(parameters, 'windows'));
|
|
218
|
+
const windows = wallAperturesEnabled
|
|
219
|
+
? Math.max(0, Math.round(generatedWindowCount ?? wallTargetCount * Math.max(1, wallCutsPerTarget)))
|
|
220
|
+
: 0;
|
|
221
|
+
const roofPattern = normalizeKey(firstText(parameters, ['roofAperturePatternKind'], 'none'), 'none');
|
|
222
|
+
const roofCountParameter = Math.max(0, Math.round(firstNumber(parameters, ['roofApertureCount', 'skylightCount'], 0) ?? 0));
|
|
223
|
+
const generatedSkylightCount = arrayCount(resolvePathValue(parameters, 'skylights'));
|
|
224
|
+
const skylights = roofAperturesEnabled
|
|
225
|
+
? Math.max(0, Math.round(generatedSkylightCount ?? (['oculus', 'atrium'].includes(roofPattern) ? 1 : roofCountParameter)))
|
|
226
|
+
: 0;
|
|
227
|
+
const windowWidth = mmToMeters(firstNumber(parameters, ['wallApertureWidthMm'])) ??
|
|
228
|
+
firstNumber(parameters, ['windowWidth', 'apertures.windowWidth'], 1.2) ??
|
|
229
|
+
1.2;
|
|
230
|
+
const windowHeight = mmToMeters(firstNumber(parameters, ['wallApertureHeightMm'])) ??
|
|
231
|
+
firstNumber(parameters, ['windowHeight', 'apertures.windowHeight'], 1.5) ??
|
|
232
|
+
1.5;
|
|
233
|
+
const roofWidth = mmToMeters(firstNumber(parameters, ['roofOculusDiameterMm', 'roofApertureWidthMm'])) ??
|
|
234
|
+
firstNumber(parameters, ['skylightSize', 'apertures.skylightSize'], 1.8) ??
|
|
235
|
+
1.8;
|
|
236
|
+
const roofHeight = mmToMeters(firstNumber(parameters, ['roofOculusDiameterMm', 'roofApertureHeightMm'])) ??
|
|
237
|
+
firstNumber(parameters, ['skylightSize', 'apertures.skylightSize'], roofWidth) ??
|
|
238
|
+
roofWidth;
|
|
239
|
+
const wallOpeningArea = windows * windowWidth * windowHeight;
|
|
240
|
+
const roofOpeningArea = roofPattern === 'oculus'
|
|
241
|
+
? skylights * Math.PI * Math.pow(roofWidth / 2, 2)
|
|
242
|
+
: skylights * roofWidth * roofHeight;
|
|
243
|
+
const aperturesEnabled = wallAperturesEnabled || roofAperturesEnabled;
|
|
244
|
+
const openingArea = aperturesEnabled ? Math.max(0, wallOpeningArea + roofOpeningArea) : 0;
|
|
245
|
+
const apertureRatio = floorArea && floorArea > 0 ? openingArea / floorArea : null;
|
|
246
|
+
const roofFamily = normalizeKey(firstText(parameters, ['roofFamily'], 'flat'), 'flat');
|
|
247
|
+
const ceilingEnabled = firstBoolean(parameters, ['roofEnabled', 'enableCeiling', 'ceiling.enabled'], true) ?? true;
|
|
248
|
+
const typeId = CEILING_TYPE_IDS[roofFamily] ?? 0;
|
|
249
|
+
const coverage = firstNumber(parameters, ['roofOculusEllipseScale', 'ceilingCoverageFactor', 'ceiling.coverage'], 1) ?? 1;
|
|
250
|
+
const apexOffset = mmToMeters(firstNumber(parameters, ['rectRoofRiseMm', 'domeOuterRiseMm'])) ?? 0;
|
|
251
|
+
const sag = mmToMeters(firstNumber(parameters, ['domeInnerRiseMm'])) ?? 0;
|
|
252
|
+
const capMode = roofFamily === 'shed'
|
|
253
|
+
? `low ${formatYesNo(firstBoolean(parameters, ['shedRoofCapLowEnd'], false) ?? false)}, high ${formatYesNo(firstBoolean(parameters, ['shedRoofCapHighEnd'], true) ?? true)}`
|
|
254
|
+
: roofFamily === 'gable'
|
|
255
|
+
? `gable ends ${formatYesNo(firstBoolean(parameters, ['gableRoofCapEnds'], true) ?? true)}`
|
|
256
|
+
: 'n/a';
|
|
257
|
+
return {
|
|
258
|
+
room: {
|
|
259
|
+
baseType,
|
|
260
|
+
shellClass,
|
|
261
|
+
floorArea,
|
|
262
|
+
perimeter,
|
|
263
|
+
roomHeight,
|
|
264
|
+
wallCount,
|
|
265
|
+
length,
|
|
266
|
+
width,
|
|
267
|
+
curvature,
|
|
268
|
+
groundSpans,
|
|
269
|
+
verticalBands,
|
|
270
|
+
},
|
|
271
|
+
apertures: {
|
|
272
|
+
windows,
|
|
273
|
+
skylights,
|
|
274
|
+
enabled: aperturesEnabled,
|
|
275
|
+
openingArea,
|
|
276
|
+
apertureRatio,
|
|
277
|
+
wallTargetCount,
|
|
278
|
+
wallCutsPerTarget,
|
|
279
|
+
wallTargetSpec: wallTargetSpec || 'all generated wall cells',
|
|
280
|
+
roofPattern,
|
|
281
|
+
},
|
|
282
|
+
ceiling: {
|
|
283
|
+
roofFamily,
|
|
284
|
+
typeId,
|
|
285
|
+
coverage,
|
|
286
|
+
sag,
|
|
287
|
+
apexOffset,
|
|
288
|
+
capMode,
|
|
289
|
+
enabled: ceilingEnabled ?? false,
|
|
290
|
+
},
|
|
291
|
+
};
|
|
292
|
+
};
|
|
293
|
+
const buildScienceInputGroups = (inputs) => [
|
|
294
|
+
{
|
|
295
|
+
id: 'base',
|
|
296
|
+
label: 'base',
|
|
297
|
+
summary: 'Science Env base type, shell class, and footprint metrics from the active model parameters.',
|
|
298
|
+
rows: [
|
|
299
|
+
{ id: 'base-type', label: 'base type', value: inputs.room.baseType },
|
|
300
|
+
{ id: 'shell-class', label: 'shell class', value: inputs.room.shellClass },
|
|
301
|
+
{ id: 'floor-area', label: 'floor area', value: formatArea(inputs.room.floorArea) },
|
|
302
|
+
{ id: 'perimeter', label: 'perimeter', value: formatMeters(inputs.room.perimeter) },
|
|
303
|
+
{ id: 'base-length', label: 'base length / major', value: formatMeters(inputs.room.length) },
|
|
304
|
+
{ id: 'base-width', label: 'base width / minor', value: formatMeters(inputs.room.width) },
|
|
305
|
+
{ id: 'height', label: 'height', value: formatMeters(inputs.room.roomHeight) },
|
|
306
|
+
{ id: 'wall-groups', label: 'wall / run groups', value: formatCount(inputs.room.wallCount) },
|
|
307
|
+
{ id: 'ground-spans', label: 'ground spans / sectors', value: formatCount(inputs.room.groundSpans) },
|
|
308
|
+
{ id: 'vertical-bands', label: 'vertical bands', value: formatCount(inputs.room.verticalBands) },
|
|
309
|
+
],
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
id: 'apertures',
|
|
313
|
+
label: 'apertures',
|
|
314
|
+
summary: 'Estimated wall and roof aperture instances from the current route, target spec, and per-target count.',
|
|
315
|
+
rows: [
|
|
316
|
+
{ id: 'wall-openings', label: 'wall openings', value: formatCount(inputs.apertures.windows) },
|
|
317
|
+
{ id: 'wall-targets', label: 'wall target cells', value: formatCount(inputs.apertures.wallTargetCount) },
|
|
318
|
+
{ id: 'cuts-per-target', label: 'cuts per target', value: formatCount(inputs.apertures.wallCutsPerTarget) },
|
|
319
|
+
{ id: 'wall-target-spec', label: 'wall target spec', value: inputs.apertures.wallTargetSpec },
|
|
320
|
+
{ id: 'roof-openings', label: 'roof openings', value: formatCount(inputs.apertures.skylights) },
|
|
321
|
+
{ id: 'roof-pattern', label: 'roof pattern', value: inputs.apertures.roofPattern },
|
|
322
|
+
{ id: 'opening-area', label: 'opening area', value: formatArea(inputs.apertures.openingArea) },
|
|
323
|
+
{ id: 'aperture-ratio', label: 'aperture ratio', value: formatDecimal(inputs.apertures.apertureRatio, 3) },
|
|
324
|
+
{ id: 'apertures-enabled', label: 'apertures enabled', value: formatYesNo(inputs.apertures.enabled) },
|
|
325
|
+
],
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
id: 'roof',
|
|
329
|
+
label: 'roof',
|
|
330
|
+
summary: 'Roof family and principal roof parameters currently driving the generated shell.',
|
|
331
|
+
rows: [
|
|
332
|
+
{ id: 'roof-family', label: 'roof family', value: inputs.ceiling.roofFamily },
|
|
333
|
+
{ id: 'roof-enabled', label: 'roof enabled', value: formatYesNo(inputs.ceiling.enabled) },
|
|
334
|
+
{ id: 'roof-type-id', label: 'roof type id', value: formatCount(inputs.ceiling.enabled ? inputs.ceiling.typeId : 0) },
|
|
335
|
+
{ id: 'coverage', label: 'coverage / scale', value: formatDecimal(inputs.ceiling.coverage) },
|
|
336
|
+
{ id: 'inner-rise', label: 'inner rise', value: formatMeters(inputs.ceiling.sag) },
|
|
337
|
+
{ id: 'outer rise', label: 'outer / ridge rise', value: formatMeters(inputs.ceiling.apexOffset) },
|
|
338
|
+
{ id: 'cap-mode', label: 'cap mode', value: inputs.ceiling.capMode },
|
|
339
|
+
],
|
|
340
|
+
},
|
|
341
|
+
];
|
|
342
|
+
const scoreValue = (value) => `${clamp(value, 0, 10).toFixed(2)} demo`;
|
|
343
|
+
const metricRow = (id, value, detail) => ({
|
|
344
|
+
id,
|
|
345
|
+
label: SCIENCE_OUTPUT_LABELS.find((metric) => metric.id === id)?.label ?? id,
|
|
346
|
+
value: scoreValue(value),
|
|
347
|
+
detail,
|
|
348
|
+
});
|
|
349
|
+
const deriveScienceResearchOutputs = (inputs) => {
|
|
350
|
+
const area = inputs.room.floorArea ?? 0;
|
|
351
|
+
const height = inputs.room.roomHeight ?? 3.2;
|
|
352
|
+
const safeArea = area > 0 ? area : 64;
|
|
353
|
+
const roomLength = inputs.room.length ?? Math.sqrt(safeArea);
|
|
354
|
+
const roomWidth = inputs.room.width ?? Math.sqrt(safeArea);
|
|
355
|
+
const longerSpan = Math.max(roomLength, roomWidth, 0);
|
|
356
|
+
const shorterSpan = Math.max(Math.min(roomLength, roomWidth), 0.1);
|
|
357
|
+
const aspectRatio = longerSpan > 0 ? longerSpan / shorterSpan : 1;
|
|
358
|
+
const perimeter = inputs.room.perimeter ?? (roomLength && roomWidth ? 2 * (roomLength + roomWidth) : 32);
|
|
359
|
+
const volume = safeArea * Math.max(height, 0.1);
|
|
360
|
+
const wallRatio = clamp01(inputs.room.wallCount / 4);
|
|
361
|
+
const areaFit = bellRatio(safeArea, 72, 0.95);
|
|
362
|
+
const mediumScale = smoothRatio(safeArea, 24, 360);
|
|
363
|
+
const largeScale = smoothRatio(safeArea, 140, 980);
|
|
364
|
+
const overscale = smoothRatio(safeArea, 420, 1400);
|
|
365
|
+
const spanScale = smoothRatio(roomLength + roomWidth, 12, 72);
|
|
366
|
+
const volumeScale = smoothRatio(volume, 120, 3600);
|
|
367
|
+
const aspectBalance = bellRatio(aspectRatio, 1.35, 0.7);
|
|
368
|
+
const perimeterCue = smoothRatio(perimeter, 24, 140);
|
|
369
|
+
const compactness = clamp01((4 * Math.PI * safeArea) / Math.max(perimeter * perimeter, 0.0001));
|
|
370
|
+
const heightComfort = bellRatio(height, 3.6, 0.38);
|
|
371
|
+
const heightOpenness = smoothRatio(height, 2.4, 7.2);
|
|
372
|
+
const apertureRatio = clamp01((inputs.apertures.apertureRatio ?? 0) / 0.18);
|
|
373
|
+
const apertureCue = clamp01((inputs.apertures.windows + inputs.apertures.skylights * 2.2) / 9);
|
|
374
|
+
const daylight = clamp01(apertureRatio * 0.62 + apertureCue * 0.2 + heightOpenness * 0.1 + (inputs.apertures.enabled ? 0.08 : 0));
|
|
375
|
+
const glareRisk = clamp01((inputs.apertures.apertureRatio ?? 0) / 0.32 + inputs.apertures.skylights * 0.045);
|
|
376
|
+
const ceilingRelief = clamp01((inputs.ceiling.enabled && inputs.ceiling.typeId > 0 ? 0.35 : 0) +
|
|
377
|
+
Math.abs(inputs.ceiling.coverage - 1) * 0.45 +
|
|
378
|
+
Math.abs(inputs.ceiling.sag) * 0.18 +
|
|
379
|
+
Math.abs(inputs.ceiling.apexOffset) * 0.12);
|
|
380
|
+
const ceilingCalm = clamp01(1 - ceilingRelief * 0.52);
|
|
381
|
+
const curvatureCue = clamp01(inputs.room.curvature);
|
|
382
|
+
const enclosure = clamp01(wallRatio * 0.72 + compactness * 0.24 - daylight * 0.16 - largeScale * 0.1);
|
|
383
|
+
const legibility = clamp01(aspectBalance * 0.33 + compactness * 0.2 + perimeterCue * 0.16 + daylight * 0.13 + ceilingRelief * 0.08 + curvatureCue * 0.1);
|
|
384
|
+
const movement = clamp01(mediumScale * 0.28 + largeScale * 0.24 + spanScale * 0.2 + aspectBalance * 0.17 + heightComfort * 0.11);
|
|
385
|
+
const socialCapacity = clamp01(mediumScale * 0.32 + largeScale * 0.26 + spanScale * 0.18 + aspectBalance * 0.14 + daylight * 0.1);
|
|
386
|
+
const scaleComfort = clamp01(areaFit * 0.34 + heightComfort * 0.24 + aspectBalance * 0.2 + compactness * 0.12 + ceilingCalm * 0.1);
|
|
387
|
+
const acousticControl = clamp01(enclosure * 0.35 + compactness * 0.26 + heightComfort * 0.17 - daylight * 0.1 - volumeScale * 0.18 - ceilingRelief * 0.08 + 0.22);
|
|
388
|
+
return [
|
|
389
|
+
metricRow('attention', 4.5 + daylight * 2.1 + scaleComfort * 1.2 + heightComfort * 0.8 + ceilingCalm * 0.45 - overscale * 0.7, 'drivers: daylight, room scale, height, ceiling calm'),
|
|
390
|
+
metricRow('navigation', 4.35 + legibility * 2.15 + perimeterCue * 0.75 + aspectBalance * 0.65 + curvatureCue * 0.35 - overscale * 0.55, 'drivers: perimeter, proportion, enclosure, curvature cues'),
|
|
391
|
+
metricRow('visual-perception', 4.55 + daylight * 2.65 + heightOpenness * 0.7 + ceilingRelief * 0.55 - glareRisk * 0.45, 'drivers: aperture area, skylights, height, ceiling relief'),
|
|
392
|
+
metricRow('locomotion', 4.2 + movement * 2.7 + largeScale * 0.75 + aspectBalance * 0.45 - overscale * 0.28, 'drivers: floor area, spans, aspect ratio, clear height'),
|
|
393
|
+
metricRow('agency', 4.25 + socialCapacity * 1.1 + scaleComfort * 0.95 + daylight * 0.7 + ceilingRelief * 0.35 + (inputs.apertures.enabled ? 0.28 : 0), 'drivers: usable scale, openings, daylight, ceiling articulation'),
|
|
394
|
+
metricRow('object-perception', 4.65 + daylight * 1.9 + scaleComfort * 0.9 + aspectBalance * 0.55 + enclosure * 0.35 - overscale * 0.25, 'drivers: daylight, proportion, enclosure, overscale'),
|
|
395
|
+
metricRow('tactile-perception', 4.05 + enclosure * 1.55 + scaleComfort * 1.2 + compactness * 0.55 - largeScale * 0.6, 'drivers: enclosure, compactness, reachable scale'),
|
|
396
|
+
metricRow('affect', 4.35 + daylight * 1.35 + scaleComfort * 1.55 + ceilingCalm * 0.6 + heightComfort * 0.45 - overscale * 0.5, 'drivers: daylight, scale comfort, height fit, ceiling calm'),
|
|
397
|
+
metricRow('social-cognition', 4.15 + socialCapacity * 1.65 + daylight * 0.65 + aspectBalance * 0.55 + scaleComfort * 0.45 - overscale * 0.28, 'drivers: social capacity, daylight, proportion, scale'),
|
|
398
|
+
metricRow('social-interaction', 4.05 + socialCapacity * 2.2 + movement * 0.65 + spanScale * 0.45 - overscale * 0.55, 'drivers: capacity, circulation, span, overscale load'),
|
|
399
|
+
metricRow('goal-directed-behavior', 4.25 + legibility * 1.55 + movement * 0.85 + scaleComfort * 0.6 + ceilingCalm * 0.28 - overscale * 0.22, 'drivers: legibility, circulation, scale fit'),
|
|
400
|
+
metricRow('auditory-perception', 4.55 + acousticControl * 2.25 + enclosure * 0.45 - volumeScale * 0.35 - glareRisk * 0.22, 'drivers: volume, enclosure, aperture ratio, ceiling relief'),
|
|
401
|
+
];
|
|
402
|
+
};
|
|
403
|
+
const renderRows = (rows) => (_jsx(SectionList, { children: rows.map((row) => (_jsxs(MetricRow, { children: [_jsxs(MetricMeta, { children: [_jsx(MetricLabel, { children: row.label }), row.detail ? _jsx(MetricHelp, { children: row.detail }) : null] }), _jsx(MetricValue, { children: row.value })] }, row.id))) }));
|
|
404
|
+
export default function ScienceMetricsPanelContent({ modeId, modeLabel, moduleLabel, moduleSummary, promptPackLabel, promptPackDescription, parameters, renderState, }) {
|
|
405
|
+
const [activeTab, setActiveTab] = useState('inputs');
|
|
406
|
+
const scienceInputs = useMemo(() => resolveScienceInputs(parameters), [parameters]);
|
|
407
|
+
const inputGroups = useMemo(() => buildScienceInputGroups(scienceInputs), [scienceInputs]);
|
|
408
|
+
const outputRows = useMemo(() => deriveScienceResearchOutputs(scienceInputs), [scienceInputs]);
|
|
409
|
+
const contextRows = [
|
|
410
|
+
{ id: 'mode', label: 'mode', value: modeLabel },
|
|
411
|
+
{ id: 'module', label: 'module', value: moduleLabel?.trim() || 'none' },
|
|
412
|
+
{ id: 'geometry-status', label: 'geometry', value: renderState?.geometryStatus?.trim() || 'idle' },
|
|
413
|
+
];
|
|
414
|
+
return (_jsxs(Root, { children: [_jsxs(Section, { "data-panel-id": "science-metrics-overview", children: [_jsx(SectionHeader, { children: _jsxs(SectionHeaderCopy, { children: [_jsx("h3", { children: "science metrics" }), _jsxs("p", { children: ["model inputs ", '->', " cognition outputs"] }), _jsx("p", { children: "Inputs summarize the generated shell. Outputs are provisional cognition estimates and need a data-capture research module before they are treated as measured results." })] }) }), _jsx(TabRow, { children: ['inputs', 'outputs'].map((tab) => (_jsx(TabButton, { type: "button", "$active": activeTab === tab, onClick: () => setActiveTab(tab), children: tab }, tab))) })] }), activeTab === 'inputs' ? (_jsx(_Fragment, { children: inputGroups.map((group) => (_jsxs(Section, { "data-panel-id": `science-metrics-${group.id}`, children: [_jsx(SectionHeader, { children: _jsxs(SectionHeaderCopy, { children: [_jsx("h3", { children: group.label }), _jsx("p", { children: group.summary })] }) }), renderRows(group.rows)] }, group.id))) })) : (_jsx(_Fragment, { children: _jsxs(Section, { "data-panel-id": "science-metrics-cognition", children: [_jsx(SectionHeader, { children: _jsxs(SectionHeaderCopy, { children: [_jsx("h3", { children: "spatial cognition indicators" }), _jsx("p", { children: "Demo scores from the current shell inputs; replace with measured plugin/Data Graph outputs." })] }) }), renderRows(outputRows)] }) })), _jsxs(Section, { "data-panel-id": "science-metrics-context", children: [_jsx(SectionHeader, { children: _jsx(SectionHeaderCopy, { children: _jsx("h3", { children: "runtime context" }) }) }), _jsxs("div", { className: "panel-section-note", children: [contextRows.map((row) => `${row.label}: ${row.value}`).join(' · '), moduleSummary?.trim() ? ` · ${moduleSummary}` : '', promptPackLabel?.trim() ? ` · prompt kit: ${promptPackLabel}` : '', promptPackDescription?.trim() ? ` · ${promptPackDescription}` : ''] })] })] }));
|
|
415
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface SpatialHudProps {
|
|
2
|
+
unitsLabel: string;
|
|
3
|
+
sceneSpanLabel: string;
|
|
4
|
+
viewWidthLabel: string;
|
|
5
|
+
gridStepLabel: string;
|
|
6
|
+
scaleBarLabel: string;
|
|
7
|
+
distanceToFloorLabel: string;
|
|
8
|
+
distanceToCeilingLabel: string;
|
|
9
|
+
distanceToNorthLabel: string;
|
|
10
|
+
distanceToSouthLabel: string;
|
|
11
|
+
distanceToEastLabel: string;
|
|
12
|
+
distanceToWestLabel: string;
|
|
13
|
+
compassAngleLabel: string;
|
|
14
|
+
pitchAngleLabel: string;
|
|
15
|
+
veilsEnabled?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export default function SpatialHud({ unitsLabel, sceneSpanLabel, viewWidthLabel, gridStepLabel, scaleBarLabel, distanceToFloorLabel, distanceToCeilingLabel, distanceToNorthLabel, distanceToSouthLabel, distanceToEastLabel, distanceToWestLabel, compassAngleLabel, pitchAngleLabel, veilsEnabled, }: SpatialHudProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
//# sourceMappingURL=SpatialHud.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpatialHud.d.ts","sourceRoot":"","sources":["../src/SpatialHud.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AA6GD,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,EACjC,UAAU,EACV,cAAc,EACd,cAAc,EACd,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EACf,YAAmB,GACpB,EAAE,eAAe,2CAgDjB"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
const HudWrapper = styled.div `
|
|
5
|
+
position: absolute;
|
|
6
|
+
right: 24px;
|
|
7
|
+
bottom: 24px;
|
|
8
|
+
pointer-events: none;
|
|
9
|
+
z-index: 18;
|
|
10
|
+
`;
|
|
11
|
+
const HudCard = styled.div `
|
|
12
|
+
position: relative;
|
|
13
|
+
background: rgba(255, 255, 255, 0.96);
|
|
14
|
+
border: 1px solid var(--brand-panel-border);
|
|
15
|
+
border-radius: 12px;
|
|
16
|
+
padding: 12px 14px;
|
|
17
|
+
box-shadow: 0 12px 24px rgba(15, 23, 42, 0.1);
|
|
18
|
+
overflow: hidden;
|
|
19
|
+
|
|
20
|
+
&[data-veil='true'] .hud-grid {
|
|
21
|
+
opacity: 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&:hover .hud-grid {
|
|
25
|
+
opacity: 1;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&:hover .hud-veil {
|
|
29
|
+
opacity: 0;
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
32
|
+
const Grid = styled.div.attrs({ className: 'hud-grid' }) `
|
|
33
|
+
display: grid;
|
|
34
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
35
|
+
gap: 8px 12px;
|
|
36
|
+
transition: opacity 0.2s ease;
|
|
37
|
+
`;
|
|
38
|
+
const MetaGrid = styled.div `
|
|
39
|
+
display: grid;
|
|
40
|
+
grid-template-columns: repeat(3, minmax(58px, 1fr));
|
|
41
|
+
gap: 8px 12px;
|
|
42
|
+
margin-bottom: 10px;
|
|
43
|
+
padding-bottom: 10px;
|
|
44
|
+
border-bottom: 1px solid rgba(148, 163, 184, 0.18);
|
|
45
|
+
`;
|
|
46
|
+
const Label = styled.div `
|
|
47
|
+
font-size: 9px;
|
|
48
|
+
text-transform: uppercase;
|
|
49
|
+
letter-spacing: 0.08em;
|
|
50
|
+
color: var(--brand-text-secondary);
|
|
51
|
+
`;
|
|
52
|
+
const Value = styled.div `
|
|
53
|
+
font-size: 12px;
|
|
54
|
+
font-weight: 600;
|
|
55
|
+
color: var(--brand-text-primary);
|
|
56
|
+
`;
|
|
57
|
+
const HudVeil = styled.div `
|
|
58
|
+
position: absolute;
|
|
59
|
+
inset: 0;
|
|
60
|
+
display: flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
font-size: 10px;
|
|
64
|
+
text-transform: uppercase;
|
|
65
|
+
letter-spacing: 0.14em;
|
|
66
|
+
color: var(--brand-text-secondary);
|
|
67
|
+
opacity: ${(props) => (props.$visible ? 0.7 : 0)};
|
|
68
|
+
transition: opacity 0.2s ease;
|
|
69
|
+
`;
|
|
70
|
+
const ScaleLegend = styled.div `
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
gap: 10px;
|
|
74
|
+
margin-bottom: 10px;
|
|
75
|
+
padding-bottom: 10px;
|
|
76
|
+
border-bottom: 1px solid rgba(148, 163, 184, 0.18);
|
|
77
|
+
`;
|
|
78
|
+
const ScaleBar = styled.div `
|
|
79
|
+
position: relative;
|
|
80
|
+
width: 84px;
|
|
81
|
+
height: 10px;
|
|
82
|
+
flex: 0 0 auto;
|
|
83
|
+
|
|
84
|
+
&::before {
|
|
85
|
+
content: '';
|
|
86
|
+
position: absolute;
|
|
87
|
+
left: 0;
|
|
88
|
+
right: 0;
|
|
89
|
+
top: 4px;
|
|
90
|
+
border-top: 2px solid rgba(15, 23, 42, 0.9);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
&::after {
|
|
94
|
+
content: '';
|
|
95
|
+
position: absolute;
|
|
96
|
+
inset: 0;
|
|
97
|
+
background:
|
|
98
|
+
linear-gradient(rgba(15, 23, 42, 0.9), rgba(15, 23, 42, 0.9)) left center / 2px 10px no-repeat,
|
|
99
|
+
linear-gradient(rgba(15, 23, 42, 0.9), rgba(15, 23, 42, 0.9)) right center / 2px 10px no-repeat;
|
|
100
|
+
}
|
|
101
|
+
`;
|
|
102
|
+
export default function SpatialHud({ unitsLabel, sceneSpanLabel, viewWidthLabel, gridStepLabel, scaleBarLabel, distanceToFloorLabel, distanceToCeilingLabel, distanceToNorthLabel, distanceToSouthLabel, distanceToEastLabel, distanceToWestLabel, compassAngleLabel, pitchAngleLabel, veilsEnabled = true, }) {
|
|
103
|
+
const scaleMetrics = [
|
|
104
|
+
{ label: 'units', value: unitsLabel },
|
|
105
|
+
{ label: 'scene', value: sceneSpanLabel },
|
|
106
|
+
{ label: 'view', value: viewWidthLabel },
|
|
107
|
+
{ label: 'grid', value: gridStepLabel },
|
|
108
|
+
{ label: 'compass', value: compassAngleLabel },
|
|
109
|
+
{ label: 'pitch', value: pitchAngleLabel },
|
|
110
|
+
];
|
|
111
|
+
const metrics = [
|
|
112
|
+
{ label: 'floor', value: distanceToFloorLabel },
|
|
113
|
+
{ label: 'ceiling', value: distanceToCeilingLabel },
|
|
114
|
+
{ label: 'north', value: distanceToNorthLabel },
|
|
115
|
+
{ label: 'south', value: distanceToSouthLabel },
|
|
116
|
+
{ label: 'east', value: distanceToEastLabel },
|
|
117
|
+
{ label: 'west', value: distanceToWestLabel },
|
|
118
|
+
];
|
|
119
|
+
return (_jsx(HudWrapper, { children: _jsxs(HudCard, { "data-veil": veilsEnabled, children: [_jsx(MetaGrid, { children: scaleMetrics.map((metric) => (_jsxs("div", { children: [_jsx(Label, { children: metric.label }), _jsx(Value, { children: metric.value })] }, metric.label))) }), _jsxs(ScaleLegend, { children: [_jsx(ScaleBar, {}), _jsxs("div", { children: [_jsx(Label, { children: "scale" }), _jsx(Value, { children: scaleBarLabel })] })] }), _jsx(Grid, { children: metrics.map((metric) => (_jsxs("div", { children: [_jsx(Label, { children: metric.label }), _jsx(Value, { children: metric.value })] }, metric.label))) }), _jsx(HudVeil, { className: "hud-veil", "$visible": veilsEnabled, children: "distances" })] }) }));
|
|
120
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type StreetviewSearchSuggestion = {
|
|
2
|
+
id: string;
|
|
3
|
+
label: string;
|
|
4
|
+
secondaryLabel?: string;
|
|
5
|
+
center: [number, number];
|
|
6
|
+
};
|
|
7
|
+
export type StreetviewModeSurfaceProps = {
|
|
8
|
+
panoramaUrl?: string | null;
|
|
9
|
+
imageAlt?: string;
|
|
10
|
+
imageCaption?: string | null;
|
|
11
|
+
locationQuery?: string;
|
|
12
|
+
captureReady?: boolean;
|
|
13
|
+
captureLabel?: string;
|
|
14
|
+
searchPlaceholder?: string;
|
|
15
|
+
providerLabel?: string;
|
|
16
|
+
statusLabel?: string;
|
|
17
|
+
loading?: boolean;
|
|
18
|
+
error?: string | null;
|
|
19
|
+
geocoderOptions?: Array<{
|
|
20
|
+
id: string;
|
|
21
|
+
label: string;
|
|
22
|
+
}>;
|
|
23
|
+
activeGeocoderId?: string;
|
|
24
|
+
searchSuggestions?: StreetviewSearchSuggestion[];
|
|
25
|
+
suggestionsOpen?: boolean;
|
|
26
|
+
suggestionsPending?: boolean;
|
|
27
|
+
activeSuggestionIndex?: number;
|
|
28
|
+
onLocationQueryChange?: (query: string) => void;
|
|
29
|
+
onLocationSearch?: (query: string) => void | Promise<void>;
|
|
30
|
+
onGeocoderChange?: (providerId: string) => void;
|
|
31
|
+
onSuggestionSelect?: (suggestion: StreetviewSearchSuggestion) => void;
|
|
32
|
+
onSuggestionActiveIndexChange?: (index: number) => void;
|
|
33
|
+
onSuggestionsOpenChange?: (open: boolean) => void;
|
|
34
|
+
};
|
|
35
|
+
export type StreetviewModeSurfaceHandle = {
|
|
36
|
+
capturePngDataUrl: () => Promise<string>;
|
|
37
|
+
};
|
|
38
|
+
declare const StreetviewModeSurface: import("react").ForwardRefExoticComponent<StreetviewModeSurfaceProps & import("react").RefAttributes<StreetviewModeSurfaceHandle>>;
|
|
39
|
+
export default StreetviewModeSurface;
|
|
40
|
+
//# sourceMappingURL=StreetviewModeSurface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StreetviewModeSurface.d.ts","sourceRoot":"","sources":["../src/StreetviewModeSurface.tsx"],"names":[],"mappings":"AAWA,MAAM,MAAM,0BAA0B,GAAG;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,eAAe,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,0BAA0B,EAAE,CAAC;IACjD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,qBAAqB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,kBAAkB,CAAC,EAAE,CAAC,UAAU,EAAE,0BAA0B,KAAK,IAAI,CAAC;IACtE,6BAA6B,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD,uBAAuB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1C,CAAC;AAiVF,QAAA,MAAM,qBAAqB,oIA6JzB,CAAC;AAEH,eAAe,qBAAqB,CAAC"}
|