@selvajs/ui 6.0.0-beta.5 → 6.0.0-beta.6
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/components/compute/AppLayout.svelte +7 -1
- package/dist/components/compute/AppLayout.svelte.d.ts +3 -0
- package/dist/components/compute/ComputeApp.svelte +19 -2
- package/dist/components/compute/ComputeApp.svelte.d.ts +15 -1
- package/dist/components/viewer/Viewer.svelte +30 -7
- package/dist/components/viewer/Viewer.svelte.d.ts +12 -1
- package/dist/public.d.ts +2 -0
- package/dist/public.js +1 -0
- package/package.json +4 -4
- package/src/lib/components/compute/AppLayout.svelte +7 -1
- package/src/lib/components/compute/ComputeApp.svelte +19 -2
- package/src/lib/components/viewer/Viewer.svelte +30 -7
- package/src/lib/public.ts +18 -0
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import type { ActionButton } from '../../types/actionButton';
|
|
4
4
|
import type { PresetLabels } from '../../types/presetLabels';
|
|
5
5
|
import { ChevronUp } from '@lucide/svelte';
|
|
6
|
+
import type { ThreeViewer } from '@selvajs/visualization/render';
|
|
6
7
|
import Viewer, { type ViewerConfig } from '../viewer/Viewer.svelte';
|
|
7
8
|
import CalculateButton from '../primitives/CalculateButton.svelte';
|
|
8
9
|
import SolvingIndicator from '../primitives/SolvingIndicator.svelte';
|
|
@@ -39,6 +40,8 @@
|
|
|
39
40
|
viewerConfig?: ViewerConfig;
|
|
40
41
|
/** Branding logo URL shown as a watermark in the viewer's bottom-right corner. */
|
|
41
42
|
logoUrl?: string;
|
|
43
|
+
/** Hands the live three.js viewer to the host. See `Viewer.svelte`. */
|
|
44
|
+
onViewerReady?: (viewer: ThreeViewer) => void | (() => void);
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
let {
|
|
@@ -60,7 +63,8 @@
|
|
|
60
63
|
onListStates,
|
|
61
64
|
presetLabels,
|
|
62
65
|
viewerConfig = {},
|
|
63
|
-
logoUrl
|
|
66
|
+
logoUrl,
|
|
67
|
+
onViewerReady
|
|
64
68
|
}: Props = $props();
|
|
65
69
|
|
|
66
70
|
const hasViewer = $derived(
|
|
@@ -189,6 +193,7 @@
|
|
|
189
193
|
isBlurred={drawerOpen}
|
|
190
194
|
{drawerOpen}
|
|
191
195
|
{logoUrl}
|
|
196
|
+
{onViewerReady}
|
|
192
197
|
viewerConfig={{
|
|
193
198
|
...viewerConfig,
|
|
194
199
|
backgroundColor: schema?.viewerOptions?.backgroundColor,
|
|
@@ -352,6 +357,7 @@
|
|
|
352
357
|
bind:isFullscreen={isViewerFullscreen}
|
|
353
358
|
{isSolving}
|
|
354
359
|
{logoUrl}
|
|
360
|
+
{onViewerReady}
|
|
355
361
|
viewerConfig={{
|
|
356
362
|
backgroundColor: schema?.viewerOptions?.backgroundColor
|
|
357
363
|
}}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { UISchema, SupportedTypes, ParameterPreset } from '@selvajs/schemas';
|
|
2
2
|
import type { ActionButton } from '../../types/actionButton';
|
|
3
3
|
import type { PresetLabels } from '../../types/presetLabels';
|
|
4
|
+
import type { ThreeViewer } from '@selvajs/visualization/render';
|
|
4
5
|
import { type ViewerConfig } from '../viewer/Viewer.svelte';
|
|
5
6
|
interface Props {
|
|
6
7
|
schema: UISchema;
|
|
@@ -23,6 +24,8 @@ interface Props {
|
|
|
23
24
|
viewerConfig?: ViewerConfig;
|
|
24
25
|
/** Branding logo URL shown as a watermark in the viewer's bottom-right corner. */
|
|
25
26
|
logoUrl?: string;
|
|
27
|
+
/** Hands the live three.js viewer to the host. See `Viewer.svelte`. */
|
|
28
|
+
onViewerReady?: (viewer: ThreeViewer) => void | (() => void);
|
|
26
29
|
}
|
|
27
30
|
declare const AppLayout: import("svelte").Component<Props, {}, "isViewerFullscreen">;
|
|
28
31
|
type AppLayout = ReturnType<typeof AppLayout>;
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
import type { PresetLabels } from '../../types/presetLabels';
|
|
8
8
|
import { createSolvingIndicator } from '../../compute/solving.svelte';
|
|
9
9
|
import { createRequestResponseDriver } from '@selvajs/solve/client';
|
|
10
|
-
import type { RetainedSolveResult } from '@selvajs/solve/client';
|
|
10
|
+
import type { RetainedSolveResult, SolveSession } from '@selvajs/solve/client';
|
|
11
11
|
import { meshPolicy } from '@selvajs/visualization/parse';
|
|
12
|
+
import type { ThreeViewer } from '@selvajs/visualization/render';
|
|
12
13
|
import { useSolveSession } from '../../compute/useSolveSession.svelte';
|
|
13
14
|
import { useFooterItem } from '../../composables/useFooterItem.svelte';
|
|
14
15
|
import { hexToOklch } from '../../utils/color';
|
|
@@ -62,7 +63,20 @@
|
|
|
62
63
|
* A getter, not a snapshot: `onReady` fires once.
|
|
63
64
|
*/
|
|
64
65
|
getLastResult: () => RetainedSolveResult | null;
|
|
66
|
+
/**
|
|
67
|
+
* The live solve session, for hosts driving solves from their own state —
|
|
68
|
+
* `setValue`/`solve` to push inputs, `subscribe` to react to results.
|
|
69
|
+
*
|
|
70
|
+
* A getter for the same reason as `getLastResult`. Values written here go through the
|
|
71
|
+
* same throttle and memo as the UI's, so it's safe to call at interaction rate.
|
|
72
|
+
*/
|
|
73
|
+
getSession: () => SolveSession;
|
|
65
74
|
}) => void;
|
|
75
|
+
/**
|
|
76
|
+
* Hands the live three.js viewer to the host once it mounts, for apps drawing their own
|
|
77
|
+
* content alongside solve results. See `Viewer.svelte` for the contract.
|
|
78
|
+
*/
|
|
79
|
+
onViewerReady?: (viewer: ThreeViewer) => void | (() => void);
|
|
66
80
|
headerRight?: Snippet;
|
|
67
81
|
// Replaces the built-in header; takes precedence over `headerRight`.
|
|
68
82
|
header?: Snippet;
|
|
@@ -104,6 +118,7 @@
|
|
|
104
118
|
headerRight,
|
|
105
119
|
header,
|
|
106
120
|
onReady,
|
|
121
|
+
onViewerReady,
|
|
107
122
|
externalScopeKey,
|
|
108
123
|
clientSlot,
|
|
109
124
|
lang
|
|
@@ -152,7 +167,8 @@
|
|
|
152
167
|
$effect(() => {
|
|
153
168
|
onReady?.({
|
|
154
169
|
loadValues: (incoming) => session.loadValues(incoming),
|
|
155
|
-
getLastResult: () => session.lastResult
|
|
170
|
+
getLastResult: () => session.lastResult,
|
|
171
|
+
getSession: () => session
|
|
156
172
|
});
|
|
157
173
|
});
|
|
158
174
|
|
|
@@ -240,6 +256,7 @@
|
|
|
240
256
|
bind:isViewerFullscreen
|
|
241
257
|
values={session.values}
|
|
242
258
|
logoUrl={logo}
|
|
259
|
+
{onViewerReady}
|
|
243
260
|
{panelActions}
|
|
244
261
|
{showSaveButton}
|
|
245
262
|
{showLoadButton}
|
|
@@ -2,7 +2,8 @@ import type { UISchema, ParameterPreset } from '@selvajs/schemas';
|
|
|
2
2
|
import type { ActionButton } from '../../types/actionButton';
|
|
3
3
|
import type { SolveFn } from '@selvajs/solve/shared';
|
|
4
4
|
import type { PresetLabels } from '../../types/presetLabels';
|
|
5
|
-
import type { RetainedSolveResult } from '@selvajs/solve/client';
|
|
5
|
+
import type { RetainedSolveResult, SolveSession } from '@selvajs/solve/client';
|
|
6
|
+
import type { ThreeViewer } from '@selvajs/visualization/render';
|
|
6
7
|
import { type ClientSlot } from '../../contexts/clientSlotContext.svelte';
|
|
7
8
|
import type { Locale } from '../../i18n/messages';
|
|
8
9
|
import type { Snippet } from 'svelte';
|
|
@@ -47,7 +48,20 @@ interface Props {
|
|
|
47
48
|
* A getter, not a snapshot: `onReady` fires once.
|
|
48
49
|
*/
|
|
49
50
|
getLastResult: () => RetainedSolveResult | null;
|
|
51
|
+
/**
|
|
52
|
+
* The live solve session, for hosts driving solves from their own state —
|
|
53
|
+
* `setValue`/`solve` to push inputs, `subscribe` to react to results.
|
|
54
|
+
*
|
|
55
|
+
* A getter for the same reason as `getLastResult`. Values written here go through the
|
|
56
|
+
* same throttle and memo as the UI's, so it's safe to call at interaction rate.
|
|
57
|
+
*/
|
|
58
|
+
getSession: () => SolveSession;
|
|
50
59
|
}) => void;
|
|
60
|
+
/**
|
|
61
|
+
* Hands the live three.js viewer to the host once it mounts, for apps drawing their own
|
|
62
|
+
* content alongside solve results. See `Viewer.svelte` for the contract.
|
|
63
|
+
*/
|
|
64
|
+
onViewerReady?: (viewer: ThreeViewer) => void | (() => void);
|
|
51
65
|
headerRight?: Snippet;
|
|
52
66
|
header?: Snippet;
|
|
53
67
|
externalScopeKey?: string;
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
type CameraProjection,
|
|
12
12
|
type MeasureTool,
|
|
13
13
|
type ViewPreset,
|
|
14
|
-
type Grid
|
|
14
|
+
type Grid,
|
|
15
|
+
type ThreeViewer
|
|
15
16
|
} from '@selvajs/visualization/render';
|
|
16
17
|
import {
|
|
17
18
|
Maximize,
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
showGridToggle?: boolean;
|
|
50
51
|
/**
|
|
51
52
|
* Expose the "Display" submenu (render style picker + edges toggle) in the tools menu.
|
|
52
|
-
* Defaults on. Starts on the 'technical' style with edges
|
|
53
|
+
* Defaults on. Starts on the 'technical' style with edges hidden.
|
|
53
54
|
*/
|
|
54
55
|
showDisplayMenu?: boolean;
|
|
55
56
|
enableMeshClick?: boolean;
|
|
@@ -68,6 +69,16 @@
|
|
|
68
69
|
* bottom-right corner. Omitted/empty renders nothing.
|
|
69
70
|
*/
|
|
70
71
|
logoUrl?: string;
|
|
72
|
+
/**
|
|
73
|
+
* Hands the live three.js viewer to the host once the canvas is up, for apps drawing their
|
|
74
|
+
* own content — `addUserGeometry` (survives solves), `tools.register` (claim clicks before
|
|
75
|
+
* selection), `labelLayer`, `cameraController`.
|
|
76
|
+
*
|
|
77
|
+
* Return a cleanup function to tear down what you added; it runs before the viewer disposes.
|
|
78
|
+
* Anything added outside a solve needs `viewer.invalidate()` to repaint — the render loop is
|
|
79
|
+
* on-demand.
|
|
80
|
+
*/
|
|
81
|
+
onViewerReady?: (viewer: ThreeViewer) => void | (() => void);
|
|
71
82
|
/**
|
|
72
83
|
* UI language for the viewer's own chrome (tools menu, panels, dialogs).
|
|
73
84
|
* When set, the viewer provides it to its subtree. When omitted, the viewer
|
|
@@ -96,6 +107,7 @@
|
|
|
96
107
|
drawerOpen = false,
|
|
97
108
|
viewerConfig = {},
|
|
98
109
|
logoUrl,
|
|
110
|
+
onViewerReady,
|
|
99
111
|
lang
|
|
100
112
|
}: Props = $props();
|
|
101
113
|
|
|
@@ -141,10 +153,10 @@
|
|
|
141
153
|
let projection: CameraProjection = $state('perspective');
|
|
142
154
|
let measureActive = $state(false);
|
|
143
155
|
let gridVisible = $state(false);
|
|
144
|
-
// Render style + edge overlays. 'technical' is the default look; edges (crease lines) start
|
|
145
|
-
//
|
|
156
|
+
// Render style + edge overlays. 'technical' is the default look; edges (crease lines) start off —
|
|
157
|
+
// both are user-switchable via the Display submenu.
|
|
146
158
|
let renderStyle: Look = $state('technical');
|
|
147
|
-
let edgesVisible = $state(
|
|
159
|
+
let edgesVisible = $state(false);
|
|
148
160
|
let selectedMeshMetadata: Record<string, any> | null = $state(null);
|
|
149
161
|
let selectedMeshName: string | null = $state(null);
|
|
150
162
|
|
|
@@ -225,7 +237,13 @@
|
|
|
225
237
|
|
|
226
238
|
const renderer = init.renderer;
|
|
227
239
|
|
|
240
|
+
// Untracked: the host's setup runs once against this canvas, and reading `meshes` or config
|
|
241
|
+
// inside it must not re-run onMount's teardown.
|
|
242
|
+
const hostCleanup = untrack(() => onViewerReady?.(init));
|
|
243
|
+
|
|
228
244
|
return () => {
|
|
245
|
+
// Before dispose, so the host can still remove its own objects from a live scene.
|
|
246
|
+
hostCleanup?.();
|
|
229
247
|
init.dispose();
|
|
230
248
|
// `{#key definitionKey}` recreates the canvas + WebGLRenderer + GL context
|
|
231
249
|
// on every definition switch; browsers cap live contexts (~16). Explicitly
|
|
@@ -352,7 +370,12 @@
|
|
|
352
370
|
: 'overflow-hidden rounded-[0.625rem]'}"
|
|
353
371
|
>
|
|
354
372
|
<Resizable.PaneGroup direction="horizontal" class="h-full w-full">
|
|
355
|
-
|
|
373
|
+
<!-- `defaultSize` must sum to 100 across the live panes. Panes register a frame before the
|
|
374
|
+
group recomputes its layout, so a sum of 115 renders one frame at the raw flex-grow ratio
|
|
375
|
+
and is then renormalized — and if the recompute short-circuits on an equal layout, the
|
|
376
|
+
scene pane keeps a sliver of its intended width. Hence 85 + 15, and the explicit
|
|
377
|
+
id/order so a conditionally-rendered pane keeps its slot. -->
|
|
378
|
+
<Resizable.Pane id="viewport" order={1} defaultSize={sceneManagerOpen ? 85 : 100} minSize={40}>
|
|
356
379
|
<div class="relative h-full w-full" style="touch-action: none;">
|
|
357
380
|
<canvas class="block h-full w-full" bind:this={canvas}></canvas>
|
|
358
381
|
|
|
@@ -565,7 +588,7 @@
|
|
|
565
588
|
<!-- Scene Manager Pane -->
|
|
566
589
|
{#if sceneManagerOpen && scene && outliner}
|
|
567
590
|
<Resizable.Handle withHandle />
|
|
568
|
-
<Resizable.Pane defaultSize={15} minSize={8} maxSize={30}>
|
|
591
|
+
<Resizable.Pane id="scene-manager" order={2} defaultSize={15} minSize={8} maxSize={30}>
|
|
569
592
|
<SceneManager {outliner} {sceneVersion} />
|
|
570
593
|
</Resizable.Pane>
|
|
571
594
|
{/if}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ThreeViewer } from '@selvajs/visualization/render';
|
|
1
2
|
import type { Locale } from '../../i18n/messages';
|
|
2
3
|
export interface ViewerConfig {
|
|
3
4
|
showScreenshotButton?: boolean;
|
|
@@ -8,7 +9,7 @@ export interface ViewerConfig {
|
|
|
8
9
|
showGridToggle?: boolean;
|
|
9
10
|
/**
|
|
10
11
|
* Expose the "Display" submenu (render style picker + edges toggle) in the tools menu.
|
|
11
|
-
* Defaults on. Starts on the 'technical' style with edges
|
|
12
|
+
* Defaults on. Starts on the 'technical' style with edges hidden.
|
|
12
13
|
*/
|
|
13
14
|
showDisplayMenu?: boolean;
|
|
14
15
|
enableMeshClick?: boolean;
|
|
@@ -26,6 +27,16 @@ interface Props {
|
|
|
26
27
|
* bottom-right corner. Omitted/empty renders nothing.
|
|
27
28
|
*/
|
|
28
29
|
logoUrl?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Hands the live three.js viewer to the host once the canvas is up, for apps drawing their
|
|
32
|
+
* own content — `addUserGeometry` (survives solves), `tools.register` (claim clicks before
|
|
33
|
+
* selection), `labelLayer`, `cameraController`.
|
|
34
|
+
*
|
|
35
|
+
* Return a cleanup function to tear down what you added; it runs before the viewer disposes.
|
|
36
|
+
* Anything added outside a solve needs `viewer.invalidate()` to repaint — the render loop is
|
|
37
|
+
* on-demand.
|
|
38
|
+
*/
|
|
39
|
+
onViewerReady?: (viewer: ThreeViewer) => void | (() => void);
|
|
29
40
|
/**
|
|
30
41
|
* UI language for the viewer's own chrome (tools menu, panels, dialogs).
|
|
31
42
|
* When set, the viewer provides it to its subtree. When omitted, the viewer
|
package/dist/public.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { default as AppLayout } from './components/compute/AppLayout.svelte';
|
|
2
2
|
export { default as ComputeApp } from './components/compute/ComputeApp.svelte';
|
|
3
3
|
export { default as Viewer, type ViewerConfig } from './components/viewer/Viewer.svelte';
|
|
4
|
+
export type { ThreeViewer, PointerTool, ToolRegistry, ToolRegistration, LabelLayer, LabelHandle, CameraController, ViewPreset } from '@selvajs/visualization/render';
|
|
5
|
+
export { appSource, isHostOwned, isOwnedBy, pointerToNdc } from '@selvajs/visualization/render';
|
|
4
6
|
export type { Locale, ViewerMessages } from './i18n/messages';
|
|
5
7
|
export { VIEWER_MESSAGES, DEFAULT_LOCALE, messagesFor } from './i18n/messages';
|
|
6
8
|
export { setLocaleContext, getLocaleContext, type LocaleContext } from './i18n/localeContext.svelte';
|
package/dist/public.js
CHANGED
|
@@ -26,6 +26,7 @@ export { default as ComputeApp } from './components/compute/ComputeApp.svelte';
|
|
|
26
26
|
// an optional `viewerConfig`. Pass `lang` to localize its chrome, or provide a
|
|
27
27
|
// locale context (setLocaleContext) at the host root to drive it app-wide.
|
|
28
28
|
export { default as Viewer } from './components/viewer/Viewer.svelte';
|
|
29
|
+
export { appSource, isHostOwned, isOwnedBy, pointerToNdc } from '@selvajs/visualization/render';
|
|
29
30
|
export { VIEWER_MESSAGES, DEFAULT_LOCALE, messagesFor } from './i18n/messages';
|
|
30
31
|
export { setLocaleContext, getLocaleContext } from './i18n/localeContext.svelte';
|
|
31
32
|
// Full-screen states a host app renders
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@selvajs/ui",
|
|
3
|
-
"version": "6.0.0-beta.
|
|
3
|
+
"version": "6.0.0-beta.6",
|
|
4
4
|
"description": "Shared UI components and utilities for Selva applications",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "VektorNode",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"@selvajs/compute": "^4.0.0-beta.5",
|
|
63
63
|
"@selvajs/schemas": "^5.0.0-beta.2",
|
|
64
64
|
"@selvajs/solve": "^1.0.0-beta.9",
|
|
65
|
-
"@selvajs/visualization": "^1.0.0-beta.
|
|
65
|
+
"@selvajs/visualization": "^1.0.0-beta.3"
|
|
66
66
|
},
|
|
67
67
|
"peerDependenciesMeta": {
|
|
68
68
|
"three": {
|
|
@@ -90,10 +90,10 @@
|
|
|
90
90
|
"svelte": "5.56.8",
|
|
91
91
|
"tailwind-variants": "^3.3.0",
|
|
92
92
|
"vitest": "^4.1.10",
|
|
93
|
-
"@selvajs/compute": "4.0.0-beta.5",
|
|
94
93
|
"@selvajs/config": "0.0.3",
|
|
95
94
|
"@selvajs/schemas": "5.0.0-beta.2",
|
|
96
|
-
"@selvajs/
|
|
95
|
+
"@selvajs/compute": "4.0.0-beta.5",
|
|
96
|
+
"@selvajs/visualization": "1.0.0-beta.3",
|
|
97
97
|
"@selvajs/solve": "1.0.0-beta.9"
|
|
98
98
|
},
|
|
99
99
|
"scripts": {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import type { ActionButton } from '../../types/actionButton';
|
|
4
4
|
import type { PresetLabels } from '../../types/presetLabels';
|
|
5
5
|
import { ChevronUp } from '@lucide/svelte';
|
|
6
|
+
import type { ThreeViewer } from '@selvajs/visualization/render';
|
|
6
7
|
import Viewer, { type ViewerConfig } from '../viewer/Viewer.svelte';
|
|
7
8
|
import CalculateButton from '../primitives/CalculateButton.svelte';
|
|
8
9
|
import SolvingIndicator from '../primitives/SolvingIndicator.svelte';
|
|
@@ -39,6 +40,8 @@
|
|
|
39
40
|
viewerConfig?: ViewerConfig;
|
|
40
41
|
/** Branding logo URL shown as a watermark in the viewer's bottom-right corner. */
|
|
41
42
|
logoUrl?: string;
|
|
43
|
+
/** Hands the live three.js viewer to the host. See `Viewer.svelte`. */
|
|
44
|
+
onViewerReady?: (viewer: ThreeViewer) => void | (() => void);
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
let {
|
|
@@ -60,7 +63,8 @@
|
|
|
60
63
|
onListStates,
|
|
61
64
|
presetLabels,
|
|
62
65
|
viewerConfig = {},
|
|
63
|
-
logoUrl
|
|
66
|
+
logoUrl,
|
|
67
|
+
onViewerReady
|
|
64
68
|
}: Props = $props();
|
|
65
69
|
|
|
66
70
|
const hasViewer = $derived(
|
|
@@ -189,6 +193,7 @@
|
|
|
189
193
|
isBlurred={drawerOpen}
|
|
190
194
|
{drawerOpen}
|
|
191
195
|
{logoUrl}
|
|
196
|
+
{onViewerReady}
|
|
192
197
|
viewerConfig={{
|
|
193
198
|
...viewerConfig,
|
|
194
199
|
backgroundColor: schema?.viewerOptions?.backgroundColor,
|
|
@@ -352,6 +357,7 @@
|
|
|
352
357
|
bind:isFullscreen={isViewerFullscreen}
|
|
353
358
|
{isSolving}
|
|
354
359
|
{logoUrl}
|
|
360
|
+
{onViewerReady}
|
|
355
361
|
viewerConfig={{
|
|
356
362
|
backgroundColor: schema?.viewerOptions?.backgroundColor
|
|
357
363
|
}}
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
import type { PresetLabels } from '../../types/presetLabels';
|
|
8
8
|
import { createSolvingIndicator } from '../../compute/solving.svelte';
|
|
9
9
|
import { createRequestResponseDriver } from '@selvajs/solve/client';
|
|
10
|
-
import type { RetainedSolveResult } from '@selvajs/solve/client';
|
|
10
|
+
import type { RetainedSolveResult, SolveSession } from '@selvajs/solve/client';
|
|
11
11
|
import { meshPolicy } from '@selvajs/visualization/parse';
|
|
12
|
+
import type { ThreeViewer } from '@selvajs/visualization/render';
|
|
12
13
|
import { useSolveSession } from '../../compute/useSolveSession.svelte';
|
|
13
14
|
import { useFooterItem } from '../../composables/useFooterItem.svelte';
|
|
14
15
|
import { hexToOklch } from '../../utils/color';
|
|
@@ -62,7 +63,20 @@
|
|
|
62
63
|
* A getter, not a snapshot: `onReady` fires once.
|
|
63
64
|
*/
|
|
64
65
|
getLastResult: () => RetainedSolveResult | null;
|
|
66
|
+
/**
|
|
67
|
+
* The live solve session, for hosts driving solves from their own state —
|
|
68
|
+
* `setValue`/`solve` to push inputs, `subscribe` to react to results.
|
|
69
|
+
*
|
|
70
|
+
* A getter for the same reason as `getLastResult`. Values written here go through the
|
|
71
|
+
* same throttle and memo as the UI's, so it's safe to call at interaction rate.
|
|
72
|
+
*/
|
|
73
|
+
getSession: () => SolveSession;
|
|
65
74
|
}) => void;
|
|
75
|
+
/**
|
|
76
|
+
* Hands the live three.js viewer to the host once it mounts, for apps drawing their own
|
|
77
|
+
* content alongside solve results. See `Viewer.svelte` for the contract.
|
|
78
|
+
*/
|
|
79
|
+
onViewerReady?: (viewer: ThreeViewer) => void | (() => void);
|
|
66
80
|
headerRight?: Snippet;
|
|
67
81
|
// Replaces the built-in header; takes precedence over `headerRight`.
|
|
68
82
|
header?: Snippet;
|
|
@@ -104,6 +118,7 @@
|
|
|
104
118
|
headerRight,
|
|
105
119
|
header,
|
|
106
120
|
onReady,
|
|
121
|
+
onViewerReady,
|
|
107
122
|
externalScopeKey,
|
|
108
123
|
clientSlot,
|
|
109
124
|
lang
|
|
@@ -152,7 +167,8 @@
|
|
|
152
167
|
$effect(() => {
|
|
153
168
|
onReady?.({
|
|
154
169
|
loadValues: (incoming) => session.loadValues(incoming),
|
|
155
|
-
getLastResult: () => session.lastResult
|
|
170
|
+
getLastResult: () => session.lastResult,
|
|
171
|
+
getSession: () => session
|
|
156
172
|
});
|
|
157
173
|
});
|
|
158
174
|
|
|
@@ -240,6 +256,7 @@
|
|
|
240
256
|
bind:isViewerFullscreen
|
|
241
257
|
values={session.values}
|
|
242
258
|
logoUrl={logo}
|
|
259
|
+
{onViewerReady}
|
|
243
260
|
{panelActions}
|
|
244
261
|
{showSaveButton}
|
|
245
262
|
{showLoadButton}
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
type CameraProjection,
|
|
12
12
|
type MeasureTool,
|
|
13
13
|
type ViewPreset,
|
|
14
|
-
type Grid
|
|
14
|
+
type Grid,
|
|
15
|
+
type ThreeViewer
|
|
15
16
|
} from '@selvajs/visualization/render';
|
|
16
17
|
import {
|
|
17
18
|
Maximize,
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
showGridToggle?: boolean;
|
|
50
51
|
/**
|
|
51
52
|
* Expose the "Display" submenu (render style picker + edges toggle) in the tools menu.
|
|
52
|
-
* Defaults on. Starts on the 'technical' style with edges
|
|
53
|
+
* Defaults on. Starts on the 'technical' style with edges hidden.
|
|
53
54
|
*/
|
|
54
55
|
showDisplayMenu?: boolean;
|
|
55
56
|
enableMeshClick?: boolean;
|
|
@@ -68,6 +69,16 @@
|
|
|
68
69
|
* bottom-right corner. Omitted/empty renders nothing.
|
|
69
70
|
*/
|
|
70
71
|
logoUrl?: string;
|
|
72
|
+
/**
|
|
73
|
+
* Hands the live three.js viewer to the host once the canvas is up, for apps drawing their
|
|
74
|
+
* own content — `addUserGeometry` (survives solves), `tools.register` (claim clicks before
|
|
75
|
+
* selection), `labelLayer`, `cameraController`.
|
|
76
|
+
*
|
|
77
|
+
* Return a cleanup function to tear down what you added; it runs before the viewer disposes.
|
|
78
|
+
* Anything added outside a solve needs `viewer.invalidate()` to repaint — the render loop is
|
|
79
|
+
* on-demand.
|
|
80
|
+
*/
|
|
81
|
+
onViewerReady?: (viewer: ThreeViewer) => void | (() => void);
|
|
71
82
|
/**
|
|
72
83
|
* UI language for the viewer's own chrome (tools menu, panels, dialogs).
|
|
73
84
|
* When set, the viewer provides it to its subtree. When omitted, the viewer
|
|
@@ -96,6 +107,7 @@
|
|
|
96
107
|
drawerOpen = false,
|
|
97
108
|
viewerConfig = {},
|
|
98
109
|
logoUrl,
|
|
110
|
+
onViewerReady,
|
|
99
111
|
lang
|
|
100
112
|
}: Props = $props();
|
|
101
113
|
|
|
@@ -141,10 +153,10 @@
|
|
|
141
153
|
let projection: CameraProjection = $state('perspective');
|
|
142
154
|
let measureActive = $state(false);
|
|
143
155
|
let gridVisible = $state(false);
|
|
144
|
-
// Render style + edge overlays. 'technical' is the default look; edges (crease lines) start
|
|
145
|
-
//
|
|
156
|
+
// Render style + edge overlays. 'technical' is the default look; edges (crease lines) start off —
|
|
157
|
+
// both are user-switchable via the Display submenu.
|
|
146
158
|
let renderStyle: Look = $state('technical');
|
|
147
|
-
let edgesVisible = $state(
|
|
159
|
+
let edgesVisible = $state(false);
|
|
148
160
|
let selectedMeshMetadata: Record<string, any> | null = $state(null);
|
|
149
161
|
let selectedMeshName: string | null = $state(null);
|
|
150
162
|
|
|
@@ -225,7 +237,13 @@
|
|
|
225
237
|
|
|
226
238
|
const renderer = init.renderer;
|
|
227
239
|
|
|
240
|
+
// Untracked: the host's setup runs once against this canvas, and reading `meshes` or config
|
|
241
|
+
// inside it must not re-run onMount's teardown.
|
|
242
|
+
const hostCleanup = untrack(() => onViewerReady?.(init));
|
|
243
|
+
|
|
228
244
|
return () => {
|
|
245
|
+
// Before dispose, so the host can still remove its own objects from a live scene.
|
|
246
|
+
hostCleanup?.();
|
|
229
247
|
init.dispose();
|
|
230
248
|
// `{#key definitionKey}` recreates the canvas + WebGLRenderer + GL context
|
|
231
249
|
// on every definition switch; browsers cap live contexts (~16). Explicitly
|
|
@@ -352,7 +370,12 @@
|
|
|
352
370
|
: 'overflow-hidden rounded-[0.625rem]'}"
|
|
353
371
|
>
|
|
354
372
|
<Resizable.PaneGroup direction="horizontal" class="h-full w-full">
|
|
355
|
-
|
|
373
|
+
<!-- `defaultSize` must sum to 100 across the live panes. Panes register a frame before the
|
|
374
|
+
group recomputes its layout, so a sum of 115 renders one frame at the raw flex-grow ratio
|
|
375
|
+
and is then renormalized — and if the recompute short-circuits on an equal layout, the
|
|
376
|
+
scene pane keeps a sliver of its intended width. Hence 85 + 15, and the explicit
|
|
377
|
+
id/order so a conditionally-rendered pane keeps its slot. -->
|
|
378
|
+
<Resizable.Pane id="viewport" order={1} defaultSize={sceneManagerOpen ? 85 : 100} minSize={40}>
|
|
356
379
|
<div class="relative h-full w-full" style="touch-action: none;">
|
|
357
380
|
<canvas class="block h-full w-full" bind:this={canvas}></canvas>
|
|
358
381
|
|
|
@@ -565,7 +588,7 @@
|
|
|
565
588
|
<!-- Scene Manager Pane -->
|
|
566
589
|
{#if sceneManagerOpen && scene && outliner}
|
|
567
590
|
<Resizable.Handle withHandle />
|
|
568
|
-
<Resizable.Pane defaultSize={15} minSize={8} maxSize={30}>
|
|
591
|
+
<Resizable.Pane id="scene-manager" order={2} defaultSize={15} minSize={8} maxSize={30}>
|
|
569
592
|
<SceneManager {outliner} {sceneVersion} />
|
|
570
593
|
</Resizable.Pane>
|
|
571
594
|
{/if}
|
package/src/lib/public.ts
CHANGED
|
@@ -29,6 +29,24 @@ export { default as ComputeApp } from './components/compute/ComputeApp.svelte';
|
|
|
29
29
|
// locale context (setLocaleContext) at the host root to drive it app-wide.
|
|
30
30
|
export { default as Viewer, type ViewerConfig } from './components/viewer/Viewer.svelte';
|
|
31
31
|
|
|
32
|
+
// Viewer app seam. `onViewerReady` (on <Viewer> and <ComputeApp>) hands over the
|
|
33
|
+
// live three.js viewer, so a host can draw its own content into the same scene as
|
|
34
|
+
// the solve results — a point cloud, draft lines, annotations — and register
|
|
35
|
+
// pointer tools that claim clicks ahead of object selection. Re-exported from
|
|
36
|
+
// @selvajs/visualization/render so hosts can annotate without depending on it
|
|
37
|
+
// directly; `three` stays a peer dep of that package either way.
|
|
38
|
+
export type {
|
|
39
|
+
ThreeViewer,
|
|
40
|
+
PointerTool,
|
|
41
|
+
ToolRegistry,
|
|
42
|
+
ToolRegistration,
|
|
43
|
+
LabelLayer,
|
|
44
|
+
LabelHandle,
|
|
45
|
+
CameraController,
|
|
46
|
+
ViewPreset
|
|
47
|
+
} from '@selvajs/visualization/render';
|
|
48
|
+
export { appSource, isHostOwned, isOwnedBy, pointerToNdc } from '@selvajs/visualization/render';
|
|
49
|
+
|
|
32
50
|
// Viewer localization. The library renders English + German chrome; switch at
|
|
33
51
|
// runtime by passing `lang` to <Viewer> or by setting a reactive locale context
|
|
34
52
|
// once at the host root (e.g. feed in an app-wide Paraglide locale). Does not
|