@ringozz/react-godot 4.7.2-579 → 4.7.2-585

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 CHANGED
@@ -123,9 +123,9 @@ Note for physics bodies: `attach="shape"` goes on the shape **resource** element
123
123
 
124
124
  Props map directly to Godot node setters and properties. Helper arrays are automatically cast to Godot types:
125
125
 
126
- - **Vectors**: `position={[0, 1, 0]}`, `scale={[2, 2, 2]}`
127
- - **Colors**: `albedoColor={[1, 0.2, 0.2]}` or `Color` objects
128
- - **Packed arrays**: `Packed*Array`-typed props (e.g. `Gradient.colors`/`offsets`) accept plain JS arrays — elements convert with the array's element type, so `colors={[[0.5, 0, 0, 1], [1, 1, 1, 1]]}` works; `new PackedVector2Array([[1, 2], [3, 4]])` works too.
126
+ - **Vectors / value types**: value types are **named tuples** — flat (`Vector3`, `Color`, `Rect2`, …) are scalar-labeled tuples (`Vector3 = [x, y, z]`, `Color = [r, g, b, a?]`); nested (`AABB`/`Transform2D`/`Basis`/`Transform3D`/`Projection`) are row-labeled tuples of the flat element alias (`Transform3D = [x, y, z, origin]`, each row a `Vector3`), so `position={[0, 1, 0]}`, `transform={[[1, 0, 0], [0, 1, 0], [0, 0, 1], [10, 20, 30]]}`. Getters return the same labeled shape (`node.position` is `[x, y, z]`); direct setters accept them too (`node.position = [1, 2, 3]`). **Wrong-arity arrays fail typecheck** (`position={[1, 2]}` errors) — except `Color`'s trailing `a`, which is optional (`[r, g, b, a?]`, alpha defaults 1; it's the only value type with a partial scalar ctor). The math API lives as named functions per type module — `import * as v3 from '@ringozz/godot/Vector3'`, then `v3.normalized(v)`, `v3.dot(a, b)`; construction uses `fromXxx` factories (`basis.fromAxisAngle(axis, angle)`, `transform3d.fromBasisOrigin(basis, origin)`).
127
+ - **Colors**: `albedoColor={[1, 0.2, 0.2]}` (a `Color` is just `[r, g, b, a?]`).
128
+ - **Packed arrays**: `Packed*Array`-typed props (e.g. `Gradient.colors`/`offsets`) accept plain JS arrays — elements convert with the array's element type, so `colors={[[0.5, 0, 0, 1], [1, 1, 1, 1]]}` works; `new PackedVector2Array([[1, 2], [3, 4]])` works too. The same element-aware conversion applies to **direct setters and method args**: `gradient.colors = [[1, 0, 0, 1], [0, 1, 0, 1]]`, `addPoint(0.5, [1, 0, 0, 1])`, and `PhysicsRayQueryParameters3D.create([1, 2, 3], [4, 5, 6])` all accept tuples.
129
129
  - **Array-typed props** (`GodotArray`, e.g. `CodeEdit.lineLengthGuidelines`, `Font.fallbacks`) accept plain JS arrays directly — they become a Godot `Array`.
130
130
  - **Sub-properties / Dash props**: Set individual vector/color components directly using dash syntax:
131
131
  ```tsx
@@ -176,7 +176,8 @@ Signal handlers are connected **before** value props are applied within a commit
176
176
  Below is a complete interactive 3D physics scene with custom camera controls and physics bodies:
177
177
 
178
178
  ```tsx
179
- import { Color, Key, MouseButton, Vector3, Engine } from '@ringozz/godot';
179
+ import { Engine, Key, MouseButton } from '@ringozz/godot';
180
+ import type { Color } from '@ringozz/godot';
180
181
  import { BoxMesh } from '@ringozz/godot/BoxMesh';
181
182
  import { BoxShape3D } from '@ringozz/godot/BoxShape3D';
182
183
  import { Camera3D } from '@ringozz/godot/Camera3D';
@@ -201,7 +202,7 @@ const tree = Engine.getMainLoop() as SceneTree;
201
202
  const root = tree.root;
202
203
 
