@ifc-lite/viewer 1.21.0 → 1.22.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.
- package/.turbo/turbo-build.log +57 -50
- package/.turbo/turbo-typecheck.log +1 -1
- package/CHANGELOG.md +10 -0
- package/dist/assets/arrow-fie-E7fe.js +20 -0
- package/dist/assets/ascii-points-source-bTjLVmUX.js +1 -0
- package/dist/assets/{basketViewActivator-Bzw51jhm.js → basketViewActivator-EHAhHlwN.js} +12 -13
- package/dist/assets/bcf-Bhx-K17f.js +281 -0
- package/dist/assets/{browser-C5TFR7sH.js → browser-CVf8ATeW.js} +6 -6
- package/dist/assets/cesium-B4ZIU9jS.js +17742 -0
- package/dist/assets/decode-worker-CYqSjk1n.js +172 -0
- package/dist/assets/e57-source-CQHxE8n3.js +1 -0
- package/dist/assets/emscripten-module.browser-DcFZLAUx.js +1 -0
- package/dist/assets/exporters-KTio0Tdm.js +5723 -0
- package/dist/assets/geometry-controller.worker-Cm2P_EJr.js +7 -0
- package/dist/assets/geometry.worker-DchLBqZ8.js +1 -0
- package/dist/assets/{ids-B7AXEv7h.js → ids-CS7VCFin.js} +5 -5
- package/dist/assets/ifc-lite-C6wEhXa6.js +7 -0
- package/dist/assets/{ifc-lite_bg-DlKs5-yM.wasm → ifc-lite_bg-CSeT3fNI.wasm} +0 -0
- package/dist/assets/{ifc-lite_bg-PqmRe3Ph.wasm → ifc-lite_bg-ns4cSnX2.wasm} +0 -0
- package/dist/assets/{index-DVNSvEMh.js → index-8k9h-ANq.js} +60997 -59926
- package/dist/assets/index-BZC2YaOP.css +1 -0
- package/dist/assets/index-HqAIQkr6.js +22 -0
- package/dist/assets/inline-worker-BpBzlmd6.js +1 -0
- package/dist/assets/las-BW6LIc_j.js +1 -0
- package/dist/assets/las-source-C_IGrgRq.js +1 -0
- package/dist/assets/laz-source-jj3xI5Y4.js +125 -0
- package/dist/assets/maplibre-gl-C4LXKM6c.js +808 -0
- package/dist/assets/{native-bridge-BiD01jI9.js → native-bridge-DNrEhx2R.js} +5 -8
- package/dist/assets/{parser.worker-Bnbrl6gy.js → parser.worker-BcjkIo89.js} +2 -2
- package/dist/assets/pcd-source-Ck0UnVDn.js +3 -0
- package/dist/assets/ply-source-C8jjyzxE.js +4 -0
- package/dist/assets/{exporters-u0sz2Upj.js → sandbox-BSn5MyEJ.js} +11745 -7412
- package/dist/assets/{server-client-DP8fMPY9.js → server-client-D-kU2XAF.js} +4 -4
- package/dist/assets/{three-CDRZThFA.js → three-DwNDHx9-.js} +163 -171
- package/dist/assets/wasm-bridge-Cha08LdC.js +1 -0
- package/dist/assets/{workerHelpers-CBbWSJmd.js → workerHelpers-pUUnk9Wc.js} +1 -1
- package/dist/assets/zip-BJqVbRkU.js +2 -0
- package/dist/index.html +10 -12
- package/package.json +11 -11
- package/src/components/mcp/PlaygroundChat.tsx +90 -52
- package/src/components/viewer/CesiumOverlay.tsx +150 -91
- package/src/components/viewer/CesiumPlacementEditor.tsx +1009 -0
- package/src/components/viewer/ChatPanel.tsx +76 -93
- package/src/components/viewer/EntityContextMenu.tsx +68 -10
- package/src/components/viewer/MainToolbar.tsx +33 -3
- package/src/components/viewer/ViewportContainer.tsx +70 -16
- package/src/components/viewer/ViewportOverlays.tsx +2 -98
- package/src/components/viewer/chat/ByokKeyModal.tsx +338 -0
- package/src/components/viewer/chat/ByokStreamingPill.tsx +62 -0
- package/src/components/viewer/chat/ByokTrustDiagram.tsx +192 -0
- package/src/components/viewer/properties/GeoreferencingPanel.tsx +49 -52
- package/src/components/viewer/properties/ModelMetadataPanel.tsx +55 -44
- package/src/components/viewer/selectionHandlers.ts +7 -1
- package/src/lib/geo/cesium-bridge.ts +86 -50
- package/src/lib/geo/cesium-placement.test.ts +244 -0
- package/src/lib/geo/cesium-placement.ts +231 -0
- package/src/lib/geo/effective-georef.test.ts +74 -1
- package/src/lib/geo/effective-georef.ts +40 -93
- package/src/lib/geo/geo-scale.ts +104 -0
- package/src/lib/geo/reproject.test.ts +130 -0
- package/src/lib/geo/reproject.ts +37 -12
- package/src/lib/geo/terrain-elevation.ts +198 -89
- package/src/lib/lens/adapter.ts +52 -6
- package/src/lib/llm/clipboard-detect.test.ts +150 -0
- package/src/lib/llm/clipboard-detect.ts +90 -0
- package/src/lib/llm/models.ts +28 -0
- package/src/lib/llm/stream-direct.ts +16 -4
- package/src/lib/llm/types.ts +8 -0
- package/src/services/playground-model.ts +55 -0
- package/src/store/index.ts +4 -5
- package/src/store/slices/cesiumSlice.ts +100 -19
- package/src/store.ts +3 -0
- package/dist/assets/arrow-CZ5kQ26f.js +0 -20
- package/dist/assets/bcf-4K724hw0.js +0 -281
- package/dist/assets/cesium-DUOzBlqv.js +0 -17817
- package/dist/assets/decode-worker-t2EGKAxO.js +0 -1708
- package/dist/assets/emscripten-module.browser-CY5t0Vfq.js +0 -1
- package/dist/assets/geometry-controller.worker-NH8pZmrU.js +0 -7
- package/dist/assets/geometry.worker-Bp4rW_R1.js +0 -1
- package/dist/assets/ifc-lite-DfZHk36-.js +0 -7
- package/dist/assets/index-CSWgTe1s.css +0 -1
- package/dist/assets/index-XwKzDuw6.js +0 -22
- package/dist/assets/maplibre-gl-CGLcoNXc.js +0 -811
- package/dist/assets/sandbox-DPD1ROr0.js +0 -9700
- package/dist/assets/wasm-bridge-CErti6zX.js +0 -1
- package/dist/assets/zip-DBEtpeu6.js +0 -12
- package/src/components/viewer/CesiumSettingsDialog.tsx +0 -100
package/src/lib/llm/models.ts
CHANGED
|
@@ -139,6 +139,20 @@ export const FREE_MODELS: LLMModel[] = rawFreeModels.map(applyCapabilities);
|
|
|
139
139
|
// Requests go directly from the browser to the provider (no server proxy).
|
|
140
140
|
|
|
141
141
|
const ANTHROPIC_BYOK_MODELS: LLMModel[] = [
|
|
142
|
+
{
|
|
143
|
+
id: 'claude-opus-4-7',
|
|
144
|
+
name: 'Claude Opus 4.7',
|
|
145
|
+
provider: 'Anthropic',
|
|
146
|
+
tier: 'byok',
|
|
147
|
+
source: 'anthropic',
|
|
148
|
+
contextWindow: 1_000_000,
|
|
149
|
+
supportsImages: true,
|
|
150
|
+
supportsFileAttachments: true,
|
|
151
|
+
cost: '$$$',
|
|
152
|
+
// Opus 4.7 returns 400 if temperature/top_p/top_k are present.
|
|
153
|
+
// See `whats-new-claude-4-7` docs § Sampling parameters removed.
|
|
154
|
+
acceptsSamplingParams: false,
|
|
155
|
+
},
|
|
142
156
|
{
|
|
143
157
|
id: 'claude-opus-4-6',
|
|
144
158
|
name: 'Claude Opus 4.6',
|
|
@@ -175,6 +189,20 @@ const ANTHROPIC_BYOK_MODELS: LLMModel[] = [
|
|
|
175
189
|
];
|
|
176
190
|
|
|
177
191
|
const OPENAI_BYOK_MODELS: LLMModel[] = [
|
|
192
|
+
{
|
|
193
|
+
id: 'gpt-5.5',
|
|
194
|
+
name: 'GPT-5.5',
|
|
195
|
+
provider: 'OpenAI',
|
|
196
|
+
tier: 'byok',
|
|
197
|
+
source: 'openai',
|
|
198
|
+
contextWindow: 1_000_000,
|
|
199
|
+
supportsImages: true,
|
|
200
|
+
supportsFileAttachments: true,
|
|
201
|
+
cost: '$$$',
|
|
202
|
+
// GPT-5 reasoning family only accepts the default temperature (1).
|
|
203
|
+
// Sending any other value returns 400 from /v1/chat/completions.
|
|
204
|
+
acceptsSamplingParams: false,
|
|
205
|
+
},
|
|
178
206
|
{
|
|
179
207
|
id: 'gpt-5.4',
|
|
180
208
|
name: 'GPT-5.4',
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import Anthropic from '@anthropic-ai/sdk';
|
|
17
17
|
import { readSseStream, type StreamMessage, type StreamOptions } from './stream-client.js';
|
|
18
|
+
import { getModelById } from './models.js';
|
|
18
19
|
|
|
19
20
|
const STREAM_REQUEST_TIMEOUT_MS = 45_000;
|
|
20
21
|
|
|
@@ -71,12 +72,19 @@ export async function streamAnthropicChat(
|
|
|
71
72
|
dangerouslyAllowBrowser: true,
|
|
72
73
|
});
|
|
73
74
|
|
|
75
|
+
// Opus 4.7+ returns 400 if `temperature` (or `top_p`/`top_k`) is present.
|
|
76
|
+
// We gate on the per-model `acceptsSamplingParams` flag in models.ts so
|
|
77
|
+
// future Claude models that adopt the same policy only need a flag bump,
|
|
78
|
+
// and Opus 4.6 / Sonnet 4.6 / Haiku 4.5 keep their tuned temperature.
|
|
79
|
+
const modelDef = getModelById(model);
|
|
80
|
+
const sendSamplingParams = modelDef?.acceptsSamplingParams !== false;
|
|
81
|
+
|
|
74
82
|
let fullText = '';
|
|
75
83
|
try {
|
|
76
84
|
const stream = client.messages.stream({
|
|
77
85
|
model,
|
|
78
86
|
max_tokens: 8192,
|
|
79
|
-
temperature: 0.3,
|
|
87
|
+
...(sendSamplingParams ? { temperature: 0.3 } : {}),
|
|
80
88
|
system: system || undefined,
|
|
81
89
|
messages: toAnthropicMessages(messages),
|
|
82
90
|
});
|
|
@@ -118,8 +126,6 @@ export async function streamAnthropicChat(
|
|
|
118
126
|
|
|
119
127
|
// ── OpenAI ─────────────────────────────────────────────────────────────────
|
|
120
128
|
|
|
121
|
-
import { getModelById } from './models.js';
|
|
122
|
-
|
|
123
129
|
/**
|
|
124
130
|
* Stream an OpenAI model. Automatically picks the right API:
|
|
125
131
|
* - Chat Completions (`/v1/chat/completions`) for standard chat models
|
|
@@ -147,13 +153,19 @@ async function streamOpenAiChatCompletions(
|
|
|
147
153
|
? [{ role: 'system', content: system }, ...messages]
|
|
148
154
|
: [...messages];
|
|
149
155
|
|
|
156
|
+
// GPT-5 reasoning models (gpt-5.5, gpt-5.5-pro) only accept the default
|
|
157
|
+
// temperature; sending any other value returns 400. Mirror the Anthropic
|
|
158
|
+
// path: when the model is flagged `acceptsSamplingParams: false`, omit.
|
|
159
|
+
const modelDef = getModelById(model);
|
|
160
|
+
const sendSamplingParams = modelDef?.acceptsSamplingParams !== false;
|
|
161
|
+
|
|
150
162
|
const { response, cleanup } = await openAiFetch(
|
|
151
163
|
'https://api.openai.com/v1/chat/completions',
|
|
152
164
|
{
|
|
153
165
|
model,
|
|
154
166
|
messages: allMessages.map((m) => ({ role: m.role, content: m.content })),
|
|
155
167
|
stream: true,
|
|
156
|
-
temperature: 0.3,
|
|
168
|
+
...(sendSamplingParams ? { temperature: 0.3 } : {}),
|
|
157
169
|
max_completion_tokens: 8192,
|
|
158
170
|
},
|
|
159
171
|
apiKey,
|
package/src/lib/llm/types.ts
CHANGED
|
@@ -163,6 +163,14 @@ export interface LLMModel {
|
|
|
163
163
|
cost?: ModelCost;
|
|
164
164
|
/** OpenAI API variant: 'chat' (default) or 'responses' (Codex-style models) */
|
|
165
165
|
openaiApi?: 'chat' | 'responses';
|
|
166
|
+
/**
|
|
167
|
+
* Whether the model accepts the classic sampling parameters (`temperature`,
|
|
168
|
+
* `top_p`, `top_k`). Default: true. Set to `false` for models that reject
|
|
169
|
+
* them (Anthropic Claude Opus 4.7 and later — see
|
|
170
|
+
* https://platform.claude.com/docs/en/about-claude/models/whats-new-claude-4-7).
|
|
171
|
+
* When `false`, the stream client omits these params from the request body.
|
|
172
|
+
*/
|
|
173
|
+
acceptsSamplingParams?: boolean;
|
|
166
174
|
}
|
|
167
175
|
|
|
168
176
|
export type ChatStatus = 'idle' | 'sending' | 'streaming' | 'error';
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Per-tab persistence for the model the /mcp/playground chat uses.
|
|
7
|
+
*
|
|
8
|
+
* The playground driver calls Anthropic's `messages.create` with the native
|
|
9
|
+
* tools API, so the selected model must be Anthropic. We keep a separate
|
|
10
|
+
* preference from the viewer's `chatActiveModel` so the two pages can default
|
|
11
|
+
* differently — the viewer often runs on a free proxy model, while playground
|
|
12
|
+
* users have already opted into BYOK by being here.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { getByokModelsForSource } from '@/lib/llm/models';
|
|
16
|
+
|
|
17
|
+
const STORAGE_KEY = 'ifc-lite:playground-model:v1';
|
|
18
|
+
const CHANGED_EVENT = 'ifc-lite:playground-model-changed';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Default fallback when nothing is in storage. Sonnet hits the sweet spot for
|
|
22
|
+
* tool-calling agentic loops — fast enough for 25 sequential tool calls,
|
|
23
|
+
* smart enough to pick the right tool. Opus 4.7 is better at planning but
|
|
24
|
+
* costs ~3x; Haiku is faster but more likely to mis-pick tools.
|
|
25
|
+
*/
|
|
26
|
+
const FALLBACK_MODEL = 'claude-sonnet-4-6';
|
|
27
|
+
|
|
28
|
+
function isValidAnthropicModel(id: string): boolean {
|
|
29
|
+
return getByokModelsForSource('anthropic').some((m) => m.id === id);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getPlaygroundModel(): string {
|
|
33
|
+
try {
|
|
34
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
35
|
+
if (stored && isValidAnthropicModel(stored)) return stored;
|
|
36
|
+
} catch {
|
|
37
|
+
/* localStorage blocked / quota-exceeded — fall through to default */
|
|
38
|
+
}
|
|
39
|
+
return FALLBACK_MODEL;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function setPlaygroundModel(modelId: string): void {
|
|
43
|
+
if (!isValidAnthropicModel(modelId)) return;
|
|
44
|
+
try {
|
|
45
|
+
localStorage.setItem(STORAGE_KEY, modelId);
|
|
46
|
+
} catch {
|
|
47
|
+
/* storage write failed — selection only lives for this tab session */
|
|
48
|
+
}
|
|
49
|
+
window.dispatchEvent(new Event(CHANGED_EVENT));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function subscribePlaygroundModel(listener: () => void): () => void {
|
|
53
|
+
window.addEventListener(CHANGED_EVENT, listener);
|
|
54
|
+
return () => window.removeEventListener(CHANGED_EVENT, listener);
|
|
55
|
+
}
|
package/src/store/index.ts
CHANGED
|
@@ -90,7 +90,7 @@ export type { ChatSlice } from './slices/chatSlice.js';
|
|
|
90
90
|
export type { DesktopEntitlementSlice } from './slices/desktopEntitlementSlice.js';
|
|
91
91
|
|
|
92
92
|
// Re-export Cesium types
|
|
93
|
-
export type { CesiumSlice, CesiumDataSource } from './slices/cesiumSlice.js';
|
|
93
|
+
export type { CesiumSlice, CesiumDataSource, CesiumPlacementDraft } from './slices/cesiumSlice.js';
|
|
94
94
|
|
|
95
95
|
// Re-export Schedule (4D) types + selectors
|
|
96
96
|
export type { ScheduleSlice, ScheduleTimeRange, GanttTimeScale } from './slices/scheduleSlice.js';
|
|
@@ -271,13 +271,12 @@ const createViewerStore = () => create<ViewerState>()((...args) => ({
|
|
|
271
271
|
cesiumAvailable: false,
|
|
272
272
|
cesiumEnabled: false,
|
|
273
273
|
cesiumTerrainHeight: null,
|
|
274
|
-
// Default the clamp toggle ON so models authored at sea-level
|
|
275
|
-
// OrthogonalHeight don't load buried below the 3D-tiles terrain on
|
|
276
|
-
// first activation. Users can still uncheck it manually.
|
|
277
|
-
cesiumTerrainClamp: true,
|
|
278
274
|
cesiumSourceModelId: null,
|
|
279
275
|
cesiumTerrainClipY: null,
|
|
280
276
|
cesiumGlbLoaded: false,
|
|
277
|
+
cesiumPlacementEditMode: false,
|
|
278
|
+
cesiumPlacementDraftModelId: null,
|
|
279
|
+
cesiumPlacementDraft: null,
|
|
281
280
|
|
|
282
281
|
// Drawing 2D
|
|
283
282
|
drawing2D: null,
|
|
@@ -15,11 +15,19 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import type { StateCreator } from 'zustand';
|
|
18
|
+
import type { MapConversion } from '@ifc-lite/parser';
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
import { clearTerrainElevationCache } from '@/lib/geo/terrain-elevation';
|
|
21
|
+
|
|
22
|
+
export type CesiumDataSource = 'google-photorealistic';
|
|
23
|
+
|
|
24
|
+
export interface CesiumPlacementDraft {
|
|
25
|
+
eastings: number;
|
|
26
|
+
northings: number;
|
|
27
|
+
orthogonalHeight: number;
|
|
28
|
+
xAxisAbscissa: number;
|
|
29
|
+
xAxisOrdinate: number;
|
|
30
|
+
}
|
|
23
31
|
|
|
24
32
|
export interface CesiumSlice {
|
|
25
33
|
// State
|
|
@@ -31,16 +39,22 @@ export interface CesiumSlice {
|
|
|
31
39
|
cesiumIonToken: string;
|
|
32
40
|
/** Terrain enabled (Cesium World Terrain). */
|
|
33
41
|
cesiumTerrainEnabled: boolean;
|
|
34
|
-
/** Clamp model to terrain height at its geodetic position. */
|
|
35
|
-
cesiumTerrainClamp: boolean;
|
|
36
42
|
/** Terrain height at model position (queried from Cesium, meters). null = not yet queried. */
|
|
37
43
|
cesiumTerrainHeight: number | null;
|
|
44
|
+
/** Human-readable source label for the sampled terrain height. */
|
|
45
|
+
cesiumTerrainSource: string | null;
|
|
38
46
|
/** Model ID that the Cesium overlay is currently displaying. */
|
|
39
47
|
cesiumSourceModelId: string | null;
|
|
40
48
|
/** Terrain clip Y position in viewer space. When set, fragments below this Y are discarded. */
|
|
41
49
|
cesiumTerrainClipY: number | null;
|
|
42
50
|
/** Whether the GLB model has been loaded into Cesium (hides WebGPU overlay). */
|
|
43
51
|
cesiumGlbLoaded: boolean;
|
|
52
|
+
/** Whether the direct placement editor is active. */
|
|
53
|
+
cesiumPlacementEditMode: boolean;
|
|
54
|
+
/** Source model currently associated with the placement draft. */
|
|
55
|
+
cesiumPlacementDraftModelId: string | null;
|
|
56
|
+
/** Preview placement values shown in Cesium before applying to IFC georeference. */
|
|
57
|
+
cesiumPlacementDraft: CesiumPlacementDraft | null;
|
|
44
58
|
|
|
45
59
|
// Actions
|
|
46
60
|
setCesiumAvailable: (available: boolean) => void;
|
|
@@ -49,11 +63,19 @@ export interface CesiumSlice {
|
|
|
49
63
|
setCesiumDataSource: (source: CesiumDataSource) => void;
|
|
50
64
|
setCesiumIonToken: (token: string) => void;
|
|
51
65
|
setCesiumTerrainEnabled: (enabled: boolean) => void;
|
|
52
|
-
setCesiumTerrainClamp: (clamp: boolean) => void;
|
|
53
66
|
setCesiumTerrainHeight: (height: number | null) => void;
|
|
67
|
+
setCesiumTerrainSource: (source: string | null) => void;
|
|
54
68
|
setCesiumSourceModelId: (modelId: string | null) => void;
|
|
55
69
|
setCesiumTerrainClipY: (y: number | null) => void;
|
|
56
70
|
setCesiumGlbLoaded: (loaded: boolean) => void;
|
|
71
|
+
setCesiumPlacementEditMode: (enabled: boolean) => void;
|
|
72
|
+
toggleCesiumPlacementEditMode: () => void;
|
|
73
|
+
beginCesiumPlacementDraft: (
|
|
74
|
+
modelId: string,
|
|
75
|
+
conversion: Pick<MapConversion, 'eastings' | 'northings' | 'orthogonalHeight' | 'xAxisAbscissa' | 'xAxisOrdinate'>,
|
|
76
|
+
) => void;
|
|
77
|
+
updateCesiumPlacementDraft: (values: Partial<CesiumPlacementDraft>) => void;
|
|
78
|
+
resetCesiumPlacementDraft: () => void;
|
|
57
79
|
}
|
|
58
80
|
|
|
59
81
|
const STORAGE_KEY_ION_TOKEN = 'ifc-lite:cesium-ion-token';
|
|
@@ -80,13 +102,9 @@ function saveToStorage(key: string, value: string): void {
|
|
|
80
102
|
} catch { /* storage unavailable */ }
|
|
81
103
|
}
|
|
82
104
|
|
|
83
|
-
const VALID_DATA_SOURCES = new Set<CesiumDataSource>(['osm-buildings', 'bing-aerial', 'google-photorealistic']);
|
|
84
|
-
|
|
85
105
|
function loadDataSource(): CesiumDataSource {
|
|
86
|
-
|
|
87
|
-
return
|
|
88
|
-
? (stored as CesiumDataSource)
|
|
89
|
-
: 'google-photorealistic';
|
|
106
|
+
// Only Google Photorealistic is supported; upgrade any stale stored value.
|
|
107
|
+
return 'google-photorealistic';
|
|
90
108
|
}
|
|
91
109
|
|
|
92
110
|
/** Resolve the Cesium ion token: user override > build-time default */
|
|
@@ -101,27 +119,90 @@ export const createCesiumSlice: StateCreator<CesiumSlice, [], [], CesiumSlice> =
|
|
|
101
119
|
cesiumDataSource: loadDataSource(),
|
|
102
120
|
cesiumIonToken: resolveIonToken(),
|
|
103
121
|
cesiumTerrainEnabled: true,
|
|
104
|
-
cesiumTerrainClamp: true,
|
|
105
122
|
cesiumTerrainHeight: null,
|
|
123
|
+
cesiumTerrainSource: null,
|
|
106
124
|
cesiumSourceModelId: null,
|
|
107
125
|
cesiumTerrainClipY: null,
|
|
108
126
|
cesiumGlbLoaded: false,
|
|
127
|
+
cesiumPlacementEditMode: false,
|
|
128
|
+
cesiumPlacementDraftModelId: null,
|
|
129
|
+
cesiumPlacementDraft: null,
|
|
109
130
|
|
|
110
131
|
setCesiumAvailable: (available) => set({ cesiumAvailable: available }),
|
|
111
132
|
setCesiumEnabled: (enabled) => set({ cesiumEnabled: enabled }),
|
|
112
|
-
toggleCesium: () => set((s) => ({
|
|
133
|
+
toggleCesium: () => set((s) => ({
|
|
134
|
+
cesiumEnabled: !s.cesiumEnabled,
|
|
135
|
+
...(s.cesiumEnabled
|
|
136
|
+
? {
|
|
137
|
+
cesiumPlacementEditMode: false,
|
|
138
|
+
cesiumPlacementDraftModelId: null,
|
|
139
|
+
cesiumPlacementDraft: null,
|
|
140
|
+
}
|
|
141
|
+
: {}),
|
|
142
|
+
})),
|
|
113
143
|
setCesiumDataSource: (source) => {
|
|
144
|
+
clearTerrainElevationCache();
|
|
114
145
|
saveToStorage(STORAGE_KEY_DATA_SOURCE, source);
|
|
115
|
-
set({
|
|
146
|
+
set({
|
|
147
|
+
cesiumDataSource: source,
|
|
148
|
+
cesiumTerrainHeight: null,
|
|
149
|
+
cesiumTerrainSource: null,
|
|
150
|
+
cesiumTerrainClipY: null,
|
|
151
|
+
});
|
|
116
152
|
},
|
|
117
153
|
setCesiumIonToken: (token) => {
|
|
154
|
+
clearTerrainElevationCache();
|
|
118
155
|
saveToStorage(STORAGE_KEY_ION_TOKEN, token);
|
|
119
|
-
set({
|
|
156
|
+
set({
|
|
157
|
+
cesiumIonToken: token || DEFAULT_ION_TOKEN,
|
|
158
|
+
cesiumTerrainHeight: null,
|
|
159
|
+
cesiumTerrainSource: null,
|
|
160
|
+
cesiumTerrainClipY: null,
|
|
161
|
+
});
|
|
162
|
+
},
|
|
163
|
+
setCesiumTerrainEnabled: (enabled) => {
|
|
164
|
+
clearTerrainElevationCache();
|
|
165
|
+
set({
|
|
166
|
+
cesiumTerrainEnabled: enabled,
|
|
167
|
+
cesiumTerrainHeight: null,
|
|
168
|
+
cesiumTerrainSource: null,
|
|
169
|
+
cesiumTerrainClipY: null,
|
|
170
|
+
});
|
|
120
171
|
},
|
|
121
|
-
setCesiumTerrainEnabled: (enabled) => set({ cesiumTerrainEnabled: enabled }),
|
|
122
|
-
setCesiumTerrainClamp: (clamp) => set({ cesiumTerrainClamp: clamp }),
|
|
123
172
|
setCesiumTerrainHeight: (height) => set({ cesiumTerrainHeight: height }),
|
|
173
|
+
setCesiumTerrainSource: (source) => set({ cesiumTerrainSource: source }),
|
|
124
174
|
setCesiumSourceModelId: (modelId) => set({ cesiumSourceModelId: modelId }),
|
|
125
175
|
setCesiumTerrainClipY: (y) => set({ cesiumTerrainClipY: y }),
|
|
126
176
|
setCesiumGlbLoaded: (loaded) => set({ cesiumGlbLoaded: loaded }),
|
|
177
|
+
setCesiumPlacementEditMode: (enabled) => set({ cesiumPlacementEditMode: enabled }),
|
|
178
|
+
toggleCesiumPlacementEditMode: () => set((s) => ({
|
|
179
|
+
cesiumPlacementEditMode: !s.cesiumPlacementEditMode,
|
|
180
|
+
...(!s.cesiumPlacementEditMode
|
|
181
|
+
? {}
|
|
182
|
+
: {
|
|
183
|
+
cesiumPlacementDraftModelId: null,
|
|
184
|
+
cesiumPlacementDraft: null,
|
|
185
|
+
}),
|
|
186
|
+
})),
|
|
187
|
+
beginCesiumPlacementDraft: (modelId, conversion) => set({
|
|
188
|
+
cesiumPlacementDraftModelId: modelId,
|
|
189
|
+
cesiumPlacementDraft: {
|
|
190
|
+
eastings: conversion.eastings,
|
|
191
|
+
northings: conversion.northings,
|
|
192
|
+
orthogonalHeight: conversion.orthogonalHeight,
|
|
193
|
+
// IFC MapConversion's x-axis cos/sin pair is optional in the schema.
|
|
194
|
+
// When absent, the convention is "no rotation": cos=1, sin=0.
|
|
195
|
+
xAxisAbscissa: conversion.xAxisAbscissa ?? 1,
|
|
196
|
+
xAxisOrdinate: conversion.xAxisOrdinate ?? 0,
|
|
197
|
+
},
|
|
198
|
+
}),
|
|
199
|
+
updateCesiumPlacementDraft: (values) => set((state) => ({
|
|
200
|
+
cesiumPlacementDraft: state.cesiumPlacementDraft
|
|
201
|
+
? { ...state.cesiumPlacementDraft, ...values }
|
|
202
|
+
: null,
|
|
203
|
+
})),
|
|
204
|
+
resetCesiumPlacementDraft: () => set({
|
|
205
|
+
cesiumPlacementDraftModelId: null,
|
|
206
|
+
cesiumPlacementDraft: null,
|
|
207
|
+
}),
|
|
127
208
|
});
|
package/src/store.ts
CHANGED
|
@@ -64,6 +64,9 @@ export { customPlaneCenter } from './store/slices/sectionSlice.js';
|
|
|
64
64
|
export { loadLastSectionMode } from './store/slices/sectionSlice.js';
|
|
65
65
|
export type { LastSectionMode } from './store/slices/sectionSlice.js';
|
|
66
66
|
|
|
67
|
+
// Re-export Cesium placement types for backwards-compatible '@/store' imports.
|
|
68
|
+
export type { CesiumPlacementDraft } from './store/slices/cesiumSlice.js';
|
|
69
|
+
|
|
67
70
|
// Re-export Schedule (4D) types + helpers
|
|
68
71
|
export type { ScheduleSlice, ScheduleTimeRange, GanttTimeScale } from './store/slices/scheduleSlice.js';
|
|
69
72
|
export {
|