pixifact 0.4.0 → 0.5.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/dist/compiler/builtinScenes.d.ts +0 -5
- package/dist/compiler/builtinScenes.js +0 -26
- package/dist/compiler/commands/index.js +8 -5
- package/dist/compiler/compileScenes.d.ts +3 -1
- package/dist/compiler/compileScenes.js +32 -2
- package/dist/compiler/index.d.ts +0 -1
- package/dist/compiler/index.js +0 -1
- package/dist/compiler/pixiNodeSchema.d.ts +4 -3
- package/dist/compiler/pixiNodeSchema.js +60 -6
- package/dist/compiler/sceneValidation.d.ts +3 -1
- package/dist/compiler/sceneValidation.js +105 -6
- package/dist/compiler/spec.d.ts +18 -7
- package/dist/compiler/spec.js +3 -1
- package/dist/compiler/templateParser.js +19 -0
- package/dist/compiler/templateSerializer.js +7 -1
- package/dist/compiler/typescriptCompiler.js +66 -8
- package/dist/compiler-node/builtinSceneInterfaces.d.ts +6 -0
- package/dist/compiler-node/builtinSceneInterfaces.js +27 -0
- package/dist/compiler-node/index.d.ts +2 -0
- package/dist/compiler-node/index.js +2 -0
- package/dist/{compiler → compiler-node}/scriptInterfaceExtractor.d.ts +1 -1
- package/dist/{compiler → compiler-node}/scriptInterfaceExtractor.js +157 -25
- package/dist/runtime/BitmapLabel.d.ts +9 -0
- package/dist/runtime/BitmapLabel.js +7 -0
- package/dist/runtime/Label.d.ts +9 -0
- package/dist/runtime/Label.js +7 -0
- package/dist/runtime/ScrollContainer.d.ts +2 -0
- package/dist/runtime/ScrollContainer.js +12 -2
- package/dist/runtime/TextBoxControl.d.ts +48 -0
- package/dist/runtime/TextBoxControl.js +162 -0
- package/dist/runtime/index.d.ts +2 -0
- package/dist/runtime/index.js +2 -0
- package/dist/runtime/runtimeBehavior.d.ts +3 -0
- package/dist/runtime/runtimeBehavior.js +11 -0
- package/dist/scene/decorators.d.ts +2 -1
- package/dist/scene/decorators.js +12 -7
- package/dist/scene/index.d.ts +1 -0
- package/dist/scene/index.js +1 -0
- package/dist/scene/sceneBindingRuntime.d.ts +10 -0
- package/dist/scene/sceneBindingRuntime.js +124 -0
- package/package.json +1 -1
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { SceneTemplateInterface } from './spec.js';
|
|
2
1
|
export declare const builtinScenePrefix = "pixifact:";
|
|
3
2
|
export declare const builtinSceneNames: readonly [];
|
|
4
3
|
export type BuiltinSceneName = typeof builtinSceneNames[number];
|
|
@@ -8,7 +7,3 @@ export declare function builtinSceneAssetId(name: string): string;
|
|
|
8
7
|
export declare function isBuiltinSceneAssetId(value: string): boolean;
|
|
9
8
|
export declare function builtinSceneNameFromAssetId(assetId: string): never;
|
|
10
9
|
export declare function builtinSceneAssetIds(): string[];
|
|
11
|
-
export declare function builtinSceneInterface(assetId: string, scriptSources: BuiltinSceneScriptSources): SceneTemplateInterface;
|
|
12
|
-
export declare function builtinSceneInterfaces(scriptSources: BuiltinSceneScriptSources): {
|
|
13
|
-
[k: string]: SceneTemplateInterface;
|
|
14
|
-
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { extractSceneScriptInterfaces } from './scriptInterfaceExtractor.js';
|
|
2
1
|
export const builtinScenePrefix = 'pixifact:';
|
|
3
2
|
export const builtinSceneNames = [];
|
|
4
3
|
const builtinSceneNameSet = new Set(builtinSceneNames);
|
|
@@ -27,28 +26,3 @@ export function builtinSceneNameFromAssetId(assetId) {
|
|
|
27
26
|
export function builtinSceneAssetIds() {
|
|
28
27
|
return builtinSceneNames.map(builtinSceneAssetId);
|
|
29
28
|
}
|
|
30
|
-
export function builtinSceneInterface(assetId, scriptSources) {
|
|
31
|
-
return builtinSceneInterfaceDescriptors(scriptSources)[assetId].interface;
|
|
32
|
-
}
|
|
33
|
-
export function builtinSceneInterfaces(scriptSources) {
|
|
34
|
-
const descriptors = builtinSceneInterfaceDescriptors(scriptSources);
|
|
35
|
-
return Object.fromEntries(builtinSceneAssetIds().map((assetId) => [assetId, descriptors[assetId].interface]));
|
|
36
|
-
}
|
|
37
|
-
function builtinSceneInterfaceDescriptors(scriptSources) {
|
|
38
|
-
const descriptors = extractSceneScriptInterfaces(builtinSceneNames.map((name) => ({
|
|
39
|
-
scene: builtinSceneAssetId(name),
|
|
40
|
-
fileName: `${name}.ts`,
|
|
41
|
-
source: scriptSources[name],
|
|
42
|
-
})));
|
|
43
|
-
for (const assetId of builtinSceneAssetIds()) {
|
|
44
|
-
const name = builtinSceneNameFromAssetId(assetId);
|
|
45
|
-
const descriptor = descriptors[assetId];
|
|
46
|
-
if (!descriptor) {
|
|
47
|
-
throw new Error(`Built-in Scene "${assetId}" is missing a @scene script.`);
|
|
48
|
-
}
|
|
49
|
-
if (descriptor.className !== name) {
|
|
50
|
-
throw new Error(`Built-in Scene "${assetId}" name must match @scene class "${descriptor.className}".`);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return descriptors;
|
|
54
|
-
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { pixiSceneNodeAcceptsChildren } from '../pixiNodeSchema.js';
|
|
2
|
+
import { isSceneTemplateBindingValue } from '../spec.js';
|
|
2
3
|
function cloneNode(node) {
|
|
3
4
|
return structuredClone(node);
|
|
4
5
|
}
|
|
@@ -217,7 +218,7 @@ function getPropPathValue(props, prop) {
|
|
|
217
218
|
return props[prop];
|
|
218
219
|
}
|
|
219
220
|
const value = props[root];
|
|
220
|
-
return value && typeof value === 'object' ? value[field] : undefined;
|
|
221
|
+
return value && typeof value === 'object' && !isSceneTemplateBindingValue(value) ? value[field] : undefined;
|
|
221
222
|
}
|
|
222
223
|
function setPropPathValue(props, prop, value) {
|
|
223
224
|
const [root, field, ...rest] = prop.split('.');
|
|
@@ -231,7 +232,7 @@ function setPropPathValue(props, prop, value) {
|
|
|
231
232
|
return;
|
|
232
233
|
}
|
|
233
234
|
const current = props[root];
|
|
234
|
-
const objectValue = current && typeof current === 'object'
|
|
235
|
+
const objectValue = current && typeof current === 'object' && !isSceneTemplateBindingValue(current)
|
|
235
236
|
? { ...current }
|
|
236
237
|
: {};
|
|
237
238
|
if (value === undefined) {
|
|
@@ -338,11 +339,13 @@ function moveNode(template, command, context) {
|
|
|
338
339
|
if (!source || !target || !canInsertNodeIntoParent(command.parent, source.node, target.acceptsSlotOutlet)) {
|
|
339
340
|
return { ok: false, command, error: 'Target cannot contain this node.' };
|
|
340
341
|
}
|
|
342
|
+
const sameChildList = source.nodes === target.nodes;
|
|
341
343
|
const [node] = source.nodes.splice(source.index, 1);
|
|
342
|
-
let index =
|
|
343
|
-
if (
|
|
344
|
+
let index = command.index;
|
|
345
|
+
if (sameChildList && source.index < index) {
|
|
344
346
|
index -= 1;
|
|
345
347
|
}
|
|
348
|
+
index = Math.max(0, Math.min(index, target.nodes.length));
|
|
346
349
|
target.nodes.splice(index, 0, node);
|
|
347
350
|
const locator = findCompilerSceneNodeLocator(template.children, node);
|
|
348
351
|
if (!locator) {
|
|
@@ -355,7 +358,7 @@ function moveNode(template, command, context) {
|
|
|
355
358
|
op: 'moveNode',
|
|
356
359
|
node: locator,
|
|
357
360
|
parent: source.parentLocator,
|
|
358
|
-
index: source.index,
|
|
361
|
+
index: sameChildList && index < source.index ? source.index + 1 : source.index,
|
|
359
362
|
},
|
|
360
363
|
selection: { type: 'node', node: locator },
|
|
361
364
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SceneValidationDiagnostic } from './sceneValidation.js';
|
|
1
2
|
export interface CompileScenesOptions {
|
|
2
3
|
projectRoot: string | URL;
|
|
3
4
|
sourceRoots?: string[];
|
|
@@ -7,6 +8,7 @@ export declare const generatedSceneAssetsFileName = "scene-assets.generated.json
|
|
|
7
8
|
export declare class CompileSceneError extends Error {
|
|
8
9
|
readonly scene: string;
|
|
9
10
|
readonly source?: string | undefined;
|
|
10
|
-
|
|
11
|
+
readonly diagnostics?: SceneValidationDiagnostic[] | undefined;
|
|
12
|
+
constructor(message: string, scene: string, source?: string | undefined, diagnostics?: SceneValidationDiagnostic[] | undefined);
|
|
11
13
|
}
|
|
12
14
|
export declare function compileScenes(options: CompileScenesOptions): Promise<void>;
|
|
@@ -4,8 +4,9 @@ import { fileURLToPath } from 'node:url';
|
|
|
4
4
|
import { builtinSceneAssetIds, builtinSceneNameFromAssetId, isBuiltinSceneAssetId, } from './builtinScenes.js';
|
|
5
5
|
import { defaultSceneSourceRoots, generatedSceneModuleImport, generatedSceneModulePath, isIgnoredSceneSourceDirectory, normalizeSceneAssetId, pairedSceneScriptPath, resolveSceneReference, sceneClassAlias, sceneLocalName, toPosixPath, } from './sceneAssetPair.js';
|
|
6
6
|
import { builtinSceneScriptPath, readBuiltinSceneScriptSources, readBuiltinSceneSource, } from '../compiler-node/builtinSceneAssets.js';
|
|
7
|
-
import { extractSceneScriptInterfaces } from '
|
|
7
|
+
import { extractSceneScriptInterfaces } from '../compiler-node/scriptInterfaceExtractor.js';
|
|
8
8
|
import { findMissingScenePartReferences } from './scenePartValidation.js';
|
|
9
|
+
import { validateSceneContent } from './sceneValidation.js';
|
|
9
10
|
import { parseSceneTemplate } from './templateParser.js';
|
|
10
11
|
import { compileSceneTemplateToTs } from './typescriptCompiler.js';
|
|
11
12
|
import { parsePixifactProjectConfig, pixifactProjectConfigFileName, } from '../project/index.js';
|
|
@@ -13,10 +14,12 @@ export const generatedSceneAssetsFileName = 'scene-assets.generated.json';
|
|
|
13
14
|
export class CompileSceneError extends Error {
|
|
14
15
|
scene;
|
|
15
16
|
source;
|
|
16
|
-
|
|
17
|
+
diagnostics;
|
|
18
|
+
constructor(message, scene, source, diagnostics) {
|
|
17
19
|
super(message);
|
|
18
20
|
this.scene = scene;
|
|
19
21
|
this.source = source;
|
|
22
|
+
this.diagnostics = diagnostics;
|
|
20
23
|
this.name = 'CompileSceneError';
|
|
21
24
|
}
|
|
22
25
|
}
|
|
@@ -64,6 +67,33 @@ export async function compileScenes(options) {
|
|
|
64
67
|
throw new CompileSceneError(error instanceof Error ? error.message : String(error), scenePath, sceneSources.get(scenePath));
|
|
65
68
|
}
|
|
66
69
|
}
|
|
70
|
+
for (const record of sceneRecords) {
|
|
71
|
+
const { scenePath } = record;
|
|
72
|
+
const template = templates.get(scenePath);
|
|
73
|
+
const source = sceneSources.get(scenePath);
|
|
74
|
+
if (!template || source === undefined) {
|
|
75
|
+
throw new Error(`Missing bound template for ${scenePath}.`);
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const result = validateSceneContent({
|
|
79
|
+
scene: scenePath,
|
|
80
|
+
bindingsOnly: true,
|
|
81
|
+
content: source,
|
|
82
|
+
sceneInterface: template.interface,
|
|
83
|
+
sceneInterfaces: sceneInterfacesFor(template, templates),
|
|
84
|
+
normalizeSceneReference: (scene) => resolveSceneReference(scenePath, scene),
|
|
85
|
+
});
|
|
86
|
+
if (!result.ok) {
|
|
87
|
+
throw new CompileSceneError(result.error, scenePath, source, result.diagnostics);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
if (error instanceof CompileSceneError) {
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
throw new CompileSceneError(error instanceof Error ? error.message : String(error), scenePath, source);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
67
97
|
const projectRegistryImports = [];
|
|
68
98
|
const builtinRegistryImports = [];
|
|
69
99
|
const projectAssets = new Set();
|
package/dist/compiler/index.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ export * from './commands/index.js';
|
|
|
2
2
|
export * from './builtinScenes.js';
|
|
3
3
|
export * from './pixiNodeSchema.js';
|
|
4
4
|
export * from './spec.js';
|
|
5
|
-
export * from './scriptInterfaceExtractor.js';
|
|
6
5
|
export * from './sceneAssetPair.js';
|
|
7
6
|
export * from './scenePartValidation.js';
|
|
8
7
|
export * from './sceneValidation.js';
|
package/dist/compiler/index.js
CHANGED
|
@@ -2,7 +2,6 @@ export * from './commands/index.js';
|
|
|
2
2
|
export * from './builtinScenes.js';
|
|
3
3
|
export * from './pixiNodeSchema.js';
|
|
4
4
|
export * from './spec.js';
|
|
5
|
-
export * from './scriptInterfaceExtractor.js';
|
|
6
5
|
export * from './sceneAssetPair.js';
|
|
7
6
|
export * from './scenePartValidation.js';
|
|
8
7
|
export * from './sceneValidation.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SceneTemplatePrimitiveType, SceneTemplateValue } from './spec.js';
|
|
2
|
-
export type PixiSceneNodeType = Extract<SceneTemplatePrimitiveType, 'Group' | 'Container' | 'Sprite' | 'NineSliceSprite' | 'TilingSprite' | 'Text' | 'BitmapText' | 'HTMLText' | 'Graphics' | 'Rect' | 'Image' | 'NineImage' | 'TileImage' | 'GridContainer' | 'HBoxContainer' | 'ScrollContainer' | 'VBoxContainer'>;
|
|
2
|
+
export type PixiSceneNodeType = Extract<SceneTemplatePrimitiveType, 'Group' | 'Container' | 'Sprite' | 'NineSliceSprite' | 'TilingSprite' | 'Text' | 'Label' | 'BitmapLabel' | 'BitmapText' | 'HTMLText' | 'Graphics' | 'Rect' | 'Image' | 'NineImage' | 'TileImage' | 'GridContainer' | 'HBoxContainer' | 'ScrollContainer' | 'VBoxContainer'>;
|
|
3
3
|
export type PixiSceneFieldType = 'string' | 'number' | 'boolean' | 'color' | 'enum';
|
|
4
4
|
export type PixiScenePropGroup = 'transform' | 'display' | 'sprite' | 'nineSlice' | 'tiling' | 'text' | 'graphics' | 'stack' | 'props';
|
|
5
5
|
export interface PixiSceneFieldSchema {
|
|
@@ -23,6 +23,7 @@ export declare const pixiSceneTileImageProps: readonly ["texture", "tilePosition
|
|
|
23
23
|
export declare const pixiSceneNineSliceProps: readonly ["leftWidth", "rightWidth", "topHeight", "bottomHeight"];
|
|
24
24
|
export declare const pixiSceneTilingProps: readonly ["tilePositionX", "tilePositionY", "tileScaleX", "tileScaleY", "tileRotation"];
|
|
25
25
|
export declare const pixiSceneTextProps: readonly ["text", "fontSize", "fontFamily", "fontWeight", "fill"];
|
|
26
|
+
export declare const pixiSceneLabelProps: readonly ["text", "fontSize", "fontFamily", "fontWeight", "fill", "lineHeight", "letterSpacing", "wordWrap", "alignX", "alignY", "overflow"];
|
|
26
27
|
export declare const pixiSceneGraphicsProps: readonly ["shape", "radius", "fill", "fillAlpha", "strokeColor", "strokeWidth", "strokeAlpha"];
|
|
27
28
|
export declare const pixiSceneRectProps: readonly ["fillColor", "fillAlpha", "strokeColor", "strokeAlpha", "strokeWidth", "radius"];
|
|
28
29
|
export declare const pixiSceneStackHBoxProps: readonly ["gap", "alignY", "justify"];
|
|
@@ -32,8 +33,8 @@ export declare const pixiSceneStackProps: readonly ["columns", "gap", "gapX", "g
|
|
|
32
33
|
export declare const pixiSceneScrollProps: readonly ["direction", "scrollX", "scrollY"];
|
|
33
34
|
export declare const pixiSceneTextStyleProps: readonly ["fontSize", "fontFamily", "fontWeight", "fill"];
|
|
34
35
|
export declare const pixiSceneSpriteLikeProps: readonly ["texture", "anchorX", "anchorY", "tint", "texture", "fit", "anchorX", "anchorY", "tint", "texture", "leftWidth", "rightWidth", "topHeight", "bottomHeight", "anchorX", "anchorY", "tint", "texture", "tilePositionX", "tilePositionY", "tileScaleX", "tileScaleY", "tileRotation", "anchorX", "anchorY", "tint", "leftWidth", "rightWidth", "topHeight", "bottomHeight", "tilePositionX", "tilePositionY", "tileScaleX", "tileScaleY", "tileRotation"];
|
|
35
|
-
export declare const pixiSceneKnownProps: readonly ["x", "y", "width", "height", "scaleX", "scaleY", "rotation", "pivotX", "pivotY", "skewX", "skewY", "left", "right", "top", "bottom", "horizontal", "vertical", "alpha", "visible", "zIndex", "eventMode", "cursor", "label", "texture", "anchorX", "anchorY", "tint", "texture", "fit", "anchorX", "anchorY", "tint", "texture", "leftWidth", "rightWidth", "topHeight", "bottomHeight", "anchorX", "anchorY", "tint", "texture", "tilePositionX", "tilePositionY", "tileScaleX", "tileScaleY", "tileRotation", "anchorX", "anchorY", "tint", "leftWidth", "rightWidth", "topHeight", "bottomHeight", "tilePositionX", "tilePositionY", "tileScaleX", "tileScaleY", "tileRotation", "text", "fontSize", "fontFamily", "fontWeight", "fill", "shape", "radius", "fill", "fillAlpha", "strokeColor", "strokeWidth", "strokeAlpha", "fillColor", "fillAlpha", "strokeColor", "strokeAlpha", "strokeWidth", "radius", "columns", "gap", "gapX", "gapY", "alignX", "alignY", "justify", "direction", "scrollX", "scrollY"];
|
|
36
|
-
export declare const pixiSceneAddableNodeTypes: readonly ["Group", "GridContainer", "HBoxContainer", "ScrollContainer", "VBoxContainer", "Container", "Sprite", "NineSliceSprite", "TilingSprite", "Text", "BitmapText", "HTMLText", "Graphics", "Rect", "Image", "NineImage", "TileImage"];
|
|
36
|
+
export declare const pixiSceneKnownProps: readonly ["x", "y", "width", "height", "scaleX", "scaleY", "rotation", "pivotX", "pivotY", "skewX", "skewY", "left", "right", "top", "bottom", "horizontal", "vertical", "alpha", "visible", "zIndex", "eventMode", "cursor", "label", "texture", "anchorX", "anchorY", "tint", "texture", "fit", "anchorX", "anchorY", "tint", "texture", "leftWidth", "rightWidth", "topHeight", "bottomHeight", "anchorX", "anchorY", "tint", "texture", "tilePositionX", "tilePositionY", "tileScaleX", "tileScaleY", "tileRotation", "anchorX", "anchorY", "tint", "leftWidth", "rightWidth", "topHeight", "bottomHeight", "tilePositionX", "tilePositionY", "tileScaleX", "tileScaleY", "tileRotation", "text", "fontSize", "fontFamily", "fontWeight", "fill", "text", "fontSize", "fontFamily", "fontWeight", "fill", "lineHeight", "letterSpacing", "wordWrap", "alignX", "alignY", "overflow", "shape", "radius", "fill", "fillAlpha", "strokeColor", "strokeWidth", "strokeAlpha", "fillColor", "fillAlpha", "strokeColor", "strokeAlpha", "strokeWidth", "radius", "columns", "gap", "gapX", "gapY", "alignX", "alignY", "justify", "direction", "scrollX", "scrollY"];
|
|
37
|
+
export declare const pixiSceneAddableNodeTypes: readonly ["Group", "GridContainer", "HBoxContainer", "ScrollContainer", "VBoxContainer", "Container", "Sprite", "NineSliceSprite", "TilingSprite", "Label", "BitmapLabel", "Text", "BitmapText", "HTMLText", "Graphics", "Rect", "Image", "NineImage", "TileImage"];
|
|
37
38
|
export declare const pixiSceneNodePropGroupOrder: readonly ["stack", "sprite", "nineSlice", "tiling", "text", "graphics", "props"];
|
|
38
39
|
export declare function pixiSceneNodeSchema(type: PixiSceneNodeType): PixiSceneNodeSchema;
|
|
39
40
|
export declare function isPixiSceneNodeType(type: SceneTemplatePrimitiveType): type is PixiSceneNodeType;
|
|
@@ -81,6 +81,15 @@ export const pixiSceneTextProps = [
|
|
|
81
81
|
'fontWeight',
|
|
82
82
|
'fill',
|
|
83
83
|
];
|
|
84
|
+
export const pixiSceneLabelProps = [
|
|
85
|
+
...pixiSceneTextProps,
|
|
86
|
+
'lineHeight',
|
|
87
|
+
'letterSpacing',
|
|
88
|
+
'wordWrap',
|
|
89
|
+
'alignX',
|
|
90
|
+
'alignY',
|
|
91
|
+
'overflow',
|
|
92
|
+
];
|
|
84
93
|
export const pixiSceneGraphicsProps = [
|
|
85
94
|
'shape',
|
|
86
95
|
'radius',
|
|
@@ -149,6 +158,7 @@ export const pixiSceneKnownProps = [
|
|
|
149
158
|
...pixiSceneDisplayProps,
|
|
150
159
|
...pixiSceneSpriteLikeProps,
|
|
151
160
|
...pixiSceneTextProps,
|
|
161
|
+
...pixiSceneLabelProps,
|
|
152
162
|
...pixiSceneGraphicsProps,
|
|
153
163
|
...pixiSceneRectProps,
|
|
154
164
|
...pixiSceneStackProps,
|
|
@@ -164,6 +174,8 @@ export const pixiSceneAddableNodeTypes = [
|
|
|
164
174
|
'Sprite',
|
|
165
175
|
'NineSliceSprite',
|
|
166
176
|
'TilingSprite',
|
|
177
|
+
'Label',
|
|
178
|
+
'BitmapLabel',
|
|
167
179
|
'Text',
|
|
168
180
|
'BitmapText',
|
|
169
181
|
'HTMLText',
|
|
@@ -223,8 +235,12 @@ const pixiSceneFieldSchemas = {
|
|
|
223
235
|
text: { key: 'text', type: 'string' },
|
|
224
236
|
fontSize: { key: 'fontSize', type: 'number' },
|
|
225
237
|
fontFamily: { key: 'fontFamily', type: 'string' },
|
|
226
|
-
fontWeight: { key: 'fontWeight', type: 'enum', options: [400, 500, 600, 700, '
|
|
238
|
+
fontWeight: { key: 'fontWeight', type: 'enum', options: [400, 500, 600, 700, 'bold'] },
|
|
227
239
|
fill: { key: 'fill', type: 'color' },
|
|
240
|
+
lineHeight: { key: 'lineHeight', type: 'number' },
|
|
241
|
+
letterSpacing: { key: 'letterSpacing', type: 'number' },
|
|
242
|
+
wordWrap: { key: 'wordWrap', type: 'boolean' },
|
|
243
|
+
overflow: { key: 'overflow', type: 'enum', options: ['visible', 'clip'] },
|
|
228
244
|
fillColor: { key: 'fillColor', type: 'color' },
|
|
229
245
|
shape: { key: 'shape', type: 'enum', options: ['roundRect', 'rect'] },
|
|
230
246
|
radius: { key: 'radius', type: 'number' },
|
|
@@ -306,24 +322,64 @@ const pixiSceneNodeSchemas = {
|
|
|
306
322
|
Text: {
|
|
307
323
|
type: 'Text',
|
|
308
324
|
acceptsChildren: false,
|
|
325
|
+
defaults: {
|
|
326
|
+
text: 'Text',
|
|
327
|
+
fontSize: 16,
|
|
328
|
+
fill: 0x111827,
|
|
329
|
+
},
|
|
330
|
+
groups: {
|
|
331
|
+
text: pixiSceneTextProps,
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
Label: {
|
|
335
|
+
type: 'Label',
|
|
336
|
+
acceptsChildren: false,
|
|
309
337
|
defaults: {
|
|
310
338
|
text: 'Text',
|
|
311
339
|
width: 120,
|
|
312
340
|
height: 28,
|
|
341
|
+
fontFamily: 'Arial',
|
|
313
342
|
fontSize: 16,
|
|
343
|
+
fontWeight: 400,
|
|
314
344
|
fill: 0x111827,
|
|
345
|
+
lineHeight: 0,
|
|
346
|
+
letterSpacing: 0,
|
|
347
|
+
wordWrap: false,
|
|
348
|
+
alignX: 'start',
|
|
349
|
+
alignY: 'start',
|
|
350
|
+
overflow: 'visible',
|
|
315
351
|
},
|
|
316
352
|
groups: {
|
|
317
|
-
text:
|
|
353
|
+
text: pixiSceneLabelProps,
|
|
318
354
|
},
|
|
319
355
|
},
|
|
320
|
-
|
|
321
|
-
type: '
|
|
356
|
+
BitmapLabel: {
|
|
357
|
+
type: 'BitmapLabel',
|
|
322
358
|
acceptsChildren: false,
|
|
323
359
|
defaults: {
|
|
324
360
|
text: 'Text',
|
|
325
361
|
width: 120,
|
|
326
362
|
height: 28,
|
|
363
|
+
fontFamily: 'Arial',
|
|
364
|
+
fontSize: 16,
|
|
365
|
+
fontWeight: 400,
|
|
366
|
+
fill: 0x111827,
|
|
367
|
+
lineHeight: 0,
|
|
368
|
+
letterSpacing: 0,
|
|
369
|
+
wordWrap: false,
|
|
370
|
+
alignX: 'start',
|
|
371
|
+
alignY: 'start',
|
|
372
|
+
overflow: 'visible',
|
|
373
|
+
},
|
|
374
|
+
groups: {
|
|
375
|
+
text: pixiSceneLabelProps,
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
BitmapText: {
|
|
379
|
+
type: 'BitmapText',
|
|
380
|
+
acceptsChildren: false,
|
|
381
|
+
defaults: {
|
|
382
|
+
text: 'Text',
|
|
327
383
|
fontSize: 16,
|
|
328
384
|
fill: 0x111827,
|
|
329
385
|
},
|
|
@@ -336,8 +392,6 @@ const pixiSceneNodeSchemas = {
|
|
|
336
392
|
acceptsChildren: false,
|
|
337
393
|
defaults: {
|
|
338
394
|
text: 'Text',
|
|
339
|
-
width: 120,
|
|
340
|
-
height: 28,
|
|
341
395
|
fontSize: 16,
|
|
342
396
|
fill: 0x111827,
|
|
343
397
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type SceneTemplate, type SceneTemplateInterface, type SceneTemplateNode, type SceneTemplateValue } from './spec.js';
|
|
2
2
|
export interface SceneTemplateNodeSummary {
|
|
3
3
|
id?: string;
|
|
4
4
|
kind: SceneTemplateNode['kind'];
|
|
@@ -29,6 +29,8 @@ export interface ValidateSceneContentOptions {
|
|
|
29
29
|
content: string;
|
|
30
30
|
existingAssets?: ReadonlySet<string>;
|
|
31
31
|
sceneInterfaces?: Record<string, SceneTemplateInterface>;
|
|
32
|
+
sceneInterface?: SceneTemplateInterface;
|
|
33
|
+
bindingsOnly?: boolean;
|
|
32
34
|
normalizeSceneReference?: (scene: string) => string;
|
|
33
35
|
}
|
|
34
36
|
export type SceneContentValidationResult = {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isSceneTemplateBindingValue, } from './spec.js';
|
|
1
2
|
import { isPixiSceneNodeType, pixiSceneFieldSchema, pixiSceneLayoutProps, pixiSceneNodeAcceptsChildren, pixiSceneNodePropKeys, pixiSceneTransformProps, pixiSceneDisplayProps, } from './pixiNodeSchema.js';
|
|
2
3
|
import { parseSceneTemplate } from './templateParser.js';
|
|
3
4
|
import { serializeSceneTemplate } from './templateSerializer.js';
|
|
@@ -57,6 +58,9 @@ export function inspectSceneTemplate(template) {
|
|
|
57
58
|
export function validateSceneContent(options) {
|
|
58
59
|
try {
|
|
59
60
|
const template = parseSceneTemplate(options.content);
|
|
61
|
+
if (options.sceneInterface) {
|
|
62
|
+
template.interface = options.sceneInterface;
|
|
63
|
+
}
|
|
60
64
|
const diagnostics = validateSceneTemplate(template, options);
|
|
61
65
|
const revision = createSceneRevision(options.content);
|
|
62
66
|
if (diagnostics.length > 0) {
|
|
@@ -123,7 +127,7 @@ function validateSceneNode(node, path, context) {
|
|
|
123
127
|
}
|
|
124
128
|
if (node.kind === 'pixi') {
|
|
125
129
|
return [
|
|
126
|
-
...validatePixiNode(node, path, context
|
|
130
|
+
...validatePixiNode(node, path, context),
|
|
127
131
|
...node.children.flatMap((child, index) => validateSceneNode(child, `${path}/${nodePathSegment(index, child)}`, context)),
|
|
128
132
|
];
|
|
129
133
|
}
|
|
@@ -132,14 +136,14 @@ function validateSceneNode(node, path, context) {
|
|
|
132
136
|
...Object.entries(node.slots).flatMap(([slot, children]) => children.flatMap((child, index) => validateSceneNode(child, `${path}/slot:${slot}/${nodePathSegment(index, child)}`, context))),
|
|
133
137
|
];
|
|
134
138
|
}
|
|
135
|
-
function validatePixiNode(node, path,
|
|
139
|
+
function validatePixiNode(node, path, context) {
|
|
136
140
|
if (!isPixiSceneNodeType(node.type)) {
|
|
137
141
|
return [{
|
|
138
142
|
path,
|
|
139
143
|
prop: 'type',
|
|
140
144
|
expected: 'supported compiler Pixi node type',
|
|
141
145
|
actual: node.type,
|
|
142
|
-
hint: 'Use GridContainer, HBoxContainer, ScrollContainer, VBoxContainer, Container, Sprite, NineSliceSprite, TilingSprite, Text, BitmapText, HTMLText, Graphics, Rect, Image, NineImage, or TileImage.',
|
|
146
|
+
hint: 'Use GridContainer, HBoxContainer, ScrollContainer, VBoxContainer, Container, Sprite, NineSliceSprite, TilingSprite, Label, BitmapLabel, Text, BitmapText, HTMLText, Graphics, Rect, Image, NineImage, or TileImage.',
|
|
143
147
|
}];
|
|
144
148
|
}
|
|
145
149
|
const diagnostics = [];
|
|
@@ -159,6 +163,9 @@ function validatePixiNode(node, path, existingAssets) {
|
|
|
159
163
|
...pixiSceneNodePropKeys(node.type),
|
|
160
164
|
]);
|
|
161
165
|
for (const [prop, value] of Object.entries(node.props)) {
|
|
166
|
+
if (context.bindingsOnly && !isSceneTemplateBindingValue(value)) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
162
169
|
if (!knownProps.has(prop)) {
|
|
163
170
|
diagnostics.push({
|
|
164
171
|
path,
|
|
@@ -170,6 +177,10 @@ function validatePixiNode(node, path, existingAssets) {
|
|
|
170
177
|
continue;
|
|
171
178
|
}
|
|
172
179
|
const schema = pixiSceneFieldSchema(prop);
|
|
180
|
+
if (isSceneTemplateBindingValue(value)) {
|
|
181
|
+
diagnostics.push(...validateSceneBinding(path, prop, value, context.sceneInterface, schema?.type, schema?.options));
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
173
184
|
if (schema && !sceneValueMatchesFieldType(value, schema.type, schema.options)) {
|
|
174
185
|
diagnostics.push({
|
|
175
186
|
path,
|
|
@@ -182,7 +193,7 @@ function validatePixiNode(node, path, existingAssets) {
|
|
|
182
193
|
}
|
|
183
194
|
const texture = node.props.texture;
|
|
184
195
|
if (typeof texture === 'string') {
|
|
185
|
-
const assetDiagnostic = validateTextureReference(path, texture, existingAssets);
|
|
196
|
+
const assetDiagnostic = validateTextureReference(path, texture, context.existingAssets);
|
|
186
197
|
if (assetDiagnostic) {
|
|
187
198
|
diagnostics.push(assetDiagnostic);
|
|
188
199
|
}
|
|
@@ -224,6 +235,9 @@ function validateSceneInstanceNode(node, path, context) {
|
|
|
224
235
|
...Object.keys(sceneInterface.props),
|
|
225
236
|
]);
|
|
226
237
|
for (const [prop, value] of Object.entries(node.props)) {
|
|
238
|
+
if (context.bindingsOnly && !isSceneTemplateBindingValue(value)) {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
227
241
|
if (!allowedProps.has(prop)) {
|
|
228
242
|
diagnostics.push({
|
|
229
243
|
path,
|
|
@@ -236,11 +250,26 @@ function validateSceneInstanceNode(node, path, context) {
|
|
|
236
250
|
}
|
|
237
251
|
const schema = pixiSceneFieldSchema(prop);
|
|
238
252
|
const contract = sceneInterface.props[prop];
|
|
253
|
+
if (isSceneTemplateBindingValue(value)) {
|
|
254
|
+
const expectedType = contract?.type === 'variant' ? 'string' : contract?.type ?? schema?.type;
|
|
255
|
+
diagnostics.push(...validateSceneBinding(path, prop, value, context.sceneInterface, expectedType, schema?.options));
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
239
258
|
if (contract?.type === 'struct') {
|
|
240
259
|
diagnostics.push(...validateSceneInstanceStructProp(node, path, prop, value, contract));
|
|
241
260
|
continue;
|
|
242
261
|
}
|
|
243
262
|
const expectedType = contract?.type ?? schema?.type;
|
|
263
|
+
if (contract?.type === 'variant' && typeof value === 'string' && !contract.variants[value]) {
|
|
264
|
+
diagnostics.push({
|
|
265
|
+
path,
|
|
266
|
+
prop,
|
|
267
|
+
expected: `one of ${Object.keys(contract.variants).map((name) => JSON.stringify(name)).join(', ')}`,
|
|
268
|
+
actual: JSON.stringify(value),
|
|
269
|
+
hint: `Set ${node.type}.${prop} to a declared Variant case.`,
|
|
270
|
+
});
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
244
273
|
if (expectedType && !sceneValueMatchesContractType(value, expectedType, schema?.options)) {
|
|
245
274
|
diagnostics.push({
|
|
246
275
|
path,
|
|
@@ -251,7 +280,7 @@ function validateSceneInstanceNode(node, path, context) {
|
|
|
251
280
|
});
|
|
252
281
|
}
|
|
253
282
|
}
|
|
254
|
-
for (const eventName of Object.keys(node.events)) {
|
|
283
|
+
for (const eventName of context.bindingsOnly ? [] : Object.keys(node.events)) {
|
|
255
284
|
if (!sceneInterface.events[eventName]) {
|
|
256
285
|
diagnostics.push({
|
|
257
286
|
path,
|
|
@@ -262,7 +291,7 @@ function validateSceneInstanceNode(node, path, context) {
|
|
|
262
291
|
});
|
|
263
292
|
}
|
|
264
293
|
}
|
|
265
|
-
for (const slot of Object.keys(node.slots)) {
|
|
294
|
+
for (const slot of context.bindingsOnly ? [] : Object.keys(node.slots)) {
|
|
266
295
|
if (!sceneInterface.slots[slot]) {
|
|
267
296
|
diagnostics.push({
|
|
268
297
|
path: `${path}/slot:${slot}`,
|
|
@@ -275,6 +304,76 @@ function validateSceneInstanceNode(node, path, context) {
|
|
|
275
304
|
}
|
|
276
305
|
return diagnostics;
|
|
277
306
|
}
|
|
307
|
+
function validateSceneBinding(path, targetProp, binding, sceneInterface, expectedType, options) {
|
|
308
|
+
const [prop, field] = binding.path;
|
|
309
|
+
const contract = sceneInterface?.props[prop];
|
|
310
|
+
if (!contract) {
|
|
311
|
+
return [{
|
|
312
|
+
path,
|
|
313
|
+
prop: targetProp,
|
|
314
|
+
expected: `Scene Prop declared by ${sceneInterface ? 'the paired script' : 'the owner Scene'}`,
|
|
315
|
+
actual: 'unknown binding prop',
|
|
316
|
+
hint: `Declare @prop "${prop}" in the paired Scene script.`,
|
|
317
|
+
}];
|
|
318
|
+
}
|
|
319
|
+
let sourceType;
|
|
320
|
+
let sourceValues;
|
|
321
|
+
if (field) {
|
|
322
|
+
if (contract.type !== 'variant') {
|
|
323
|
+
return [{
|
|
324
|
+
path,
|
|
325
|
+
prop: targetProp,
|
|
326
|
+
expected: `Variant Prop for binding field "${field}"`,
|
|
327
|
+
actual: `${contract.type} binding`,
|
|
328
|
+
hint: `Use {${prop}} or declare ${prop} with defineVariants().`,
|
|
329
|
+
}];
|
|
330
|
+
}
|
|
331
|
+
sourceValues = Object.values(contract.variants).map((variant) => variant[field]).filter((value) => value !== undefined);
|
|
332
|
+
if (sourceValues.length !== Object.keys(contract.variants).length) {
|
|
333
|
+
return [{
|
|
334
|
+
path,
|
|
335
|
+
prop: targetProp,
|
|
336
|
+
expected: `Variant field declared by ${prop}`,
|
|
337
|
+
actual: 'unknown binding field',
|
|
338
|
+
hint: `Use one of ${Object.keys(Object.values(contract.variants)[0] ?? {}).map((name) => JSON.stringify(name)).join(', ')}.`,
|
|
339
|
+
}];
|
|
340
|
+
}
|
|
341
|
+
sourceType = typeof sourceValues[0];
|
|
342
|
+
}
|
|
343
|
+
else if (contract.type === 'struct') {
|
|
344
|
+
sourceType = 'object';
|
|
345
|
+
}
|
|
346
|
+
else if (contract.type === 'variant') {
|
|
347
|
+
sourceType = 'string';
|
|
348
|
+
sourceValues = Object.keys(contract.variants);
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
sourceType = contract.type;
|
|
352
|
+
if (contract.default !== undefined) {
|
|
353
|
+
sourceValues = [contract.default];
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
const textAcceptsScalar = targetProp === 'text' && ['string', 'number', 'boolean'].includes(sourceType);
|
|
357
|
+
if (expectedType && !textAcceptsScalar && !bindingSourceMatchesTarget(sourceType, sourceValues, expectedType, options)) {
|
|
358
|
+
return [{
|
|
359
|
+
path,
|
|
360
|
+
prop: targetProp,
|
|
361
|
+
expected: expectedType,
|
|
362
|
+
actual: `${sourceType} binding`,
|
|
363
|
+
hint: `Bind ${targetProp} to a Scene Prop with a compatible value type.`,
|
|
364
|
+
}];
|
|
365
|
+
}
|
|
366
|
+
return [];
|
|
367
|
+
}
|
|
368
|
+
function bindingSourceMatchesTarget(sourceType, values, expectedType, options) {
|
|
369
|
+
if (expectedType === 'color') {
|
|
370
|
+
return sourceType === 'number' || (sourceType === 'string' && values?.every((value) => (typeof value === 'string' && /^#[0-9a-fA-F]{6}$/.test(value))) === true);
|
|
371
|
+
}
|
|
372
|
+
if (expectedType === 'enum') {
|
|
373
|
+
return !!values && values.every((value) => options?.includes(value));
|
|
374
|
+
}
|
|
375
|
+
return sourceType === expectedType;
|
|
376
|
+
}
|
|
278
377
|
function validateSceneInstanceStructProp(node, path, prop, value, contract) {
|
|
279
378
|
if (!value || typeof value !== 'object') {
|
|
280
379
|
return [{
|
package/dist/compiler/spec.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
export type SceneTemplatePrimitiveType = 'Group' | 'Container' | 'Sprite' | 'Text' | 'Graphics' | 'Rect' | 'Image' | 'NineImage' | 'TileImage' | 'BitmapText' | 'HTMLText' | 'Mesh' | 'NineSliceSprite' | 'TilingSprite' | 'DOMContainer' | 'GridContainer' | 'HBoxContainer' | 'ScrollContainer' | 'VBoxContainer';
|
|
1
|
+
export type SceneTemplatePrimitiveType = 'Group' | 'Container' | 'Sprite' | 'Text' | 'Label' | 'BitmapLabel' | 'Graphics' | 'Rect' | 'Image' | 'NineImage' | 'TileImage' | 'BitmapText' | 'HTMLText' | 'Mesh' | 'NineSliceSprite' | 'TilingSprite' | 'DOMContainer' | 'GridContainer' | 'HBoxContainer' | 'ScrollContainer' | 'VBoxContainer';
|
|
2
2
|
export type SceneTemplateScalarValue = string | number | boolean;
|
|
3
3
|
export type SceneTemplateStructValue = Record<string, SceneTemplateScalarValue>;
|
|
4
|
-
export
|
|
4
|
+
export interface SceneTemplateBindingValue {
|
|
5
|
+
kind: 'binding';
|
|
6
|
+
path: [string] | [string, string];
|
|
7
|
+
}
|
|
8
|
+
export type SceneTemplateValue = SceneTemplateScalarValue | SceneTemplateStructValue | SceneTemplateBindingValue;
|
|
9
|
+
export declare function isSceneTemplateBindingValue(value: SceneTemplateValue | undefined): value is SceneTemplateBindingValue;
|
|
5
10
|
export type SceneTemplatePrimitivePropType = 'string' | 'number' | 'boolean';
|
|
6
11
|
export interface SceneTemplatePrimitivePropContract {
|
|
7
12
|
type: SceneTemplatePrimitivePropType;
|
|
@@ -17,7 +22,14 @@ export interface SceneTemplateStructPropContract {
|
|
|
17
22
|
sourceScene?: string;
|
|
18
23
|
fields: Record<string, SceneTemplateStructFieldContract>;
|
|
19
24
|
}
|
|
20
|
-
export type
|
|
25
|
+
export type SceneTemplateVariantFields = Record<string, SceneTemplateScalarValue>;
|
|
26
|
+
export type SceneTemplateVariants = Record<string, SceneTemplateVariantFields>;
|
|
27
|
+
export interface SceneTemplateVariantPropContract {
|
|
28
|
+
type: 'variant';
|
|
29
|
+
default: string;
|
|
30
|
+
variants: SceneTemplateVariants;
|
|
31
|
+
}
|
|
32
|
+
export type SceneTemplatePropContract = SceneTemplatePrimitivePropContract | SceneTemplateStructPropContract | SceneTemplateVariantPropContract;
|
|
21
33
|
export interface SceneTemplateEventContract {
|
|
22
34
|
type: 'action';
|
|
23
35
|
}
|
|
@@ -84,11 +96,10 @@ export interface SceneScriptInterface {
|
|
|
84
96
|
}
|
|
85
97
|
export type SceneClassDecorator = ClassDecorator;
|
|
86
98
|
export type SceneMemberDecorator = PropertyDecorator & MethodDecorator;
|
|
87
|
-
export type
|
|
88
|
-
export type ScenePropType = StringConstructor | NumberConstructor | BooleanConstructor | SceneStructConstructor;
|
|
99
|
+
export type SceneVariants = Record<string, Record<string, SceneTemplateScalarValue>>;
|
|
89
100
|
export interface ScenePropDecoratorOptions {
|
|
90
|
-
|
|
91
|
-
|
|
101
|
+
default?: SceneTemplateScalarValue;
|
|
102
|
+
variants?: SceneVariants;
|
|
92
103
|
}
|
|
93
104
|
export interface SceneEventDecoratorOptions {
|
|
94
105
|
name?: string;
|
package/dist/compiler/spec.js
CHANGED
|
@@ -5,6 +5,8 @@ const pixiTypes = new Set([
|
|
|
5
5
|
'Container',
|
|
6
6
|
'Sprite',
|
|
7
7
|
'Text',
|
|
8
|
+
'Label',
|
|
9
|
+
'BitmapLabel',
|
|
8
10
|
'Graphics',
|
|
9
11
|
'Rect',
|
|
10
12
|
'Image',
|
|
@@ -148,6 +150,10 @@ function isEventAttribute(name) {
|
|
|
148
150
|
return eventName(name) !== undefined;
|
|
149
151
|
}
|
|
150
152
|
function parseAttributeValue(name, value) {
|
|
153
|
+
const binding = parseBindingValue(value);
|
|
154
|
+
if (binding) {
|
|
155
|
+
return binding;
|
|
156
|
+
}
|
|
151
157
|
if (pixiSceneFieldSchema(name)?.type === 'string') {
|
|
152
158
|
return value;
|
|
153
159
|
}
|
|
@@ -161,6 +167,19 @@ function parseAttributeValue(name, value) {
|
|
|
161
167
|
return Number.parseInt(value.slice(1), 16);
|
|
162
168
|
return value;
|
|
163
169
|
}
|
|
170
|
+
function parseBindingValue(value) {
|
|
171
|
+
const match = /^\{([A-Za-z_$][\w$]*)(?:\.([A-Za-z_$][\w$]*))?\}$/.exec(value);
|
|
172
|
+
if (!match) {
|
|
173
|
+
if (value.startsWith('{') || value.endsWith('}')) {
|
|
174
|
+
throw new Error(`Unsupported Scene binding "${value}". Use {prop} or {variant.field}.`);
|
|
175
|
+
}
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
kind: 'binding',
|
|
180
|
+
path: match[2] ? [match[1], match[2]] : [match[1]],
|
|
181
|
+
};
|
|
182
|
+
}
|
|
164
183
|
function removeAttribute(element, name) {
|
|
165
184
|
const attributes = { ...element.attributes };
|
|
166
185
|
delete attributes[name];
|