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.
- package/README.md +44 -0
- package/dist/authoring/ActionRegistry.d.ts +10 -0
- package/dist/authoring/ActionRegistry.js +26 -0
- package/dist/authoring/ComponentPalette.d.ts +16 -0
- package/dist/authoring/ComponentPalette.js +50 -0
- package/dist/authoring/DesignToken.d.ts +16 -0
- package/dist/authoring/DesignToken.js +33 -0
- package/dist/authoring/DiffModel.d.ts +10 -0
- package/dist/authoring/DiffModel.js +104 -0
- package/dist/authoring/InspectorModel.d.ts +27 -0
- package/dist/authoring/InspectorModel.js +139 -0
- package/dist/authoring/LogicGraph.d.ts +66 -0
- package/dist/authoring/LogicGraph.js +184 -0
- package/dist/authoring/OverrideJournal.d.ts +18 -0
- package/dist/authoring/OverrideJournal.js +23 -0
- package/dist/authoring/PreferenceMemory.d.ts +21 -0
- package/dist/authoring/PreferenceMemory.js +39 -0
- package/dist/authoring/ProjectConfig.d.ts +20 -0
- package/dist/authoring/ProjectConfig.js +78 -0
- package/dist/authoring/RuntimePreview.d.ts +11 -0
- package/dist/authoring/RuntimePreview.js +25 -0
- package/dist/authoring/SceneDocument.d.ts +74 -0
- package/dist/authoring/SceneDocument.js +332 -0
- package/dist/authoring/SceneProjectState.d.ts +20 -0
- package/dist/authoring/SceneProjectState.js +8 -0
- package/dist/authoring/SceneSelection.d.ts +11 -0
- package/dist/authoring/SceneSelection.js +1 -0
- package/dist/authoring/index.d.ts +13 -0
- package/dist/authoring/index.js +13 -0
- package/dist/commands/Command.d.ts +63 -0
- package/dist/commands/Command.js +1 -0
- package/dist/commands/CommandStack.d.ts +14 -0
- package/dist/commands/CommandStack.js +45 -0
- package/dist/commands/applyCommand.d.ts +23 -0
- package/dist/commands/applyCommand.js +316 -0
- package/dist/commands/index.d.ts +4 -0
- package/dist/commands/index.js +4 -0
- package/dist/commands/utils.d.ts +17 -0
- package/dist/commands/utils.js +77 -0
- package/dist/commands/validateCommand.d.ts +12 -0
- package/dist/commands/validateCommand.js +285 -0
- package/dist/compiler/commands/index.d.ts +109 -0
- package/dist/compiler/commands/index.js +492 -0
- package/dist/compiler/compileScenes.d.ts +11 -0
- package/dist/compiler/compileScenes.js +220 -0
- package/dist/compiler/decorators.d.ts +6 -0
- package/dist/compiler/decorators.js +79 -0
- package/dist/compiler/eventRuntime.d.ts +8 -0
- package/dist/compiler/eventRuntime.js +29 -0
- package/dist/compiler/index.d.ts +14 -0
- package/dist/compiler/index.js +14 -0
- package/dist/compiler/pixiNodeSchema.d.ts +39 -0
- package/dist/compiler/pixiNodeSchema.js +267 -0
- package/dist/compiler/sceneAssetPair.d.ts +11 -0
- package/dist/compiler/sceneAssetPair.js +92 -0
- package/dist/compiler/scenePartValidation.d.ts +6 -0
- package/dist/compiler/scenePartValidation.js +24 -0
- package/dist/compiler/sceneRuntime.d.ts +13 -0
- package/dist/compiler/sceneRuntime.js +22 -0
- package/dist/compiler/sceneValidation.d.ts +48 -0
- package/dist/compiler/sceneValidation.js +383 -0
- package/dist/compiler/scriptInterfaceExtractor.d.ts +6 -0
- package/dist/compiler/scriptInterfaceExtractor.js +258 -0
- package/dist/compiler/slotRuntime.d.ts +3 -0
- package/dist/compiler/slotRuntime.js +17 -0
- package/dist/compiler/spec.d.ts +95 -0
- package/dist/compiler/spec.js +1 -0
- package/dist/compiler/templateParser.d.ts +2 -0
- package/dist/compiler/templateParser.js +302 -0
- package/dist/compiler/templateSerializer.d.ts +2 -0
- package/dist/compiler/templateSerializer.js +101 -0
- package/dist/compiler/typescriptCompiler.d.ts +2 -0
- package/dist/compiler/typescriptCompiler.js +494 -0
- package/dist/compiler-node/index.d.ts +2 -0
- package/dist/compiler-node/index.js +2 -0
- package/dist/compiler-node/vite.d.ts +8 -0
- package/dist/compiler-node/vite.js +21 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/nodes/Input.d.ts +91 -0
- package/dist/nodes/Input.js +352 -0
- package/dist/nodes/Textarea.d.ts +10 -0
- package/dist/nodes/Textarea.js +38 -0
- package/dist/nodes/components/Button.d.ts +8 -0
- package/dist/nodes/components/Button.js +45 -0
- package/dist/nodes/components/InputField.d.ts +12 -0
- package/dist/nodes/components/InputField.js +53 -0
- package/dist/nodes/components/ProgressBar.d.ts +14 -0
- package/dist/nodes/components/ProgressBar.js +66 -0
- package/dist/nodes/components/ScrollRect.d.ts +23 -0
- package/dist/nodes/components/ScrollRect.js +108 -0
- package/dist/nodes/components/Selectable.d.ts +32 -0
- package/dist/nodes/components/Selectable.js +153 -0
- package/dist/nodes/components/index.d.ts +5 -0
- package/dist/nodes/components/index.js +5 -0
- package/dist/nodes/graphics/Graphic.d.ts +14 -0
- package/dist/nodes/graphics/Graphic.js +47 -0
- package/dist/nodes/graphics/ImageGraphic.d.ts +9 -0
- package/dist/nodes/graphics/ImageGraphic.js +42 -0
- package/dist/nodes/graphics/RoundedRectGraphic.d.ts +13 -0
- package/dist/nodes/graphics/RoundedRectGraphic.js +65 -0
- package/dist/nodes/graphics/TextGraphic.d.ts +13 -0
- package/dist/nodes/graphics/TextGraphic.js +70 -0
- package/dist/nodes/graphics/index.d.ts +4 -0
- package/dist/nodes/graphics/index.js +4 -0
- package/dist/nodes/index.d.ts +3 -0
- package/dist/nodes/index.js +3 -0
- package/dist/runtime/Application.d.ts +15 -0
- package/dist/runtime/Application.js +67 -0
- package/dist/runtime/GameObject.d.ts +100 -0
- package/dist/runtime/GameObject.js +297 -0
- package/dist/runtime/GameObjectEvent.d.ts +52 -0
- package/dist/runtime/GameObjectEvent.js +38 -0
- package/dist/runtime/Transform.d.ts +32 -0
- package/dist/runtime/Transform.js +157 -0
- package/dist/runtime/component/Component.d.ts +13 -0
- package/dist/runtime/component/Component.js +13 -0
- package/dist/runtime/component/ComponentRegistry.d.ts +49 -0
- package/dist/runtime/component/ComponentRegistry.js +25 -0
- package/dist/runtime/component/Flex.d.ts +6 -0
- package/dist/runtime/component/Flex.js +15 -0
- package/dist/runtime/component/FlexGroup.d.ts +26 -0
- package/dist/runtime/component/FlexGroup.js +136 -0
- package/dist/runtime/component/GridLayout.d.ts +47 -0
- package/dist/runtime/component/GridLayout.js +129 -0
- package/dist/runtime/component/Layout.d.ts +104 -0
- package/dist/runtime/component/Layout.js +248 -0
- package/dist/runtime/component/decorators.d.ts +4 -0
- package/dist/runtime/component/decorators.js +52 -0
- package/dist/runtime/component/index.d.ts +7 -0
- package/dist/runtime/component/index.js +7 -0
- package/dist/runtime/graphics/index.d.ts +11 -0
- package/dist/runtime/graphics/index.js +29 -0
- package/dist/runtime/group/index.d.ts +39 -0
- package/dist/runtime/group/index.js +102 -0
- package/dist/runtime/image/Image.d.ts +19 -0
- package/dist/runtime/image/Image.js +60 -0
- package/dist/runtime/image/NineSliceImage.d.ts +23 -0
- package/dist/runtime/image/NineSliceImage.js +64 -0
- package/dist/runtime/image/index.d.ts +2 -0
- package/dist/runtime/image/index.js +2 -0
- package/dist/runtime/index.d.ts +9 -0
- package/dist/runtime/index.js +9 -0
- package/dist/runtime/label/Label.d.ts +11 -0
- package/dist/runtime/label/Label.js +26 -0
- package/dist/runtime/label/LabelStyle.d.ts +3 -0
- package/dist/runtime/label/LabelStyle.js +3 -0
- package/dist/runtime/label/index.d.ts +2 -0
- package/dist/runtime/label/index.js +2 -0
- package/dist/runtime/utils/index.d.ts +1 -0
- package/dist/runtime/utils/index.js +1 -0
- package/dist/runtime/utils/onlyOnceQueueMicrotask.d.ts +1 -0
- package/dist/runtime/utils/onlyOnceQueueMicrotask.js +12 -0
- package/dist/runtime/utils/setProps.d.ts +10 -0
- package/dist/runtime/utils/setProps.js +17 -0
- package/dist/scene/dsl.d.ts +25 -0
- package/dist/scene/dsl.js +112 -0
- package/dist/scene/index.d.ts +5 -0
- package/dist/scene/index.js +5 -0
- package/dist/scene/instantiate.d.ts +8 -0
- package/dist/scene/instantiate.js +208 -0
- package/dist/scene/nodesDsl.d.ts +42 -0
- package/dist/scene/nodesDsl.js +169 -0
- package/dist/scene/spec.d.ts +100 -0
- package/dist/scene/spec.js +1 -0
- package/dist/scene/templates.d.ts +21 -0
- package/dist/scene/templates.js +239 -0
- 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;
|