@vectoriox/iox-ui 5.76.1 → 5.78.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/design-core/index.d.ts +34 -4
- package/fesm2022/vectoriox-iox-ui-design-core.mjs +34 -4
- package/fesm2022/vectoriox-iox-ui-design-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-generation-core.mjs +647 -12
- package/fesm2022/vectoriox-iox-ui-generation-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-harness-core.mjs +38 -3
- package/fesm2022/vectoriox-iox-ui-harness-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-media-core.mjs +11 -11
- package/fesm2022/vectoriox-iox-ui-media-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-scene-core.mjs +29 -1
- package/fesm2022/vectoriox-iox-ui-scene-core.mjs.map +1 -1
- package/generation-core/index.d.ts +411 -7
- package/harness-core/index.d.ts +37 -2
- package/media-core/index.d.ts +7 -5
- package/package.json +1 -1
- package/scene-core/index.d.ts +19 -2
- package/src/design-core/flavor-spec.generated.ts +34 -4
- package/src/generation-core/compose-site.ts +12 -2
- package/src/generation-core/patterns.ts +37 -4
- package/src/generation-core/serialized-node.ts +3 -0
- package/src/generation-core/showcase.ts +18 -7
- package/src/generation-core/site-spec.generated.ts +577 -0
- package/src/generation-core/validate-site-plan.ts +24 -2
- package/src/harness-core/business-types.ts +3 -0
- package/src/harness-core/harness-spec.generated.ts +32 -2
- package/src/harness-core/motion.ts +8 -1
- package/src/media-core/frames.ts +10 -11
- package/src/scene-core/scene-edit.ts +35 -0
|
@@ -206,6 +206,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
206
206
|
"animate": "subtle",
|
|
207
207
|
"between": "cinematic"
|
|
208
208
|
},
|
|
209
|
+
"ceiling": "expressive",
|
|
209
210
|
"imagery": "Your own photos only, large and uncropped; never stock.",
|
|
210
211
|
"pages": [
|
|
211
212
|
"home",
|
|
@@ -310,6 +311,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
310
311
|
"animate": "subtle",
|
|
311
312
|
"between": "cinematic"
|
|
312
313
|
},
|
|
314
|
+
"ceiling": "expressive",
|
|
313
315
|
"imagery": "Wide, bright room shots; details of materials.",
|
|
314
316
|
"pages": [
|
|
315
317
|
"home",
|
|
@@ -419,6 +421,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
419
421
|
"animate": "subtle",
|
|
420
422
|
"between": "fade"
|
|
421
423
|
},
|
|
424
|
+
"ceiling": "expressive",
|
|
422
425
|
"imagery": "Large project photos and drawings.",
|
|
423
426
|
"pages": [
|
|
424
427
|
"home",
|
|
@@ -524,9 +527,10 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
524
527
|
"motion": "standard",
|
|
525
528
|
"moves": {
|
|
526
529
|
"scroll": "smooth",
|
|
527
|
-
"animate": "
|
|
530
|
+
"animate": "expressive",
|
|
528
531
|
"between": "slide"
|
|
529
532
|
},
|
|
533
|
+
"ceiling": "expressive",
|
|
530
534
|
"imagery": "Case-study images of your own work.",
|
|
531
535
|
"pages": [
|
|
532
536
|
"home",
|
|
@@ -625,6 +629,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
625
629
|
"animate": "standard",
|
|
626
630
|
"between": "fade"
|
|
627
631
|
},
|
|
632
|
+
"ceiling": "expressive",
|
|
628
633
|
"imagery": "Your pieces, photographed flat and true to colour.",
|
|
629
634
|
"pages": [
|
|
630
635
|
"home",
|
|
@@ -726,9 +731,10 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
726
731
|
"motion": "calm",
|
|
727
732
|
"moves": {
|
|
728
733
|
"scroll": "normal",
|
|
729
|
-
"animate": "
|
|
734
|
+
"animate": "expressive",
|
|
730
735
|
"between": "slide"
|
|
731
736
|
},
|
|
737
|
+
"ceiling": "expressive",
|
|
732
738
|
"imagery": "Your pieces and hands at work; natural light.",
|
|
733
739
|
"pages": [
|
|
734
740
|
"home",
|
|
@@ -826,6 +832,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
826
832
|
"animate": "subtle",
|
|
827
833
|
"between": "cinematic"
|
|
828
834
|
},
|
|
835
|
+
"ceiling": "expressive",
|
|
829
836
|
"imagery": "Close-ups on a plain ground.",
|
|
830
837
|
"pages": [
|
|
831
838
|
"home",
|
|
@@ -927,6 +934,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
927
934
|
"animate": "standard",
|
|
928
935
|
"between": "slide"
|
|
929
936
|
},
|
|
937
|
+
"ceiling": "expressive",
|
|
930
938
|
"imagery": "Healed work, clean photos.",
|
|
931
939
|
"pages": [
|
|
932
940
|
"home",
|
|
@@ -1022,6 +1030,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
1022
1030
|
"animate": "subtle",
|
|
1023
1031
|
"between": "fade"
|
|
1024
1032
|
},
|
|
1033
|
+
"ceiling": "standard",
|
|
1025
1034
|
"imagery": "Rooms, textures, hands; no faces needed.",
|
|
1026
1035
|
"pages": [
|
|
1027
1036
|
"home",
|
|
@@ -1140,6 +1149,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
1140
1149
|
"animate": "standard",
|
|
1141
1150
|
"between": "fade"
|
|
1142
1151
|
},
|
|
1152
|
+
"ceiling": "lively",
|
|
1143
1153
|
"imagery": "Your own before-and-after photos.",
|
|
1144
1154
|
"pages": [
|
|
1145
1155
|
"home",
|
|
@@ -1241,6 +1251,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
1241
1251
|
"animate": "subtle",
|
|
1242
1252
|
"between": "fade"
|
|
1243
1253
|
},
|
|
1254
|
+
"ceiling": "lively",
|
|
1244
1255
|
"imagery": "The room, real classes, daylight.",
|
|
1245
1256
|
"pages": [
|
|
1246
1257
|
"home",
|
|
@@ -1350,6 +1361,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
1350
1361
|
"animate": "lively",
|
|
1351
1362
|
"between": "slide"
|
|
1352
1363
|
},
|
|
1364
|
+
"ceiling": "lively",
|
|
1353
1365
|
"imagery": "Real people training; movement.",
|
|
1354
1366
|
"pages": [
|
|
1355
1367
|
"home",
|
|
@@ -1451,6 +1463,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
1451
1463
|
"animate": "subtle",
|
|
1452
1464
|
"between": "none"
|
|
1453
1465
|
},
|
|
1466
|
+
"ceiling": "subtle",
|
|
1454
1467
|
"imagery": "A calm room, daylight; your portrait.",
|
|
1455
1468
|
"pages": [
|
|
1456
1469
|
"home",
|
|
@@ -1560,6 +1573,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
1560
1573
|
"animate": "subtle",
|
|
1561
1574
|
"between": "fade"
|
|
1562
1575
|
},
|
|
1576
|
+
"ceiling": "subtle",
|
|
1563
1577
|
"imagery": "Your team and rooms; no stock doctors.",
|
|
1564
1578
|
"pages": [
|
|
1565
1579
|
"home",
|
|
@@ -1664,6 +1678,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
1664
1678
|
"animate": "subtle",
|
|
1665
1679
|
"between": "fade"
|
|
1666
1680
|
},
|
|
1681
|
+
"ceiling": "subtle",
|
|
1667
1682
|
"imagery": "Your clinic and team.",
|
|
1668
1683
|
"pages": [
|
|
1669
1684
|
"home",
|
|
@@ -1769,6 +1784,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
1769
1784
|
"animate": "standard",
|
|
1770
1785
|
"between": "fade"
|
|
1771
1786
|
},
|
|
1787
|
+
"ceiling": "standard",
|
|
1772
1788
|
"imagery": "Real animals you treat (with permission).",
|
|
1773
1789
|
"pages": [
|
|
1774
1790
|
"home",
|
|
@@ -1866,6 +1882,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
1866
1882
|
"animate": "standard",
|
|
1867
1883
|
"between": "fade"
|
|
1868
1884
|
},
|
|
1885
|
+
"ceiling": "expressive",
|
|
1869
1886
|
"imagery": "Your own food and room photos.",
|
|
1870
1887
|
"pages": [
|
|
1871
1888
|
"home",
|
|
@@ -1974,6 +1991,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
1974
1991
|
"animate": "standard",
|
|
1975
1992
|
"between": "slide"
|
|
1976
1993
|
},
|
|
1994
|
+
"ceiling": "lively",
|
|
1977
1995
|
"imagery": "Your bakes and drinks, close up.",
|
|
1978
1996
|
"pages": [
|
|
1979
1997
|
"home",
|
|
@@ -2076,6 +2094,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
2076
2094
|
"animate": "subtle",
|
|
2077
2095
|
"between": "cinematic"
|
|
2078
2096
|
},
|
|
2097
|
+
"ceiling": "expressive",
|
|
2079
2098
|
"imagery": "The cellar, the bar, the bottle.",
|
|
2080
2099
|
"pages": [
|
|
2081
2100
|
"home",
|
|
@@ -2178,6 +2197,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
2178
2197
|
"animate": "standard",
|
|
2179
2198
|
"between": "slide"
|
|
2180
2199
|
},
|
|
2200
|
+
"ceiling": "expressive",
|
|
2181
2201
|
"imagery": "Product photos on plain grounds; real people wearing them.",
|
|
2182
2202
|
"pages": [
|
|
2183
2203
|
"home",
|
|
@@ -2272,6 +2292,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
2272
2292
|
"animate": "standard",
|
|
2273
2293
|
"between": "fade"
|
|
2274
2294
|
},
|
|
2295
|
+
"ceiling": "lively",
|
|
2275
2296
|
"imagery": "Your own arrangements in daylight.",
|
|
2276
2297
|
"pages": [
|
|
2277
2298
|
"home",
|
|
@@ -2367,6 +2388,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
2367
2388
|
"animate": "subtle",
|
|
2368
2389
|
"between": "fade"
|
|
2369
2390
|
},
|
|
2391
|
+
"ceiling": "expressive",
|
|
2370
2392
|
"imagery": "Pieces in real rooms and on plain grounds.",
|
|
2371
2393
|
"pages": [
|
|
2372
2394
|
"home",
|
|
@@ -2463,6 +2485,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
2463
2485
|
"animate": "subtle",
|
|
2464
2486
|
"between": "fade"
|
|
2465
2487
|
},
|
|
2488
|
+
"ceiling": "standard",
|
|
2466
2489
|
"imagery": "Your portrait; diagrams over stock.",
|
|
2467
2490
|
"pages": [
|
|
2468
2491
|
"home",
|
|
@@ -2563,6 +2586,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
2563
2586
|
"animate": "standard",
|
|
2564
2587
|
"between": "fade"
|
|
2565
2588
|
},
|
|
2589
|
+
"ceiling": "standard",
|
|
2566
2590
|
"imagery": "Your portrait, working with people.",
|
|
2567
2591
|
"pages": [
|
|
2568
2592
|
"home",
|
|
@@ -2658,6 +2682,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
2658
2682
|
"animate": "still",
|
|
2659
2683
|
"between": "none"
|
|
2660
2684
|
},
|
|
2685
|
+
"ceiling": "subtle",
|
|
2661
2686
|
"imagery": "Your team; your office.",
|
|
2662
2687
|
"pages": [
|
|
2663
2688
|
"home",
|
|
@@ -2767,6 +2792,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
2767
2792
|
"animate": "standard",
|
|
2768
2793
|
"between": "slide"
|
|
2769
2794
|
},
|
|
2795
|
+
"ceiling": "lively",
|
|
2770
2796
|
"imagery": "Your own property photos; never stock.",
|
|
2771
2797
|
"pages": [
|
|
2772
2798
|
"home",
|
|
@@ -2871,6 +2897,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
2871
2897
|
"animate": "standard",
|
|
2872
2898
|
"between": "fade"
|
|
2873
2899
|
},
|
|
2900
|
+
"ceiling": "standard",
|
|
2874
2901
|
"imagery": "Real people you help (with permission).",
|
|
2875
2902
|
"pages": [
|
|
2876
2903
|
"home",
|
|
@@ -2963,6 +2990,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
2963
2990
|
"animate": "standard",
|
|
2964
2991
|
"between": "cinematic"
|
|
2965
2992
|
},
|
|
2993
|
+
"ceiling": "expressive",
|
|
2966
2994
|
"imagery": "Real events at your venue.",
|
|
2967
2995
|
"pages": [
|
|
2968
2996
|
"home",
|
|
@@ -3064,6 +3092,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
3064
3092
|
"animate": "subtle",
|
|
3065
3093
|
"between": "cinematic"
|
|
3066
3094
|
},
|
|
3095
|
+
"ceiling": "expressive",
|
|
3067
3096
|
"imagery": "Rooms, views, breakfast; your own photos.",
|
|
3068
3097
|
"pages": [
|
|
3069
3098
|
"home",
|
|
@@ -3171,6 +3200,7 @@ export const SPEC_BUSINESS_TYPES = [
|
|
|
3171
3200
|
"animate": "lively",
|
|
3172
3201
|
"between": "slide"
|
|
3173
3202
|
},
|
|
3203
|
+
"ceiling": "lively",
|
|
3174
3204
|
"imagery": "Real classes (with permission).",
|
|
3175
3205
|
"pages": [
|
|
3176
3206
|
"home",
|
|
@@ -25,6 +25,8 @@ export interface MotionBasis {
|
|
|
25
25
|
references: boolean;
|
|
26
26
|
/** The owner asked for little movement: all three were capped. */
|
|
27
27
|
littleMovement: boolean;
|
|
28
|
+
/** SC6: How animated was held at the type's ceiling (a law firm stays calm whatever the style or references). */
|
|
29
|
+
ceiling: boolean;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
export interface MotionStart extends DesignMotion {
|
|
@@ -66,9 +68,14 @@ export function motionFor(
|
|
|
66
68
|
const wanted = ref ?? base.animate;
|
|
67
69
|
let out: DesignMotion = { scroll: base.scroll, animate: calmerChoice('animate', wanted, preset.moves.animate), between: base.between };
|
|
68
70
|
const why = [type ? `${type.label}: ${triple(base)}` : `${preset.name}: the style's own`];
|
|
69
|
-
const basis: MotionBasis = { type: type?.id ?? null, calmerStyle: out.animate !== wanted, references: !!(ref && type), littleMovement: false };
|
|
71
|
+
const basis: MotionBasis = { type: type?.id ?? null, calmerStyle: out.animate !== wanted, references: !!(ref && type), littleMovement: false, ceiling: false };
|
|
70
72
|
if (basis.calmerStyle) why.push(`${preset.name} moves less (${label('animate', preset.moves.animate)})`);
|
|
71
73
|
if (basis.references) why.push(`your references: ${label('animate', ref!)}`);
|
|
74
|
+
if (type && calmerChoice('animate', out.animate, type.ceiling) !== out.animate) {
|
|
75
|
+
out = { ...out, animate: type.ceiling };
|
|
76
|
+
basis.ceiling = true;
|
|
77
|
+
why.push(`${type.label} goes no further than ${label('animate', type.ceiling)}`);
|
|
78
|
+
}
|
|
72
79
|
if ((input.avoid ?? []).includes(LITTLE_MOVEMENT_AVOID)) {
|
|
73
80
|
basis.littleMovement = true;
|
|
74
81
|
out = {
|
package/src/media-core/frames.ts
CHANGED
|
@@ -42,8 +42,12 @@ export enum FrameExtension {
|
|
|
42
42
|
Jpeg = 'jpg',
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
/**
|
|
46
|
-
|
|
45
|
+
/**
|
|
46
|
+
* The most pictures one set may hold. A film's smoothness is its density, pictures per screen of scrolling: 210 over 18
|
|
47
|
+
* screens stepped on Doron's iPhone as a glide slowed, 1050 did not (2026-11-05). Memory is not what limits it: the player
|
|
48
|
+
* keeps the downloaded files (about 10 kB a phone picture) and only a window of them unpacked (`unpackedFrameCount`).
|
|
49
|
+
*/
|
|
50
|
+
export const MAX_FRAMES = 1200;
|
|
47
51
|
/** The most one picture may weigh when uploaded. */
|
|
48
52
|
export const MAX_FRAME_BYTES = 3 * 1024 * 1024;
|
|
49
53
|
/** A sequence's name in storage: what the browser generates and what the server accepts in a key. */
|
|
@@ -206,10 +210,10 @@ export interface FrameSizes {
|
|
|
206
210
|
* tall stage by its height, so a picture sized to the phone's width would be enlarged five times. */
|
|
207
211
|
export const WIDE_LONG_SIDE = 1600;
|
|
208
212
|
export const PHONE_LONG_SIDE = 1100;
|
|
209
|
-
/** How many pictures a second of video becomes, before the cap. */
|
|
210
|
-
const VIDEO_FRAMES_PER_SECOND =
|
|
211
|
-
/** A video's cap:
|
|
212
|
-
const VIDEO_MAX_FRAMES =
|
|
213
|
+
/** How many pictures a second of video becomes, before the cap: what most recordings hold, so few are the same twice. */
|
|
214
|
+
const VIDEO_FRAMES_PER_SECOND = 30;
|
|
215
|
+
/** A video's cap: half a minute at that rate. A set of pictures is the author's own count, up to `MAX_FRAMES`. */
|
|
216
|
+
const VIDEO_MAX_FRAMES = 900;
|
|
213
217
|
|
|
214
218
|
/** `width`×`height` fitted inside `longSide`, never enlarged. */
|
|
215
219
|
export function fittedFrameSize(width: number, height: number, longSide: number): FrameSizes {
|
|
@@ -229,11 +233,6 @@ export function sampledFrameIndexes(sourceCount: number, count: number): number[
|
|
|
229
233
|
return Array.from({ length: count }, (_, i) => Math.round((i * (sourceCount - 1)) / (count - 1)));
|
|
230
234
|
}
|
|
231
235
|
|
|
232
|
-
/** A phone plays half the pictures: it has less memory, and half is plenty on a small screen. */
|
|
233
|
-
export function phoneFrameCount(wideCount: number): number {
|
|
234
|
-
return Math.max(2, Math.ceil(wideCount / 2));
|
|
235
|
-
}
|
|
236
|
-
|
|
237
236
|
const NATURAL = new Intl.Collator('en', { numeric: true, sensitivity: 'base' });
|
|
238
237
|
|
|
239
238
|
/** Rendered frames in their real order: `frame2` before `frame10`. */
|
|
@@ -238,6 +238,41 @@ export function sceneHeightForScreens(screens: number): string {
|
|
|
238
238
|
return `${roundTo((held + 1) * 100, 1)}vh`;
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
+
/**
|
|
242
|
+
* Pictures per screen of scrolling at which a played film stays smooth as a glide slows to a stop. Measured on Doron's
|
|
243
|
+
* iPhone (2026-11-05): Studio Alma's film at about 11 a screen stepped, at about 55 it did not.
|
|
244
|
+
*/
|
|
245
|
+
export const SCENE_FILM_SMOOTH = 40;
|
|
246
|
+
|
|
247
|
+
export interface SceneFilmDensity {
|
|
248
|
+
/** Pictures per screen of scrolling over most of the play: half its scroll holds at least this many. */
|
|
249
|
+
perScreen: number;
|
|
250
|
+
/** The screens of scrolling the film plays over; where it holds still is not counted. */
|
|
251
|
+
screens: number;
|
|
252
|
+
smooth: boolean;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* How densely a film of `count` pictures plays on a Scene of `screens`, by its Play track; null when nothing plays. A
|
|
257
|
+
* film's smoothness is its density, never the drawing: spread thin, a slowing glide holds each picture for many refreshes.
|
|
258
|
+
*/
|
|
259
|
+
export function sceneFilmDensity(tracks: ReadonlyArray<SceneTrack> | null | undefined, screens: number | null, count: number): SceneFilmDensity | null {
|
|
260
|
+
const keys = normalizeTracks(tracks).find(track => track.prop === SceneProp.Play)?.keys ?? [];
|
|
261
|
+
if (count < 2 || !screens || screens <= 0) return null;
|
|
262
|
+
const stretches: { perScreen: number; screens: number }[] = [];
|
|
263
|
+
for (let i = 1; i < keys.length; i++) {
|
|
264
|
+
const scrolled = (keys[i][0] - keys[i - 1][0]) * screens;
|
|
265
|
+
const played = Math.abs(Number(keys[i][1]) - Number(keys[i - 1][1]));
|
|
266
|
+
if (scrolled > 0 && played > 0) stretches.push({ perScreen: ((played / 100) * (count - 1)) / scrolled, screens: scrolled });
|
|
267
|
+
}
|
|
268
|
+
if (!stretches.length) return null;
|
|
269
|
+
stretches.sort((a, b) => a.perScreen - b.perScreen);
|
|
270
|
+
const total = stretches.reduce((sum, stretch) => sum + stretch.screens, 0);
|
|
271
|
+
let covered = 0;
|
|
272
|
+
const typical = stretches.find(stretch => (covered += stretch.screens) >= total / 2)!;
|
|
273
|
+
return { perScreen: Math.round(typical.perScreen), screens: roundTo(total, 1), smooth: typical.perScreen >= SCENE_FILM_SMOOTH };
|
|
274
|
+
}
|
|
275
|
+
|
|
241
276
|
/** Motion v2 §3d's limits for scroll-linked work on one page. */
|
|
242
277
|
export const SCENE_BUDGET = { heldSections: 1, movingLayers: 8 } as const;
|
|
243
278
|
|