@selvajs/ui 6.0.0-beta.7 → 6.0.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/README.md +5 -7
- package/dist/components/compute/AppLayout.svelte +2 -8
- package/dist/components/compute/AppLayout.svelte.d.ts +0 -2
- package/dist/components/compute/ComputeApp.svelte +25 -43
- package/dist/components/compute/ComputeApp.svelte.d.ts +8 -24
- package/dist/components/compute/ParameterPresetManager.svelte +4 -9
- package/dist/components/compute/ParameterPresetManager.svelte.d.ts +1 -1
- package/dist/components/layout/AppShell.svelte +5 -13
- package/dist/components/layout/AppShell.svelte.d.ts +1 -2
- package/dist/components/layout/EmptyState.svelte +2 -3
- package/dist/components/layout/PageFooter.svelte +0 -4
- package/dist/components/layout/index.js +0 -1
- package/dist/components/preview/Group.svelte +2 -5
- package/dist/components/preview/ImageOutput.svelte +5 -4
- package/dist/components/preview/InputControl.svelte +27 -28
- package/dist/components/preview/InputControl.svelte.d.ts +3 -3
- package/dist/components/preview/OutputDisplay.svelte +3 -6
- package/dist/components/preview/TabLayout.svelte +3 -3
- package/dist/components/preview/inputs/FileInput.svelte +9 -12
- package/dist/components/primitives/field/field-error.svelte +1 -3
- package/dist/components/primitives/index.js +0 -1
- package/dist/components/viewer/SceneManager.svelte +9 -20
- package/dist/components/viewer/SceneManager.svelte.d.ts +3 -3
- package/dist/components/viewer/Viewer.svelte +36 -61
- package/dist/components/viewer/Viewer.svelte.d.ts +6 -19
- package/dist/composables/useFooterItem.svelte.d.ts +3 -4
- package/dist/composables/useFooterItem.svelte.js +3 -3
- package/dist/compute/solving.svelte.d.ts +6 -6
- package/dist/compute/solving.svelte.js +6 -6
- package/dist/compute/useSolveSession.svelte.d.ts +5 -7
- package/dist/compute/useSolveSession.svelte.js +11 -24
- package/dist/constants.js +4 -4
- package/dist/contexts/clientSlotContext.svelte.d.ts +3 -5
- package/dist/contexts/footerContext.svelte.d.ts +6 -8
- package/dist/external/storage.js +2 -3
- package/dist/i18n/localeContext.svelte.d.ts +5 -7
- package/dist/i18n/localeContext.svelte.js +8 -15
- package/dist/i18n/messages.d.ts +0 -2
- package/dist/i18n/messages.js +3 -8
- package/dist/index.js +7 -13
- package/dist/public.js +14 -30
- package/dist/schema/defaults.js +2 -3
- package/dist/schema/dynamic-value-list.js +13 -15
- package/dist/schema/param-exporter.d.ts +7 -14
- package/dist/schema/param-exporter.js +6 -12
- package/dist/schema/traversal.js +3 -4
- package/dist/schema/visibility-rules.d.ts +4 -8
- package/dist/schema/visibility-rules.js +4 -8
- package/dist/types/actionButton.d.ts +1 -25
- package/dist/types/presetLabels.d.ts +2 -3
- package/dist/utils/color.js +6 -9
- package/dist/utils/debounce.d.ts +1 -11
- package/dist/utils/debounce.js +1 -11
- package/dist/utils/file-download.js +4 -2
- package/dist/utils/loadScript.d.ts +2 -7
- package/dist/utils/loadScript.js +2 -7
- package/package.json +16 -16
- package/src/lib/components/compute/AppLayout.svelte +2 -8
- package/src/lib/components/compute/ComputeApp.svelte +25 -43
- package/src/lib/components/compute/ParameterPresetManager.svelte +4 -9
- package/src/lib/components/layout/AppShell.svelte +5 -13
- package/src/lib/components/layout/EmptyState.svelte +2 -3
- package/src/lib/components/layout/PageFooter.svelte +0 -4
- package/src/lib/components/layout/index.ts +0 -1
- package/src/lib/components/preview/Group.svelte +2 -5
- package/src/lib/components/preview/ImageOutput.svelte +5 -4
- package/src/lib/components/preview/InputControl.svelte +27 -28
- package/src/lib/components/preview/OutputDisplay.svelte +3 -6
- package/src/lib/components/preview/TabLayout.svelte +3 -3
- package/src/lib/components/preview/inputs/FileInput.svelte +9 -12
- package/src/lib/components/primitives/field/field-error.svelte +1 -3
- package/src/lib/components/primitives/index.ts +0 -2
- package/src/lib/components/viewer/SceneManager.svelte +9 -20
- package/src/lib/components/viewer/Viewer.svelte +36 -61
- package/src/lib/composables/useFooterItem.svelte.ts +3 -4
- package/src/lib/compute/solving.svelte.ts +6 -6
- package/src/lib/compute/useSolveSession.svelte.ts +11 -24
- package/src/lib/constants.ts +4 -4
- package/src/lib/contexts/clientSlotContext.svelte.ts +7 -12
- package/src/lib/contexts/footerContext.svelte.ts +6 -8
- package/src/lib/external/storage.ts +2 -3
- package/src/lib/i18n/localeContext.svelte.ts +8 -17
- package/src/lib/i18n/messages.ts +3 -10
- package/src/lib/index.ts +8 -13
- package/src/lib/public.ts +22 -40
- package/src/lib/schema/defaults.ts +2 -3
- package/src/lib/schema/dynamic-value-list.ts +13 -15
- package/src/lib/schema/param-exporter.ts +9 -16
- package/src/lib/schema/traversal.ts +3 -4
- package/src/lib/schema/visibility-rules.ts +4 -8
- package/src/lib/types/actionButton.ts +1 -25
- package/src/lib/types/presetLabels.ts +2 -3
- package/src/lib/utils/color.ts +6 -9
- package/src/lib/utils/debounce.ts +1 -11
- package/src/lib/utils/file-download.ts +4 -2
- package/src/lib/utils/loadScript.ts +2 -7
|
@@ -2,13 +2,13 @@ import { untrack } from 'svelte';
|
|
|
2
2
|
import { APP_DEFAULTS } from '../constants';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Adaptive solving indicator: measures solve durations and adjusts its
|
|
6
|
+
* visibility delay from the running average.
|
|
7
7
|
*
|
|
8
|
-
* -
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
11
|
-
* - First solve:
|
|
8
|
+
* - Average under FAST_THRESHOLD_MS: never shown, so quick solves don't flicker
|
|
9
|
+
* - Average over SLOW_THRESHOLD_MS: shown immediately
|
|
10
|
+
* - Between the two: shown after a proportional slice of ANIMATION_DELAY
|
|
11
|
+
* - First solve: shown immediately, there is no history yet
|
|
12
12
|
*/
|
|
13
13
|
export function createSolvingIndicator(isSolving: () => boolean): { readonly show: boolean } {
|
|
14
14
|
const { FAST_THRESHOLD_MS, SLOW_THRESHOLD_MS, HISTORY_SIZE, ANIMATION_DELAY } =
|
|
@@ -1,40 +1,27 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// exposes plain getters plus a `subscribe()` seam. That makes it usable headless, but a
|
|
5
|
-
// plain getter read inside Svelte markup is NOT reactive — nothing tells the component to
|
|
6
|
-
// re-run. This adapter closes that gap: it subscribes once, bumps a `$state` version
|
|
7
|
-
// counter on every notification, and reads that counter inside each getter so any
|
|
8
|
-
// component touching one re-runs when the session changes.
|
|
9
|
-
//
|
|
10
|
-
// The counter (rather than mirroring each field into its own `$state`) keeps this a pure
|
|
11
|
-
// republish: no field list to keep in sync as the session grows, and no risk of a mirrored
|
|
12
|
-
// copy drifting from the source of truth.
|
|
1
|
+
// A version counter, rather than mirroring each field into its own `$state`, keeps this a
|
|
2
|
+
// pure republish: no field list to keep in sync as the session grows, and no mirrored copy
|
|
3
|
+
// that can drift from the source of truth.
|
|
13
4
|
|
|
14
5
|
import type { SolveSession, SolveSessionArgs } from '@selvajs/solve/client';
|
|
15
6
|
import { createSolveSession } from '@selvajs/solve/client';
|
|
16
7
|
|
|
17
8
|
/**
|
|
18
|
-
* Wraps a Solve Session so its state reads reactively inside Svelte components.
|
|
9
|
+
* Wraps a Solve Session so its state reads reactively inside Svelte components. Use this in
|
|
10
|
+
* a component, never `createSolveSession` directly: the raw session's getters return correct
|
|
11
|
+
* values but never re-render.
|
|
19
12
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* session it wraps.
|
|
23
|
-
*
|
|
24
|
-
* Must be called during component initialization (it uses `$effect` to manage the
|
|
25
|
-
* subscription, so teardown follows the owning component's lifecycle).
|
|
13
|
+
* Must be called during component initialization — the subscription is managed by `$effect`,
|
|
14
|
+
* so teardown follows the owning component's lifecycle.
|
|
26
15
|
*/
|
|
27
16
|
export function useSolveSession(args: SolveSessionArgs): SolveSession {
|
|
28
17
|
const session = createSolveSession(args);
|
|
29
18
|
|
|
30
|
-
//
|
|
31
|
-
// the dependency; the value itself is never meaningful.
|
|
19
|
+
// Reading this inside a getter is what registers the dependency; the value never matters.
|
|
32
20
|
let version = $state(0);
|
|
33
21
|
|
|
34
22
|
$effect(() => {
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
// whole cleanup.
|
|
23
|
+
// The session is created alongside this adapter and outlives no other component, so
|
|
24
|
+
// dropping the subscription is the whole cleanup.
|
|
38
25
|
return session.subscribe(() => {
|
|
39
26
|
version += 1;
|
|
40
27
|
});
|
package/src/lib/constants.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export const APP_DEFAULTS = {
|
|
2
2
|
// File upload limits
|
|
3
3
|
// TEMP (dev): raised 150 MB → 300 MB so large dev file-widget inputs aren't
|
|
4
|
-
// blocked client-side. The server request cap (COMPUTE_REQUEST_MAX_BYTES)
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// before release.
|
|
4
|
+
// blocked client-side. The server request cap (COMPUTE_REQUEST_MAX_BYTES)
|
|
5
|
+
// still defaults to 210 MB, and base64 inflates a raw file by ~4/3, so
|
|
6
|
+
// anything over ~157 MB is rejected with a 413 after the client accepts it.
|
|
7
|
+
// Revert to 150 before release.
|
|
8
8
|
FILE_UPLOAD: {
|
|
9
9
|
MAX_SIZE_MB: 300,
|
|
10
10
|
MAX_SIZE_BYTES: 300 * 1024 * 1024
|
|
@@ -3,27 +3,22 @@ import type { Snippet } from 'svelte';
|
|
|
3
3
|
import type { SupportedTypes } from '@selvajs/schemas';
|
|
4
4
|
|
|
5
5
|
// Carries the host app's slot renderer down to InputControl without threading a
|
|
6
|
-
// prop through every layout layer
|
|
7
|
-
// TabContent → Group → InputControl).
|
|
6
|
+
// prop through every layout layer.
|
|
8
7
|
//
|
|
9
8
|
// An input with source.kind === 'client' and source.client.presentation === 'slot'
|
|
10
|
-
// reserves its cell but renders nothing itself.
|
|
11
|
-
// so the host can render its own element (
|
|
12
|
-
//
|
|
13
|
-
// back via `onValueChange`, which flows into the solve exactly like any built-in
|
|
14
|
-
// widget's change.
|
|
9
|
+
// reserves its cell but renders nothing itself. Selva invokes this snippet instead
|
|
10
|
+
// so the host can render its own element (an "Edit JSON" button, a custom picker)
|
|
11
|
+
// and never interprets what comes back.
|
|
15
12
|
|
|
16
13
|
export interface ClientSlotArgs {
|
|
17
14
|
/** Grasshopper parameter instance GUID (LayoutItem.paramId / SchemaInput.id). */
|
|
18
15
|
inputId: string;
|
|
19
16
|
displayName: string;
|
|
20
|
-
/** The current value held for this input (e.g. the prefilled JSON), if any. */
|
|
21
17
|
value: unknown;
|
|
22
18
|
/**
|
|
23
|
-
* Commit a value for this input
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* Lets a slot be an interactive control (a custom picker), not just a display cell.
|
|
19
|
+
* Commit a value for this input — the same channel a built-in widget's change uses, so a
|
|
20
|
+
* slot can be an interactive control rather than just a display cell. `forceSolve`
|
|
21
|
+
* requests a solve even in manual-solve mode.
|
|
27
22
|
*/
|
|
28
23
|
onValueChange: (value: SupportedTypes, forceSolve?: boolean) => void;
|
|
29
24
|
}
|
|
@@ -2,19 +2,17 @@ import { getContext, setContext, type Component } from 'svelte';
|
|
|
2
2
|
import { SvelteMap } from 'svelte/reactivity';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* reactive state from it (e.g. `() => ({ status: myState.status })`) to keep the footer
|
|
8
|
-
* in sync.
|
|
9
|
-
*
|
|
10
|
-
* Generic over the component's props `P` so registration is type-checked at the call
|
|
11
|
-
* site. The store holds a heterogeneous mix of items, so its map widens `P` to
|
|
5
|
+
* Generic over the component's props `P` so registration is type-checked at the call site.
|
|
6
|
+
* The store holds a heterogeneous mix of items, so its map widens `P` to
|
|
12
7
|
* `Record<string, unknown>` at the boundary.
|
|
13
8
|
*/
|
|
14
9
|
export interface FooterItem<P extends Record<string, unknown> = Record<string, unknown>> {
|
|
15
10
|
id: string;
|
|
16
11
|
component: Component<P>;
|
|
17
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Re-invoked on every render. Return reactive state — `() => ({ status: s.status })` —
|
|
14
|
+
* or the footer never updates.
|
|
15
|
+
*/
|
|
18
16
|
getProps: () => P;
|
|
19
17
|
position: 'left' | 'right';
|
|
20
18
|
priority: number;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
// Moved to
|
|
2
|
-
//
|
|
3
|
-
// the pre-step producer routes that import it — working unchanged.
|
|
1
|
+
// Moved to @selvajs/solve/client, beside the Solve Session that hydrates from it.
|
|
2
|
+
// Re-exported to keep the published `@selvajs/ui/external` sub-path working unchanged.
|
|
4
3
|
|
|
5
4
|
export {
|
|
6
5
|
writeExternalValue,
|
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
import { getContext, setContext } from 'svelte';
|
|
2
2
|
import { type Locale, type ViewerMessages, messagesFor, DEFAULT_LOCALE } from './messages';
|
|
3
3
|
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// ============================================================================
|
|
7
|
-
//
|
|
8
|
-
// Carries the current UI locale down to the viewer and its panels without
|
|
9
|
-
// threading a `lang` prop through every layer. The value is a getter so the
|
|
10
|
-
// host can back it with reactive state — flip the language and the viewer
|
|
11
|
-
// re-renders live.
|
|
4
|
+
// Carries the UI locale down to the viewer and its panels without threading a
|
|
5
|
+
// `lang` prop through every layer.
|
|
12
6
|
//
|
|
13
7
|
// Resolution order for any consuming component:
|
|
14
8
|
// explicit `lang` prop → nearest locale context → English default
|
|
@@ -16,21 +10,19 @@ import { type Locale, type ViewerMessages, messagesFor, DEFAULT_LOCALE } from '.
|
|
|
16
10
|
// Two ways to provide it:
|
|
17
11
|
// - Standalone: <Viewer lang="de" /> — Viewer provides the context itself.
|
|
18
12
|
// - In an app: call setLocaleContext(() => app.locale) once at the root; the
|
|
19
|
-
// viewer (and anything else) reads it.
|
|
20
|
-
// locale in here.
|
|
13
|
+
// viewer (and anything else) reads it.
|
|
21
14
|
|
|
22
15
|
const LOCALE_CONTEXT_KEY = Symbol('viewer-locale-context');
|
|
23
16
|
|
|
24
17
|
export interface LocaleContext {
|
|
25
|
-
/** Current locale. Called reactively — return reactive state to enable live switching. */
|
|
26
18
|
readonly locale: Locale;
|
|
27
|
-
/** Resolved message catalog for the current locale. */
|
|
28
19
|
readonly messages: ViewerMessages;
|
|
29
20
|
}
|
|
30
21
|
|
|
31
22
|
/**
|
|
32
|
-
* Provide the locale to descendants. Pass a getter
|
|
33
|
-
* `$state`,
|
|
23
|
+
* Provide the locale to descendants. Pass a getter, not a value: it is re-read on every
|
|
24
|
+
* consumer render, so a reactive source (a `$state`, the app's Paraglide locale) switches
|
|
25
|
+
* the language live.
|
|
34
26
|
*/
|
|
35
27
|
export function setLocaleContext(getLocale: () => Locale | undefined): void {
|
|
36
28
|
const ctx: LocaleContext = {
|
|
@@ -45,9 +37,8 @@ export function setLocaleContext(getLocale: () => Locale | undefined): void {
|
|
|
45
37
|
}
|
|
46
38
|
|
|
47
39
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* need a null check.
|
|
40
|
+
* Falls back to an English-only context when no provider exists (a primitive used in
|
|
41
|
+
* isolation), so consumers never need a null check.
|
|
51
42
|
*/
|
|
52
43
|
export function getLocaleContext(): LocaleContext {
|
|
53
44
|
return (
|
package/src/lib/i18n/messages.ts
CHANGED
|
@@ -2,14 +2,9 @@
|
|
|
2
2
|
// Viewer message catalog (library-owned strings)
|
|
3
3
|
// ============================================================================
|
|
4
4
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// names): those live in the user's .gh file and can't be translated here.
|
|
9
|
-
//
|
|
10
|
-
// The library ships English + German. A host app can switch locale at runtime
|
|
11
|
-
// via the locale context (see ./localeContext.svelte.ts) — e.g. selva feeds its
|
|
12
|
-
// own Paraglide locale in. With no provider, components fall back to English.
|
|
5
|
+
// Only the strings @selvajs/ui renders itself. Names coming from a Grasshopper
|
|
6
|
+
// definition — meshes, layers, metadata keys — live in the user's .gh file and
|
|
7
|
+
// cannot be translated here.
|
|
13
8
|
|
|
14
9
|
export type Locale = 'en' | 'de';
|
|
15
10
|
|
|
@@ -22,9 +17,7 @@ export interface ViewerMessages {
|
|
|
22
17
|
views: string;
|
|
23
18
|
measure: string;
|
|
24
19
|
grid: string;
|
|
25
|
-
/** "Display" submenu label — groups render style + edges. */
|
|
26
20
|
display: string;
|
|
27
|
-
/** Edges show/hide toggle label. */
|
|
28
21
|
edges: string;
|
|
29
22
|
sceneManager: string;
|
|
30
23
|
screenshot: string;
|
package/src/lib/index.ts
CHANGED
|
@@ -1,31 +1,28 @@
|
|
|
1
|
-
//
|
|
1
|
+
// The in-monorepo barrel: everything public.ts publishes, plus the design system,
|
|
2
|
+
// page chrome, contexts and utils that never ship to npm. Reached via the
|
|
3
|
+
// "selva-source" export condition.
|
|
4
|
+
|
|
2
5
|
export * from './components/layout';
|
|
3
6
|
|
|
4
|
-
// Compute app (schema + viewer + solve controls composed into a runnable app)
|
|
5
7
|
export { default as AppLayout } from './components/compute/AppLayout.svelte';
|
|
6
8
|
export { default as ComputeApp } from './components/compute/ComputeApp.svelte';
|
|
7
9
|
|
|
8
|
-
// Error screen
|
|
9
10
|
export { default as ErrorScreen } from './components/ErrorScreen.svelte';
|
|
10
11
|
|
|
11
12
|
// Design-system primitives (shadcn-svelte + custom)
|
|
12
13
|
export * from './components/primitives';
|
|
13
14
|
export { default as StateDisplay } from './components/primitives/StateDisplay.svelte';
|
|
14
15
|
|
|
15
|
-
// 3D viewer
|
|
16
16
|
export { default as Viewer } from './components/viewer/Viewer.svelte';
|
|
17
17
|
|
|
18
|
-
// Utilities
|
|
19
18
|
export * from './schema/defaults';
|
|
20
19
|
export * from './schema/dynamic-value-list';
|
|
21
20
|
export * from './schema/traversal';
|
|
22
21
|
export * from './compute/solving.svelte';
|
|
23
22
|
|
|
24
|
-
// Solve Session seam.
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
// markup. Re-exported so transports outside this package — e.g. plugin-ui's WebSocket
|
|
28
|
-
// driver — can satisfy SolveDriver and drive a session. See CONTEXT.md.
|
|
23
|
+
// Solve Session seam. Re-exported so transports outside this package — e.g.
|
|
24
|
+
// plugin-ui's WebSocket driver — can satisfy SolveDriver and drive a session.
|
|
25
|
+
// See CONTEXT.md.
|
|
29
26
|
export { useSolveSession } from './compute/useSolveSession.svelte';
|
|
30
27
|
export {
|
|
31
28
|
createSolveSession,
|
|
@@ -36,7 +33,7 @@ export {
|
|
|
36
33
|
type SolveReporter
|
|
37
34
|
} from '@selvajs/solve/client';
|
|
38
35
|
|
|
39
|
-
//
|
|
36
|
+
// Pre-step producer transit storage
|
|
40
37
|
export {
|
|
41
38
|
writeExternalValue,
|
|
42
39
|
readExternalValue,
|
|
@@ -46,12 +43,10 @@ export {
|
|
|
46
43
|
type ExternalInput
|
|
47
44
|
} from '@selvajs/solve/client';
|
|
48
45
|
|
|
49
|
-
// Contexts & Composables
|
|
50
46
|
export * from './contexts/footerContext.svelte';
|
|
51
47
|
export * from './contexts/clientSlotContext.svelte';
|
|
52
48
|
export * from './composables/useFooterItem.svelte';
|
|
53
49
|
|
|
54
|
-
// Utils (cn function)
|
|
55
50
|
export * from './utils';
|
|
56
51
|
export { randomId } from './utils/randomId';
|
|
57
52
|
|
package/src/lib/public.ts
CHANGED
|
@@ -1,40 +1,24 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
// ============================================================================
|
|
1
|
+
// The only surface npm consumers of @selvajs/ui see. package.json maps the "."
|
|
2
|
+
// export's `svelte`/`types` conditions at this file's build output.
|
|
4
3
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// Grasshopper-driven app (ComputeApp), drive solves, and wire pre-step
|
|
10
|
-
// producers. Verified against real external host apps: they import
|
|
11
|
-
// ComputeApp + its types, the solve seam, and the external-input storage
|
|
12
|
-
// helpers. Nothing else.
|
|
13
|
-
//
|
|
14
|
-
// Deliberately NOT public: design-system primitives (Button, Card, Dialog, …),
|
|
15
|
-
// page-chrome layout (AppShell, SideNav, …), toast/Toaster, ThemeSwitcher,
|
|
16
|
-
// DataTable, contexts/composables, cn/randomId. These remain importable from
|
|
17
|
-
// the full barrel (./index.ts) INSIDE the monorepo via the "@selvajs/source"
|
|
18
|
-
// export condition, but never ship to npm. If an external consumer ever needs a
|
|
19
|
-
// primitive directly, promote it here explicitly rather than re-exporting the
|
|
20
|
-
// whole primitives barrel.
|
|
4
|
+
// Design-system primitives, page chrome, toasts, DataTable, contexts and small
|
|
5
|
+
// utils stay out on purpose: they remain importable from ./index.ts inside the
|
|
6
|
+
// monorepo via the "selva-source" export condition, but never ship to npm.
|
|
7
|
+
// Promote a symbol here explicitly rather than re-exporting a whole barrel.
|
|
21
8
|
|
|
22
|
-
// Compute app
|
|
9
|
+
// Compute app: schema + viewer + solve controls composed into a runnable app.
|
|
23
10
|
export { default as AppLayout } from './components/compute/AppLayout.svelte';
|
|
24
11
|
export { default as ComputeApp } from './components/compute/ComputeApp.svelte';
|
|
25
12
|
|
|
26
|
-
// Standalone 3D viewer
|
|
27
|
-
//
|
|
28
|
-
// an optional `viewerConfig`. Pass `lang` to localize its chrome, or provide a
|
|
29
|
-
// locale context (setLocaleContext) at the host root to drive it app-wide.
|
|
13
|
+
// Standalone 3D viewer: renders Grasshopper meshes outside a ComputeApp host,
|
|
14
|
+
// driven directly with a `meshes` array and an optional `viewerConfig`.
|
|
30
15
|
export { default as Viewer, type ViewerConfig } from './components/viewer/Viewer.svelte';
|
|
31
16
|
|
|
32
17
|
// Viewer app seam. `onViewerReady` (on <Viewer> and <ComputeApp>) hands over the
|
|
33
18
|
// live three.js viewer, so a host can draw its own content into the same scene as
|
|
34
|
-
// the solve results
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
// directly; `three` stays a peer dep of that package either way.
|
|
19
|
+
// the solve results and register pointer tools that claim clicks ahead of object
|
|
20
|
+
// selection. Re-exported from @selvajs/visualization/render so hosts can annotate
|
|
21
|
+
// without depending on it directly; `three` stays a peer dep of that package either way.
|
|
38
22
|
export type {
|
|
39
23
|
ThreeViewer,
|
|
40
24
|
PointerTool,
|
|
@@ -47,10 +31,9 @@ export type {
|
|
|
47
31
|
} from '@selvajs/visualization/render';
|
|
48
32
|
export { appSource, isHostOwned, isOwnedBy, pointerToNdc } from '@selvajs/visualization/render';
|
|
49
33
|
|
|
50
|
-
// Viewer localization
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
// translate Grasshopper-sourced names/metadata.
|
|
34
|
+
// Viewer localization: English + German chrome, switched by passing `lang` to
|
|
35
|
+
// <Viewer> or by setting a reactive locale context once at the host root.
|
|
36
|
+
// Does not translate Grasshopper-sourced names/metadata.
|
|
54
37
|
export type { Locale, ViewerMessages } from './i18n/messages';
|
|
55
38
|
export { VIEWER_MESSAGES, DEFAULT_LOCALE, messagesFor } from './i18n/messages';
|
|
56
39
|
export {
|
|
@@ -59,14 +42,13 @@ export {
|
|
|
59
42
|
type LocaleContext
|
|
60
43
|
} from './i18n/localeContext.svelte';
|
|
61
44
|
|
|
62
|
-
// Full-screen states a host app renders
|
|
45
|
+
// Full-screen states a host app renders.
|
|
63
46
|
export { default as ErrorScreen } from './components/ErrorScreen.svelte';
|
|
64
47
|
|
|
65
|
-
// Solve Session seam
|
|
66
|
-
//
|
|
48
|
+
// Solve Session seam, for hosts driving a session themselves rather than
|
|
49
|
+
// embedding <ComputeApp>. The session lives in `@selvajs/solve/client` and is
|
|
67
50
|
// framework-free; `useSolveSession` is the Svelte binding that makes its getters
|
|
68
|
-
// read reactively inside components.
|
|
69
|
-
// both are re-exported for hosts driving a session themselves. See CONTEXT.md.
|
|
51
|
+
// read reactively inside components. See CONTEXT.md.
|
|
70
52
|
export { useSolveSession } from './compute/useSolveSession.svelte';
|
|
71
53
|
export {
|
|
72
54
|
createSolveSession,
|
|
@@ -77,8 +59,8 @@ export {
|
|
|
77
59
|
type SolveReporter
|
|
78
60
|
} from '@selvajs/solve/client';
|
|
79
61
|
|
|
80
|
-
// Client-slot context
|
|
81
|
-
//
|
|
62
|
+
// Client-slot context: host apps render their own cell for client-sourced inputs,
|
|
63
|
+
// and may commit a value back via ClientSlotArgs.onValueChange.
|
|
82
64
|
export type { ClientSlotArgs, ClientSlot } from './contexts/clientSlotContext.svelte';
|
|
83
65
|
|
|
84
66
|
// Pre-step producer transit storage (host apps wire producers via these).
|
|
@@ -91,7 +73,7 @@ export {
|
|
|
91
73
|
type ExternalInput
|
|
92
74
|
} from '@selvajs/solve/client';
|
|
93
75
|
|
|
94
|
-
// Schema utilities a ComputeApp host
|
|
76
|
+
// Schema utilities a ComputeApp host needs to read/shape values.
|
|
95
77
|
export * from './schema/defaults';
|
|
96
78
|
export * from './schema/traversal';
|
|
97
79
|
export * from './schema/dynamic-value-list';
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// Moved to @selvajs/schemas so server-side callers can share it without pulling
|
|
2
|
-
//
|
|
3
|
-
// working.
|
|
1
|
+
// Moved to @selvajs/schemas so server-side callers can share it without pulling in the
|
|
2
|
+
// UI package. Re-exported to keep existing @selvajs/ui importers working.
|
|
4
3
|
export { getDefaultValue } from '@selvajs/schemas';
|
|
@@ -41,12 +41,11 @@ function coercePayload(value: unknown): DynamicValueListPayload | null {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
// Memoizes string-payload coercion. In compute mode the payload arrives as a JSON
|
|
44
|
-
// string —
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
// SameValueZero, so an identical response string from a later solve also hits.
|
|
44
|
+
// string — several MB for a large options list — and the options map is derived from
|
|
45
|
+
// `values` (TabLayout), so it recomputes on EVERY value change. Unmemoized, each
|
|
46
|
+
// keystroke re-parses megabytes and allocates a fresh options object whose new
|
|
47
|
+
// identity re-renders the whole dropdown subtree; a 6.4 MB payload drove a tab OOM.
|
|
48
|
+
// Map keys use SameValueZero, so an identical string from a later solve also hits.
|
|
50
49
|
const coerceCache = new Map<string, DynamicValueListPayload | null>();
|
|
51
50
|
const COERCE_CACHE_MAX = 8;
|
|
52
51
|
|
|
@@ -62,10 +61,9 @@ function coercePayloadMemo(value: unknown): DynamicValueListPayload | null {
|
|
|
62
61
|
}
|
|
63
62
|
const parseStart = performance.now();
|
|
64
63
|
const parsed = coercePayload(value);
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
// pattern that could OOM a tab.
|
|
64
|
+
// An expensive parse should happen ONCE per distinct solve result. If this line
|
|
65
|
+
// storms the console, memoization is being defeated (payload strings differing
|
|
66
|
+
// per recompute) — the churn pattern that could OOM a tab.
|
|
69
67
|
if (value.length > 256 * 1024) {
|
|
70
68
|
const optionCount = parsed?.options ? Object.keys(parsed.options).length : 0;
|
|
71
69
|
console.info(
|
|
@@ -90,11 +88,11 @@ interface DynamicValueListSource {
|
|
|
90
88
|
/**
|
|
91
89
|
* Every dynamicValueList output reference in the schema.
|
|
92
90
|
*
|
|
93
|
-
* Canonical location is `schema.outputs[]` — the plugin's SchemaSynchronizer
|
|
94
|
-
* dynamicValueList layout item
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
91
|
+
* Canonical location is `schema.outputs[]` — the plugin's SchemaSynchronizer mirrors every
|
|
92
|
+
* dynamicValueList layout item there (`CanonicalizeDynamicValueListOutputs`). The layout scan is
|
|
93
|
+
* back-compat defense for schemas persisted by an older plugin that lacked that invariant; on
|
|
94
|
+
* current schemas it finds nothing new. Deduped by id, `outputs[]` winning so the canonical
|
|
95
|
+
* record's targetInputId takes precedence.
|
|
98
96
|
*/
|
|
99
97
|
function collectDynamicValueListSources(schema: UISchema): DynamicValueListSource[] {
|
|
100
98
|
const byId = new Map<string, DynamicValueListSource>();
|
|
@@ -107,11 +107,9 @@ export function validateSavedState(
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
/**
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* own "loadable" rule from the schema rather than re-reading validation issues — no
|
|
114
|
-
* coupling to how sentinel ids are encoded.
|
|
110
|
+
* Params the current schema no longer has are dropped. That's the same condition
|
|
111
|
+
* `validateSavedState` flags as an error, but the rule is re-derived from the schema here
|
|
112
|
+
* rather than read back off the issue list — nothing couples to how sentinel ids are encoded.
|
|
115
113
|
*/
|
|
116
114
|
export function extractLoadableValues(
|
|
117
115
|
savedState: ParameterPreset,
|
|
@@ -127,23 +125,18 @@ export function extractLoadableValues(
|
|
|
127
125
|
return values;
|
|
128
126
|
}
|
|
129
127
|
|
|
130
|
-
/** The full outcome of loading a preset: what to apply, plus the diagnostics to surface. */
|
|
131
128
|
export interface PresetLoadResult {
|
|
132
|
-
/**
|
|
129
|
+
/** Safe to apply now — the params that still exist in the schema. */
|
|
133
130
|
values: Record<string, unknown>;
|
|
134
|
-
/**
|
|
131
|
+
/** Errors and warnings both, for the load dialog. */
|
|
135
132
|
issues: ValidationIssueMessage[];
|
|
136
133
|
/** No issues at all — load silently. */
|
|
137
134
|
isValid: boolean;
|
|
138
|
-
/** No
|
|
135
|
+
/** No errors — warnings alone still allow the load. */
|
|
139
136
|
canLoad: boolean;
|
|
140
137
|
}
|
|
141
138
|
|
|
142
|
-
/**
|
|
143
|
-
* Single entry point for applying a preset: validates against the current schema and, in
|
|
144
|
-
* the same pass, computes the loadable values. Callers get one object instead of
|
|
145
|
-
* threading a validation result back into a separate extraction step.
|
|
146
|
-
*/
|
|
139
|
+
/** Validates and computes the loadable values in one pass, so callers thread one object. */
|
|
147
140
|
export function loadPreset(savedState: ParameterPreset, currentSchema: UISchema): PresetLoadResult {
|
|
148
141
|
const validation = validateSavedState(savedState, currentSchema);
|
|
149
142
|
return {
|
|
@@ -161,8 +154,8 @@ export function exportStateAsJson(savedState: ParameterPreset): void {
|
|
|
161
154
|
|
|
162
155
|
const link = document.createElement('a');
|
|
163
156
|
link.href = URL.createObjectURL(blob);
|
|
164
|
-
// .slvp = Selva parameter preset. Import accepts
|
|
165
|
-
//
|
|
157
|
+
// .slvp = Selva parameter preset. Import still accepts the pre-rename .sps — same JSON,
|
|
158
|
+
// only the writer's extension changed.
|
|
166
159
|
link.download = `${safeName}_${date}.slvp`;
|
|
167
160
|
link.click();
|
|
168
161
|
URL.revokeObjectURL(link.href);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// consumers.
|
|
1
|
+
// Moved to @selvajs/schemas, next to the types it walks, so any package depending on the
|
|
2
|
+
// schema can traverse it without pulling in @selvajs/ui. Re-exported to keep existing
|
|
3
|
+
// @selvajs/ui importers working.
|
|
5
4
|
export { getGroups, getLayoutItems, getInputItems, type InputLayoutItem } from '@selvajs/schemas';
|
|
@@ -84,19 +84,15 @@ export function evaluateVisibility(
|
|
|
84
84
|
return actionFn(met, defaultValue);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
/**
|
|
88
|
-
* Stable key for a layout item: `paramId` for inputs/outputs, `id` for linebreaks.
|
|
89
|
-
* Mirrors the key the renderers use in their `{#each}` blocks.
|
|
90
|
-
*/
|
|
87
|
+
/** Must stay identical to the key the renderers use in their `{#each}` blocks. */
|
|
91
88
|
export function itemKey(item: LayoutItem): string {
|
|
92
89
|
return item.type === 'linebreak' ? item.id : item.paramId;
|
|
93
90
|
}
|
|
94
91
|
|
|
95
92
|
/**
|
|
96
|
-
* Evaluates
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* re-evaluating per access — one source of truth for "what's visible right now".
|
|
93
|
+
* Evaluates each item once per render. Callers that touch the same item several times
|
|
94
|
+
* (column layout, then cell render, then the default-value sweep) read this map instead of
|
|
95
|
+
* re-evaluating, so they can't disagree about what's visible mid-render.
|
|
100
96
|
*/
|
|
101
97
|
export function buildVisibilityMap(
|
|
102
98
|
items: LayoutItem[],
|
|
@@ -1,31 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Defines an action button for use in the UI.
|
|
3
|
-
*
|
|
4
|
-
* @example
|
|
5
|
-
* ```typescript
|
|
6
|
-
* const saveButton: ActionButton = {
|
|
7
|
-
* id: 'save-btn',
|
|
8
|
-
* label: 'Save Changes',
|
|
9
|
-
* icon: SaveIcon,
|
|
10
|
-
* variant: 'default',
|
|
11
|
-
* size: 'lg',
|
|
12
|
-
* onclick: async () => {
|
|
13
|
-
* await saveData();
|
|
14
|
-
* showNotification('Saved!');
|
|
15
|
-
* }
|
|
16
|
-
* };
|
|
17
|
-
* ```
|
|
18
|
-
*
|
|
19
|
-
* @property id - Unique identifier for this button
|
|
20
|
-
* @property label - Display text shown on the button
|
|
21
|
-
* @property icon - Optional icon component to display alongside the label
|
|
22
|
-
* @property variant - Button styling variant (controls color/appearance)
|
|
23
|
-
* @property size - Button size preset
|
|
24
|
-
* @property onclick - Callback function executed when clicked; can be async
|
|
25
|
-
*/
|
|
26
1
|
export interface ActionButton {
|
|
27
2
|
id: string;
|
|
28
3
|
label: string;
|
|
4
|
+
/** A Svelte component, rendered before the label. */
|
|
29
5
|
icon?: any;
|
|
30
6
|
variant?: 'default' | 'outline' | 'destructive' | 'secondary' | 'ghost';
|
|
31
7
|
size?: 'default' | 'sm' | 'lg';
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Overridable UI strings for the parameter-preset manager
|
|
3
|
-
*
|
|
4
|
-
* defaults in `DEFAULT_PRESET_LABELS`. Pass a partial object to localize.
|
|
2
|
+
* Overridable UI strings for the parameter-preset manager. Pass a partial object to localize;
|
|
3
|
+
* unset keys fall back to `DEFAULT_PRESET_LABELS`.
|
|
5
4
|
*/
|
|
6
5
|
export interface PresetLabels {
|
|
7
6
|
// Toolbar buttons
|