@selvajs/ui 6.0.0-beta.8 → 6.1.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 +3 -8
- package/dist/components/compute/AppLayout.svelte.d.ts +0 -2
- package/dist/components/compute/ComputeApp.svelte +30 -43
- package/dist/components/compute/ComputeApp.svelte.d.ts +11 -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 +42 -63
- package/dist/components/viewer/Viewer.svelte.d.ts +8 -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/loadScript.d.ts +2 -7
- package/dist/utils/loadScript.js +2 -7
- package/package.json +17 -17
- package/src/lib/components/compute/AppLayout.svelte +3 -8
- package/src/lib/components/compute/ComputeApp.svelte +30 -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 +42 -63
- 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/loadScript.ts +2 -7
package/dist/index.js
CHANGED
|
@@ -1,34 +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.
|
|
2
4
|
export * from './components/layout';
|
|
3
|
-
// Compute app (schema + viewer + solve controls composed into a runnable app)
|
|
4
5
|
export { default as AppLayout } from './components/compute/AppLayout.svelte';
|
|
5
6
|
export { default as ComputeApp } from './components/compute/ComputeApp.svelte';
|
|
6
|
-
// Error screen
|
|
7
7
|
export { default as ErrorScreen } from './components/ErrorScreen.svelte';
|
|
8
8
|
// Design-system primitives (shadcn-svelte + custom)
|
|
9
9
|
export * from './components/primitives';
|
|
10
10
|
export { default as StateDisplay } from './components/primitives/StateDisplay.svelte';
|
|
11
|
-
// 3D viewer
|
|
12
11
|
export { default as Viewer } from './components/viewer/Viewer.svelte';
|
|
13
|
-
// Utilities
|
|
14
12
|
export * from './schema/defaults';
|
|
15
13
|
export * from './schema/dynamic-value-list';
|
|
16
14
|
export * from './schema/traversal';
|
|
17
15
|
export * from './compute/solving.svelte';
|
|
18
|
-
// Solve Session seam.
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
// markup. Re-exported so transports outside this package — e.g. plugin-ui's WebSocket
|
|
22
|
-
// driver — can satisfy SolveDriver and drive a session. See CONTEXT.md.
|
|
16
|
+
// Solve Session seam. Re-exported so transports outside this package — e.g.
|
|
17
|
+
// plugin-ui's WebSocket driver — can satisfy SolveDriver and drive a session.
|
|
18
|
+
// See CONTEXT.md.
|
|
23
19
|
export { useSolveSession } from './compute/useSolveSession.svelte';
|
|
24
20
|
export { createSolveSession, createRequestResponseDriver } from '@selvajs/solve/client';
|
|
25
|
-
//
|
|
21
|
+
// Pre-step producer transit storage
|
|
26
22
|
export { writeExternalValue, readExternalValue, clearExternalValue, getExternalInputs } from '@selvajs/solve/client';
|
|
27
|
-
// Contexts & Composables
|
|
28
23
|
export * from './contexts/footerContext.svelte';
|
|
29
24
|
export * from './contexts/clientSlotContext.svelte';
|
|
30
25
|
export * from './composables/useFooterItem.svelte';
|
|
31
|
-
// Utils (cn function)
|
|
32
26
|
export * from './utils';
|
|
33
27
|
export { randomId } from './utils/randomId';
|
|
34
28
|
export { DEFAULT_PRESET_LABELS } from './types/presetLabels';
|
package/dist/public.js
CHANGED
|
@@ -1,46 +1,30 @@
|
|
|
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
|
-
//
|
|
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.
|
|
21
|
-
// Compute app (schema + viewer + solve controls composed into a runnable app)
|
|
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.
|
|
8
|
+
// Compute app: schema + viewer + solve controls composed into a runnable app.
|
|
22
9
|
export { default as AppLayout } from './components/compute/AppLayout.svelte';
|
|
23
10
|
export { default as ComputeApp } from './components/compute/ComputeApp.svelte';
|
|
24
|
-
// Standalone 3D viewer
|
|
25
|
-
//
|
|
26
|
-
// an optional `viewerConfig`. Pass `lang` to localize its chrome, or provide a
|
|
27
|
-
// locale context (setLocaleContext) at the host root to drive it app-wide.
|
|
11
|
+
// Standalone 3D viewer: renders Grasshopper meshes outside a ComputeApp host,
|
|
12
|
+
// driven directly with a `meshes` array and an optional `viewerConfig`.
|
|
28
13
|
export { default as Viewer } from './components/viewer/Viewer.svelte';
|
|
29
14
|
export { appSource, isHostOwned, isOwnedBy, pointerToNdc } from '@selvajs/visualization/render';
|
|
30
15
|
export { VIEWER_MESSAGES, DEFAULT_LOCALE, messagesFor } from './i18n/messages';
|
|
31
16
|
export { setLocaleContext, getLocaleContext } from './i18n/localeContext.svelte';
|
|
32
|
-
// Full-screen states a host app renders
|
|
17
|
+
// Full-screen states a host app renders.
|
|
33
18
|
export { default as ErrorScreen } from './components/ErrorScreen.svelte';
|
|
34
|
-
// Solve Session seam
|
|
35
|
-
//
|
|
19
|
+
// Solve Session seam, for hosts driving a session themselves rather than
|
|
20
|
+
// embedding <ComputeApp>. The session lives in `@selvajs/solve/client` and is
|
|
36
21
|
// framework-free; `useSolveSession` is the Svelte binding that makes its getters
|
|
37
|
-
// read reactively inside components.
|
|
38
|
-
// both are re-exported for hosts driving a session themselves. See CONTEXT.md.
|
|
22
|
+
// read reactively inside components. See CONTEXT.md.
|
|
39
23
|
export { useSolveSession } from './compute/useSolveSession.svelte';
|
|
40
24
|
export { createSolveSession, createRequestResponseDriver } from '@selvajs/solve/client';
|
|
41
25
|
// Pre-step producer transit storage (host apps wire producers via these).
|
|
42
26
|
export { writeExternalValue, readExternalValue, clearExternalValue, getExternalInputs } from '@selvajs/solve/client';
|
|
43
|
-
// Schema utilities a ComputeApp host
|
|
27
|
+
// Schema utilities a ComputeApp host needs to read/shape values.
|
|
44
28
|
export * from './schema/defaults';
|
|
45
29
|
export * from './schema/traversal';
|
|
46
30
|
export * from './schema/dynamic-value-list';
|
package/dist/schema/defaults.js
CHANGED
|
@@ -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';
|
|
@@ -27,12 +27,11 @@ function coercePayload(value) {
|
|
|
27
27
|
return null;
|
|
28
28
|
}
|
|
29
29
|
// Memoizes string-payload coercion. In compute mode the payload arrives as a JSON
|
|
30
|
-
// string —
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
// SameValueZero, so an identical response string from a later solve also hits.
|
|
30
|
+
// string — several MB for a large options list — and the options map is derived from
|
|
31
|
+
// `values` (TabLayout), so it recomputes on EVERY value change. Unmemoized, each
|
|
32
|
+
// keystroke re-parses megabytes and allocates a fresh options object whose new
|
|
33
|
+
// identity re-renders the whole dropdown subtree; a 6.4 MB payload drove a tab OOM.
|
|
34
|
+
// Map keys use SameValueZero, so an identical string from a later solve also hits.
|
|
36
35
|
const coerceCache = new Map();
|
|
37
36
|
const COERCE_CACHE_MAX = 8;
|
|
38
37
|
function coercePayloadMemo(value) {
|
|
@@ -48,10 +47,9 @@ function coercePayloadMemo(value) {
|
|
|
48
47
|
}
|
|
49
48
|
const parseStart = performance.now();
|
|
50
49
|
const parsed = coercePayload(value);
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
// pattern that could OOM a tab.
|
|
50
|
+
// An expensive parse should happen ONCE per distinct solve result. If this line
|
|
51
|
+
// storms the console, memoization is being defeated (payload strings differing
|
|
52
|
+
// per recompute) — the churn pattern that could OOM a tab.
|
|
55
53
|
if (value.length > 256 * 1024) {
|
|
56
54
|
const optionCount = parsed?.options ? Object.keys(parsed.options).length : 0;
|
|
57
55
|
console.info(`[DVL] parsed ${(value.length / (1024 * 1024)).toFixed(1)} MB options payload ` +
|
|
@@ -68,11 +66,11 @@ function coercePayloadMemo(value) {
|
|
|
68
66
|
/**
|
|
69
67
|
* Every dynamicValueList output reference in the schema.
|
|
70
68
|
*
|
|
71
|
-
* Canonical location is `schema.outputs[]` — the plugin's SchemaSynchronizer
|
|
72
|
-
* dynamicValueList layout item
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
69
|
+
* Canonical location is `schema.outputs[]` — the plugin's SchemaSynchronizer mirrors every
|
|
70
|
+
* dynamicValueList layout item there (`CanonicalizeDynamicValueListOutputs`). The layout scan is
|
|
71
|
+
* back-compat defense for schemas persisted by an older plugin that lacked that invariant; on
|
|
72
|
+
* current schemas it finds nothing new. Deduped by id, `outputs[]` winning so the canonical
|
|
73
|
+
* record's targetInputId takes precedence.
|
|
76
74
|
*/
|
|
77
75
|
function collectDynamicValueListSources(schema) {
|
|
78
76
|
const byId = new Map();
|
|
@@ -11,29 +11,22 @@ export declare function validateSavedState(savedState: ParameterPreset, currentS
|
|
|
11
11
|
canLoad: boolean;
|
|
12
12
|
};
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* own "loadable" rule from the schema rather than re-reading validation issues — no
|
|
18
|
-
* coupling to how sentinel ids are encoded.
|
|
14
|
+
* Params the current schema no longer has are dropped. That's the same condition
|
|
15
|
+
* `validateSavedState` flags as an error, but the rule is re-derived from the schema here
|
|
16
|
+
* rather than read back off the issue list — nothing couples to how sentinel ids are encoded.
|
|
19
17
|
*/
|
|
20
18
|
export declare function extractLoadableValues(savedState: ParameterPreset, currentSchema: UISchema): Record<string, unknown>;
|
|
21
|
-
/** The full outcome of loading a preset: what to apply, plus the diagnostics to surface. */
|
|
22
19
|
export interface PresetLoadResult {
|
|
23
|
-
/**
|
|
20
|
+
/** Safe to apply now — the params that still exist in the schema. */
|
|
24
21
|
values: Record<string, unknown>;
|
|
25
|
-
/**
|
|
22
|
+
/** Errors and warnings both, for the load dialog. */
|
|
26
23
|
issues: ValidationIssueMessage[];
|
|
27
24
|
/** No issues at all — load silently. */
|
|
28
25
|
isValid: boolean;
|
|
29
|
-
/** No
|
|
26
|
+
/** No errors — warnings alone still allow the load. */
|
|
30
27
|
canLoad: boolean;
|
|
31
28
|
}
|
|
32
|
-
/**
|
|
33
|
-
* Single entry point for applying a preset: validates against the current schema and, in
|
|
34
|
-
* the same pass, computes the loadable values. Callers get one object instead of
|
|
35
|
-
* threading a validation result back into a separate extraction step.
|
|
36
|
-
*/
|
|
29
|
+
/** Validates and computes the loadable values in one pass, so callers thread one object. */
|
|
37
30
|
export declare function loadPreset(savedState: ParameterPreset, currentSchema: UISchema): PresetLoadResult;
|
|
38
31
|
export declare function exportStateAsJson(savedState: ParameterPreset): void;
|
|
39
32
|
export declare function importStateFromJson(file: File): Promise<ParameterPreset>;
|
|
@@ -75,11 +75,9 @@ export function validateSavedState(savedState, currentSchema) {
|
|
|
75
75
|
return { isValid: issues.length === 0, issues, canLoad: !hasErrors };
|
|
76
76
|
}
|
|
77
77
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* own "loadable" rule from the schema rather than re-reading validation issues — no
|
|
82
|
-
* coupling to how sentinel ids are encoded.
|
|
78
|
+
* Params the current schema no longer has are dropped. That's the same condition
|
|
79
|
+
* `validateSavedState` flags as an error, but the rule is re-derived from the schema here
|
|
80
|
+
* rather than read back off the issue list — nothing couples to how sentinel ids are encoded.
|
|
83
81
|
*/
|
|
84
82
|
export function extractLoadableValues(savedState, currentSchema) {
|
|
85
83
|
const values = {};
|
|
@@ -91,11 +89,7 @@ export function extractLoadableValues(savedState, currentSchema) {
|
|
|
91
89
|
}
|
|
92
90
|
return values;
|
|
93
91
|
}
|
|
94
|
-
/**
|
|
95
|
-
* Single entry point for applying a preset: validates against the current schema and, in
|
|
96
|
-
* the same pass, computes the loadable values. Callers get one object instead of
|
|
97
|
-
* threading a validation result back into a separate extraction step.
|
|
98
|
-
*/
|
|
92
|
+
/** Validates and computes the loadable values in one pass, so callers thread one object. */
|
|
99
93
|
export function loadPreset(savedState, currentSchema) {
|
|
100
94
|
const validation = validateSavedState(savedState, currentSchema);
|
|
101
95
|
return {
|
|
@@ -111,8 +105,8 @@ export function exportStateAsJson(savedState) {
|
|
|
111
105
|
const date = savedState.timestamp.split('T')[0].replace(/-/g, '_');
|
|
112
106
|
const link = document.createElement('a');
|
|
113
107
|
link.href = URL.createObjectURL(blob);
|
|
114
|
-
// .slvp = Selva parameter preset. Import accepts
|
|
115
|
-
//
|
|
108
|
+
// .slvp = Selva parameter preset. Import still accepts the pre-rename .sps — same JSON,
|
|
109
|
+
// only the writer's extension changed.
|
|
116
110
|
link.download = `${safeName}_${date}.slvp`;
|
|
117
111
|
link.click();
|
|
118
112
|
URL.revokeObjectURL(link.href);
|
package/dist/schema/traversal.js
CHANGED
|
@@ -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 } from '@selvajs/schemas';
|
|
@@ -7,16 +7,12 @@ export interface VisibilityResult {
|
|
|
7
7
|
export declare function evaluateRule(rule: VisibilityRule, values: Record<string, unknown>): boolean;
|
|
8
8
|
export declare function evaluateCondition(condition: VisibilityCondition | GroupVisibilityCondition, values: Record<string, unknown>): boolean;
|
|
9
9
|
export declare function evaluateVisibility(item: LayoutItem, values: Record<string, unknown>): VisibilityResult;
|
|
10
|
-
/**
|
|
11
|
-
* Stable key for a layout item: `paramId` for inputs/outputs, `id` for linebreaks.
|
|
12
|
-
* Mirrors the key the renderers use in their `{#each}` blocks.
|
|
13
|
-
*/
|
|
10
|
+
/** Must stay identical to the key the renderers use in their `{#each}` blocks. */
|
|
14
11
|
export declare function itemKey(item: LayoutItem): string;
|
|
15
12
|
/**
|
|
16
|
-
* Evaluates
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* re-evaluating per access — one source of truth for "what's visible right now".
|
|
13
|
+
* Evaluates each item once per render. Callers that touch the same item several times
|
|
14
|
+
* (column layout, then cell render, then the default-value sweep) read this map instead of
|
|
15
|
+
* re-evaluating, so they can't disagree about what's visible mid-render.
|
|
20
16
|
*/
|
|
21
17
|
export declare function buildVisibilityMap(items: LayoutItem[], values: Record<string, unknown>): Record<string, VisibilityResult>;
|
|
22
18
|
export declare function evaluateGroupVisibility(group: {
|
|
@@ -57,18 +57,14 @@ export function evaluateVisibility(item, values) {
|
|
|
57
57
|
const actionFn = ACTIONS[action] ?? ACTIONS.show;
|
|
58
58
|
return actionFn(met, defaultValue);
|
|
59
59
|
}
|
|
60
|
-
/**
|
|
61
|
-
* Stable key for a layout item: `paramId` for inputs/outputs, `id` for linebreaks.
|
|
62
|
-
* Mirrors the key the renderers use in their `{#each}` blocks.
|
|
63
|
-
*/
|
|
60
|
+
/** Must stay identical to the key the renderers use in their `{#each}` blocks. */
|
|
64
61
|
export function itemKey(item) {
|
|
65
62
|
return item.type === 'linebreak' ? item.id : item.paramId;
|
|
66
63
|
}
|
|
67
64
|
/**
|
|
68
|
-
* Evaluates
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* re-evaluating per access — one source of truth for "what's visible right now".
|
|
65
|
+
* Evaluates each item once per render. Callers that touch the same item several times
|
|
66
|
+
* (column layout, then cell render, then the default-value sweep) read this map instead of
|
|
67
|
+
* re-evaluating, so they can't disagree about what's visible mid-render.
|
|
72
68
|
*/
|
|
73
69
|
export function buildVisibilityMap(items, values) {
|
|
74
70
|
const map = {};
|
|
@@ -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
|
saveButton: string;
|
package/dist/utils/color.js
CHANGED
|
@@ -1,38 +1,35 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Hex → OKLCH. A non-null input that isn't a 6-digit hex comes back unchanged, so
|
|
2
|
+
// callers can pass through CSS colors this function doesn't understand; null gives ''.
|
|
2
3
|
export const hexToOklch = (hex) => {
|
|
3
4
|
if (!hex)
|
|
4
5
|
return '';
|
|
5
|
-
// Normalize hex (remove # if present)
|
|
6
6
|
const normalized = hex.replace(/^#/, '');
|
|
7
|
-
// If it's not a valid hex color, return as-is
|
|
8
7
|
if (!/^[0-9A-Fa-f]{6}$/.test(normalized)) {
|
|
9
8
|
return hex;
|
|
10
9
|
}
|
|
11
|
-
// Parse hex to RGB
|
|
12
10
|
const r = parseInt(normalized.slice(0, 2), 16) / 255;
|
|
13
11
|
const g = parseInt(normalized.slice(2, 4), 16) / 255;
|
|
14
12
|
const b = parseInt(normalized.slice(4, 6), 16) / 255;
|
|
15
|
-
//
|
|
13
|
+
// sRGB transfer function, inverted: gamma-encoded channel → linear light.
|
|
16
14
|
const toLinear = (c) => (c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));
|
|
17
15
|
const lr = toLinear(r);
|
|
18
16
|
const lg = toLinear(g);
|
|
19
17
|
const lb = toLinear(b);
|
|
20
|
-
//
|
|
18
|
+
// Linear sRGB → CIE XYZ (D65 white point).
|
|
21
19
|
const x = lr * 0.4124564 + lg * 0.3575761 + lb * 0.1804375;
|
|
22
20
|
const y = lr * 0.2126729 + lg * 0.7151522 + lb * 0.072175;
|
|
23
21
|
const z = lr * 0.0193339 + lg * 0.119192 + lb * 0.9503041;
|
|
24
|
-
//
|
|
22
|
+
// XYZ → OKLab: the M1 cone-response matrix, cube root, then the M2 matrix.
|
|
25
23
|
const l_ = Math.cbrt(0.8189330101 * x + 0.3618667424 * y - 0.1288597137 * z);
|
|
26
24
|
const m_ = Math.cbrt(0.0329845436 * x + 0.9293118715 * y + 0.0361456387 * z);
|
|
27
25
|
const s_ = Math.cbrt(0.0482003018 * x + 0.2643662691 * y + 0.633851707 * z);
|
|
28
26
|
const L = 0.2104542553 * l_ + 0.793617785 * m_ - 0.0040720468 * s_;
|
|
29
27
|
const a = 1.9779984951 * l_ - 2.428592205 * m_ + 0.4505937099 * s_;
|
|
30
28
|
const b_lab = 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_;
|
|
31
|
-
//
|
|
29
|
+
// OKLab → OKLCH: chroma and hue as polar coordinates of (a, b).
|
|
32
30
|
const C = Math.sqrt(a * a + b_lab * b_lab);
|
|
33
31
|
let H = (Math.atan2(b_lab, a) * 180) / Math.PI;
|
|
34
32
|
if (H < 0)
|
|
35
33
|
H += 360;
|
|
36
|
-
// Format as OKLCH (round to reasonable precision)
|
|
37
34
|
return `oklch(${L.toFixed(3)} ${C.toFixed(3)} ${H.toFixed(1)})`;
|
|
38
35
|
};
|
package/dist/utils/debounce.d.ts
CHANGED
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Debounce utility - delays function execution until after a specified wait time
|
|
3
|
-
* has elapsed since the last time it was invoked.
|
|
4
|
-
*
|
|
5
|
-
* Use for text inputs, search boxes, etc. where you want to wait for the user
|
|
6
|
-
* to finish typing before executing.
|
|
7
|
-
*
|
|
8
|
-
* @param func The function to debounce
|
|
9
|
-
* @param wait The delay in milliseconds
|
|
10
|
-
* @returns A debounced version of the function with a cancel method
|
|
11
|
-
*/
|
|
1
|
+
/** Runs `func` once `wait` ms have passed with no further calls. `cancel()` drops a pending call. */
|
|
12
2
|
export declare function debounce<T extends (...args: any[]) => any>(func: T, wait: number): ((...args: Parameters<T>) => void) & {
|
|
13
3
|
cancel: () => void;
|
|
14
4
|
};
|
package/dist/utils/debounce.js
CHANGED
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Debounce utility - delays function execution until after a specified wait time
|
|
3
|
-
* has elapsed since the last time it was invoked.
|
|
4
|
-
*
|
|
5
|
-
* Use for text inputs, search boxes, etc. where you want to wait for the user
|
|
6
|
-
* to finish typing before executing.
|
|
7
|
-
*
|
|
8
|
-
* @param func The function to debounce
|
|
9
|
-
* @param wait The delay in milliseconds
|
|
10
|
-
* @returns A debounced version of the function with a cancel method
|
|
11
|
-
*/
|
|
1
|
+
/** Runs `func` once `wait` ms have passed with no further calls. `cancel()` drops a pending call. */
|
|
12
2
|
export function debounce(func, wait) {
|
|
13
3
|
let timeout = null;
|
|
14
4
|
function debounced(...args) {
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* preventing duplicate script tags and race conditions.
|
|
5
|
-
*
|
|
6
|
-
* @param src The script URL
|
|
7
|
-
* @param options Script attributes (crossOrigin, async, etc.)
|
|
8
|
-
* @returns Promise that resolves when script is loaded
|
|
2
|
+
* Appends a `<script>` for `src` and resolves on load. Concurrent calls for the same URL share
|
|
3
|
+
* one promise, so a script never gets a second tag. A failed load is evicted so a retry can run.
|
|
9
4
|
*/
|
|
10
5
|
export declare function loadScript(src: string, options?: {
|
|
11
6
|
crossOrigin?: string;
|
package/dist/utils/loadScript.js
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
const scriptLoaders = new Map();
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* preventing duplicate script tags and race conditions.
|
|
6
|
-
*
|
|
7
|
-
* @param src The script URL
|
|
8
|
-
* @param options Script attributes (crossOrigin, async, etc.)
|
|
9
|
-
* @returns Promise that resolves when script is loaded
|
|
3
|
+
* Appends a `<script>` for `src` and resolves on load. Concurrent calls for the same URL share
|
|
4
|
+
* one promise, so a script never gets a second tag. A failed load is evicted so a retry can run.
|
|
10
5
|
*/
|
|
11
6
|
export function loadScript(src, options) {
|
|
12
7
|
if (scriptLoaders.has(src)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@selvajs/ui",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.1.0",
|
|
4
4
|
"description": "Shared UI components and utilities for Selva applications",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "VektorNode",
|
|
@@ -57,12 +57,12 @@
|
|
|
57
57
|
"@sveltejs/kit": "^2",
|
|
58
58
|
"bits-ui": "^2.18.0",
|
|
59
59
|
"svelte": "^5",
|
|
60
|
-
"tailwind-variants": "^3.3.
|
|
60
|
+
"tailwind-variants": "^3.3.1",
|
|
61
61
|
"three": "^0.185.1",
|
|
62
|
-
"@selvajs/compute": "^4.0.
|
|
63
|
-
"@selvajs/schemas": "^5.0.0
|
|
64
|
-
"@selvajs/solve": "^1.0.
|
|
65
|
-
"@selvajs/visualization": "^1.0.0
|
|
62
|
+
"@selvajs/compute": "^4.0.1",
|
|
63
|
+
"@selvajs/schemas": "^5.0.0",
|
|
64
|
+
"@selvajs/solve": "^1.0.2",
|
|
65
|
+
"@selvajs/visualization": "^1.0.0"
|
|
66
66
|
},
|
|
67
67
|
"peerDependenciesMeta": {
|
|
68
68
|
"three": {
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"dependencies": {
|
|
73
73
|
"@floating-ui/dom": "^1.8.0",
|
|
74
74
|
"@iconify/svelte": "^5.2.1",
|
|
75
|
-
"@lucide/svelte": "^1.
|
|
75
|
+
"@lucide/svelte": "^1.30.0",
|
|
76
76
|
"clsx": "^2.1.1",
|
|
77
77
|
"mode-watcher": "^1.1.0",
|
|
78
78
|
"paneforge": "^1.0.2",
|
|
@@ -81,20 +81,20 @@
|
|
|
81
81
|
"tw-animate-css": "^1.4.0"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
|
-
"@internationalized/date": "^3.12.
|
|
85
|
-
"@sveltejs/kit": "2.70.
|
|
84
|
+
"@internationalized/date": "^3.12.3",
|
|
85
|
+
"@sveltejs/kit": "2.70.2",
|
|
86
86
|
"@sveltejs/vite-plugin-svelte": "^7.2.0",
|
|
87
|
-
"@types/three": "^0.185.
|
|
87
|
+
"@types/three": "^0.185.4",
|
|
88
88
|
"bits-ui": "^2.18.0",
|
|
89
|
-
"rimraf": "^6.
|
|
89
|
+
"rimraf": "^6.1.3",
|
|
90
90
|
"svelte": "5.56.8",
|
|
91
|
-
"tailwind-variants": "^3.3.
|
|
91
|
+
"tailwind-variants": "^3.3.1",
|
|
92
92
|
"vitest": "^4.1.10",
|
|
93
|
-
"@selvajs/
|
|
94
|
-
"@selvajs/schemas": "5.0.0
|
|
95
|
-
"@selvajs/
|
|
96
|
-
"@selvajs/
|
|
97
|
-
"@selvajs/
|
|
93
|
+
"@selvajs/visualization": "1.0.0",
|
|
94
|
+
"@selvajs/schemas": "5.0.0",
|
|
95
|
+
"@selvajs/solve": "1.0.2",
|
|
96
|
+
"@selvajs/config": "0.0.4",
|
|
97
|
+
"@selvajs/compute": "4.0.1"
|
|
98
98
|
},
|
|
99
99
|
"scripts": {
|
|
100
100
|
"predev": "node ../../scripts/sync-shared-assets.js",
|
|
@@ -38,9 +38,7 @@
|
|
|
38
38
|
onListStates?: () => ParameterPreset[] | Promise<ParameterPreset[]>;
|
|
39
39
|
presetLabels?: Partial<PresetLabels>;
|
|
40
40
|
viewerConfig?: ViewerConfig;
|
|
41
|
-
/** Branding logo URL shown as a watermark in the viewer's bottom-right corner. */
|
|
42
41
|
logoUrl?: string;
|
|
43
|
-
/** Hands the live three.js viewer to the host. See `Viewer.svelte`. */
|
|
44
42
|
onViewerReady?: (viewer: ThreeViewer) => void | (() => void);
|
|
45
43
|
}
|
|
46
44
|
|
|
@@ -79,7 +77,7 @@
|
|
|
79
77
|
const hasLeftPanel = $derived(leftTabs.length > 0);
|
|
80
78
|
const hasRightPanel = $derived(rightTabs.length > 0);
|
|
81
79
|
const hasSidebar = $derived(hasViewer || hasRightPanel);
|
|
82
|
-
//
|
|
80
|
+
// Left + right, no viewer — both panes grow to fill the full width.
|
|
83
81
|
const isTwoPanelMode = $derived(!hasViewer && hasLeftPanel && hasRightPanel);
|
|
84
82
|
|
|
85
83
|
let isMobile = $state(false);
|
|
@@ -298,7 +296,7 @@
|
|
|
298
296
|
</div>
|
|
299
297
|
{:else}
|
|
300
298
|
<div class="min-h-0 flex h-full flex-1">
|
|
301
|
-
<!--
|
|
299
|
+
<!-- Outside PaneGroup so the strip keeps a fixed width. -->
|
|
302
300
|
{#if leftCollapsed && hasSidebar && hasLeftPanel}
|
|
303
301
|
<CollapsedPanelStrip
|
|
304
302
|
side="left"
|
|
@@ -317,7 +315,6 @@
|
|
|
317
315
|
autoSaveId="selva-layout-{layoutKey}"
|
|
318
316
|
class="min-h-0 flex-1"
|
|
319
317
|
>
|
|
320
|
-
<!-- Left pane -->
|
|
321
318
|
{#if hasLeftPanel}
|
|
322
319
|
<Resizable.Pane
|
|
323
320
|
bind:this={leftPaneRef}
|
|
@@ -349,7 +346,6 @@
|
|
|
349
346
|
/>
|
|
350
347
|
{/if}
|
|
351
348
|
|
|
352
|
-
<!-- Viewer pane -->
|
|
353
349
|
{#if hasViewer}
|
|
354
350
|
<Resizable.Pane order={2} minSize={20} class="min-h-0 mx-1 flex flex-col">
|
|
355
351
|
<Viewer
|
|
@@ -359,13 +355,13 @@
|
|
|
359
355
|
{logoUrl}
|
|
360
356
|
{onViewerReady}
|
|
361
357
|
viewerConfig={{
|
|
358
|
+
...viewerConfig,
|
|
362
359
|
backgroundColor: schema?.viewerOptions?.backgroundColor
|
|
363
360
|
}}
|
|
364
361
|
/>
|
|
365
362
|
</Resizable.Pane>
|
|
366
363
|
{/if}
|
|
367
364
|
|
|
368
|
-
<!-- Right pane -->
|
|
369
365
|
{#if hasRightPanel}
|
|
370
366
|
<Resizable.Handle
|
|
371
367
|
withHandle
|
|
@@ -398,7 +394,6 @@
|
|
|
398
394
|
{/if}
|
|
399
395
|
</Resizable.PaneGroup>
|
|
400
396
|
|
|
401
|
-
<!-- Right collapsed strip -->
|
|
402
397
|
{#if rightCollapsed && hasRightPanel}
|
|
403
398
|
<CollapsedPanelStrip
|
|
404
399
|
side="right"
|