pixifact 0.1.3

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.
Files changed (168) hide show
  1. package/README.md +44 -0
  2. package/dist/authoring/ActionRegistry.d.ts +10 -0
  3. package/dist/authoring/ActionRegistry.js +26 -0
  4. package/dist/authoring/ComponentPalette.d.ts +16 -0
  5. package/dist/authoring/ComponentPalette.js +50 -0
  6. package/dist/authoring/DesignToken.d.ts +16 -0
  7. package/dist/authoring/DesignToken.js +33 -0
  8. package/dist/authoring/DiffModel.d.ts +10 -0
  9. package/dist/authoring/DiffModel.js +104 -0
  10. package/dist/authoring/InspectorModel.d.ts +27 -0
  11. package/dist/authoring/InspectorModel.js +139 -0
  12. package/dist/authoring/LogicGraph.d.ts +66 -0
  13. package/dist/authoring/LogicGraph.js +184 -0
  14. package/dist/authoring/OverrideJournal.d.ts +18 -0
  15. package/dist/authoring/OverrideJournal.js +23 -0
  16. package/dist/authoring/PreferenceMemory.d.ts +21 -0
  17. package/dist/authoring/PreferenceMemory.js +39 -0
  18. package/dist/authoring/ProjectConfig.d.ts +20 -0
  19. package/dist/authoring/ProjectConfig.js +78 -0
  20. package/dist/authoring/RuntimePreview.d.ts +11 -0
  21. package/dist/authoring/RuntimePreview.js +25 -0
  22. package/dist/authoring/SceneDocument.d.ts +74 -0
  23. package/dist/authoring/SceneDocument.js +332 -0
  24. package/dist/authoring/SceneProjectState.d.ts +20 -0
  25. package/dist/authoring/SceneProjectState.js +8 -0
  26. package/dist/authoring/SceneSelection.d.ts +11 -0
  27. package/dist/authoring/SceneSelection.js +1 -0
  28. package/dist/authoring/index.d.ts +13 -0
  29. package/dist/authoring/index.js +13 -0
  30. package/dist/commands/Command.d.ts +63 -0
  31. package/dist/commands/Command.js +1 -0
  32. package/dist/commands/CommandStack.d.ts +14 -0
  33. package/dist/commands/CommandStack.js +45 -0
  34. package/dist/commands/applyCommand.d.ts +23 -0
  35. package/dist/commands/applyCommand.js +316 -0
  36. package/dist/commands/index.d.ts +4 -0
  37. package/dist/commands/index.js +4 -0
  38. package/dist/commands/utils.d.ts +17 -0
  39. package/dist/commands/utils.js +77 -0
  40. package/dist/commands/validateCommand.d.ts +12 -0
  41. package/dist/commands/validateCommand.js +285 -0
  42. package/dist/compiler/commands/index.d.ts +109 -0
  43. package/dist/compiler/commands/index.js +492 -0
  44. package/dist/compiler/compileScenes.d.ts +11 -0
  45. package/dist/compiler/compileScenes.js +220 -0
  46. package/dist/compiler/decorators.d.ts +6 -0
  47. package/dist/compiler/decorators.js +79 -0
  48. package/dist/compiler/eventRuntime.d.ts +8 -0
  49. package/dist/compiler/eventRuntime.js +29 -0
  50. package/dist/compiler/index.d.ts +14 -0
  51. package/dist/compiler/index.js +14 -0
  52. package/dist/compiler/pixiNodeSchema.d.ts +39 -0
  53. package/dist/compiler/pixiNodeSchema.js +267 -0
  54. package/dist/compiler/sceneAssetPair.d.ts +11 -0
  55. package/dist/compiler/sceneAssetPair.js +92 -0
  56. package/dist/compiler/scenePartValidation.d.ts +6 -0
  57. package/dist/compiler/scenePartValidation.js +24 -0
  58. package/dist/compiler/sceneRuntime.d.ts +13 -0
  59. package/dist/compiler/sceneRuntime.js +22 -0
  60. package/dist/compiler/sceneValidation.d.ts +48 -0
  61. package/dist/compiler/sceneValidation.js +383 -0
  62. package/dist/compiler/scriptInterfaceExtractor.d.ts +6 -0
  63. package/dist/compiler/scriptInterfaceExtractor.js +258 -0
  64. package/dist/compiler/slotRuntime.d.ts +3 -0
  65. package/dist/compiler/slotRuntime.js +17 -0
  66. package/dist/compiler/spec.d.ts +95 -0
  67. package/dist/compiler/spec.js +1 -0
  68. package/dist/compiler/templateParser.d.ts +2 -0
  69. package/dist/compiler/templateParser.js +302 -0
  70. package/dist/compiler/templateSerializer.d.ts +2 -0
  71. package/dist/compiler/templateSerializer.js +101 -0
  72. package/dist/compiler/typescriptCompiler.d.ts +2 -0
  73. package/dist/compiler/typescriptCompiler.js +494 -0
  74. package/dist/compiler-node/index.d.ts +2 -0
  75. package/dist/compiler-node/index.js +2 -0
  76. package/dist/compiler-node/vite.d.ts +8 -0
  77. package/dist/compiler-node/vite.js +21 -0
  78. package/dist/index.d.ts +6 -0
  79. package/dist/index.js +6 -0
  80. package/dist/nodes/Input.d.ts +91 -0
  81. package/dist/nodes/Input.js +352 -0
  82. package/dist/nodes/Textarea.d.ts +10 -0
  83. package/dist/nodes/Textarea.js +38 -0
  84. package/dist/nodes/components/Button.d.ts +8 -0
  85. package/dist/nodes/components/Button.js +45 -0
  86. package/dist/nodes/components/InputField.d.ts +12 -0
  87. package/dist/nodes/components/InputField.js +53 -0
  88. package/dist/nodes/components/ProgressBar.d.ts +14 -0
  89. package/dist/nodes/components/ProgressBar.js +66 -0
  90. package/dist/nodes/components/ScrollRect.d.ts +23 -0
  91. package/dist/nodes/components/ScrollRect.js +108 -0
  92. package/dist/nodes/components/Selectable.d.ts +32 -0
  93. package/dist/nodes/components/Selectable.js +153 -0
  94. package/dist/nodes/components/index.d.ts +5 -0
  95. package/dist/nodes/components/index.js +5 -0
  96. package/dist/nodes/graphics/Graphic.d.ts +14 -0
  97. package/dist/nodes/graphics/Graphic.js +47 -0
  98. package/dist/nodes/graphics/ImageGraphic.d.ts +9 -0
  99. package/dist/nodes/graphics/ImageGraphic.js +42 -0
  100. package/dist/nodes/graphics/RoundedRectGraphic.d.ts +13 -0
  101. package/dist/nodes/graphics/RoundedRectGraphic.js +65 -0
  102. package/dist/nodes/graphics/TextGraphic.d.ts +13 -0
  103. package/dist/nodes/graphics/TextGraphic.js +70 -0
  104. package/dist/nodes/graphics/index.d.ts +4 -0
  105. package/dist/nodes/graphics/index.js +4 -0
  106. package/dist/nodes/index.d.ts +3 -0
  107. package/dist/nodes/index.js +3 -0
  108. package/dist/runtime/Application.d.ts +15 -0
  109. package/dist/runtime/Application.js +67 -0
  110. package/dist/runtime/GameObject.d.ts +100 -0
  111. package/dist/runtime/GameObject.js +297 -0
  112. package/dist/runtime/GameObjectEvent.d.ts +52 -0
  113. package/dist/runtime/GameObjectEvent.js +38 -0
  114. package/dist/runtime/Transform.d.ts +32 -0
  115. package/dist/runtime/Transform.js +157 -0
  116. package/dist/runtime/component/Component.d.ts +13 -0
  117. package/dist/runtime/component/Component.js +13 -0
  118. package/dist/runtime/component/ComponentRegistry.d.ts +49 -0
  119. package/dist/runtime/component/ComponentRegistry.js +25 -0
  120. package/dist/runtime/component/Flex.d.ts +6 -0
  121. package/dist/runtime/component/Flex.js +15 -0
  122. package/dist/runtime/component/FlexGroup.d.ts +26 -0
  123. package/dist/runtime/component/FlexGroup.js +136 -0
  124. package/dist/runtime/component/GridLayout.d.ts +47 -0
  125. package/dist/runtime/component/GridLayout.js +129 -0
  126. package/dist/runtime/component/Layout.d.ts +104 -0
  127. package/dist/runtime/component/Layout.js +248 -0
  128. package/dist/runtime/component/decorators.d.ts +4 -0
  129. package/dist/runtime/component/decorators.js +52 -0
  130. package/dist/runtime/component/index.d.ts +7 -0
  131. package/dist/runtime/component/index.js +7 -0
  132. package/dist/runtime/graphics/index.d.ts +11 -0
  133. package/dist/runtime/graphics/index.js +29 -0
  134. package/dist/runtime/group/index.d.ts +39 -0
  135. package/dist/runtime/group/index.js +102 -0
  136. package/dist/runtime/image/Image.d.ts +19 -0
  137. package/dist/runtime/image/Image.js +60 -0
  138. package/dist/runtime/image/NineSliceImage.d.ts +23 -0
  139. package/dist/runtime/image/NineSliceImage.js +64 -0
  140. package/dist/runtime/image/index.d.ts +2 -0
  141. package/dist/runtime/image/index.js +2 -0
  142. package/dist/runtime/index.d.ts +9 -0
  143. package/dist/runtime/index.js +9 -0
  144. package/dist/runtime/label/Label.d.ts +11 -0
  145. package/dist/runtime/label/Label.js +26 -0
  146. package/dist/runtime/label/LabelStyle.d.ts +3 -0
  147. package/dist/runtime/label/LabelStyle.js +3 -0
  148. package/dist/runtime/label/index.d.ts +2 -0
  149. package/dist/runtime/label/index.js +2 -0
  150. package/dist/runtime/utils/index.d.ts +1 -0
  151. package/dist/runtime/utils/index.js +1 -0
  152. package/dist/runtime/utils/onlyOnceQueueMicrotask.d.ts +1 -0
  153. package/dist/runtime/utils/onlyOnceQueueMicrotask.js +12 -0
  154. package/dist/runtime/utils/setProps.d.ts +10 -0
  155. package/dist/runtime/utils/setProps.js +17 -0
  156. package/dist/scene/dsl.d.ts +25 -0
  157. package/dist/scene/dsl.js +112 -0
  158. package/dist/scene/index.d.ts +5 -0
  159. package/dist/scene/index.js +5 -0
  160. package/dist/scene/instantiate.d.ts +8 -0
  161. package/dist/scene/instantiate.js +208 -0
  162. package/dist/scene/nodesDsl.d.ts +42 -0
  163. package/dist/scene/nodesDsl.js +169 -0
  164. package/dist/scene/spec.d.ts +100 -0
  165. package/dist/scene/spec.js +1 -0
  166. package/dist/scene/templates.d.ts +21 -0
  167. package/dist/scene/templates.js +239 -0
  168. package/package.json +90 -0
