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
package/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # Pixifact
2
+
3
+ Pixifact is a Bun-first framework for AI-editable 2D game UI, HUDs, menus, and lightweight scenes on top of PixiJS v8.
4
+
5
+ Use `pixifact` for runtime APIs, Scene instantiation, compiler APIs, and generated Scene runtime support.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ bun add pixifact pixi.js
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```ts
16
+ import { Application } from 'pixifact/runtime';
17
+ import { scene, container, text } from 'pixifact/scene';
18
+ ```
19
+
20
+ Compiler Scene projects usually also install the CLI:
21
+
22
+ ```bash
23
+ bun add -d pixifact-cli
24
+ ```
25
+
26
+ Then validate and compile `.scene` files:
27
+
28
+ ```bash
29
+ pixifact scene validate --project-root . --all
30
+ pixifact compile-scenes --project-root .
31
+ ```
32
+
33
+ ## Scene Workflow
34
+
35
+ Pixifact treats project-relative `.scene` files as the source of truth. External agents edit `.scene` source directly, then run validation and compilation.
36
+
37
+ Generated files such as `.pixifact/generated/**` are build artifacts and should not be edited by hand.
38
+
39
+ ## Requirements
40
+
41
+ - Bun
42
+ - PixiJS v8
43
+
44
+ The desktop editor is distributed separately from the npm runtime package.
@@ -0,0 +1,10 @@
1
+ export interface ActionSpec {
2
+ key: string;
3
+ label?: string;
4
+ description?: string;
5
+ source?: 'manual' | 'agent' | 'code';
6
+ }
7
+ export declare function actionExists(actions: readonly ActionSpec[] | undefined, key: string): boolean;
8
+ export declare function upsertAction(actions: ActionSpec[], action: ActionSpec): ActionSpec;
9
+ export declare function createRuntimeActions(actions: readonly ActionSpec[] | undefined, handler?: (action: ActionSpec, args: unknown[]) => void): Record<string, (...args: unknown[]) => void>;
10
+ export declare function summarizeActionsForAgent(actions: readonly ActionSpec[] | undefined): string;
@@ -0,0 +1,26 @@
1
+ export function actionExists(actions, key) {
2
+ return (actions ?? []).some((action) => action.key === key);
3
+ }
4
+ export function upsertAction(actions, action) {
5
+ const existing = actions.find((item) => item.key === action.key);
6
+ if (existing) {
7
+ Object.assign(existing, action);
8
+ return existing;
9
+ }
10
+ actions.push(action);
11
+ return action;
12
+ }
13
+ export function createRuntimeActions(actions, handler) {
14
+ const runtimeActions = {};
15
+ for (const action of actions ?? []) {
16
+ runtimeActions[action.key] = (...args) => {
17
+ handler?.(action, args);
18
+ };
19
+ }
20
+ return runtimeActions;
21
+ }
22
+ export function summarizeActionsForAgent(actions) {
23
+ return (actions ?? [])
24
+ .map((action) => `- ${action.key}${action.label ? ` (${action.label})` : ''}${action.description ? `: ${action.description}` : ''}`)
25
+ .join('\n');
26
+ }
@@ -0,0 +1,16 @@
1
+ import type { ComponentSchema } from "../runtime/index.js";
2
+ import type { ComponentSpec, SceneSpec } from "../scene/index.js";
3
+ export interface PaletteComponentItem {
4
+ type: string;
5
+ displayName: string;
6
+ category: string;
7
+ description?: string;
8
+ disabledReason?: string;
9
+ schema: ComponentSchema;
10
+ }
11
+ export interface ComponentPaletteOptions {
12
+ scene?: SceneSpec;
13
+ node?: string;
14
+ }
15
+ export declare function listPaletteComponents(options?: ComponentPaletteOptions): PaletteComponentItem[];
16
+ export declare function createComponentSpecFromSchema(schemaOrType: ComponentSchema | string, id?: string): ComponentSpec;
@@ -0,0 +1,50 @@
1
+ import { ComponentRegistry } from "../runtime/index.js";
2
+ import { findNode } from "../commands/utils.js";
3
+ function componentIdFromType(type) {
4
+ const raw = type.split('.').at(-1) ?? type;
5
+ return raw.charAt(0).toLowerCase() + raw.slice(1);
6
+ }
7
+ function disabledReason(schema, options) {
8
+ if (!schema.disallowMultiple || !options.scene || !options.node) {
9
+ return undefined;
10
+ }
11
+ const node = findNode(options.scene, options.node)?.node;
12
+ if (!node) {
13
+ return undefined;
14
+ }
15
+ return (node.components ?? []).some((component) => component.type === schema.type)
16
+ ? `${schema.displayName ?? schema.type} already exists on this node.`
17
+ : undefined;
18
+ }
19
+ export function listPaletteComponents(options = {}) {
20
+ return ComponentRegistry.list()
21
+ .filter((schema) => schema.category !== 'UI/Graphic')
22
+ .map((schema) => ({
23
+ type: schema.type,
24
+ displayName: schema.displayName ?? schema.type,
25
+ category: schema.category ?? 'Other',
26
+ description: schema.description,
27
+ disabledReason: disabledReason(schema, options),
28
+ schema,
29
+ }))
30
+ .sort((a, b) => a.category.localeCompare(b.category) || a.displayName.localeCompare(b.displayName));
31
+ }
32
+ export function createComponentSpecFromSchema(schemaOrType, id) {
33
+ const schema = typeof schemaOrType === 'string'
34
+ ? ComponentRegistry.get(schemaOrType)
35
+ : schemaOrType;
36
+ if (!schema) {
37
+ throw new Error(`Unknown component type "${schemaOrType}".`);
38
+ }
39
+ const props = {};
40
+ for (const prop of schema.props) {
41
+ if (prop.default !== undefined && prop.serialize !== false) {
42
+ props[prop.key] = structuredClone(prop.default);
43
+ }
44
+ }
45
+ return {
46
+ id: id ?? componentIdFromType(schema.type),
47
+ type: schema.type,
48
+ props,
49
+ };
50
+ }
@@ -0,0 +1,16 @@
1
+ export interface DesignTokenSpec {
2
+ colors?: Record<string, number>;
3
+ spacing?: Record<string, number>;
4
+ radius?: Record<string, number>;
5
+ typography?: Record<string, {
6
+ fontFamily: string;
7
+ fontSize: number;
8
+ fontWeight?: string;
9
+ }>;
10
+ }
11
+ export interface DesignTokenWarning {
12
+ target: string;
13
+ value: unknown;
14
+ message: string;
15
+ }
16
+ export declare function validateDesignTokenValue(tokens: DesignTokenSpec | undefined, target: string, prop: string, value: unknown): DesignTokenWarning | undefined;
@@ -0,0 +1,33 @@
1
+ function hasNumberValue(record, value) {
2
+ return typeof value === 'number' && !!record && Object.values(record).includes(value);
3
+ }
4
+ export function validateDesignTokenValue(tokens, target, prop, value) {
5
+ if (!tokens) {
6
+ return undefined;
7
+ }
8
+ if ((prop === 'color' || prop.toLowerCase().includes('color')) && !hasNumberValue(tokens.colors, value)) {
9
+ return {
10
+ target,
11
+ value,
12
+ message: `${target} uses color ${String(value)} outside design tokens.`,
13
+ };
14
+ }
15
+ if ((prop === 'radius' || prop.toLowerCase().includes('radius')) && !hasNumberValue(tokens.radius, value)) {
16
+ return {
17
+ target,
18
+ value,
19
+ message: `${target} uses radius ${String(value)} outside design tokens.`,
20
+ };
21
+ }
22
+ if ((prop === 'x' || prop === 'y' || prop === 'width' || prop === 'height') && typeof value === 'number' && tokens.spacing) {
23
+ const spacingValues = Object.values(tokens.spacing);
24
+ if (spacingValues.length > 0 && !spacingValues.some((spacing) => value % spacing === 0)) {
25
+ return {
26
+ target,
27
+ value,
28
+ message: `${target} uses ${prop}=${value}, which does not align to spacing tokens.`,
29
+ };
30
+ }
31
+ }
32
+ return undefined;
33
+ }
@@ -0,0 +1,10 @@
1
+ import type { SceneCommand } from "../commands/index.js";
2
+ import type { SceneSpec } from "../scene/index.js";
3
+ export interface DiffEntry {
4
+ target: string;
5
+ before: unknown;
6
+ after: unknown;
7
+ command: SceneCommand;
8
+ }
9
+ export declare function diffCommand(scene: SceneSpec, command: SceneCommand): DiffEntry[];
10
+ export declare function diffCommands(scene: SceneSpec, commands: SceneCommand[]): DiffEntry[];
@@ -0,0 +1,104 @@
1
+ import { findComponent, findNode } from "../commands/utils.js";
2
+ function formatTarget(parts) {
3
+ return parts.filter(Boolean).join('.');
4
+ }
5
+ export function diffCommand(scene, command) {
6
+ switch (command.op) {
7
+ case 'setNodeProp': {
8
+ const node = findNode(scene, command.node)?.node;
9
+ return [{
10
+ target: formatTarget([command.node, command.prop]),
11
+ before: node?.[command.prop],
12
+ after: command.value,
13
+ command,
14
+ }];
15
+ }
16
+ case 'setTransform': {
17
+ const node = findNode(scene, command.node)?.node;
18
+ return Object.entries(command.values).map(([key, value]) => ({
19
+ target: formatTarget([command.node, 'transform', key]),
20
+ before: node?.transform?.[key],
21
+ after: value,
22
+ command,
23
+ }));
24
+ }
25
+ case 'setNodeData': {
26
+ const node = findNode(scene, command.node)?.node;
27
+ const data = node && node.kind === command.field
28
+ ? node[command.field]
29
+ : undefined;
30
+ return [{
31
+ target: formatTarget([command.node, command.field, command.prop]),
32
+ before: data?.[command.prop],
33
+ after: command.value,
34
+ command,
35
+ }];
36
+ }
37
+ case 'setComponentProp': {
38
+ const node = findNode(scene, command.node)?.node;
39
+ const component = node ? findComponent(node, command.component)?.component : undefined;
40
+ return [{
41
+ target: formatTarget([command.node, command.component, command.prop]),
42
+ before: component?.props?.[command.prop],
43
+ after: command.value,
44
+ command,
45
+ }];
46
+ }
47
+ case 'addComponent':
48
+ return [{
49
+ target: formatTarget([command.node, command.component.id ?? command.component.type]),
50
+ before: undefined,
51
+ after: command.component,
52
+ command,
53
+ }];
54
+ case 'removeComponent': {
55
+ const node = findNode(scene, command.node)?.node;
56
+ const component = node ? findComponent(node, command.component)?.component : undefined;
57
+ return [{
58
+ target: formatTarget([command.node, command.component]),
59
+ before: component,
60
+ after: undefined,
61
+ command,
62
+ }];
63
+ }
64
+ case 'createNode':
65
+ return [{
66
+ target: formatTarget([command.parent ?? 'root', command.node.id ?? command.node.key ?? command.node.name]),
67
+ before: undefined,
68
+ after: command.node,
69
+ command,
70
+ }];
71
+ case 'deleteNode': {
72
+ const node = findNode(scene, command.node)?.node;
73
+ return [{
74
+ target: command.node,
75
+ before: node,
76
+ after: undefined,
77
+ command,
78
+ }];
79
+ }
80
+ case 'reparentNode': {
81
+ const node = findNode(scene, command.node);
82
+ return [{
83
+ target: formatTarget([command.node, 'parent']),
84
+ before: node?.parent?.key ?? node?.parent?.id ?? node?.parent?.name,
85
+ after: command.parent ?? 'root',
86
+ command,
87
+ }];
88
+ }
89
+ case 'reorderNode': {
90
+ const node = findNode(scene, command.node);
91
+ return [{
92
+ target: formatTarget([command.node, 'index']),
93
+ before: node?.index,
94
+ after: command.index,
95
+ command,
96
+ }];
97
+ }
98
+ case 'batch':
99
+ return diffCommands(scene, command.commands);
100
+ }
101
+ }
102
+ export function diffCommands(scene, commands) {
103
+ return commands.flatMap((command) => diffCommand(scene, command));
104
+ }
@@ -0,0 +1,27 @@
1
+ import type { ComponentSchema, PropSchema } from "../runtime/index.js";
2
+ import type { NodeSpec, SceneSpec } from "../scene/index.js";
3
+ export interface InspectorFieldModel {
4
+ key: string;
5
+ label: string;
6
+ type: string;
7
+ value: unknown;
8
+ schema?: PropSchema;
9
+ }
10
+ export interface InspectorComponentModel {
11
+ id?: string;
12
+ type: string;
13
+ displayName: string;
14
+ schema?: ComponentSchema;
15
+ fields: InspectorFieldModel[];
16
+ }
17
+ export interface InspectorNodeModel {
18
+ id?: string;
19
+ key?: string;
20
+ role?: string;
21
+ name?: string;
22
+ kind: NodeSpec['kind'];
23
+ transform: InspectorFieldModel[];
24
+ display: InspectorComponentModel[];
25
+ components: InspectorComponentModel[];
26
+ }
27
+ export declare function buildInspectorModel(scene: SceneSpec, nodeLocator: string): InspectorNodeModel | undefined;
@@ -0,0 +1,139 @@
1
+ import { ComponentRegistry } from "../runtime/index.js";
2
+ import { findNode } from "../commands/utils.js";
3
+ const transformFields = [
4
+ 'x',
5
+ 'y',
6
+ 'width',
7
+ 'height',
8
+ 'anchorX',
9
+ 'anchorY',
10
+ 'scaleX',
11
+ 'scaleY',
12
+ 'rotation',
13
+ ];
14
+ function buildTransformFields(node) {
15
+ const transform = node.transform ?? {};
16
+ return transformFields.map((key) => ({
17
+ key,
18
+ label: key,
19
+ type: 'number',
20
+ value: transform[key],
21
+ }));
22
+ }
23
+ function field(key, type, value, schema) {
24
+ return {
25
+ key,
26
+ label: key,
27
+ type,
28
+ value,
29
+ schema,
30
+ };
31
+ }
32
+ function enumSchema(key, options) {
33
+ return {
34
+ key,
35
+ type: 'enum',
36
+ options,
37
+ };
38
+ }
39
+ function buildDisplayModel(node) {
40
+ switch (node.kind) {
41
+ case 'image':
42
+ return [{
43
+ type: 'node.image',
44
+ displayName: 'Image',
45
+ fields: [
46
+ field('mode', 'enum', node.image?.mode, enumSchema('mode', ['sprite', 'nineSlice'])),
47
+ field('src', 'string', node.image?.src),
48
+ field('tint', 'color', node.image?.tint),
49
+ field('leftWidth', 'number', node.image?.leftWidth),
50
+ field('rightWidth', 'number', node.image?.rightWidth),
51
+ field('topHeight', 'number', node.image?.topHeight),
52
+ field('bottomHeight', 'number', node.image?.bottomHeight),
53
+ ],
54
+ }];
55
+ case 'text':
56
+ return [{
57
+ type: 'node.text',
58
+ displayName: 'Text',
59
+ fields: [
60
+ field('value', 'string', node.text?.value),
61
+ field('color', 'color', node.text?.color),
62
+ field('fontSize', 'number', node.text?.fontSize),
63
+ field('fontFamily', 'string', Array.isArray(node.text?.fontFamily) ? node.text.fontFamily.join(',') : node.text?.fontFamily),
64
+ field('fontWeight', 'enum', node.text?.fontWeight, enumSchema('fontWeight', ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900'])),
65
+ field('center', 'boolean', node.text?.center),
66
+ ],
67
+ }];
68
+ case 'input':
69
+ return [{
70
+ type: 'node.input',
71
+ displayName: 'Input',
72
+ fields: [
73
+ field('value', 'string', node.input?.value),
74
+ field('backgroundColor', 'color', node.input?.backgroundColor),
75
+ field('borderColor', 'color', node.input?.borderColor),
76
+ field('borderSize', 'number', node.input?.borderSize),
77
+ field('textColor', 'string', node.input?.textColor),
78
+ field('fontSize', 'number', node.input?.fontSize),
79
+ field('fontFamily', 'string', Array.isArray(node.input?.fontFamily) ? node.input.fontFamily.join(',') : node.input?.fontFamily),
80
+ field('paddingLeft', 'number', node.input?.paddingLeft),
81
+ field('paddingRight', 'number', node.input?.paddingRight),
82
+ field('paddingTop', 'number', node.input?.paddingTop),
83
+ field('paddingBottom', 'number', node.input?.paddingBottom),
84
+ ],
85
+ }];
86
+ case 'shape':
87
+ return [{
88
+ type: 'node.shape',
89
+ displayName: 'Shape',
90
+ fields: [
91
+ field('type', 'enum', node.shape?.type, enumSchema('type', ['rect', 'roundedRect'])),
92
+ field('color', 'color', node.shape?.color),
93
+ field('fillAlpha', 'number', node.shape?.fillAlpha),
94
+ field('radius', 'number', node.shape?.radius),
95
+ field('strokeColor', 'color', node.shape?.strokeColor),
96
+ field('strokeWidth', 'number', node.shape?.strokeWidth),
97
+ field('strokeAlpha', 'number', node.shape?.strokeAlpha),
98
+ ],
99
+ }];
100
+ case 'container':
101
+ return [];
102
+ }
103
+ }
104
+ function buildComponentFields(component, schema) {
105
+ return (schema?.props ?? []).filter((prop) => !prop.hidden).map((prop) => ({
106
+ key: prop.key,
107
+ label: prop.key,
108
+ type: prop.type,
109
+ value: component.props?.[prop.key] ?? prop.default,
110
+ schema: prop,
111
+ }));
112
+ }
113
+ function buildComponentModel(component) {
114
+ const schema = ComponentRegistry.get(component.type);
115
+ return {
116
+ id: component.id,
117
+ type: component.type,
118
+ displayName: schema?.displayName ?? component.type,
119
+ schema,
120
+ fields: buildComponentFields(component, schema),
121
+ };
122
+ }
123
+ export function buildInspectorModel(scene, nodeLocator) {
124
+ const located = findNode(scene, nodeLocator);
125
+ if (!located) {
126
+ return undefined;
127
+ }
128
+ const node = located.node;
129
+ return {
130
+ id: node.id,
131
+ key: node.key,
132
+ role: node.role,
133
+ name: node.name,
134
+ kind: node.kind,
135
+ transform: buildTransformFields(node),
136
+ display: buildDisplayModel(node),
137
+ components: (node.components ?? []).map(buildComponentModel),
138
+ };
139
+ }
@@ -0,0 +1,66 @@
1
+ import type { SceneSpec } from "../scene/index.js";
2
+ import type { ActionSpec } from "./ActionRegistry.js";
3
+ export type LogicValue = {
4
+ kind: 'const';
5
+ value: unknown;
6
+ } | {
7
+ kind: 'state';
8
+ path: string;
9
+ } | {
10
+ kind: 'payload';
11
+ path: string;
12
+ };
13
+ export type LogicConditionOperator = 'eq' | 'neq' | 'lt' | 'lte' | 'gt' | 'gte' | 'truthy' | 'falsy';
14
+ export interface LogicConditionSpec {
15
+ left: LogicValue;
16
+ op: LogicConditionOperator;
17
+ right?: LogicValue;
18
+ }
19
+ export type LogicStepSpec = {
20
+ id: string;
21
+ type: 'condition';
22
+ condition: LogicConditionSpec;
23
+ } | {
24
+ id: string;
25
+ type: 'setState';
26
+ path: string;
27
+ value: LogicValue;
28
+ } | {
29
+ id: string;
30
+ type: 'setNodeVisible';
31
+ node: string;
32
+ visible: boolean;
33
+ } | {
34
+ id: string;
35
+ type: 'emitAction';
36
+ action: string;
37
+ } | {
38
+ id: string;
39
+ type: 'comment';
40
+ text: string;
41
+ };
42
+ export interface LogicFlowSpec {
43
+ id: string;
44
+ action: string;
45
+ name?: string;
46
+ description?: string;
47
+ steps: LogicStepSpec[];
48
+ }
49
+ export interface LogicGraphSpec {
50
+ version: 1;
51
+ type: 'pixifact.logicGraph';
52
+ flows: LogicFlowSpec[];
53
+ }
54
+ export interface LogicValidationContext {
55
+ actions?: readonly ActionSpec[];
56
+ scene?: SceneSpec;
57
+ }
58
+ export interface LogicValidationResult {
59
+ ok: boolean;
60
+ errors: string[];
61
+ }
62
+ export declare function createLogicGraph(flows?: LogicFlowSpec[]): LogicGraphSpec;
63
+ export declare function createUseInventoryItemFlow(): LogicFlowSpec;
64
+ export declare function validateLogicGraph(graph: LogicGraphSpec, context?: LogicValidationContext): LogicValidationResult;
65
+ export declare function compileLogicGraphToTypescript(graph: LogicGraphSpec): string;
66
+ export declare function summarizeLogicGraphForAi(graph: LogicGraphSpec | undefined): string;