@hyprune/schema 0.8.0 → 0.10.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/CHANGELOG.md +73 -0
- package/README.md +17 -1
- package/lib/validate.mjs +47 -3
- package/package.json +1 -1
- package/v0.10/object.schema.json +171 -5
- package/v0.24/ipc.schema.json +19679 -0
- package/v0.25/ipc.schema.json +20085 -0
package/CHANGELOG.md
CHANGED
|
@@ -390,3 +390,76 @@ Publishes IPC 0.21 (markers, personal quests, focus sessions) and IPC 0.22 (obje
|
|
|
390
390
|
## @hyprune/schema 0.8.0 (2026-10-12)
|
|
391
391
|
|
|
392
392
|
Publishes IPC 0.23 and world format 0.9: characters and NPC instances, authored paths (route/track/generic) with stops, seats, keepClear volumes and NPC interactions.
|
|
393
|
+
|
|
394
|
+
## Unreleased: object manifest 0.10 draft additions
|
|
395
|
+
|
|
396
|
+
Draft only; no version changes. `v0.10/object.schema.json` and the validator:
|
|
397
|
+
|
|
398
|
+
- Mappings take an `average` modifier `{windowS? (1–3600, default 60),
|
|
399
|
+
override?}`: the running average of a numeric value, alone or under `scale`,
|
|
400
|
+
`thresholds`, `cases` or `format`; never with `becomes` or `counter`. The
|
|
401
|
+
validator requires an `int` or `number` input, and in drives an `override`
|
|
402
|
+
that names another non-event input of the machine (recipes take no override).
|
|
403
|
+
A direct average of an `int` is fractional, so counts need a rounded scale.
|
|
404
|
+
- New slot kind `levels` (at most 8): `{id, node, mode: fill|slide, axis?
|
|
405
|
+
(unit vector, default [0, 1, 0]), length (> 0, ≤ 10), surface? (fill only),
|
|
406
|
+
settle?: ease|spring}` and drive target `level:<id>` (outputs 0–1). With the
|
|
407
|
+
glTF: `node` and `surface` exist in the scene, the surface is not inside the
|
|
408
|
+
fill node's subtree, no node is moved by two slots, and the collision proxy
|
|
409
|
+
is never moved.
|
|
410
|
+
- `stacks`: `cap` up to 100 (was 64) and an optional `rate` (copies per second,
|
|
411
|
+
0–1000). `layout.spacing` components may be negative (−10 to 10 m; copies fill
|
|
412
|
+
from the node towards the sign) and must be non-zero where that axis's grid is
|
|
413
|
+
above 1.
|
|
414
|
+
|
|
415
|
+
## IPC 0.24: machine inputs in the catalog and live machine state (RFC-0013)
|
|
416
|
+
|
|
417
|
+
- `objects.catalog` entries gain required `machine`: null for an entry without
|
|
418
|
+
the machine trait, else `{primary: input id|null, inputs[1..16]}`. Each input
|
|
419
|
+
is the object 0.10 declaration as authored: `{id, type (enum|int|number|bool|
|
|
420
|
+
text|event|items), label, explain, default?, values? (enum), min?/max?
|
|
421
|
+
(int, number), maxLength? (text), repeat? (2–16, not expanded), states?
|
|
422
|
+
(items)}`.
|
|
423
|
+
- Required state key `live {instances[<=64]}`, published on change and never
|
|
424
|
+
per frame. An instance is `{id, asset, ref|null, source|null (the source
|
|
425
|
+
every bound input reads, null when they differ), status (ok|error|null, the
|
|
426
|
+
source's _status), error (<=256|null, its _error), stale, inputs}`; `inputs`
|
|
427
|
+
maps up to 16 input ids, repeated inputs expanded (`health.1`), to `{source|null,
|
|
428
|
+
key|null, value, raw, condition (fresh|stale|missing|error), publishedAt}`.
|
|
429
|
+
Values are scalars (strings <=256) or null; `publishedAt` is the wall-clock
|
|
430
|
+
Unix ms of the publication core holds, null when missing.
|
|
431
|
+
- Everything else is inherited from 0.23 unchanged (no new method, grant or
|
|
432
|
+
event); `session.hello` reports `0.24`. Inheritance test included.
|
|
433
|
+
|
|
434
|
+
## @hyprune/schema 0.9.0 (2026-10-13)
|
|
435
|
+
|
|
436
|
+
Publishes IPC 0.24: `objects.catalog` entries carry a placed machine's declared inputs (`machine`), and a new `live` state key reports each placed machine's source, status and per-input values (published only on change). Inherited from 0.23 otherwise; no new grant. Also the object 0.10 draft additions for gauges: the `average` mapping modifier, `levels` slots (fill/slide with spring) and stack rate, cap 100 and signed spacing.
|
|
437
|
+
|
|
438
|
+
## IPC 0.25 (draft): companions (RFC-0010 companions amendment)
|
|
439
|
+
|
|
440
|
+
- Methods (grant `objects.control`): `companion.set {ref}` (a companion-capable
|
|
441
|
+
library or user catalog ref), `companion.clear {}` and `companion.mode {mode:
|
|
442
|
+
follow|stay}`; each returns `{revision}`. Existing methods take the reserved
|
|
443
|
+
object id `companion`: `npcs.bind` (a route or a standing spot), `paths.draw
|
|
444
|
+
{bind}` and `interaction.choose`. No new grant, no new event.
|
|
445
|
+
- Required state key `companion {ref|null, name|null (<=32), state
|
|
446
|
+
(none|loading|unavailable|active|waiting|hidden), reason|null (<=64), mode
|
|
447
|
+
(follow|stay|route), path|null (qualified path ref), activity|null
|
|
448
|
+
(following|walking|running|idle|talking|sitting|waiting|carried),
|
|
449
|
+
position|null, carried}`.
|
|
450
|
+
- `objects.catalog` entries gain required `companion`: null, or `{capable,
|
|
451
|
+
name, reason|null}`.
|
|
452
|
+
- `npcs.aimed.interactions[].type` gains `companion` (Stay and Follow).
|
|
453
|
+
- Everything else is inherited from 0.24 unchanged; `session.hello` reports
|
|
454
|
+
`0.25`. Inheritance test included.
|
|
455
|
+
|
|
456
|
+
## Object format 0.10 (draft): companion block
|
|
457
|
+
|
|
458
|
+
- Optional `companion {name? (1-32), followDistance? (1.5-2.5 m), idles?
|
|
459
|
+
(<=4 unique clip names), say? (<=8 lines of <=160 characters)}`, only on
|
|
460
|
+
characters; idle variants must be clips of `traits.character` and not
|
|
461
|
+
canonical RFC-0010 clip names (`talk` is a new canonical name).
|
|
462
|
+
|
|
463
|
+
## @hyprune/schema 0.10.0 (2026-10-13)
|
|
464
|
+
|
|
465
|
+
Publishes IPC 0.25: companions. `companion.set`, `companion.clear` and `companion.mode` (follow or stay), a required `companion` state key, a companion capability on catalog entries and the aimed interaction type `companion`; and the object 0.10 draft `companion` block (name, followDistance, idles, say). Inherits 0.24.
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **Hyprune** is an explorable, world-driven desktop environment for Hyprland: your workspaces become places you can walk through, with live windows on the walls. This repository holds the versioned **schemas** for world packages, IPC and manifests, and their validator. Install Hyprune through the [hyprune](https://github.com/hyprune/hyprune) repository (starter kits, Nix, Arch); learn more at [hyprune.com](https://hyprune.com). Licence: MIT.
|
|
4
4
|
|
|
5
|
-
Versioned contracts for Hyprune, a world-driven Hyprland desktop: world packages (formats 0.1–0.9), the IPC protocol (0.1–0.
|
|
5
|
+
Versioned contracts for Hyprune, a world-driven Hyprland desktop: world packages (formats 0.1–0.9), the IPC protocol (0.1–0.25, 0.25 draft), object and library manifests (0.9, draft 0.10), extension and screen-type manifests and the input keymap. JSON Schema dialect: **2020-12**. Every version is frozen once published; core, the shell and the SDK pin these exact files. Readable reference: [hyprune.com/reference/schema](https://hyprune.com/reference/schema/).
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -203,6 +203,22 @@ values, `use`/`name`/`speed`/`stops` on editor paths, the reticle action
|
|
|
203
203
|
(objects.control), `paths.list` (state.read) and `interaction.choose`
|
|
204
204
|
(world.control). Every other 0.22 definition is unchanged.
|
|
205
205
|
|
|
206
|
+
IPC **0.24** adds live machine state (RFC-0013): `objects.catalog` entries gain
|
|
207
|
+
a required `machine` (null, or the machine trait's `primary` and its input
|
|
208
|
+
declarations as authored, repeat not expanded) and a required `live` state key
|
|
209
|
+
lists at most 64 bound machine instances with each input's binding, mapped and
|
|
210
|
+
raw value, condition (`fresh`, `stale`, `missing`, `error`) and `publishedAt`
|
|
211
|
+
(wall-clock Unix ms of the held publication, or null). No new method, event or
|
|
212
|
+
grant; every other 0.23 definition is unchanged. See the RFC-0013 amendment
|
|
213
|
+
"Live state in IPC 0.24".
|
|
214
|
+
|
|
215
|
+
IPC **0.25** (draft, the RFC-0010 companions amendment) adds the companion: a
|
|
216
|
+
required `companion` state key (the chosen ref and name, its state, mode
|
|
217
|
+
`follow`/`stay`/`route`, bound path, activity and position), a `companion`
|
|
218
|
+
capability on catalog entries, the aimed interaction type `companion`, and
|
|
219
|
+
`companion.set/clear/mode` (objects.control). Object format 0.10 (draft) gains
|
|
220
|
+
the optional `companion` block. Every other 0.24 definition is unchanged.
|
|
221
|
+
|
|
206
222
|
## Objects and libraries (RFC-0011)
|
|
207
223
|
|
|
208
224
|
Object manifests (`object.json`) and library manifests (`library.json`) use the
|
package/lib/validate.mjs
CHANGED
|
@@ -257,6 +257,17 @@ function validateObjectGltf(value, version, gltf, options, fail, warn) {
|
|
|
257
257
|
}
|
|
258
258
|
}
|
|
259
259
|
for (const s of slots.stacks || []) node(s.node, `slots.stacks ${s.id}`);
|
|
260
|
+
const levelNodes = new Map();
|
|
261
|
+
for (const l of slots.levels || []) {
|
|
262
|
+
const where = `slots.levels ${l.id}`;
|
|
263
|
+
for (const name of [l.node, l.surface]) {
|
|
264
|
+
if (name === undefined) continue;
|
|
265
|
+
if (name === traits.collidable?.node) { fail(`${where}: the collision proxy can't be moved`); continue; }
|
|
266
|
+
if (levelNodes.has(name)) fail(`${where}: node "${name}" is also moved by levels slot ${levelNodes.get(name)}`); else levelNodes.set(name, l.id);
|
|
267
|
+
}
|
|
268
|
+
const i = node(l.node, where), j = l.surface === undefined ? undefined : node(l.surface, where);
|
|
269
|
+
if (i !== undefined && j !== undefined && subtree(i).includes(j)) fail(`${where}: surface "${l.surface}" must not be inside the fill node's subtree`);
|
|
270
|
+
}
|
|
260
271
|
for (const t of slots.toggles || []) for (const name of t.nodes) {
|
|
261
272
|
if (name === traits.collidable?.node) fail(`slots.toggles ${t.id}: the collision proxy can't be toggled`);
|
|
262
273
|
node(name, `slots.toggles ${t.id}`);
|
|
@@ -328,7 +339,18 @@ function validateMachine(value, fail) {
|
|
|
328
339
|
return input;
|
|
329
340
|
};
|
|
330
341
|
// What a mapping can produce from an input: a set of scalars, a numeric range, or a kind.
|
|
342
|
+
const numeric = input => input?.type === 'int' || input?.type === 'number';
|
|
331
343
|
const outputs = (map, input) => {
|
|
344
|
+
if (map?.average) {
|
|
345
|
+
// The form applies to the running average (or the override's value): an int input's
|
|
346
|
+
// average is fractional, and an override widens a direct range.
|
|
347
|
+
const {average, ...form} = map, o = outputs(form, input);
|
|
348
|
+
if (Object.keys(form).length) return o;
|
|
349
|
+
const over = average.override === undefined ? undefined : inputs.get(average.override.split('.')[0]);
|
|
350
|
+
if (o.range && numeric(over)) o.range = [Math.min(o.range[0], over.min), Math.max(o.range[1], over.max)];
|
|
351
|
+
else if (over && !numeric(over)) return { kind: over.type };
|
|
352
|
+
return o.range ? { ...o, integer: false } : o;
|
|
353
|
+
}
|
|
332
354
|
if (!map || !Object.keys(map).length) {
|
|
333
355
|
if (input.type === 'enum') return { values: input.values };
|
|
334
356
|
if (input.type === 'bool') return { values: [true, false] };
|
|
@@ -355,16 +377,25 @@ function validateMachine(value, fail) {
|
|
|
355
377
|
const [kind, name] = [drive.target.slice(0, drive.target.indexOf(':')), drive.target.slice(drive.target.indexOf(':') + 1)];
|
|
356
378
|
const known = { animation: () => animated.has(name), position: () => positions.has(name), state: () => palettes.has(name), emission: () => palettes.has(name),
|
|
357
379
|
label: () => slotIds('labels').has(name), visible: () => slotIds('toggles').has(name), count: () => slotIds('stacks').has(name),
|
|
358
|
-
spawn: () => slotIds('spawns').has(name), effect: () => slotIds('effects').has(name) }[kind];
|
|
380
|
+
spawn: () => slotIds('spawns').has(name), effect: () => slotIds('effects').has(name), level: () => slotIds('levels').has(name) }[kind];
|
|
359
381
|
if (!known()) { fail(`${where}: unknown ${kind} target "${name}"`); continue; }
|
|
360
382
|
if (!input) continue;
|
|
383
|
+
if (drive.map?.average) {
|
|
384
|
+
const ref = drive.map.average.override;
|
|
385
|
+
if (!numeric(input)) { fail(`${where}: average needs an int or number input`); continue; }
|
|
386
|
+
if (ref !== undefined) {
|
|
387
|
+
const over = resolve(ref, `${where} average override`);
|
|
388
|
+
if (ref === drive.input) fail(`${where}: average override must name another input`);
|
|
389
|
+
else if (over && (over.type === 'event' || over.type === 'items')) fail(`${where}: average override ${ref} must be a value input, not ${over.type}`);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
361
392
|
const o = outputs(drive.map, input), event = o.kind === 'event' || (input.type === 'event' && !drive.map);
|
|
362
393
|
if (kind === 'state') {
|
|
363
394
|
if (!o.values) fail(`${where}: needs palette ids (an enum input or cases)`);
|
|
364
395
|
else for (const v of o.values) if (!palettes.get(name).has(v)) fail(`${where}: "${v}" is not a palette id of ${name}`);
|
|
365
396
|
} else if (kind === 'animation') within(o, 0, 10, where);
|
|
366
397
|
else if (kind === 'emission') within(o, 0, 16, where);
|
|
367
|
-
else if (kind === 'position') within(o, 0, 1, where);
|
|
398
|
+
else if (kind === 'position' || kind === 'level') within(o, 0, 1, where);
|
|
368
399
|
else if (kind === 'count') { within(o, 0, Infinity, where); if (o.range && !o.integer) fail(`${where}: counts need an int input or a rounded scale`); }
|
|
369
400
|
else if (kind === 'visible') { if (!o.values || o.values.some(v => typeof v !== 'boolean')) fail(`${where}: needs boolean outputs`); }
|
|
370
401
|
else if (kind === 'spawn' || kind === 'effect') { if (!event) fail(`${where}: needs an event (an event input, counter or becomes)`); }
|
|
@@ -378,6 +409,8 @@ function validateMachine(value, fail) {
|
|
|
378
409
|
for (const ref of Object.keys(recipe.fields)) resolve(ref, `${label} recipe ${recipe.name}`);
|
|
379
410
|
for (const [ref, map] of Object.entries(recipe.maps || {})) {
|
|
380
411
|
const input = resolve(ref, `${label} recipe ${recipe.name}`);
|
|
412
|
+
if (map.average && input && !numeric(input)) fail(`${label} recipe ${recipe.name}: ${ref} average needs an int or number input`);
|
|
413
|
+
if (map.average?.override !== undefined) fail(`${label} recipe ${recipe.name}: ${ref} average override is for drives only`);
|
|
381
414
|
if (input?.type === 'enum') for (const v of outputs(map, { type: 'text' }).values || [])
|
|
382
415
|
if (!input.values.includes(v)) fail(`${label} recipe ${recipe.name}: ${ref} maps to "${v}", not a value of the input`);
|
|
383
416
|
}
|
|
@@ -390,6 +423,8 @@ function validateMachine(value, fail) {
|
|
|
390
423
|
function traitCombinations(names, fail) {
|
|
391
424
|
for (const a of names) for (const c of names) if (a < c && !(traitPairs[a] || []).includes(c)) fail(`traits: ${a} and ${c} can't be combined`);
|
|
392
425
|
}
|
|
426
|
+
// RFC-0010 canonical character clip names (talk: the companions amendment).
|
|
427
|
+
const canonicalClips = ['idle', 'idle_fidget', 'walk', 'run', 'turn_left', 'turn_right', 'look_around', 'wave', 'nod', 'sit', 'sit_idle', 'stand', 'talk'];
|
|
393
428
|
function objectTraitRules(traits, slots, fail, inside = () => {}) {
|
|
394
429
|
const quat = (q, where) => { if (Math.abs(Math.hypot(...q) - 1) > 0.002) fail(`${where}: quaternion must be normalized`); };
|
|
395
430
|
const unique = (items, field, label) => { const ids = items.map(v => v[field]); if (new Set(ids).size !== ids.length) fail(`${label}: duplicate ${field}`); };
|
|
@@ -430,15 +465,24 @@ function objectTraitRules(traits, slots, fail, inside = () => {}) {
|
|
|
430
465
|
if (!tr) fail(`slots.spawns ${s.id}: unknown track ${s.track}`);
|
|
431
466
|
else for (const a of s.items || []) if (tr.items && !tr.items.assets.includes(a)) fail(`slots.spawns ${s.id}: item ${a} is not in track ${tr.id}'s items`);
|
|
432
467
|
}
|
|
468
|
+
for (const l of slots.levels || []) if (l.axis && Math.abs(Math.hypot(...l.axis) - 1) > 0.001) fail(`slots.levels ${l.id}: axis must be a unit vector`);
|
|
433
469
|
for (const s of slots.stacks || []) {
|
|
434
470
|
if (s.cap > s.layout.grid.reduce((x, y) => x * y, 1)) fail(`slots.stacks ${s.id}: cap exceeds the layout grid`);
|
|
435
|
-
s.layout.grid.forEach((n, j) => { if (n > 1 && !(s.layout.spacing[j]
|
|
471
|
+
s.layout.grid.forEach((n, j) => { if (n > 1 && !(Number.isFinite(s.layout.spacing[j]) && s.layout.spacing[j] !== 0)) fail(`slots.stacks ${s.id}: spacing ${'xyz'[j]} must be non-zero for ${n} cells`); });
|
|
436
472
|
}
|
|
437
473
|
}
|
|
438
474
|
function validateObjectManifest(value, version, options, fail, warn) {
|
|
439
475
|
const traits = value.traits, slots = value.slots || {}, names = Object.keys(traits), b = value.bounds;
|
|
440
476
|
if (options.directory !== undefined && options.directory !== value.id) fail(`id: "${value.id}" differs from its directory "${options.directory}"`);
|
|
441
477
|
traitCombinations(names, fail);
|
|
478
|
+
// Companion block (object format 0.10 draft, RFC-0010 companions amendment).
|
|
479
|
+
if (value.companion) {
|
|
480
|
+
if (!traits.character) fail('companion: only characters can be companions');
|
|
481
|
+
else for (const clip of value.companion.idles || []) {
|
|
482
|
+
if (!(clip in traits.character.clips)) fail(`companion: idle variant "${clip}" is not a clip of traits.character`);
|
|
483
|
+
if (canonicalClips.includes(clip)) fail(`companion: "${clip}" is a canonical clip, not an idle variant`);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
442
486
|
for (let j = 0; j < 3; j++) if (b.min[j] >= b.max[j]) fail(`bounds: min ${'xyz'[j]} must be below max`);
|
|
443
487
|
const t = objectLimits.boundsTolerance, surfaces = new Set(traits.movable ? traits.movable.surfaces || ['floor'] : []);
|
|
444
488
|
const one = surfaces.size === 1 ? [...surfaces][0] : undefined;
|
package/package.json
CHANGED
package/v0.10/object.schema.json
CHANGED
|
@@ -420,7 +420,7 @@
|
|
|
420
420
|
"target": {
|
|
421
421
|
"type": "string",
|
|
422
422
|
"maxLength": 160,
|
|
423
|
-
"pattern": "^(animation|state|emission|label|visible|count|spawn|effect|position):[^:\\s]{1,128}$"
|
|
423
|
+
"pattern": "^(animation|state|emission|label|visible|count|spawn|effect|position|level):[^:\\s]{1,128}$"
|
|
424
424
|
},
|
|
425
425
|
"map": {
|
|
426
426
|
"$ref": "#/$defs/mapping"
|
|
@@ -647,6 +647,45 @@
|
|
|
647
647
|
"additionalProperties": false,
|
|
648
648
|
"description": "Trait name to trait data. Unknown or reserved names reject the object."
|
|
649
649
|
},
|
|
650
|
+
"companion": {
|
|
651
|
+
"type": "object",
|
|
652
|
+
"properties": {
|
|
653
|
+
"name": {
|
|
654
|
+
"type": "string",
|
|
655
|
+
"minLength": 1,
|
|
656
|
+
"maxLength": 32,
|
|
657
|
+
"pattern": "^[^\\u0000-\\u001f\\u007f]+$"
|
|
658
|
+
},
|
|
659
|
+
"followDistance": {
|
|
660
|
+
"type": "number",
|
|
661
|
+
"minimum": 1.5,
|
|
662
|
+
"maximum": 2.5
|
|
663
|
+
},
|
|
664
|
+
"idles": {
|
|
665
|
+
"type": "array",
|
|
666
|
+
"items": {
|
|
667
|
+
"type": "string",
|
|
668
|
+
"minLength": 1,
|
|
669
|
+
"maxLength": 64,
|
|
670
|
+
"pattern": "^[a-z][a-z0-9_]*$"
|
|
671
|
+
},
|
|
672
|
+
"maxItems": 4,
|
|
673
|
+
"uniqueItems": true
|
|
674
|
+
},
|
|
675
|
+
"say": {
|
|
676
|
+
"type": "array",
|
|
677
|
+
"items": {
|
|
678
|
+
"type": "string",
|
|
679
|
+
"minLength": 1,
|
|
680
|
+
"maxLength": 160,
|
|
681
|
+
"pattern": "^[^\\u0000-\\u001f\\u007f]+$"
|
|
682
|
+
},
|
|
683
|
+
"maxItems": 8
|
|
684
|
+
}
|
|
685
|
+
},
|
|
686
|
+
"additionalProperties": false,
|
|
687
|
+
"description": "Companion settings (RFC-0010 companions amendment, draft): display name, follow distance, idle variants (clips of traits.character) and Talk lines. Characters only."
|
|
688
|
+
},
|
|
650
689
|
"slots": {
|
|
651
690
|
"type": "object",
|
|
652
691
|
"properties": {
|
|
@@ -1005,7 +1044,7 @@
|
|
|
1005
1044
|
"cap": {
|
|
1006
1045
|
"type": "integer",
|
|
1007
1046
|
"minimum": 1,
|
|
1008
|
-
"maximum":
|
|
1047
|
+
"maximum": 100
|
|
1009
1048
|
},
|
|
1010
1049
|
"layout": {
|
|
1011
1050
|
"type": "object",
|
|
@@ -1025,10 +1064,11 @@
|
|
|
1025
1064
|
"items": {
|
|
1026
1065
|
"type": "number",
|
|
1027
1066
|
"maximum": 10,
|
|
1028
|
-
"minimum":
|
|
1067
|
+
"minimum": -10
|
|
1029
1068
|
},
|
|
1030
1069
|
"minItems": 3,
|
|
1031
|
-
"maxItems": 3
|
|
1070
|
+
"maxItems": 3,
|
|
1071
|
+
"description": "Metres between copies per axis; a negative component fills from the node towards -axis. Non-zero where that axis's grid is above 1."
|
|
1032
1072
|
},
|
|
1033
1073
|
"fill": {
|
|
1034
1074
|
"enum": [
|
|
@@ -1047,6 +1087,12 @@
|
|
|
1047
1087
|
"fill"
|
|
1048
1088
|
],
|
|
1049
1089
|
"additionalProperties": false
|
|
1090
|
+
},
|
|
1091
|
+
"rate": {
|
|
1092
|
+
"type": "number",
|
|
1093
|
+
"minimum": 0,
|
|
1094
|
+
"maximum": 1000,
|
|
1095
|
+
"description": "Copies per second the shown count moves toward the target; 0 or absent: at once."
|
|
1050
1096
|
}
|
|
1051
1097
|
},
|
|
1052
1098
|
"required": [
|
|
@@ -1089,6 +1135,78 @@
|
|
|
1089
1135
|
"minItems": 1,
|
|
1090
1136
|
"maxItems": 8
|
|
1091
1137
|
},
|
|
1138
|
+
"levels": {
|
|
1139
|
+
"type": "array",
|
|
1140
|
+
"items": {
|
|
1141
|
+
"type": "object",
|
|
1142
|
+
"properties": {
|
|
1143
|
+
"id": {
|
|
1144
|
+
"$ref": "../v0.8/common.schema.json#/$defs/slug"
|
|
1145
|
+
},
|
|
1146
|
+
"node": {
|
|
1147
|
+
"type": "string",
|
|
1148
|
+
"minLength": 1,
|
|
1149
|
+
"maxLength": 128
|
|
1150
|
+
},
|
|
1151
|
+
"mode": {
|
|
1152
|
+
"enum": [
|
|
1153
|
+
"fill",
|
|
1154
|
+
"slide"
|
|
1155
|
+
]
|
|
1156
|
+
},
|
|
1157
|
+
"axis": {
|
|
1158
|
+
"type": "array",
|
|
1159
|
+
"items": {
|
|
1160
|
+
"type": "number",
|
|
1161
|
+
"minimum": -1,
|
|
1162
|
+
"maximum": 1
|
|
1163
|
+
},
|
|
1164
|
+
"minItems": 3,
|
|
1165
|
+
"maxItems": 3
|
|
1166
|
+
},
|
|
1167
|
+
"length": {
|
|
1168
|
+
"type": "number",
|
|
1169
|
+
"exclusiveMinimum": 0,
|
|
1170
|
+
"maximum": 10
|
|
1171
|
+
},
|
|
1172
|
+
"surface": {
|
|
1173
|
+
"type": "string",
|
|
1174
|
+
"minLength": 1,
|
|
1175
|
+
"maxLength": 128
|
|
1176
|
+
},
|
|
1177
|
+
"settle": {
|
|
1178
|
+
"enum": [
|
|
1179
|
+
"ease",
|
|
1180
|
+
"spring"
|
|
1181
|
+
]
|
|
1182
|
+
}
|
|
1183
|
+
},
|
|
1184
|
+
"required": [
|
|
1185
|
+
"id",
|
|
1186
|
+
"node",
|
|
1187
|
+
"mode",
|
|
1188
|
+
"length"
|
|
1189
|
+
],
|
|
1190
|
+
"additionalProperties": false,
|
|
1191
|
+
"not": {
|
|
1192
|
+
"properties": {
|
|
1193
|
+
"mode": {
|
|
1194
|
+
"const": "slide"
|
|
1195
|
+
},
|
|
1196
|
+
"surface": {
|
|
1197
|
+
"type": "string"
|
|
1198
|
+
}
|
|
1199
|
+
},
|
|
1200
|
+
"required": [
|
|
1201
|
+
"mode",
|
|
1202
|
+
"surface"
|
|
1203
|
+
]
|
|
1204
|
+
},
|
|
1205
|
+
"description": "A subtree scaled (fill) or moved (slide) by level:<id>, 0-1. axis: asset-space unit vector, default [0, 1, 0]; surface: fill only; settle default ease."
|
|
1206
|
+
},
|
|
1207
|
+
"minItems": 1,
|
|
1208
|
+
"maxItems": 8
|
|
1209
|
+
},
|
|
1092
1210
|
"ports": {
|
|
1093
1211
|
"type": "array",
|
|
1094
1212
|
"items": {
|
|
@@ -1548,10 +1666,58 @@
|
|
|
1548
1666
|
}
|
|
1549
1667
|
},
|
|
1550
1668
|
"additionalProperties": false
|
|
1669
|
+
},
|
|
1670
|
+
"average": {
|
|
1671
|
+
"type": "object",
|
|
1672
|
+
"properties": {
|
|
1673
|
+
"windowS": {
|
|
1674
|
+
"type": "number",
|
|
1675
|
+
"minimum": 1,
|
|
1676
|
+
"maximum": 3600
|
|
1677
|
+
},
|
|
1678
|
+
"override": {
|
|
1679
|
+
"type": "string",
|
|
1680
|
+
"minLength": 1,
|
|
1681
|
+
"maxLength": 40,
|
|
1682
|
+
"pattern": "^[a-z][A-Za-z0-9]*(\\.[1-9][0-9]?)?$"
|
|
1683
|
+
}
|
|
1684
|
+
},
|
|
1685
|
+
"additionalProperties": false,
|
|
1686
|
+
"description": "Running average modifier: the time-weighted mean of the value over the last windowS seconds (default 60) of fresh data; the mapping's form applies to it. override (drives only): another non-event input whose value, while present, replaces the average."
|
|
1551
1687
|
}
|
|
1552
1688
|
},
|
|
1553
1689
|
"additionalProperties": false,
|
|
1554
|
-
"description": "RFC-0013 mapping language: direct ({}), cases, thresholds, scale, becomes, counter or format."
|
|
1690
|
+
"description": "RFC-0013 mapping language: direct ({}), cases, thresholds, scale, becomes, counter or format; average modifies the value forms (not becomes or counter).",
|
|
1691
|
+
"not": {
|
|
1692
|
+
"anyOf": [
|
|
1693
|
+
{
|
|
1694
|
+
"properties": {
|
|
1695
|
+
"average": {
|
|
1696
|
+
"type": "object"
|
|
1697
|
+
},
|
|
1698
|
+
"becomes": {}
|
|
1699
|
+
},
|
|
1700
|
+
"required": [
|
|
1701
|
+
"average",
|
|
1702
|
+
"becomes"
|
|
1703
|
+
]
|
|
1704
|
+
},
|
|
1705
|
+
{
|
|
1706
|
+
"properties": {
|
|
1707
|
+
"average": {
|
|
1708
|
+
"type": "object"
|
|
1709
|
+
},
|
|
1710
|
+
"counter": {
|
|
1711
|
+
"const": true
|
|
1712
|
+
}
|
|
1713
|
+
},
|
|
1714
|
+
"required": [
|
|
1715
|
+
"average",
|
|
1716
|
+
"counter"
|
|
1717
|
+
]
|
|
1718
|
+
}
|
|
1719
|
+
]
|
|
1720
|
+
}
|
|
1555
1721
|
},
|
|
1556
1722
|
"input": {
|
|
1557
1723
|
"type": "object",
|