203
204
  function PhysBody({ color, shape, children, ...rest }: {
204
- color: Color | number[];
205
+ color: Color;
205
206
  shape: ReactElement;
206
207
  } & ComponentProps<typeof RigidBody3D>) {
207
208
  return (
@@ -224,8 +225,8 @@ export function App() {
224
225
 
225
226
  useSignal(root.windowInput, (event) => {
226
227
  if (event instanceof InputEventMouseMotion && isDragging.current) {
227
- yaw.current -= event.relative.x * 0.005;
228
- pitch.current = Math.max(-1.4, Math.min(1.4, pitch.current + event.relative.y * 0.005));
228
+ yaw.current -= event.relative[0] * 0.005;
229
+ pitch.current = Math.max(-1.4, Math.min(1.4, pitch.current + event.relative[1] * 0.005));
229
230
  } else if (event instanceof InputEventMouseButton) {
230
231
  if (event.buttonIndex === MouseButton.MOUSE_BUTTON_LEFT) {
231
232
  isDragging.current = event.pressed;
@@ -241,8 +242,8 @@ export function App() {
241
242
  const cx = zoom.current * Math.cos(pitch.current) * Math.sin(yaw.current);
242
243
  const cy = zoom.current * Math.sin(pitch.current) + 1;
243
244
  const cz = zoom.current * Math.cos(pitch.current) * Math.cos(yaw.current);
244
- camera.position = new Vector3(cx, cy, cz);
245
- camera.lookAt(new Vector3(0, 1, 0), new Vector3(0, 1, 0));
245
+ camera.position = [cx, cy, cz];
246
+ camera.lookAt([0, 1, 0], [0, 1, 0]);
246
247
  });
247
248
 
248
249
  return (
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ringozz/react-godot",
3
3
  "author": "Vladimir Davidovich",
4
- "version": "4.7.2-579",
4
+ "version": "4.7.2-585",
5
5
  "description": "A React renderer for Godot Engine via @ringozz/godot",
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -23,7 +23,7 @@
23
23
  "*.md"
24
24
  ],
25
25
  "dependencies": {
26
- "@ringozz/godot": "^4.7.2-579",
26
+ "@ringozz/godot": "^4.7.2-585",
27
27
  "@types/react-reconciler": "^0.33.0",
28
28
  "react-reconciler": "^0.33.0"
29
29
  },
@@ -6,10 +6,10 @@ import type { Signal } from '@ringozz/godot';
6
6
  import type { Node } from '@ringozz/godot/Node';
7
7
  import type { Object as Instance } from '@ringozz/godot/Object';
8
8
  import { PropertyTweener } from '@ringozz/godot/PropertyTweener';
9
- import type { GodotVar } from '@ringozz/godot/runtime';
10
9
  import { Tween, type EaseType, type TransitionType } from '@ringozz/godot/Tween';
11
10
  import type React from 'react';
12
11
  import { useEffect, useMemo, useRef } from 'react';
12
+ import { toValueType, type GodotVar } from '@ringozz/godot/runtime';
13
13
 
14
14
  // Tween/PropertyTweener are only type-used here; value-reference them so their
15
15
  // `_R` class registration isn't tree-shaken (otherwise createTween's wrapper
@@ -47,7 +47,9 @@ export function useSignal<T extends (...args: any[]) => any>(signal: Signal<T>,
47
47
  // identity diff drops them, so nothing restarts); changing deps creates a new
48
48
  // tween (the previous one is killed).
49
49
 
50
- export type TweenValue = number | number[] | GodotVar;
50
+ // Value-type goals are named tuples on the JS side — flat ones are `number[]`,
51
+ // nested ones (Transform2D/Basis/Transform3D/Projection/AABB) are `number[][]`.
52
+ export type TweenValue = number | number[] | number[][] | GodotVar;
51
53
 
52
54
  export interface TweenConfig {
53
55
  /** Seconds (default 1). */
@@ -81,14 +83,10 @@ export interface TweenProps<T extends Record<string, TweenValue> = Record<string
81
83
 
82
84
  export type Spring<T extends Record<string, TweenValue>> = { [K in keyof T]: T[K] };
83
85
 
84
- // tweenProperty/set reject JS arrays, so array goals are reconstructed as the
85
- // property's value type (read via get(native).constructor).
86
- function toValueType(node: Instance, native: string, value: TweenValue): unknown {
87
- if (!Array.isArray(value)) return value;
88
- const Ctor = (node.get(native) as any)?.constructor;
89
- if (typeof Ctor === 'function') return new Ctor(...value);
90
- return value;
91
- }
86
+ // tweenProperty/set reject generic JS arrays, so array goals are reconstructed
87
+ // as the property's value type by `toValueType` (resolves the node's
88
+ // property type and returns a GodotVar carrier the `_C` tween path unwraps;
89
+ // non-array goals pass through unchanged).
92
90
 
93
91
  function makeSpring<T extends Record<string, TweenValue>>(
94
92
  tweenRef: { current: Tween | null },
@@ -4,7 +4,7 @@
4
4
 
5
5
  import type React from 'react';
6
6
  import type { Object } from '@ringozz/godot/Object';
7
- import type { Signal, ValueTypes, GodotDictionary } from '@ringozz/godot';
7
+ import type { Signal, GodotDictionary } from '@ringozz/godot';
8
8
  import type { PackedScene } from '@ringozz/godot/PackedScene';
9
9
 
10
10
  // Identity check sensitive to readonly (deferred-conditional trick; the
@@ -39,29 +39,27 @@ type SettableKey<T, K extends keyof T> =
39
39
 
40
40
  // Container props accept plain JS forms: JSX values flow through
41
41
  // `Instance.assign` (`napi_to_variant_typed`), which builds packed arrays,
42
- // Godot `Array`s, and `Dictionary`s from JS arrays/objects. Only gen heap
43
- // containers (10 packed + `GodotArray` + `GodotDictionary`) implement
44
- // `Iterable`, so that is the gate; `get(index: number)` recovers the concrete
45
- // element type (packed class getters return it even though the class generic
46
- // defaults to `any`). Scalar-packed -> `number[]`/`string[]`; element-packed
47
- // -> `(E | number[])[]` (instances or value-type tuples); `GodotArray<E>` ->
48
- // `(E | number[])[]`; `GodotDictionary<K,V>` -> `Record<string, V>`.
42
+ // Godot `Array`s, and `Dictionary`s from JS arrays/objects. Every gen heap
43
+ // container implements `Iterable` and has `get(index: number)`; `get` recovers
44
+ // the concrete element type (packed class getters return it even though the
45
+ // class generic defaults to `any`), and `GodotDictionary` routes to
46
+ // `Record<string, V>` first. Scalar-packed -> `number[]`/`string[]`;
47
+ // element-packed -> `(E | number[])[]` (instances or value-type tuples);
48
+ // `GodotArray<E>` -> `(E | number[])[]`; `GodotDictionary<K,V>` ->
49
+ // `Record<string, V>`.
49
50
  type ContainerForm<T> =
50
51
  T extends GodotDictionary<any, infer V> ? Record<string, V>
51
52
  : T extends { get(index: number): infer E } ? (E extends number | string ? E[] : (E | number[])[])
52
- : T extends Iterable<infer E> ? E[]
53
53
  : never;
54
54
 
55
55
  // JSX props are settable instance members, typed as the reconciler's
56
- // `Instance.assign` accepts them: ValueTypes also accept JSX arrays, signals
57
- // (getter `Signal<CB>`, setter `CB | null`) take the callback directly, and
58
- // containers accept plain JS arrays/objects. Maps over `keyof T`
59
- // (homomorphic) so optional/readonly modifiers survive.
56
+ // `Instance.assign` accepts them: value types are plain tuples (the getter
57
+ // type IS the array form), signals (getter `Signal<CB>`, setter `CB | null`)
58
+ // take the callback directly, and containers accept plain JS arrays/objects.
59
+ // Maps over `keyof T` (homomorphic) so optional/readonly modifiers survive.
60
60
  type Properties<T> = {
61
61
  [K in keyof T as SettableKey<T, K>]:
62
- T[K] extends ValueTypes ? T[K] | number[]
63
- : T[K] extends Signal<infer CB> ? CB | null
64
- : T[K] extends GodotDictionary<any, any> ? T[K] | ContainerForm<T[K]>
62
+ T[K] extends Signal<infer CB> ? CB | null
65
63
  : T[K] extends Iterable<unknown> ? T[K] | ContainerForm<T[K]>
66
64
  : T[K];
67
65
  };