@@ -0,0 +1,11 @@
1
+ export declare const defaultSceneSourceRoots: readonly ["src"];
2
+ export declare const ignoredSceneSourceDirectories: Set<string>;
3
+ export declare function toPosixPath(value: string): string;
4
+ export declare function normalizeSceneAssetId(value: string): string;
5
+ export declare function isIgnoredSceneSourceDirectory(name: string): boolean;
6
+ export declare function sceneLocalName(scenePath: string): string;
7
+ export declare function pairedSceneScriptPath(scenePath: string): string;
8
+ export declare function generatedSceneModulePath(scenePath: string): string;
9
+ export declare function generatedSceneModuleImport(scenePath: string): string;
10
+ export declare function sceneClassAlias(scenePath: string): string;
11
+ export declare function resolveSceneReference(fromScenePath: string, reference: string): string;
@@ -0,0 +1,92 @@
1
+ export const defaultSceneSourceRoots = ['src'];
2
+ export const ignoredSceneSourceDirectories = new Set([
3
+ 'node_modules',
4
+ 'build',
5
+ 'dist',
6
+ 'generated',
7
+ '.pixifact',
8
+ 'coverage',
9
+ 'test-results',
10
+ ]);
11
+ export function toPosixPath(value) {
12
+ return value.replaceAll('\\', '/');
13
+ }
14
+ function posixBasename(value, suffix = '') {
15
+ const basename = value.split('/').filter(Boolean).at(-1) ?? '';
16
+ return suffix && basename.endsWith(suffix)
17
+ ? basename.slice(0, -suffix.length)
18
+ : basename;
19
+ }
20
+ function posixDirname(value) {
21
+ const normalized = toPosixPath(value).replace(/\/+$/, '');
22
+ const index = normalized.lastIndexOf('/');
23
+ return index >= 0 ? normalized.slice(0, index) : '';
24
+ }
25
+ function normalizePosixPath(value) {
26
+ const segments = [];
27
+ for (const segment of toPosixPath(value).split('/')) {
28
+ if (!segment || segment === '.') {
29
+ continue;
30
+ }
31
+ if (segment === '..') {
32
+ if (segments.length && segments[segments.length - 1] !== '..') {
33
+ segments.pop();
34
+ }
35
+ else {
36
+ segments.push(segment);
37
+ }
38
+ continue;
39
+ }
40
+ segments.push(segment);
41
+ }
42
+ return segments.join('/');
43
+ }
44
+ function posixJoin(...parts) {
45
+ return normalizePosixPath(parts.filter(Boolean).join('/'));
46
+ }
47
+ export function normalizeSceneAssetId(value) {
48
+ const normalized = toPosixPath(value).replace(/^\.\/+/, '').replace(/^\/+/, '');
49
+ const parts = normalized.split('/').filter(Boolean);
50
+ if (parts.includes('..')) {
51
+ throw new Error(`Scene path "${value}" must stay inside projectRoot.`);
52
+ }
53
+ if (!normalized.endsWith('.scene')) {
54
+ throw new Error(`Scene path "${value}" must end with .scene.`);
55
+ }
56
+ return parts.join('/');
57
+ }
58
+ export function isIgnoredSceneSourceDirectory(name) {
59
+ return ignoredSceneSourceDirectories.has(name);
60
+ }
61
+ export function sceneLocalName(scenePath) {
62
+ return posixBasename(normalizeSceneAssetId(scenePath), '.scene');
63
+ }
64
+ export function pairedSceneScriptPath(scenePath) {
65
+ const assetId = normalizeSceneAssetId(scenePath);
66
+ return `${assetId.slice(0, -'.scene'.length)}.ts`;
67
+ }
68
+ export function generatedSceneModulePath(scenePath) {
69
+ return `${normalizeSceneAssetId(scenePath).slice(0, -'.scene'.length)}.scene.generated.ts`;
70
+ }
71
+ export function generatedSceneModuleImport(scenePath) {
72
+ return `./${generatedSceneModulePath(scenePath).replace(/\.ts$/, '')}`;
73
+ }
74
+ export function sceneClassAlias(scenePath) {
75
+ const base = normalizeSceneAssetId(scenePath)
76
+ .slice(0, -'.scene'.length)
77
+ .split('/')
78
+ .map((segment) => segment.replace(/[^A-Za-z0-9]/g, (character) => `_x${character.charCodeAt(0).toString(16)}_`))
79
+ .join('_');
80
+ return `SceneClass_${base}`;
81
+ }
82
+ export function resolveSceneReference(fromScenePath, reference) {
83
+ const value = toPosixPath(reference.trim());
84
+ if (!value.endsWith('.scene')) {
85
+ throw new Error('Scene references must use .scene paths.');
86
+ }
87
+ if (value.startsWith('./') || value.startsWith('../')) {
88
+ const baseDir = posixDirname(normalizeSceneAssetId(fromScenePath));
89
+ return normalizeSceneAssetId(posixJoin(baseDir, value));
90
+ }
91
+ return normalizeSceneAssetId(value);
92
+ }
@@ -0,0 +1,6 @@
1
+ import type { SceneTemplate } from './spec.js';
2
+ export interface MissingScenePartReference {
3
+ property: string;
4
+ id: string;
5
+ }
6
+ export declare function findMissingScenePartReferences(template: SceneTemplate, parts: Readonly<Record<string, string>>): MissingScenePartReference[];
@@ -0,0 +1,24 @@
1
+ export function findMissingScenePartReferences(template, parts) {
2
+ const nodeIds = collectSceneNodeIds(template.children);
3
+ return Object.entries(parts)
4
+ .filter(([, id]) => !nodeIds.has(id))
5
+ .map(([property, id]) => ({ property, id }));
6
+ }
7
+ function collectSceneNodeIds(nodes, ids = new Set()) {
8
+ for (const node of nodes) {
9
+ if (node.kind === 'slotOutlet') {
10
+ continue;
11
+ }
12
+ if (node.id) {
13
+ ids.add(node.id);
14
+ }
15
+ if (node.kind === 'pixi') {
16
+ collectSceneNodeIds(node.children, ids);
17
+ continue;
18
+ }
19
+ for (const children of Object.values(node.slots)) {
20
+ collectSceneNodeIds(children, ids);
21
+ }
22
+ }
23
+ return ids;
24
+ }
@@ -0,0 +1,13 @@
1
+ import type { Container } from 'pixi.js';
2
+ export interface SceneMountResult {
3
+ root: Container;
4
+ parts: Record<string, Container>;
5
+ slots: Record<string, Container>;
6
+ }
7
+ export interface SceneDefinition {
8
+ mount(root: Container): SceneMountResult;
9
+ }
10
+ export declare function registerScene(path: string, definition: SceneDefinition): void;
11
+ export declare function registerSceneClass(constructor: object, path: string): void;
12
+ export declare function mountScene(root: Container, path: string): SceneMountResult;
13
+ export declare function mountSceneClass(root: Container, constructor: object): SceneMountResult;
@@ -0,0 +1,22 @@
1
+ const sceneDefinitions = new Map();
2
+ const scenePathsByConstructor = new WeakMap();
3
+ export function registerScene(path, definition) {
4
+ sceneDefinitions.set(path, definition);
5
+ }
6
+ export function registerSceneClass(constructor, path) {
7
+ scenePathsByConstructor.set(constructor, path);
8
+ }
9
+ export function mountScene(root, path) {
10
+ const definition = sceneDefinitions.get(path);
11
+ if (!definition) {
12
+ throw new Error(`Scene "${path}" has not been registered.`);
13
+ }
14
+ return definition.mount(root);
15
+ }
16
+ export function mountSceneClass(root, constructor) {
17
+ const path = scenePathsByConstructor.get(constructor);
18
+ if (!path) {
19
+ throw new Error(`Scene class "${constructor instanceof Function ? constructor.name : 'unknown'}" has not been bound to a .scene file.`);
20
+ }
21
+ return mountScene(root, path);
22
+ }
@@ -0,0 +1,48 @@
1
+ import type { SceneTemplate, SceneTemplateInterface, SceneTemplateNode, SceneTemplateValue } from './spec.js';
2
+ export interface SceneTemplateNodeSummary {
3
+ id?: string;
4
+ kind: SceneTemplateNode['kind'];
5
+ type: string;
6
+ path: string;
7
+ propKeys: string[];
8
+ childCount: number;
9
+ }
10
+ export interface SceneTemplateInspection {
11
+ name: string;
12
+ props: Record<string, SceneTemplateValue>;
13
+ nodeCount: number;
14
+ nodes: SceneTemplateNodeSummary[];
15
+ }
16
+ export interface SceneValidationDiagnostic {
17
+ path: string;
18
+ prop: string;
19
+ expected: string;
20
+ actual: string;
21
+ hint?: string;
22
+ line?: number;
23
+ column?: number;
24
+ }
25
+ export declare function createSceneRevision(source: string): string;
26
+ export declare function inspectSceneTemplate(template: SceneTemplate): SceneTemplateInspection;
27
+ export interface ValidateSceneContentOptions {
28
+ scene: string;
29
+ content: string;
30
+ existingAssets?: ReadonlySet<string>;
31
+ sceneInterfaces?: Record<string, SceneTemplateInterface>;
32
+ normalizeSceneReference?: (scene: string) => string;
33
+ }
34
+ export type SceneContentValidationResult = {
35
+ ok: true;
36
+ scene: string;
37
+ revision: string;
38
+ canonicalContent: string;
39
+ summary: SceneTemplateInspection;
40
+ } | {
41
+ ok: false;
42
+ scene: string;
43
+ revision?: string;
44
+ error: string;
45
+ diagnostics?: SceneValidationDiagnostic[];
46
+ hint?: string;
47
+ };
48
+ export declare function validateSceneContent(options: ValidateSceneContentOptions): SceneContentValidationResult;
@@ -0,0 +1,383 @@
1
+ import { isPixiSceneNodeType, pixiSceneFieldSchema, pixiSceneNodePropKeys, pixiSceneTransformProps, pixiSceneDisplayProps, } from './pixiNodeSchema.js';
2
+ import { parseSceneTemplate } from './templateParser.js';
3
+ import { serializeSceneTemplate } from './templateSerializer.js';
4
+ export function createSceneRevision(source) {
5
+ const canonical = serializeSceneTemplate(parseSceneTemplate(source));
6
+ let hash = 0x811c9dc5;
7
+ for (let index = 0; index < canonical.length; index += 1) {
8
+ hash = Math.imul(hash ^ canonical.charCodeAt(index), 0x01000193);
9
+ }
10
+ return `scene:${canonical.length}:${(hash >>> 0).toString(16).padStart(8, '0')}`;
11
+ }
12
+ export function inspectSceneTemplate(template) {
13
+ const nodes = [];
14
+ function visit(node, path) {
15
+ if (node.kind === 'slotOutlet') {
16
+ nodes.push({
17
+ kind: 'slotOutlet',
18
+ type: 'slot',
19
+ path,
20
+ propKeys: ['name'],
21
+ childCount: 0,
22
+ });
23
+ return;
24
+ }
25
+ if (node.kind === 'pixi') {
26
+ nodes.push({
27
+ id: node.id,
28
+ kind: node.kind,
29
+ type: node.type,
30
+ path,
31
+ propKeys: Object.keys(node.props).sort(),
32
+ childCount: node.children.length,
33
+ });
34
+ node.children.forEach((child, index) => visit(child, `${path}/${nodePathSegment(index, child)}`));
35
+ return;
36
+ }
37
+ nodes.push({
38
+ id: node.id,
39
+ kind: node.kind,
40
+ type: node.type,
41
+ path,
42
+ propKeys: Object.keys(node.props).sort(),
43
+ childCount: Object.values(node.slots).reduce((sum, children) => sum + children.length, 0),
44
+ });
45
+ for (const [slot, children] of Object.entries(node.slots)) {
46
+ children.forEach((child, index) => visit(child, `${path}/slot:${slot}/${nodePathSegment(index, child)}`));
47
+ }
48
+ }
49
+ template.children.forEach((child, index) => visit(child, nodePathSegment(index, child)));
50
+ return {
51
+ name: template.name,
52
+ props: template.props,
53
+ nodeCount: nodes.length,
54
+ nodes,
55
+ };
56
+ }
57
+ export function validateSceneContent(options) {
58
+ try {
59
+ const template = parseSceneTemplate(options.content);
60
+ const diagnostics = validateSceneTemplate(template, options);
61
+ const revision = createSceneRevision(options.content);
62
+ if (diagnostics.length > 0) {
63
+ return {
64
+ ok: false,
65
+ scene: options.scene,
66
+ revision,
67
+ error: 'Scene validation failed.',
68
+ diagnostics,
69
+ hint: 'Fix the listed diagnostics, then run scene validate again.',
70
+ };
71
+ }
72
+ return {
73
+ ok: true,
74
+ scene: options.scene,
75
+ revision,
76
+ canonicalContent: serializeSceneTemplate(template),
77
+ summary: inspectSceneTemplate(template),
78
+ };
79
+ }
80
+ catch (error) {
81
+ const message = error instanceof Error ? error.message : String(error);
82
+ return {
83
+ ok: false,
84
+ scene: options.scene,
85
+ error: 'Scene parse failed.',
86
+ diagnostics: [sceneSourceDiagnostic(options.content, message)],
87
+ hint: 'Fix the listed diagnostics, then run scene validate again.',
88
+ };
89
+ }
90
+ }
91
+ function sceneSourceDiagnostic(source, message) {
92
+ return {
93
+ path: '__scene__',
94
+ prop: 'source',
95
+ expected: 'valid Pixifact .scene source',
96
+ actual: message,
97
+ hint: 'Fix the .scene source syntax near the reported location.',
98
+ ...sourcePositionFromMessage(source, message),
99
+ };
100
+ }
101
+ function sourcePositionFromMessage(source, message) {
102
+ const match = message.match(/\boffset (\d+)\b/);
103
+ if (!match) {
104
+ return {};
105
+ }
106
+ const offset = Number(match[1]);
107
+ if (!Number.isInteger(offset) || offset < 0) {
108
+ return {};
109
+ }
110
+ const before = source.slice(0, offset);
111
+ const lines = before.split('\n');
112
+ return {
113
+ line: lines.length,
114
+ column: lines.at(-1).length + 1,
115
+ };
116
+ }
117
+ function validateSceneTemplate(template, context) {
118
+ return template.children.flatMap((child, index) => validateSceneNode(child, nodePathSegment(index, child), context));
119
+ }
120
+ function validateSceneNode(node, path, context) {
121
+ if (node.kind === 'slotOutlet') {
122
+ return [];
123
+ }
124
+ if (node.kind === 'pixi') {
125
+ return [
126
+ ...validatePixiNode(node, path, context.existingAssets),
127
+ ...node.children.flatMap((child, index) => validateSceneNode(child, `${path}/${nodePathSegment(index, child)}`, context)),
128
+ ];
129
+ }
130
+ return [
131
+ ...validateSceneInstanceNode(node, path, context),
132
+ ...Object.entries(node.slots).flatMap(([slot, children]) => children.flatMap((child, index) => validateSceneNode(child, `${path}/slot:${slot}/${nodePathSegment(index, child)}`, context))),
133
+ ];
134
+ }
135
+ function validatePixiNode(node, path, existingAssets) {
136
+ if (!isPixiSceneNodeType(node.type)) {
137
+ return [{
138
+ path,
139
+ prop: 'type',
140
+ expected: 'supported compiler Pixi node type',
141
+ actual: node.type,
142
+ hint: 'Use Container, Sprite, NineSliceSprite, TilingSprite, Text, BitmapText, HTMLText, or Graphics.',
143
+ }];
144
+ }
145
+ const diagnostics = [];
146
+ const knownProps = new Set([
147
+ ...pixiSceneTransformProps,
148
+ ...pixiSceneDisplayProps,
149
+ ...pixiSceneNodePropKeys(node.type),
150
+ ]);
151
+ for (const [prop, value] of Object.entries(node.props)) {
152
+ if (!knownProps.has(prop)) {
153
+ diagnostics.push({
154
+ path,
155
+ prop,
156
+ expected: `known ${node.type} prop`,
157
+ actual: 'unknown prop',
158
+ hint: unknownPixiPropHint(prop),
159
+ });
160
+ continue;
161
+ }
162
+ const schema = pixiSceneFieldSchema(prop);
163
+ if (schema && !sceneValueMatchesFieldType(value, schema.type, schema.options)) {
164
+ diagnostics.push({
165
+ path,
166
+ prop,
167
+ expected: schema.type === 'enum' ? `one of ${schema.options?.map((option) => JSON.stringify(option)).join(', ')}` : schema.type,
168
+ actual: sceneValueType(value),
169
+ hint: `Set ${node.type}.${prop} to ${fieldTypeDescription(schema.type)}.`,
170
+ });
171
+ }
172
+ }
173
+ const texture = node.props.texture;
174
+ if (typeof texture === 'string') {
175
+ const assetDiagnostic = validateTextureReference(path, texture, existingAssets);
176
+ if (assetDiagnostic) {
177
+ diagnostics.push(assetDiagnostic);
178
+ }
179
+ }
180
+ return diagnostics;
181
+ }
182
+ function validateSceneInstanceNode(node, path, context) {
183
+ let scene = node.scene;
184
+ try {
185
+ scene = context.normalizeSceneReference ? context.normalizeSceneReference(node.scene) : node.scene;
186
+ }
187
+ catch (error) {
188
+ return [{
189
+ path,
190
+ prop: 'scene',
191
+ expected: 'project-relative or relative .scene path',
192
+ actual: node.scene,
193
+ hint: error instanceof Error ? error.message : 'Use a .scene path.',
194
+ }];
195
+ }
196
+ const sceneInterface = context.sceneInterfaces?.[scene];
197
+ if (!sceneInterface) {
198
+ if (context.sceneInterfaces) {
199
+ return [{
200
+ path,
201
+ prop: 'scene',
202
+ expected: 'known compiler Scene contract',
203
+ actual: node.scene,
204
+ hint: 'Ensure the referenced .scene file exists and has a readable paired script.',
205
+ }];
206
+ }
207
+ return [];
208
+ }
209
+ const diagnostics = [];
210
+ const allowedProps = new Set([
211
+ ...pixiSceneTransformProps,
212
+ ...pixiSceneDisplayProps,
213
+ ...Object.keys(sceneInterface.props),
214
+ ]);
215
+ for (const [prop, value] of Object.entries(node.props)) {
216
+ if (!allowedProps.has(prop)) {
217
+ diagnostics.push({
218
+ path,
219
+ prop,
220
+ expected: `public prop declared by ${scene}`,
221
+ actual: 'unknown prop',
222
+ hint: 'Expose the property with @prop on the child Scene script before setting it from a parent Scene.',
223
+ });
224
+ continue;
225
+ }
226
+ const schema = pixiSceneFieldSchema(prop);
227
+ const contract = sceneInterface.props[prop];
228
+ if (contract?.type === 'struct') {
229
+ diagnostics.push(...validateSceneInstanceStructProp(node, path, prop, value, contract));
230
+ continue;
231
+ }
232
+ const expectedType = contract?.type ?? schema?.type;
233
+ if (expectedType && !sceneValueMatchesContractType(value, expectedType, schema?.options)) {
234
+ diagnostics.push({
235
+ path,
236
+ prop,
237
+ expected: expectedType,
238
+ actual: sceneValueType(value),
239
+ hint: `Set ${node.type}.${prop} to ${fieldTypeDescription(expectedType)}.`,
240
+ });
241
+ }
242
+ }
243
+ for (const eventName of Object.keys(node.events)) {
244
+ if (!sceneInterface.events[eventName]) {
245
+ diagnostics.push({
246
+ path,
247
+ prop: `@${eventName}`,
248
+ expected: `public event declared by ${scene}`,
249
+ actual: 'unknown event',
250
+ hint: 'Expose the event with @event on the child Scene script before binding it from a parent Scene.',
251
+ });
252
+ }
253
+ }
254
+ for (const slot of Object.keys(node.slots)) {
255
+ if (!sceneInterface.slots[slot]) {
256
+ diagnostics.push({
257
+ path: `${path}/slot:${slot}`,
258
+ prop: 'slot',
259
+ expected: `public slot declared by ${scene}`,
260
+ actual: 'unknown slot',
261
+ hint: 'Expose the slot with @slot on the child Scene script before placing children into it.',
262
+ });
263
+ }
264
+ }
265
+ return diagnostics;
266
+ }
267
+ function validateSceneInstanceStructProp(node, path, prop, value, contract) {
268
+ if (!value || typeof value !== 'object') {
269
+ return [{
270
+ path,
271
+ prop,
272
+ expected: contract.struct,
273
+ actual: sceneValueType(value),
274
+ hint: `Set ${node.type}.${prop} fields using dot-path attributes such as ${prop}.x="0".`,
275
+ }];
276
+ }
277
+ const diagnostics = [];
278
+ for (const [field, fieldValue] of Object.entries(value)) {
279
+ const fieldContract = contract.fields[field];
280
+ if (!fieldContract) {
281
+ diagnostics.push({
282
+ path,
283
+ prop: `${prop}.${field}`,
284
+ expected: `field declared by ${contract.struct}`,
285
+ actual: 'unknown field',
286
+ hint: `Use one of ${Object.keys(contract.fields).map((name) => JSON.stringify(name)).join(', ')}.`,
287
+ });
288
+ continue;
289
+ }
290
+ if (!sceneValueMatchesContractType(fieldValue, fieldContract.type)) {
291
+ diagnostics.push({
292
+ path,
293
+ prop: `${prop}.${field}`,
294
+ expected: fieldContract.type,
295
+ actual: sceneValueType(fieldValue),
296
+ hint: `Set ${node.type}.${prop}.${field} to ${fieldTypeDescription(fieldContract.type)}.`,
297
+ });
298
+ }
299
+ }
300
+ return diagnostics;
301
+ }
302
+ function validateTextureReference(path, texture, existingAssets) {
303
+ if (!isProjectRelativeAssetPath(texture)) {
304
+ return {
305
+ path,
306
+ prop: 'texture',
307
+ expected: 'project-relative asset path inside project root',
308
+ actual: texture,
309
+ hint: 'Use a project-relative path such as "assets/play.png".',
310
+ };
311
+ }
312
+ if (existingAssets && !existingAssets.has(normalizeAssetPath(texture))) {
313
+ return {
314
+ path,
315
+ prop: 'texture',
316
+ expected: 'existing project asset',
317
+ actual: texture,
318
+ hint: 'Use an asset path that exists in the project before validating the scene.',
319
+ };
320
+ }
321
+ return undefined;
322
+ }
323
+ function isProjectRelativeAssetPath(value) {
324
+ if (value.trim() === ''
325
+ || value.startsWith('/')
326
+ || value.startsWith('./')
327
+ || value.includes('\\')
328
+ || /^[a-zA-Z][a-zA-Z\d+.-]*:/.test(value)) {
329
+ return false;
330
+ }
331
+ return !value.split('/').includes('..');
332
+ }
333
+ function normalizeAssetPath(value) {
334
+ return value.split('/').filter((part) => part !== '' && part !== '.').join('/');
335
+ }
336
+ function sceneValueMatchesFieldType(value, type, options) {
337
+ if (type === 'enum') {
338
+ return options?.includes(value) ?? false;
339
+ }
340
+ return sceneValueMatchesContractType(value, type);
341
+ }
342
+ function sceneValueMatchesContractType(value, type, options) {
343
+ if (type === 'enum') {
344
+ return options?.includes(value) ?? false;
345
+ }
346
+ if (type === 'number' || type === 'color') {
347
+ return typeof value === 'number';
348
+ }
349
+ if (type === 'boolean') {
350
+ return typeof value === 'boolean';
351
+ }
352
+ return typeof value === 'string';
353
+ }
354
+ function sceneValueType(value) {
355
+ if (value && typeof value === 'object') {
356
+ return 'object';
357
+ }
358
+ return typeof value;
359
+ }
360
+ function fieldTypeDescription(type) {
361
+ if (type === 'number') {
362
+ return 'a numeric value';
363
+ }
364
+ if (type === 'color') {
365
+ return 'a color value such as "#ffffff"';
366
+ }
367
+ if (type === 'enum') {
368
+ return 'one of the allowed values';
369
+ }
370
+ return `a ${type} value`;
371
+ }
372
+ function unknownPixiPropHint(prop) {
373
+ if (prop === 'textrue') {
374
+ return 'Use "texture" for Sprite image assets.';
375
+ }
376
+ return 'Use the editor inspector or scene inspect command to list supported props for this node type.';
377
+ }
378
+ function nodePathSegment(index, node) {
379
+ if (node.kind === 'slotOutlet') {
380
+ return `${index}:slot:${node.name}`;
381
+ }
382
+ return `${index}:${node.id ?? node.kind}`;
383
+ }
@@ -0,0 +1,6 @@
1
+ import type { SceneScriptInterface } from './spec.js';
2
+ export interface ExtractSceneScriptInterfaceOptions {
3
+ scene: string;
4
+ }
5
+ export declare function extractSceneScriptInterface(source: string, fileName: string | undefined, options: ExtractSceneScriptInterfaceOptions): SceneScriptInterface;
6
+ export declare function emitSceneScriptInterfaceDescriptor(source: string, fileName: string | undefined, options: ExtractSceneScriptInterfaceOptions): string;