@3driseai/3drise-core 1.0.21 → 1.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/autoAnimate/composer.d.ts +6 -0
- package/dist/autoAnimate/composer.d.ts.map +1 -1
- package/dist/autoAnimate/composer.js +4 -3
- package/dist/autoAnimate/describeScene.js +1 -1
- package/dist/catalog/catalogInfo.d.ts +22 -0
- package/dist/catalog/catalogInfo.d.ts.map +1 -0
- package/dist/catalog/catalogInfo.js +204 -0
- package/dist/catalog/index.d.ts +3 -0
- package/dist/catalog/index.d.ts.map +1 -0
- package/dist/catalog/index.js +2 -0
- package/dist/catalog/objectCatalog.d.ts +33 -0
- package/dist/catalog/objectCatalog.d.ts.map +1 -0
- package/dist/catalog/objectCatalog.js +94 -0
- package/dist/data/allMaterials.js +2 -2
- package/dist/data/defaults.d.ts +0 -12
- package/dist/data/defaults.d.ts.map +1 -1
- package/dist/data/defaults.js +0 -12
- package/dist/data/environmentDefaults.d.ts +10 -0
- package/dist/data/environmentDefaults.d.ts.map +1 -0
- package/dist/data/environmentDefaults.js +126 -0
- package/dist/data/gridsDefaults.d.ts +2 -0
- package/dist/data/gridsDefaults.d.ts.map +1 -1
- package/dist/data/gridsDefaults.js +57 -0
- package/dist/data/index.d.ts +4 -0
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/index.js +4 -0
- package/dist/data/landDefaults.d.ts +26 -0
- package/dist/data/landDefaults.d.ts.map +1 -0
- package/dist/data/landDefaults.js +76 -0
- package/dist/data/spaceDefaults.d.ts +10 -0
- package/dist/data/spaceDefaults.d.ts.map +1 -0
- package/dist/data/spaceDefaults.js +46 -0
- package/dist/data/textDefaults.d.ts +16 -0
- package/dist/data/textDefaults.d.ts.map +1 -0
- package/dist/data/textDefaults.js +219 -0
- package/dist/environment/oceanConfigs.d.ts +5 -4
- package/dist/environment/oceanConfigs.d.ts.map +1 -1
- package/dist/environment/oceanConfigs.js +2 -2
- package/dist/environment/terrainConfigs.d.ts +78 -0
- package/dist/environment/terrainConfigs.d.ts.map +1 -1
- package/dist/environment/terrainConfigs.js +72 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/properties/registry.d.ts +15 -1
- package/dist/properties/registry.d.ts.map +1 -1
- package/dist/properties/registry.js +99 -13
- package/dist/properties/tables/environmentProperties.d.ts +9 -0
- package/dist/properties/tables/environmentProperties.d.ts.map +1 -0
- package/dist/properties/tables/environmentProperties.js +127 -0
- package/dist/properties/tables/geometryProperties.d.ts +7 -0
- package/dist/properties/tables/geometryProperties.d.ts.map +1 -1
- package/dist/properties/tables/geometryProperties.js +9 -1
- package/dist/properties/tables/gridProperties.d.ts.map +1 -1
- package/dist/properties/tables/gridProperties.js +117 -93
- package/dist/properties/tables/index.d.ts +4 -0
- package/dist/properties/tables/index.d.ts.map +1 -1
- package/dist/properties/tables/index.js +4 -0
- package/dist/properties/tables/landProperties.d.ts +5 -0
- package/dist/properties/tables/landProperties.d.ts.map +1 -0
- package/dist/properties/tables/landProperties.js +24 -0
- package/dist/properties/tables/optionalProperties.d.ts +1 -2
- package/dist/properties/tables/optionalProperties.d.ts.map +1 -1
- package/dist/properties/tables/optionalProperties.js +22 -0
- package/dist/properties/tables/spaceProperties.d.ts +8 -0
- package/dist/properties/tables/spaceProperties.d.ts.map +1 -0
- package/dist/properties/tables/spaceProperties.js +42 -0
- package/dist/properties/tables/textProperties.d.ts +4 -0
- package/dist/properties/tables/textProperties.d.ts.map +1 -0
- package/dist/properties/tables/textProperties.js +387 -0
- package/dist/properties/types.d.ts +1 -1
- package/dist/properties/types.d.ts.map +1 -1
- package/dist/services/MaterialRegistry.d.ts.map +1 -1
- package/dist/services/MaterialRegistry.js +2 -0
- package/dist/services/SceneActions.d.ts +9 -0
- package/dist/services/SceneActions.d.ts.map +1 -1
- package/dist/services/SceneActions.js +30 -0
- package/dist/services/SkySystemManager.d.ts +22 -0
- package/dist/services/SkySystemManager.d.ts.map +1 -1
- package/dist/services/SkySystemManager.js +50 -18
- package/dist/services/TextureResolver.d.ts.map +1 -1
- package/dist/services/TextureResolver.js +22 -0
- package/dist/services/index.d.ts +1 -1
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +1 -1
- package/dist/shaders/SkyboxMaterial.js +3 -3
- package/dist/shaders/StarsMaterial.d.ts +5 -0
- package/dist/shaders/StarsMaterial.d.ts.map +1 -1
- package/dist/shaders/StarsMaterial.js +59 -57
- package/dist/three/LetterV.d.ts +7 -1
- package/dist/three/LetterV.d.ts.map +1 -1
- package/dist/three/Text2DV.d.ts +7 -1
- package/dist/three/Text2DV.d.ts.map +1 -1
- package/dist/three/Text3DV.d.ts +7 -1
- package/dist/three/Text3DV.d.ts.map +1 -1
- package/dist/types/actions.d.ts +6 -6
- package/dist/types/actions.d.ts.map +1 -1
- package/dist/types/commands.d.ts +61 -0
- package/dist/types/commands.d.ts.map +1 -0
- package/dist/types/commands.js +1 -0
- package/dist/types/environment.d.ts +34 -1
- package/dist/types/environment.d.ts.map +1 -1
- package/dist/types/environment.js +15 -0
- package/dist/types/environmentObjects.d.ts +152 -0
- package/dist/types/environmentObjects.d.ts.map +1 -0
- package/dist/types/environmentObjects.js +10 -0
- package/dist/types/index.d.ts +5 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/landObjects.d.ts +15 -0
- package/dist/types/landObjects.d.ts.map +1 -0
- package/dist/types/landObjects.js +1 -0
- package/dist/types/objectSettings.d.ts +189 -16
- package/dist/types/objectSettings.d.ts.map +1 -1
- package/dist/types/objectsConfigs.d.ts +0 -3
- package/dist/types/objectsConfigs.d.ts.map +1 -1
- package/dist/types/scene3d.d.ts +12 -5
- package/dist/types/scene3d.d.ts.map +1 -1
- package/dist/types/spaceObjects.d.ts +65 -0
- package/dist/types/spaceObjects.d.ts.map +1 -0
- package/dist/types/spaceObjects.js +11 -0
- package/dist/types/textObjects.d.ts +371 -0
- package/dist/types/textObjects.d.ts.map +1 -0
- package/dist/types/textObjects.js +14 -0
- package/dist/types/types.d.ts +3 -6
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/users.d.ts +3 -1
- package/dist/types/users.d.ts.map +1 -1
- package/dist/types/users.js +2 -0
- package/dist/ui/index.d.ts +5 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +4 -0
- package/dist/ui/uiBindings.d.ts +45 -0
- package/dist/ui/uiBindings.d.ts.map +1 -0
- package/dist/ui/uiBindings.js +172 -0
- package/dist/ui/uiKeys.d.ts +14 -0
- package/dist/ui/uiKeys.d.ts.map +1 -0
- package/dist/ui/uiKeys.js +22 -0
- package/dist/ui/uiSanitize.d.ts +18 -0
- package/dist/ui/uiSanitize.d.ts.map +1 -0
- package/dist/ui/uiSanitize.js +251 -0
- package/dist/ui/uiSpec.d.ts +116 -0
- package/dist/ui/uiSpec.d.ts.map +1 -0
- package/dist/ui/uiSpec.js +173 -0
- package/dist/utils/dataUtils.d.ts.map +1 -1
- package/dist/utils/dataUtils.js +0 -6
- package/dist/utils/environmentMigration.d.ts +3 -0
- package/dist/utils/environmentMigration.d.ts.map +1 -0
- package/dist/utils/environmentMigration.js +50 -0
- package/dist/utils/generativeAnimatable.d.ts +1 -1
- package/dist/utils/generativeAnimatable.d.ts.map +1 -1
- package/dist/utils/generativeAnimatable.js +12 -0
- package/dist/utils/getAnimatableProperties.d.ts +1 -1
- package/dist/utils/getAnimatableProperties.d.ts.map +1 -1
- package/dist/utils/getAnimatableProperties.js +1 -1
- package/dist/utils/gridMigration.d.ts +3 -0
- package/dist/utils/gridMigration.d.ts.map +1 -0
- package/dist/utils/gridMigration.js +34 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +6 -0
- package/dist/utils/objectEffects.d.ts +24 -2
- package/dist/utils/objectEffects.d.ts.map +1 -1
- package/dist/utils/objectEffects.js +28 -2
- package/dist/utils/objectMigration.d.ts +9 -0
- package/dist/utils/objectMigration.d.ts.map +1 -0
- package/dist/utils/objectMigration.js +13 -0
- package/dist/utils/skyPhase.d.ts +61 -0
- package/dist/utils/skyPhase.d.ts.map +1 -0
- package/dist/utils/skyPhase.js +93 -0
- package/dist/utils/spaceMigration.d.ts +3 -0
- package/dist/utils/spaceMigration.d.ts.map +1 -0
- package/dist/utils/spaceMigration.js +46 -0
- package/dist/utils/sunUtils.d.ts +26 -1
- package/dist/utils/sunUtils.d.ts.map +1 -1
- package/dist/utils/sunUtils.js +37 -6
- package/dist/utils/textMigration.d.ts +3 -0
- package/dist/utils/textMigration.d.ts.map +1 -0
- package/dist/utils/textMigration.js +56 -0
- package/package.json +1 -1
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Reading and validating the bindings of a layer
|
|
3
|
+
// =============================================================================
|
|
4
|
+
//
|
|
5
|
+
// `collectUiBindings` reads every binding attribute of a rendered (or parsed) layer.
|
|
6
|
+
// The binder uses it to wire the DOM and to `inspect`; `validateUiLayer` uses it to
|
|
7
|
+
// tell the agent what is wrong before anything is saved. Both read the same grammar
|
|
8
|
+
// (uiSpec), so what validates is what runs.
|
|
9
|
+
//
|
|
10
|
+
import { UI_ATTR, UI_CLASSES, UI_SET_ENVIRONMENT, UI_TRIGGER_ATTRS, parseAnchor, parseBind, parseCommands, parseConditions, parseOffset, parseSetTarget, parseSetValue, } from './uiSpec.js';
|
|
11
|
+
import { resolveUiRef } from './uiKeys.js';
|
|
12
|
+
const BINDING_SELECTOR = Object.values(UI_ATTR).map(a => `[${a}]`).join(',');
|
|
13
|
+
export function describeUiElement(el) {
|
|
14
|
+
const cls = (el.getAttribute('class') ?? '').trim().split(/\s+/).filter(Boolean).slice(0, 2).join('.');
|
|
15
|
+
const text = (el.textContent ?? '').replace(/\s+/g, ' ').trim().slice(0, 30);
|
|
16
|
+
return `<${el.localName}${cls ? '.' + cls : ''}${el.id ? '#' + el.id : ''}>${text ? ` "${text}"` : ''}`;
|
|
17
|
+
}
|
|
18
|
+
/** Every element of `root` that carries a binding attribute. */
|
|
19
|
+
export function collectUiBindings(root) {
|
|
20
|
+
const uses = [];
|
|
21
|
+
for (const el of Array.from(root.querySelectorAll(BINDING_SELECTOR))) {
|
|
22
|
+
const use = {
|
|
23
|
+
el, label: describeUiElement(el), errors: [], commands: [], state: [], showWhen: [],
|
|
24
|
+
anchorOcclude: el.hasAttribute(UI_ATTR.anchorOcclude), anchorOffset: null,
|
|
25
|
+
};
|
|
26
|
+
for (const trigger of Object.keys(UI_TRIGGER_ATTRS)) {
|
|
27
|
+
const attr = UI_TRIGGER_ATTRS[trigger];
|
|
28
|
+
if (!el.hasAttribute(attr))
|
|
29
|
+
continue;
|
|
30
|
+
const { commands, errors } = parseCommands(el.getAttribute(attr));
|
|
31
|
+
use.errors.push(...errors.map(e => `${attr}: ${e}`));
|
|
32
|
+
use.commands.push({ trigger, items: commands });
|
|
33
|
+
}
|
|
34
|
+
if (el.hasAttribute(UI_ATTR.state)) {
|
|
35
|
+
const { conditions, errors } = parseConditions(el.getAttribute(UI_ATTR.state));
|
|
36
|
+
use.state = conditions;
|
|
37
|
+
use.errors.push(...errors.map(e => `${UI_ATTR.state}: ${e}`));
|
|
38
|
+
}
|
|
39
|
+
if (el.hasAttribute(UI_ATTR.showWhen)) {
|
|
40
|
+
const { conditions, errors } = parseConditions(el.getAttribute(UI_ATTR.showWhen));
|
|
41
|
+
use.showWhen = conditions;
|
|
42
|
+
use.errors.push(...errors.map(e => `${UI_ATTR.showWhen}: ${e}`));
|
|
43
|
+
}
|
|
44
|
+
if (el.hasAttribute(UI_ATTR.bind)) {
|
|
45
|
+
const { bind, error } = parseBind(el.getAttribute(UI_ATTR.bind));
|
|
46
|
+
if (bind)
|
|
47
|
+
use.bind = bind;
|
|
48
|
+
if (error)
|
|
49
|
+
use.errors.push(`${UI_ATTR.bind}: ${error}`);
|
|
50
|
+
}
|
|
51
|
+
if (el.hasAttribute(UI_ATTR.anchor)) {
|
|
52
|
+
const { anchor, error } = parseAnchor(el.getAttribute(UI_ATTR.anchor));
|
|
53
|
+
if (anchor)
|
|
54
|
+
use.anchor = anchor;
|
|
55
|
+
if (error)
|
|
56
|
+
use.errors.push(`${UI_ATTR.anchor}: ${error}`);
|
|
57
|
+
}
|
|
58
|
+
if (el.hasAttribute(UI_ATTR.set)) {
|
|
59
|
+
const { set, error } = parseSetTarget(el.getAttribute(UI_ATTR.set));
|
|
60
|
+
if (set)
|
|
61
|
+
use.set = set;
|
|
62
|
+
if (error)
|
|
63
|
+
use.errors.push(`${UI_ATTR.set}: ${error}`);
|
|
64
|
+
}
|
|
65
|
+
if (el.hasAttribute(UI_ATTR.setValue))
|
|
66
|
+
use.setValue = parseSetValue(el.getAttribute(UI_ATTR.setValue));
|
|
67
|
+
if (el.hasAttribute(UI_ATTR.anchorOffset)) {
|
|
68
|
+
use.anchorOffset = parseOffset(el.getAttribute(UI_ATTR.anchorOffset));
|
|
69
|
+
if (!use.anchorOffset)
|
|
70
|
+
use.errors.push(`${UI_ATTR.anchorOffset}: expected two numbers in px, like "0 -12".`);
|
|
71
|
+
}
|
|
72
|
+
uses.push(use);
|
|
73
|
+
}
|
|
74
|
+
return uses;
|
|
75
|
+
}
|
|
76
|
+
const known = (items) => items.map(i => i.key || i.name).join(', ') || 'none yet';
|
|
77
|
+
function checkRef(kind, ref, refs, layerIds) {
|
|
78
|
+
if (kind === 'ui')
|
|
79
|
+
return layerIds.has(ref.slice(1)) ? null : `no element with id "${ref.slice(1)}" in this layer.`;
|
|
80
|
+
const list = kind === 'action' ? refs.actions : kind === 'clip' ? refs.clips : refs.sequences;
|
|
81
|
+
return resolveUiRef(list, ref) ? null : `no ${kind} "${ref}". Known ${kind}s: ${known(list)}.`;
|
|
82
|
+
}
|
|
83
|
+
/** Generic control classes: a control with none of them gets a nudge, so users can restyle it in one place. */
|
|
84
|
+
const GENERIC = new Set(UI_CLASSES.map(c => c.name));
|
|
85
|
+
const hasGenericClass = (el) => (el.getAttribute('class') ?? '').split(/\s+/).some(c => GENERIC.has(c));
|
|
86
|
+
export function validateUiRoot(root, refs) {
|
|
87
|
+
const issues = [];
|
|
88
|
+
const add = (level, element, message) => issues.push({ level, element, message });
|
|
89
|
+
const layerIds = new Set(Array.from(root.querySelectorAll('[id]')).map(e => e.id));
|
|
90
|
+
const uses = collectUiBindings(root);
|
|
91
|
+
for (const use of uses) {
|
|
92
|
+
for (const e of use.errors)
|
|
93
|
+
add('error', use.label, e);
|
|
94
|
+
for (const group of use.commands) {
|
|
95
|
+
for (const c of group.items) {
|
|
96
|
+
const problem = checkRef(c.kind, c.ref, refs, layerIds);
|
|
97
|
+
if (problem)
|
|
98
|
+
add('error', use.label, `${UI_TRIGGER_ATTRS[group.trigger]}: ${problem}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
for (const [attr, list] of [[UI_ATTR.state, use.state], [UI_ATTR.showWhen, use.showWhen]]) {
|
|
102
|
+
for (const c of list) {
|
|
103
|
+
const problem = checkRef(c.kind, c.ref, refs, layerIds);
|
|
104
|
+
if (problem)
|
|
105
|
+
add('error', use.label, `${attr}: ${problem}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (use.bind) {
|
|
109
|
+
const problem = checkRef(use.bind.kind, use.bind.ref, refs, layerIds);
|
|
110
|
+
if (problem)
|
|
111
|
+
add('error', use.label, `${UI_ATTR.bind}: ${problem}`);
|
|
112
|
+
if (!(use.el.localName === 'input' && use.el.getAttribute('type') === 'range')) {
|
|
113
|
+
add('error', use.label, `${UI_ATTR.bind} only works on <input type="range">.`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (use.anchor && !resolveUiRef(refs.objects, use.anchor.object)) {
|
|
117
|
+
add('error', use.label, `${UI_ATTR.anchor}: no object "${use.anchor.object}". Known objects: ${known(refs.objects)}.`);
|
|
118
|
+
}
|
|
119
|
+
if (use.set) {
|
|
120
|
+
const t = use.set;
|
|
121
|
+
const env = UI_SET_ENVIRONMENT.includes(t.target);
|
|
122
|
+
if (!env && !resolveUiRef(refs.objects, t.target)) {
|
|
123
|
+
add('error', use.label, `${UI_ATTR.set}: no object "${t.target}". Use ${UI_SET_ENVIRONMENT.join(', ')} or an object (known: ${known(refs.objects)}).`);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
const problem = refs.checkSet?.(t);
|
|
127
|
+
if (problem)
|
|
128
|
+
add('error', use.label, `${UI_ATTR.set}: ${problem}`);
|
|
129
|
+
}
|
|
130
|
+
const tag = use.el.localName;
|
|
131
|
+
const type = (use.el.getAttribute('type') ?? 'text').toLowerCase();
|
|
132
|
+
const isInput = tag === 'input' || tag === 'select';
|
|
133
|
+
if (!isInput && use.setValue === undefined) {
|
|
134
|
+
add('error', use.label, `${UI_ATTR.set} on a <${tag}> needs ${UI_ATTR.setValue} (what a press writes); or use an <input> / <select>.`);
|
|
135
|
+
}
|
|
136
|
+
if (isInput && use.setValue !== undefined)
|
|
137
|
+
add('warning', use.label, `${UI_ATTR.setValue} is for buttons; on an input the control's own value is written.`);
|
|
138
|
+
if (tag === 'input' && type === 'range' && (!use.el.hasAttribute('min') || !use.el.hasAttribute('max'))) {
|
|
139
|
+
add('warning', use.label, 'a range with data-set writes its own value: set min and max in the property\'s units (the default is 0–100).');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else if (use.setValue !== undefined) {
|
|
143
|
+
add('error', use.label, `${UI_ATTR.setValue} needs ${UI_ATTR.set} (the property to write).`);
|
|
144
|
+
}
|
|
145
|
+
const hover = use.commands.some(c => (c.trigger === 'hoverIn' || c.trigger === 'hoverOut') && c.items.length);
|
|
146
|
+
const click = use.commands.some(c => c.trigger === 'click' && c.items.length);
|
|
147
|
+
if (hover && !click && !use.anchor) {
|
|
148
|
+
add('warning', use.label, 'hover does not fire on touch screens — give it a data-click too.');
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// data-show-when rewrites `hidden` on every refresh, so a ui command on the same element would be undone.
|
|
152
|
+
const uiTargets = new Set();
|
|
153
|
+
for (const use of uses)
|
|
154
|
+
for (const g of use.commands)
|
|
155
|
+
for (const c of g.items)
|
|
156
|
+
if (c.kind === 'ui')
|
|
157
|
+
uiTargets.add(c.ref.slice(1));
|
|
158
|
+
for (const use of uses) {
|
|
159
|
+
if (use.showWhen.length && use.el.id && uiTargets.has(use.el.id)) {
|
|
160
|
+
add('warning', use.label, 'has data-show-when and is also the target of a ui command; data-show-when wins on every refresh. Use one of the two.');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Generic classes: controls without one are hard for users to restyle together.
|
|
164
|
+
for (const el of Array.from(root.querySelectorAll('button, input, select'))) {
|
|
165
|
+
if (hasGenericClass(el))
|
|
166
|
+
continue;
|
|
167
|
+
const t = el.localName === 'input' ? el.getAttribute('type') ?? 'text' : el.localName;
|
|
168
|
+
const want = t === 'range' ? 'rise-slider' : el.localName === 'button' ? 'rise-button (or rise-toggle)' : 'rise-input';
|
|
169
|
+
add('warning', describeUiElement(el), `has no generic class — add ${want} so users can restyle every control in one place.`);
|
|
170
|
+
}
|
|
171
|
+
return issues;
|
|
172
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { isValidActionKey, uniqueActionKey } from '../utils/actionKeys';
|
|
2
|
+
export declare const UI_KEY_MAX_LENGTH = 64;
|
|
3
|
+
export declare const isValidUiLayerKey: typeof isValidActionKey;
|
|
4
|
+
export declare const uniqueUiLayerKey: typeof uniqueActionKey;
|
|
5
|
+
/** "Door controls" -> "door-controls". A name with no latin letters or digits (Hebrew, emoji…) falls back to "ui". */
|
|
6
|
+
export declare function toUiLayerKey(name: string | undefined | null): string;
|
|
7
|
+
export interface Refable {
|
|
8
|
+
id: string;
|
|
9
|
+
key?: string;
|
|
10
|
+
name: string;
|
|
11
|
+
}
|
|
12
|
+
/** Key, then id, then exact name — the same order SceneHandle uses. Undefined when nothing matches. */
|
|
13
|
+
export declare function resolveUiRef<T extends Refable>(items: readonly T[], ref: string): T | undefined;
|
|
14
|
+
//# sourceMappingURL=uiKeys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uiKeys.d.ts","sourceRoot":"","sources":["../../src/ui/uiKeys.ts"],"names":[],"mappings":"AAOA,OAAO,EAAyB,gBAAgB,EAAe,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE5G,eAAO,MAAM,iBAAiB,KAAwB,CAAC;AACvD,eAAO,MAAM,iBAAiB,yBAAmB,CAAC;AAClD,eAAO,MAAM,gBAAgB,wBAAkB,CAAC;AAEhD,sHAAsH;AACtH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAGpE;AAED,MAAM,WAAW,OAAO;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AAEnE,uGAAuG;AACvG,wBAAgB,YAAY,CAAC,CAAC,SAAS,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAG/F"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// UI layer keys — same slug rules as action keys
|
|
3
|
+
// =============================================================================
|
|
4
|
+
//
|
|
5
|
+
// Derived once from the name, then frozen; unique per project (the data-center
|
|
6
|
+
// enforces it on save, like action keys — it keeps its own copy of the slug code).
|
|
7
|
+
//
|
|
8
|
+
import { ACTION_KEY_MAX_LENGTH, isValidActionKey, toActionKey, uniqueActionKey } from '../utils/actionKeys.js';
|
|
9
|
+
export const UI_KEY_MAX_LENGTH = ACTION_KEY_MAX_LENGTH;
|
|
10
|
+
export const isValidUiLayerKey = isValidActionKey;
|
|
11
|
+
export const uniqueUiLayerKey = uniqueActionKey;
|
|
12
|
+
/** "Door controls" -> "door-controls". A name with no latin letters or digits (Hebrew, emoji…) falls back to "ui". */
|
|
13
|
+
export function toUiLayerKey(name) {
|
|
14
|
+
const slug = toActionKey(name);
|
|
15
|
+
return slug === 'action' && !/\baction\b/i.test(name ?? '') ? 'ui' : slug;
|
|
16
|
+
}
|
|
17
|
+
/** Key, then id, then exact name — the same order SceneHandle uses. Undefined when nothing matches. */
|
|
18
|
+
export function resolveUiRef(items, ref) {
|
|
19
|
+
if (!ref)
|
|
20
|
+
return undefined;
|
|
21
|
+
return items.find(i => i.key === ref) ?? items.find(i => i.id === ref) ?? items.find(i => i.name === ref);
|
|
22
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface SanitizeResult {
|
|
2
|
+
out: string;
|
|
3
|
+
/** What was removed or changed, for the agent / the studio to show. */
|
|
4
|
+
removed: string[];
|
|
5
|
+
}
|
|
6
|
+
/** Keep the rules the browser understood and that are safe; drop the rest. */
|
|
7
|
+
export declare function sanitizeUiCss(css: string): SanitizeResult;
|
|
8
|
+
/** Parse, clean and serialise a layer's HTML. Always returns something safe to assign to innerHTML. */
|
|
9
|
+
export declare function sanitizeUiHtml(html: string): SanitizeResult;
|
|
10
|
+
/** Both fields of a layer. */
|
|
11
|
+
export declare function sanitizeUiLayer<T extends {
|
|
12
|
+
html: string;
|
|
13
|
+
css: string;
|
|
14
|
+
}>(layer: T): {
|
|
15
|
+
layer: T;
|
|
16
|
+
removed: string[];
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=uiSanitize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uiSanitize.d.ts","sourceRoot":"","sources":["../../src/ui/uiSanitize.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,cAAc;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,uEAAuE;IACvE,OAAO,EAAE,MAAM,EAAE,CAAC;CACrB;AAmFD,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAQzD;AA2ED,uGAAuG;AACvG,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAK3D;AAED,8BAA8B;AAC9B,wBAAgB,eAAe,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE,CAAC,GAAG;IAAE,KAAK,EAAE,CAAC,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAIlH"}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Sanitizer for UI layers
|
|
3
|
+
// =============================================================================
|
|
4
|
+
//
|
|
5
|
+
// Agent-written HTML/CSS runs on 3drise.ai and on other people's sites, so it is
|
|
6
|
+
// reduced to a closed, script-free subset before it is stored and again before it is
|
|
7
|
+
// rendered (the viewer never trusts what it is handed). Browser only: it needs
|
|
8
|
+
// DOMParser and constructable stylesheets.
|
|
9
|
+
//
|
|
10
|
+
// HTML: tag + attribute allow-lists. Scripts, styles, forms, frames, `on*`,
|
|
11
|
+
// `javascript:` links and non-https / non-image sources are removed.
|
|
12
|
+
// CSS: parsed by the browser (CSSOM), so escapes cannot hide anything; rules are
|
|
13
|
+
// re-serialised from what the browser understood. @import, @font-face and
|
|
14
|
+
// other at-rules are dropped (system fonts only); a rule with a `url()` that
|
|
15
|
+
// is not https: / data:image / #fragment is dropped. External https images are
|
|
16
|
+
// allowed — that is the one thing a layer can fetch.
|
|
17
|
+
//
|
|
18
|
+
const HTML_TAGS = new Set([
|
|
19
|
+
'div', 'span', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'button', 'a', 'label', 'input', 'select', 'option',
|
|
20
|
+
'ul', 'ol', 'li', 'img', 'small', 'strong', 'em', 'b', 'i', 'u', 'br', 'hr', 'section', 'header', 'footer', 'nav',
|
|
21
|
+
'article', 'aside', 'figure', 'figcaption', 'table', 'thead', 'tbody', 'tr', 'td', 'th', 'dl', 'dt', 'dd', 'sub', 'sup', 'kbd', 'code', 'pre',
|
|
22
|
+
]);
|
|
23
|
+
const SVG_TAGS = new Set([
|
|
24
|
+
'svg', 'g', 'path', 'circle', 'ellipse', 'rect', 'line', 'polyline', 'polygon', 'defs', 'lineargradient', 'radialgradient',
|
|
25
|
+
'stop', 'title', 'desc', 'text', 'tspan', 'clippath', 'mask',
|
|
26
|
+
]);
|
|
27
|
+
/** Removed with their whole content (not unwrapped). */
|
|
28
|
+
const DROP_TAGS = new Set([
|
|
29
|
+
'script', 'style', 'iframe', 'frame', 'frameset', 'object', 'embed', 'applet', 'link', 'meta', 'base', 'template', 'noscript',
|
|
30
|
+
'form', 'textarea', 'video', 'audio', 'source', 'track', 'canvas', 'portal', 'dialog',
|
|
31
|
+
'use', 'foreignobject', 'animate', 'animatetransform', 'animatemotion', 'set', 'image', 'filter', 'pattern', 'symbol', 'marker',
|
|
32
|
+
]);
|
|
33
|
+
const GLOBAL_ATTRS = new Set([
|
|
34
|
+
'class', 'id', 'style', 'title', 'role', 'hidden', 'lang', 'dir', 'draggable',
|
|
35
|
+
]);
|
|
36
|
+
const HTML_ATTRS = new Set([
|
|
37
|
+
'href', 'src', 'alt', 'width', 'height', 'type', 'min', 'max', 'step', 'value', 'placeholder', 'checked', 'disabled',
|
|
38
|
+
'for', 'tabindex', 'target', 'rel', 'colspan', 'rowspan', 'selected', 'readonly', 'maxlength', 'inputmode',
|
|
39
|
+
]);
|
|
40
|
+
const SVG_ATTRS = new Set([
|
|
41
|
+
'viewbox', 'd', 'fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin', 'stroke-opacity', 'fill-opacity',
|
|
42
|
+
'opacity', 'cx', 'cy', 'r', 'rx', 'ry', 'x', 'y', 'x1', 'y1', 'x2', 'y2', 'width', 'height', 'points', 'transform',
|
|
43
|
+
'offset', 'stop-color', 'stop-opacity', 'gradientunits', 'gradienttransform', 'fill-rule', 'clip-rule',
|
|
44
|
+
'preserveaspectratio', 'xmlns', 'font-size', 'text-anchor', 'dominant-baseline', 'stroke-dasharray', 'stroke-dashoffset',
|
|
45
|
+
'clip-path', 'mask', 'vector-effect',
|
|
46
|
+
]);
|
|
47
|
+
const INPUT_TYPES = new Set(['range', 'text', 'number', 'checkbox', 'radio', 'search', 'color']);
|
|
48
|
+
const IMG_DATA = /^data:image\/(png|jpe?g|gif|webp|avif|svg\+xml)[;,]/i;
|
|
49
|
+
const isHttps = (u) => /^https:\/\//i.test(u);
|
|
50
|
+
/** Normalised CSS text is checked, never the raw text: the browser has already resolved escapes. */
|
|
51
|
+
const BAD_CSS = /expression\s*\(|javascript:|vbscript:|-moz-binding|behavior\s*:|image-set\s*\(|\bsrc\s*\(|@import/i;
|
|
52
|
+
const URL_RE = /url\(\s*(?:"([^"]*)"|'([^']*)'|([^)\s]*))\s*\)/gi;
|
|
53
|
+
function cssTextIsSafe(text) {
|
|
54
|
+
if (BAD_CSS.test(text))
|
|
55
|
+
return false;
|
|
56
|
+
URL_RE.lastIndex = 0;
|
|
57
|
+
let m;
|
|
58
|
+
while ((m = URL_RE.exec(text))) {
|
|
59
|
+
const u = (m[1] ?? m[2] ?? m[3] ?? '').trim();
|
|
60
|
+
if (!(isHttps(u) || IMG_DATA.test(u) || u.startsWith('#')))
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
function newSheet() {
|
|
66
|
+
try {
|
|
67
|
+
return new CSSStyleSheet();
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const GROUPING = /^@(media|supports|container|layer)\b/i;
|
|
74
|
+
function serializeRule(rule, removed) {
|
|
75
|
+
const text = rule.cssText;
|
|
76
|
+
if (rule instanceof CSSStyleRule) {
|
|
77
|
+
const own = rule.style.cssText;
|
|
78
|
+
if (!cssTextIsSafe(own) || !cssTextIsSafe(rule.selectorText)) {
|
|
79
|
+
removed.push(`rule ${rule.selectorText}`);
|
|
80
|
+
return '';
|
|
81
|
+
}
|
|
82
|
+
const nested = Array.from(rule.cssRules ?? []).map(r => serializeRule(r, removed)).join('');
|
|
83
|
+
return `${rule.selectorText}{${own}${nested ? ';' + nested : ''}}`;
|
|
84
|
+
}
|
|
85
|
+
if (rule instanceof CSSKeyframesRule) {
|
|
86
|
+
const frames = Array.from(rule.cssRules).map(f => {
|
|
87
|
+
const k = f;
|
|
88
|
+
return cssTextIsSafe(k.style.cssText) ? `${k.keyText}{${k.style.cssText}}` : '';
|
|
89
|
+
}).join('');
|
|
90
|
+
return `@keyframes ${rule.name}{${frames}}`;
|
|
91
|
+
}
|
|
92
|
+
if (GROUPING.test(text) && 'cssRules' in rule) {
|
|
93
|
+
const header = text.slice(0, text.indexOf('{')).trim();
|
|
94
|
+
if (!cssTextIsSafe(header)) {
|
|
95
|
+
removed.push(header);
|
|
96
|
+
return '';
|
|
97
|
+
}
|
|
98
|
+
const inner = Array.from(rule.cssRules).map(r => serializeRule(r, removed)).join('');
|
|
99
|
+
return `${header}{${inner}}`;
|
|
100
|
+
}
|
|
101
|
+
removed.push(text.slice(0, 40).replace(/\s+/g, ' '));
|
|
102
|
+
return '';
|
|
103
|
+
}
|
|
104
|
+
/** Keep the rules the browser understood and that are safe; drop the rest. */
|
|
105
|
+
export function sanitizeUiCss(css) {
|
|
106
|
+
const removed = [];
|
|
107
|
+
const sheet = newSheet();
|
|
108
|
+
if (!sheet)
|
|
109
|
+
return { out: '', removed: ['this browser cannot sanitize CSS (no constructable stylesheets); the CSS was not applied'] };
|
|
110
|
+
try {
|
|
111
|
+
sheet.replaceSync(css ?? '');
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return { out: '', removed: ['the CSS could not be parsed'] };
|
|
115
|
+
}
|
|
116
|
+
const out = Array.from(sheet.cssRules).map(r => serializeRule(r, removed)).join('\n');
|
|
117
|
+
if (/@import/i.test(css ?? ''))
|
|
118
|
+
removed.push('@import');
|
|
119
|
+
return { out, removed };
|
|
120
|
+
}
|
|
121
|
+
/** An inline `style="…"` value, normalised, or '' when unsafe. */
|
|
122
|
+
function sanitizeStyleAttr(value, removed) {
|
|
123
|
+
const sheet = newSheet();
|
|
124
|
+
if (!sheet) {
|
|
125
|
+
removed.push('style attribute');
|
|
126
|
+
return '';
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
sheet.replaceSync(`x{${value}}`);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return '';
|
|
133
|
+
}
|
|
134
|
+
const rule = sheet.cssRules[0];
|
|
135
|
+
const text = rule instanceof CSSStyleRule ? rule.style.cssText : '';
|
|
136
|
+
if (!cssTextIsSafe(text)) {
|
|
137
|
+
removed.push('style attribute (unsafe url or function)');
|
|
138
|
+
return '';
|
|
139
|
+
}
|
|
140
|
+
return text;
|
|
141
|
+
}
|
|
142
|
+
const describe = (el) => `<${el.localName}>`;
|
|
143
|
+
function cleanElement(el, removed) {
|
|
144
|
+
const tag = el.localName.toLowerCase();
|
|
145
|
+
const isSvg = el.namespaceURI === 'http://www.w3.org/2000/svg';
|
|
146
|
+
const allowedAttrs = isSvg ? SVG_ATTRS : HTML_ATTRS;
|
|
147
|
+
for (const attr of Array.from(el.attributes)) {
|
|
148
|
+
const name = attr.name.toLowerCase();
|
|
149
|
+
const value = attr.value;
|
|
150
|
+
let keep = false;
|
|
151
|
+
if (name.startsWith('data-') || name.startsWith('aria-'))
|
|
152
|
+
keep = true;
|
|
153
|
+
else if (GLOBAL_ATTRS.has(name))
|
|
154
|
+
keep = true;
|
|
155
|
+
else if (allowedAttrs.has(name))
|
|
156
|
+
keep = true;
|
|
157
|
+
if (!keep) {
|
|
158
|
+
removed.push(`${describe(el)} ${name}`);
|
|
159
|
+
el.removeAttribute(attr.name);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (name === 'style') {
|
|
163
|
+
const clean = sanitizeStyleAttr(value, removed);
|
|
164
|
+
if (clean)
|
|
165
|
+
el.setAttribute('style', clean);
|
|
166
|
+
else
|
|
167
|
+
el.removeAttribute('style');
|
|
168
|
+
}
|
|
169
|
+
else if (name === 'href') {
|
|
170
|
+
if (!(isHttps(value) || /^mailto:/i.test(value) || value.startsWith('#'))) {
|
|
171
|
+
removed.push(`${describe(el)} href`);
|
|
172
|
+
el.removeAttribute(attr.name);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
else if (name === 'src') {
|
|
176
|
+
if (!(isHttps(value) || IMG_DATA.test(value))) {
|
|
177
|
+
removed.push(`${describe(el)} src`);
|
|
178
|
+
el.removeAttribute(attr.name);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else if (name === 'target') {
|
|
182
|
+
if (value !== '_blank')
|
|
183
|
+
el.removeAttribute(attr.name);
|
|
184
|
+
}
|
|
185
|
+
else if (name === 'tabindex') {
|
|
186
|
+
if (value !== '0' && value !== '-1')
|
|
187
|
+
el.removeAttribute(attr.name);
|
|
188
|
+
}
|
|
189
|
+
else if (name === 'type') {
|
|
190
|
+
if (tag === 'input' && !INPUT_TYPES.has(value.toLowerCase())) {
|
|
191
|
+
removed.push(`input type=${value}`);
|
|
192
|
+
el.setAttribute('type', 'text');
|
|
193
|
+
}
|
|
194
|
+
if (tag === 'button')
|
|
195
|
+
el.setAttribute('type', 'button');
|
|
196
|
+
}
|
|
197
|
+
else if (isSvg && (name === 'fill' || name === 'stroke' || name === 'clip-path' || name === 'mask')) {
|
|
198
|
+
if (!cssTextIsSafe(value)) {
|
|
199
|
+
removed.push(`${describe(el)} ${name}`);
|
|
200
|
+
el.removeAttribute(attr.name);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (tag === 'button' && !el.hasAttribute('type'))
|
|
205
|
+
el.setAttribute('type', 'button');
|
|
206
|
+
if (tag === 'a') {
|
|
207
|
+
el.setAttribute('rel', 'noopener noreferrer');
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function cleanChildren(parent, removed) {
|
|
211
|
+
for (const child of Array.from(parent.childNodes)) {
|
|
212
|
+
if (child.nodeType === 1) {
|
|
213
|
+
const el = child;
|
|
214
|
+
const tag = el.localName.toLowerCase();
|
|
215
|
+
const isSvg = el.namespaceURI === 'http://www.w3.org/2000/svg';
|
|
216
|
+
if (DROP_TAGS.has(tag)) {
|
|
217
|
+
removed.push(`<${tag}>`);
|
|
218
|
+
parent.removeChild(el);
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
if (!(isSvg ? SVG_TAGS : HTML_TAGS).has(tag)) {
|
|
222
|
+
// Unknown tag: keep its content, drop the tag.
|
|
223
|
+
removed.push(`<${tag}>`);
|
|
224
|
+
while (el.firstChild)
|
|
225
|
+
parent.insertBefore(el.firstChild, el);
|
|
226
|
+
parent.removeChild(el);
|
|
227
|
+
// The moved children are before `el`'s old position; re-clean the parent's new children.
|
|
228
|
+
cleanChildren(parent, removed);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
cleanElement(el, removed);
|
|
232
|
+
cleanChildren(el, removed);
|
|
233
|
+
}
|
|
234
|
+
else if (child.nodeType !== 3) {
|
|
235
|
+
parent.removeChild(child); // comments, processing instructions
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/** Parse, clean and serialise a layer's HTML. Always returns something safe to assign to innerHTML. */
|
|
240
|
+
export function sanitizeUiHtml(html) {
|
|
241
|
+
const removed = [];
|
|
242
|
+
const doc = new DOMParser().parseFromString(`<!doctype html><body>${html ?? ''}`, 'text/html');
|
|
243
|
+
cleanChildren(doc.body, removed);
|
|
244
|
+
return { out: doc.body.innerHTML, removed: Array.from(new Set(removed)) };
|
|
245
|
+
}
|
|
246
|
+
/** Both fields of a layer. */
|
|
247
|
+
export function sanitizeUiLayer(layer) {
|
|
248
|
+
const h = sanitizeUiHtml(layer.html);
|
|
249
|
+
const c = sanitizeUiCss(layer.css);
|
|
250
|
+
return { layer: { ...layer, html: h.out, css: c.out }, removed: [...h.removed, ...c.removed] };
|
|
251
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
export interface UiLayer {
|
|
2
|
+
id: string;
|
|
3
|
+
/** Stable, code-facing name; unique per project (see uiKeys). */
|
|
4
|
+
key: string;
|
|
5
|
+
name: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
html: string;
|
|
8
|
+
css: string;
|
|
9
|
+
visible: boolean;
|
|
10
|
+
/** Position in the project's list (junction table `layerOrder`). */
|
|
11
|
+
layerOrder?: number;
|
|
12
|
+
}
|
|
13
|
+
export type UiRefKind = 'action' | 'sequence' | 'clip';
|
|
14
|
+
/** `ui` addresses an element of the same layer by CSS id: `ui.toggle:#menu`. */
|
|
15
|
+
export type UiCommandKind = UiRefKind | 'ui';
|
|
16
|
+
/** Attributes that run commands. */
|
|
17
|
+
export declare const UI_TRIGGER_ATTRS: {
|
|
18
|
+
readonly click: "data-click";
|
|
19
|
+
readonly hoverIn: "data-hover-in";
|
|
20
|
+
readonly hoverOut: "data-hover-out";
|
|
21
|
+
};
|
|
22
|
+
export type UiTrigger = keyof typeof UI_TRIGGER_ATTRS;
|
|
23
|
+
export declare const UI_ATTR: {
|
|
24
|
+
readonly bind: "data-bind";
|
|
25
|
+
readonly state: "data-state";
|
|
26
|
+
readonly showWhen: "data-show-when";
|
|
27
|
+
readonly anchor: "data-anchor";
|
|
28
|
+
readonly anchorOcclude: "data-anchor-occlude";
|
|
29
|
+
readonly anchorOffset: "data-anchor-offset";
|
|
30
|
+
readonly set: "data-set";
|
|
31
|
+
readonly setValue: "data-set-value";
|
|
32
|
+
readonly click: "data-click";
|
|
33
|
+
readonly hoverIn: "data-hover-in";
|
|
34
|
+
readonly hoverOut: "data-hover-out";
|
|
35
|
+
};
|
|
36
|
+
/** Verbs per command kind. `toggle` for sequences and clips alternates forward / backward. */
|
|
37
|
+
export declare const UI_VERBS: Record<UiCommandKind, readonly string[]>;
|
|
38
|
+
export interface UiCommand {
|
|
39
|
+
kind: UiCommandKind;
|
|
40
|
+
verb: string;
|
|
41
|
+
ref: string;
|
|
42
|
+
}
|
|
43
|
+
/** A condition on scene state: `action:key`, `!sequence:key`, `ui:#panel`. */
|
|
44
|
+
export interface UiCondition {
|
|
45
|
+
negate: boolean;
|
|
46
|
+
kind: UiCommandKind;
|
|
47
|
+
ref: string;
|
|
48
|
+
}
|
|
49
|
+
export interface UiBind {
|
|
50
|
+
kind: 'sequence' | 'clip';
|
|
51
|
+
ref: string;
|
|
52
|
+
}
|
|
53
|
+
export interface UiAnchor {
|
|
54
|
+
/** Object ref (id or name). */
|
|
55
|
+
object: string;
|
|
56
|
+
/** Name of a node inside a model. */
|
|
57
|
+
node?: string;
|
|
58
|
+
}
|
|
59
|
+
/** What `data-set` writes to: an object (or a node of a model), or one of the environment elements. */
|
|
60
|
+
export declare const UI_SET_ENVIRONMENT: readonly ["camera", "sky", "clouds", "ocean", "terrain"];
|
|
61
|
+
export interface UiSetTarget {
|
|
62
|
+
/** `camera` / `sky` / `clouds` / `ocean` / `terrain`, or an object ref (key, id or name). */
|
|
63
|
+
target: string;
|
|
64
|
+
/** Name of a node inside a model. */
|
|
65
|
+
node?: string;
|
|
66
|
+
/** Settings path, the same ones clip tracks use: `materialSettings.opacity`, `meshSettings.position.y`, `config.intensity`. */
|
|
67
|
+
path: string;
|
|
68
|
+
}
|
|
69
|
+
/** `engine:materialSettings.opacity`, `engine/Door_L:meshSettings.rotation.y`, `sky:elevation`. */
|
|
70
|
+
export declare function parseSetTarget(value: string | null | undefined): {
|
|
71
|
+
set?: UiSetTarget;
|
|
72
|
+
error?: string;
|
|
73
|
+
};
|
|
74
|
+
/** The value of `data-set-value`: a number, true/false, a JSON array (`[1,0,0]`), or a string (`#ff0000`). */
|
|
75
|
+
export declare function parseSetValue(value: string | null | undefined): number | boolean | string | number[] | undefined;
|
|
76
|
+
/** `action.toggle:open-door sequence.play:intro` */
|
|
77
|
+
export declare function parseCommands(value: string | null | undefined): {
|
|
78
|
+
commands: UiCommand[];
|
|
79
|
+
errors: string[];
|
|
80
|
+
};
|
|
81
|
+
/** `action:open-door !sequence:intro ui:#menu` — all must hold. */
|
|
82
|
+
export declare function parseConditions(value: string | null | undefined): {
|
|
83
|
+
conditions: UiCondition[];
|
|
84
|
+
errors: string[];
|
|
85
|
+
};
|
|
86
|
+
/** `sequence:intro` or `clip:spin` (for <input type="range">). */
|
|
87
|
+
export declare function parseBind(value: string | null | undefined): {
|
|
88
|
+
bind?: UiBind;
|
|
89
|
+
error?: string;
|
|
90
|
+
};
|
|
91
|
+
/** `object:engine` or `object:engine/Door_L` (the node name is everything after the first slash). */
|
|
92
|
+
export declare function parseAnchor(value: string | null | undefined): {
|
|
93
|
+
anchor?: UiAnchor;
|
|
94
|
+
error?: string;
|
|
95
|
+
};
|
|
96
|
+
/** `12 -8` (px, from the projected point). */
|
|
97
|
+
export declare function parseOffset(value: string | null | undefined): [number, number] | null;
|
|
98
|
+
export declare const UI_CLASS_PREFIX = "rise-";
|
|
99
|
+
export interface UiClassInfo {
|
|
100
|
+
name: string;
|
|
101
|
+
/** What it is for — shown in the agent docs. */
|
|
102
|
+
use: string;
|
|
103
|
+
/** Elements the validator expects it on (warns when a control has none of the generic classes). */
|
|
104
|
+
for?: string;
|
|
105
|
+
}
|
|
106
|
+
export declare const UI_CLASSES: readonly UiClassInfo[];
|
|
107
|
+
export interface UiVariableInfo {
|
|
108
|
+
name: string;
|
|
109
|
+
default: string;
|
|
110
|
+
use: string;
|
|
111
|
+
}
|
|
112
|
+
/** Set on the page (or the host element) to restyle every layer at once. */
|
|
113
|
+
export declare const UI_VARIABLES: readonly UiVariableInfo[];
|
|
114
|
+
/** Class of the host element each layer is rendered in (target it from the page to override variables). */
|
|
115
|
+
export declare const UI_HOST_CLASS = "rise-ui-layer";
|
|
116
|
+
//# sourceMappingURL=uiSpec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uiSpec.d.ts","sourceRoot":"","sources":["../../src/ui/uiSpec.ts"],"names":[],"mappings":"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"}
|