lecodes-sdk 0.20.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/dist/global.d.ts +35 -9
  2. package/dist/types/animate/tween/Animation.d.ts +69 -0
  3. package/dist/types/animate/tween/Timeline.d.ts +55 -0
  4. package/dist/types/animate/tween/animateValue.d.ts +27 -0
  5. package/dist/types/animate/tween/easing.d.ts +29 -0
  6. package/dist/types/animate/tween/spec.d.ts +178 -0
  7. package/dist/types/canvas/Canvas.d.ts +2 -0
  8. package/dist/types/g2/Node2D.d.ts +16 -0
  9. package/dist/types/g2/Sprite.d.ts +11 -1
  10. package/dist/types/gl/Camera.d.ts +15 -1
  11. package/dist/types/gl/DecalSet.d.ts +48 -3
  12. package/dist/types/gl/Foliage.d.ts +47 -0
  13. package/dist/types/gl/Geometry.d.ts +36 -0
  14. package/dist/types/gl/Light.d.ts +25 -7
  15. package/dist/types/gl/Lightmap.d.ts +90 -51
  16. package/dist/types/gl/Material.d.ts +32 -20
  17. package/dist/types/gl/Mesh.d.ts +7 -1
  18. package/dist/types/gl/Model.d.ts +41 -5
  19. package/dist/types/gl/Node.d.ts +18 -0
  20. package/dist/types/gl/Particles.d.ts +53 -1
  21. package/dist/types/gl/Scene.d.ts +21 -1
  22. package/dist/types/gl/animation/AnimationClip.d.ts +19 -0
  23. package/dist/types/gl/animation/Animator.d.ts +27 -0
  24. package/dist/types/gl/animation/DynamicBone.d.ts +184 -0
  25. package/dist/types/gl/animation/IK.d.ts +109 -0
  26. package/dist/types/gl/{Locomotion.d.ts → animation/Locomotion.d.ts} +6 -6
  27. package/dist/types/gl/animation/Warp.d.ts +2 -1
  28. package/dist/types/gl/animation/core.d.ts +35 -4
  29. package/dist/types/gl/{AudioSource.d.ts → audio/AudioSource.d.ts} +6 -6
  30. package/dist/types/gl/{AudioZone.d.ts → audio/AudioZone.d.ts} +4 -4
  31. package/dist/types/gl/{SceneAudio.d.ts → audio/SceneAudio.d.ts} +1 -1
  32. package/dist/types/gl/{NavAgent.d.ts → nav/NavAgent.d.ts} +4 -4
  33. package/dist/types/gl/{NavMesh.d.ts → nav/NavMesh.d.ts} +4 -4
  34. package/dist/types/gl/{CharacterController.d.ts → physics/CharacterController.d.ts} +5 -5
  35. package/dist/types/gl/{Physics.d.ts → physics/Physics.d.ts} +6 -5
  36. package/dist/types/gl/physics/Ragdoll.d.ts +161 -0
  37. package/dist/types/gl/{Shape.d.ts → physics/Shape.d.ts} +3 -3
  38. package/dist/types/gl/{Trigger.d.ts → physics/Trigger.d.ts} +2 -2
  39. package/dist/types/gl/{Terrain.d.ts → terrain/Terrain.d.ts} +10 -8
  40. package/dist/types/gl/{terrainMesh.d.ts → terrain/terrainMesh.d.ts} +1 -1
  41. package/dist/types/gl/vehicle/Vehicle.d.ts +300 -0
  42. package/dist/types/gl/vehicle/Wheel.d.ts +147 -0
  43. package/dist/types/inject.d.ts +35 -27
  44. package/dist/types/runtime/files.d.ts +24 -1
  45. package/dist/types/scene/defineScene.d.ts +50 -31
  46. package/dist/types/ui/UIButton.d.ts +3 -1
  47. package/dist/types/ui/UIInput.d.ts +5 -1
  48. package/dist/types/ui/UINode.d.ts +24 -24
  49. package/dist/types.json +1 -1
  50. package/package.json +1 -1
  51. package/prompts/README.md +142 -142
  52. package/prompts/core-design.md +27 -4
  53. package/prompts/core.md +35 -6
  54. package/prompts/select.ts +19 -4
  55. package/src/animate/tween/Animation.ts +378 -0
  56. package/src/animate/tween/Timeline.ts +175 -0
  57. package/src/animate/tween/animateValue.ts +100 -0
  58. package/src/animate/tween/easing.ts +172 -0
  59. package/src/animate/tween/spec.ts +479 -0
  60. package/src/bridges.d.ts +1760 -1481
  61. package/src/canvas/Canvas.ts +21 -0
  62. package/src/compile/__tests__/assetMacro.test.ts +26 -0
  63. package/src/compile/__tests__/compile.test.ts +11 -0
  64. package/src/compile/__tests__/detectEntry.test.ts +19 -0
  65. package/src/compile/__tests__/serverSplit.test.ts +27 -0
  66. package/src/compile/bundler.ts +34 -4
  67. package/src/compile/compileProject.ts +31 -1
  68. package/src/compile/detectEntry.ts +8 -3
  69. package/src/compile/header.ts +6 -3
  70. package/src/compile/index.ts +3 -0
  71. package/src/compile/sceneEditor.ts +42 -1
  72. package/src/compile/serverSplit.ts +9 -3
  73. package/src/g2/Node2D.ts +38 -0
  74. package/src/g2/Sprite.ts +20 -1
  75. package/src/gl/Camera.ts +34 -1
  76. package/src/gl/DecalSet.ts +132 -5
  77. package/src/gl/Foliage.ts +102 -0
  78. package/src/gl/Geometry.ts +109 -0
  79. package/src/gl/Light.ts +46 -16
  80. package/src/gl/Lightmap.ts +440 -249
  81. package/src/gl/Material.ts +69 -36
  82. package/src/gl/Mesh.ts +120 -102
  83. package/src/gl/Model.ts +167 -124
  84. package/src/gl/Node.ts +40 -1
  85. package/src/gl/Particles.ts +82 -5
  86. package/src/gl/Scene.ts +35 -2
  87. package/src/gl/animation/AnimationClip.ts +52 -0
  88. package/src/gl/animation/Animator.ts +42 -2
  89. package/src/gl/animation/DynamicBone.ts +482 -0
  90. package/src/gl/animation/IK.ts +214 -0
  91. package/src/gl/{Locomotion.ts → animation/Locomotion.ts} +7 -7
  92. package/src/gl/animation/Playback.ts +5 -4
  93. package/src/gl/animation/Warp.ts +5 -2
  94. package/src/gl/animation/core.ts +65 -4
  95. package/src/gl/{AudioSource.ts → audio/AudioSource.ts} +7 -7
  96. package/src/gl/{AudioZone.ts → audio/AudioZone.ts} +75 -75
  97. package/src/gl/{SceneAudio.ts → audio/SceneAudio.ts} +2 -2
  98. package/src/gl/{NavAgent.ts → nav/NavAgent.ts} +5 -5
  99. package/src/gl/{NavMesh.ts → nav/NavMesh.ts} +8 -8
  100. package/src/gl/{CharacterController.ts → physics/CharacterController.ts} +5 -5
  101. package/src/gl/{Physics.ts → physics/Physics.ts} +12 -5
  102. package/src/gl/physics/Ragdoll.ts +451 -0
  103. package/src/gl/{Shape.ts → physics/Shape.ts} +3 -3
  104. package/src/gl/{Trigger.ts → physics/Trigger.ts} +45 -45
  105. package/src/gl/{physicsEvents.ts → physics/physicsEvents.ts} +1 -1
  106. package/src/gl/{Terrain.ts → terrain/Terrain.ts} +14 -12
  107. package/src/gl/{terrainMesh.ts → terrain/terrainMesh.ts} +1 -1
  108. package/src/gl/vehicle/Vehicle.ts +666 -0
  109. package/src/gl/vehicle/Wheel.ts +290 -0
  110. package/src/inject.ts +236 -224
  111. package/src/runtime/files.ts +32 -2
  112. package/src/scene/defineScene.ts +92 -66
  113. package/src/scene/level.ts +2 -2
  114. package/src/ui/UIButton.ts +2 -2
  115. package/src/ui/UIInput.ts +3 -3
  116. package/src/ui/UINode.ts +61 -36
  117. package/dist/types/animate/animate.d.ts +0 -20
  118. package/dist/types/gl/Gearbox.d.ts +0 -86
  119. package/dist/types/gl/IK.d.ts +0 -53
  120. package/dist/types/gl/Ragdoll.d.ts +0 -86
  121. package/dist/types/gl/Vehicle.d.ts +0 -191
  122. package/dist/types/gl/Wheel.d.ts +0 -95
  123. package/src/animate/animate.ts +0 -238
  124. package/src/gl/Gearbox.ts +0 -212
  125. package/src/gl/IK.ts +0 -193
  126. package/src/gl/Ragdoll.ts +0 -270
  127. package/src/gl/Vehicle.ts +0 -473
  128. package/src/gl/Wheel.ts +0 -240
  129. /package/dist/types/gl/{physicsEvents.d.ts → physics/physicsEvents.d.ts} +0 -0
@@ -0,0 +1,147 @@
1
+ import { Aspect } from "../../core/Aspect";
2
+ import type { FieldMeta } from "../../core/fields";
3
+ import { Vec3 } from "../../math/vec";
4
+ import { Node } from "../Node";
5
+ /** Floats `vehicleGetState` writes before the per-wheel block: speed, rpm, contacts, vx, vy, vz, wx, wy, wz,
6
+ * steerDeg, steerTorque. */
7
+ export declare const STATE_HEADER = 11;
8
+ /** Floats per wheel: contact, slip, slipAngle, suspensionLength, steerDeg, spin, fLat, fLong, vLat, vSlip, load. */
9
+ export declare const WHEEL_STATE = 11;
10
+ /** Fully compressed = this fraction of `travel` (the engine's one suspension constant, mirrored here
11
+ * so `compression` and the resting-height maths read the same number). */
12
+ export declare const SUSPENSION_MIN_FRACTION = 0.4;
13
+ /** A point on a curve — a tire's friction over slip, an engine's torque over rpm. */
14
+ export type Point = {
15
+ x: number;
16
+ y: number;
17
+ };
18
+ export interface WheelSuspension {
19
+ /** Total travel in metres, full droop to fully compressed (default 0.4). */
20
+ travel?: number;
21
+ /** Spring frequency in Hz — bigger is stiffer (default 1.4; a race car ~2.5, a truck ~1). It also
22
+ * sets the ride-height sag, and the sag is how far the wheel can still extend DOWN. */
23
+ stiffness?: number;
24
+ /** Damping ratio 0..1 (default 0.5). Low values pogo. */
25
+ damping?: number;
26
+ }
27
+ /**
28
+ * A tire, as the engine takes it: two friction curves and two numbers. `Tire.road()` and the other
29
+ * presets are starting points — copy one and edit, or `Tire.scale()` it.
30
+ */
31
+ export interface Tire {
32
+ /** Lateral friction over slip ANGLE: `{ x: degrees, y: friction }`, x ascending. Friction is the
33
+ * coefficient the tire's load is multiplied by; the last value holds to 90°. The grip a wheel
34
+ * actually gets is combined with the ground it stands on as `sqrt(tire × groundFriction)`, and a
35
+ * body's friction defaults to 0.6 — a road a car should really corner on wants `Physics { friction: 1 }`. */
36
+ side: Point[];
37
+ /** Longitudinal friction over slip RATIO: `{ x: ratio, y: friction }` (0 = rolling in sync, 1 =
38
+ * locked or spinning freely). */
39
+ forward: Point[];
40
+ /** The longitudinal impulse clamp as a multiple of `friction × load` — for DRIVE only; a braking
41
+ * wheel is always clamped at the physical limit (or a locked rear would stop the car at a full g
42
+ * and kill every handbrake turn). `1` is the physical tire; `road`'s 10 is Jolt's own sample
43
+ * value (it keeps a driven wheel from ever spinning up). Anything a friction circle should bite
44
+ * on wants this near 1. */
45
+ traction?: number;
46
+ /** Friction circle, 0..1: how much of the lateral capacity the longitudinal impulse in use takes
47
+ * away. `0` = the two axes independent (a spinning wheel corners as well as a rolling one);
48
+ * `1` = the full circle — wheelspin or a locked wheel lets go sideways. */
49
+ circle?: number;
50
+ }
51
+ /**
52
+ * Tire presets — fresh point lists every call (editing one never touches another), and `scale`.
53
+ *
54
+ * - `road` — Jolt's stock tire: lateral peak 1.2 at 3° falling to 1.0 by 20° and flat after, the
55
+ * two axes independent, and the longitudinal clamp ×10 (Jolt's own sample runs this; it means a
56
+ * driven wheel practically never spins up). Grippy, glued, hard to unsettle.
57
+ * - `arcade` — the peak held all the way out on both axes: the car does not let go at all.
58
+ * - `drift` — a tire that can be held sideways: lateral grip falls to 0.7 past 25°, the longitudinal
59
+ * clamp is near the physical one (`traction: 2` — a launch at exactly 1 is a 0.4 g crawl; 2 keeps
60
+ * it punchy) so the rear can be spun up, and a friction circle (`circle: 0.85` — most of it, so a
61
+ * spinning rear loses most but not all of its lateral grip and the slide stays progressive):
62
+ * power oversteer, and a slide the throttle keeps alive. A 600 N·m car lights its tires off the
63
+ * line on it; that is the tire, and a traction control is the game's.
64
+ */
65
+ export declare const Tire: {
66
+ road: () => Tire;
67
+ arcade: () => Tire;
68
+ drift: () => Tire;
69
+ /** Both curves multiplied by `k` — a grip slider, a cold tire, a wet road. A new tire; `t` is untouched. */
70
+ scale: (t: Tire, k: number) => Tire;
71
+ };
72
+ export declare function resolveTire(tire: Tire): ResolvedTire;
73
+ export declare class Wheel extends Aspect<"wheel", Node> {
74
+ static readonly aspect = "wheel";
75
+ /** Wheel radius in metres. Default: measured from the node's rendered bounds, else 0.35. Too small
76
+ * and the car scrapes, too large and it floats. */
77
+ radius?: number;
78
+ /** Wheel width in metres. Default: measured, else 0.25. */
79
+ width?: number;
80
+ /** Axle group, front to back. Default: derived from Z. Set it for a car whose wheels are not in neat
81
+ * left/right pairs — an axle's wheels are paired for the differential and the anti-roll bar. */
82
+ axle?: number;
83
+ /** Does this wheel steer? Default: the wheels on the front axle. */
84
+ steer?: boolean;
85
+ /** This wheel's SHARE of the engine's torque, 0 = not driven. Default: from the vehicle's
86
+ * `driveAxle` (1 on that axle). An axle's share is the sum of its two wheels', the left/right
87
+ * split their ratio, so `0.6` / `0.4` front-to-rear is `driven: 0.3` on each front wheel and
88
+ * `0.2` on each rear. Structural: baked at attach. */
89
+ driven?: number;
90
+ suspension: WheelSuspension;
91
+ /** The tire (default `Tire.road()`). Live: re-assign it or re-configure the aspect and the car
92
+ * retunes in place — a grip slider, a tire that heats up. */
93
+ tire: Tire;
94
+ /** INPUT: brake torque on this wheel THIS frame, N·m (default 0). Sticky, like `Vehicle.throttle`:
95
+ * write it every frame from a driver system's `updateBefore` — the foot brake's share, the
96
+ * handbrake's 4500 on a rear wheel, an ABS's pulses — and the same physics step applies it. A
97
+ * braking wheel is clamped at the physical tire limit (the `traction` boost is for drive only). */
98
+ brake: number;
99
+ /** Scene-editor inspector. `steer` / `driven` are deliberately absent: they are tri-state
100
+ * (undefined = derive it from the axle), which a slider cannot express. */
101
+ static fields: FieldMeta<Wheel>;
102
+ static editor: {
103
+ rebuild: boolean;
104
+ };
105
+ onDetach(): void;
106
+ /** Re-configuring a wheel on a live car retunes the car: the tire is live; size, suspension, the
107
+ * driven share and the axle are structural (re-attach the Vehicle). */
108
+ onReconfigure(): void;
109
+ private _state;
110
+ /** Touching the ground this step. */
111
+ get contact(): boolean;
112
+ /** Longitudinal slip RATIO, signed: 0 = rolling in sync with the ground, towards +1 = spinning up
113
+ * (wheelspin), towards −1 = locking under braking — whichever way the car travels. Smoke, tire
114
+ * marks, wheelspin sound and traction control key off this. */
115
+ get slip(): number;
116
+ /** Slip ANGLE in degrees, signed: how far the tire's travel direction is off from where it points,
117
+ * positive when the contact patch slides to the tire's RIGHT. Past the curve's peak (~3°) the tire
118
+ * is letting go sideways. */
119
+ get slipAngle(): number;
120
+ /** This wheel's steer angle in degrees, right-positive. */
121
+ get steerAngle(): number;
122
+ /** Wheel spin in rad/s (× radius ≈ the speed the tire is laying down). Forward-positive; a model
123
+ * turns about the right-hand axle by MINUS the integrated angle. */
124
+ get spin(): number;
125
+ /** The tire's lateral force this step, N, positive toward the tire's right — the road pushing the
126
+ * patch back against its slide. */
127
+ get sideForce(): number;
128
+ /** The tire's longitudinal force this step, N, positive pushing the car forward. */
129
+ get forwardForce(): number;
130
+ /** How fast the contact patch slides sideways, m/s, positive to the tire's right. */
131
+ get sideSlipSpeed(): number;
132
+ /** How fast the tread runs over the road, m/s, positive spinning up, negative locking. Force ×
133
+ * sliding speed (`|sideForce × sideSlipSpeed| + |forwardForce × slipSpeed|`) is the friction's
134
+ * heat into the tread, W — a tire-temperature model integrates it. */
135
+ get slipSpeed(): number;
136
+ /** The suspension's force on the wheel this step, N — the tire's LOAD (0 in the air). What a
137
+ * friction budget divides by: `hypot(sideForce, forwardForce) / (load × μ)`. */
138
+ get load(): number;
139
+ /** Suspension compression, 0 = fully extended (drooping) … 1 = fully compressed. */
140
+ get compression(): number;
141
+ /** Where the wheel's centre is right now, in CHASSIS space: the suspension's attachment point,
142
+ * down by however far it hangs this step. Put a wheel model here (see the file header). */
143
+ get position(): Vec3;
144
+ /** Edit mode: this wheel's own circle at its resting height, its travel range, and a mark when the
145
+ * engine drives it — enough to place a wheel without running the game. */
146
+ rebuild(): void;
147
+ }
@@ -58,8 +58,12 @@ export { Presentable } from "./ui/UI";
58
58
  export type { PresentOptions, Transition, TransitionName, TransitionSpec, TransitionTransform } from "./ui/UI";
59
59
  export type { UINode, UINodeChild } from "./ui/UINode";
60
60
  export { theme, type ThemeValues, type ThemeAccessors } from "./ui/theme";
61
- export { animate, animateMat4, stopAnimation, pauseAnimation, resumeAnimation } from "./animate/animate";
61
+ export { animate, type AnimateOptions, type AnimateValue, type AnimateOut } from "./animate/tween/animateValue";
62
62
  export { cubicBezier } from "./animate/bezier";
63
+ export { Timeline, type TimelineOptions, type TimelineAddOptions, type TimelinePosition } from "./animate/tween/Timeline";
64
+ export { type Animation } from "./animate/tween/Animation";
65
+ export { type TweenMeta } from "./animate/tween/spec";
66
+ export { type EasingInput } from "./animate/tween/easing";
63
67
  export { easeIn, easeOut, easeInOut } from "./animate/easings";
64
68
  export { QRScanner } from "./plugins/qr";
65
69
  export { CameraView, type CameraFacing } from "./plugins/camera";
@@ -85,24 +89,24 @@ export { Scene, ARScene, VRScene } from "./gl/Scene";
85
89
  export { Node } from "./gl/Node";
86
90
  export { Mesh } from "./gl/Mesh";
87
91
  export { Model } from "./gl/Model";
88
- export { Shape } from "./gl/Shape";
89
- export { Physics } from "./gl/Physics";
90
- export { Trigger } from "./gl/Trigger";
91
- export { AudioSource, type AudioCone } from "./gl/AudioSource";
92
- export { AudioZone } from "./gl/AudioZone";
93
- export { SceneAudio } from "./gl/SceneAudio";
94
- export { CharacterController } from "./gl/CharacterController";
95
- export { Vehicle } from "./gl/Vehicle";
96
- export type { DriveLayout, DifferentialMode, EngineConfig, SteeringConfig, AeroConfig } from "./gl/Vehicle";
97
- export { Wheel } from "./gl/Wheel";
98
- export { Ragdoll } from "./gl/Ragdoll";
99
- export type { RagdollPart, RagdollHinge } from "./gl/Ragdoll";
100
- export type { TireCurve, WheelSuspension, WheelTire, WheelVisual } from "./gl/Wheel";
101
- export { Gearbox } from "./gl/Gearbox";
92
+ export { Shape } from "./gl/physics/Shape";
93
+ export { Physics } from "./gl/physics/Physics";
94
+ export { Trigger } from "./gl/physics/Trigger";
95
+ export { AudioSource, type AudioCone } from "./gl/audio/AudioSource";
96
+ export { AudioZone } from "./gl/audio/AudioZone";
97
+ export { SceneAudio } from "./gl/audio/SceneAudio";
98
+ export { CharacterController } from "./gl/physics/CharacterController";
99
+ export { Vehicle } from "./gl/vehicle/Vehicle";
100
+ export type { DriveLayout, DifferentialMode, EngineConfig, SteeringConfig, SteeringFeedback, SteeringDriver, AeroConfig } from "./gl/vehicle/Vehicle";
101
+ export { Wheel, Tire, resolveTire } from "./gl/vehicle/Wheel";
102
+ export { Ragdoll } from "./gl/physics/Ragdoll";
103
+ export type { RagdollPart, RagdollHinge, RagdollFacing, RagdollActivateOptions, RagdollDeactivateOptions, RagdollHitOptions } from "./gl/physics/Ragdoll";
104
+ export type { Point, WheelSuspension } from "./gl/vehicle/Wheel";
102
105
  export { AnimationClip } from "./gl/animation/AnimationClip";
103
106
  export type { ClipDef, ClipTrackDef, ClipKey } from "./gl/animation/AnimationClip";
104
107
  export { Animator } from "./gl/animation/Animator";
105
108
  export type { LodMode } from "./gl/animation/Animator";
109
+ export type { TrianglePick } from "./gl/Model";
106
110
  export { Feet } from "./gl/animation/Feet";
107
111
  export type { FeetOptions, FootState } from "./gl/animation/Feet";
108
112
  export { Warp } from "./gl/animation/Warp";
@@ -111,18 +115,22 @@ export { Playback } from "./gl/animation/Playback";
111
115
  export { Loop } from "./gl/animation/Loop";
112
116
  export { Layer } from "./gl/animation/Layer";
113
117
  export type { PlayOptions, StopOptions, LayerOptions, LoopOptions, LoopDef, BlendPosition, BlendMember, ActiveClip, ClipInfo, ClipCycle, ClipAlign, KneeAxisReport, StepHandler, ClipEventHandler } from "./gl/animation/core";
114
- export { IK } from "./gl/IK";
115
- export type { IKTwoBone, IKLookAt } from "./gl/IK";
118
+ export { IK } from "./gl/animation/IK";
119
+ export { DynamicBone, DynamicBoneCollider } from "./gl/animation/DynamicBone";
120
+ export type { DynamicBoneCurve, DynamicBoneFloor, DynamicBoneColliders } from "./gl/animation/DynamicBone";
121
+ export type { IKTwoBone, IKLookAt } from "./gl/animation/IK";
116
122
  export { Light } from "./gl/Light";
117
123
  export { Lightmap } from "./gl/Lightmap";
118
- export { Terrain } from "./gl/Terrain";
119
- export type { TerrainOptions, TerrainLayer, TerrainRegion, ConformOptions, RibbonOptions, AutoPaintRules, SculptOptions, TerrainHit, TerrainSnapshot } from "./gl/Terrain";
120
- export { NavMesh, NavCrowd } from "./gl/NavMesh";
121
- export type { NavMeshLoadOptions, NavMeshInfo, NavPath, NavQueryOptions, NavRaycastHit, NavAgentSize } from "./gl/NavMesh";
122
- export { NavAgent } from "./gl/NavAgent";
123
- export type { NavAgentState, NavAgentAvoidance, NavAgentDrive, NavAgentEvents } from "./gl/NavAgent";
124
- export { Locomotion } from "./gl/Locomotion";
125
- export type { LocomotionSet, LocomotionClip, LocomotionTuning, LocomotionState, LocomotionEvents, Displacement, Gait } from "./gl/Locomotion";
124
+ export { Foliage } from "./gl/Foliage";
125
+ export type { FoliageOptions, FoliageWind } from "./gl/Foliage";
126
+ export { Terrain } from "./gl/terrain/Terrain";
127
+ export type { TerrainOptions, TerrainLayer, TerrainRegion, ConformOptions, RibbonOptions, AutoPaintRules, SculptOptions, TerrainHit, TerrainSnapshot } from "./gl/terrain/Terrain";
128
+ export { NavMesh, NavCrowd } from "./gl/nav/NavMesh";
129
+ export type { NavMeshLoadOptions, NavMeshInfo, NavPath, NavQueryOptions, NavRaycastHit, NavAgentSize } from "./gl/nav/NavMesh";
130
+ export { NavAgent } from "./gl/nav/NavAgent";
131
+ export type { NavAgentState, NavAgentAvoidance, NavAgentDrive, NavAgentEvents } from "./gl/nav/NavAgent";
132
+ export { Locomotion } from "./gl/animation/Locomotion";
133
+ export type { LocomotionSet, LocomotionClip, LocomotionTuning, LocomotionState, LocomotionEvents, Displacement, Gait } from "./gl/animation/Locomotion";
126
134
  export { Material } from "./gl/Material";
127
135
  export { Geometry } from "./gl/Geometry";
128
136
  export { InstancedMesh } from "./gl/InstancedMesh";
@@ -134,5 +142,5 @@ export { Ray } from "./gl/Ray";
134
142
  export { Plane } from "./gl/Plane";
135
143
  export { Noise } from "./gl/Noise";
136
144
  export { Particles, Trail, curve, colorCurve } from "./gl/Particles";
137
- export { DecalSet } from "./gl/DecalSet";
138
- export type { DecalOptions, DecalSetOptions, DecalSpawnOptions, DecalPlacement } from "./gl/DecalSet";
145
+ export { DecalSet, DecalTrail } from "./gl/DecalSet";
146
+ export type { DecalOptions, DecalSetOptions, DecalSpawnOptions, DecalPlacement, DecalTrailOptions } from "./gl/DecalSet";
@@ -11,6 +11,19 @@ export declare function openFilePicker(options: OpenFilePickerOptions & {
11
11
  /** Anything writable as-is: text, raw bytes, or a buffer the host already holds
12
12
  * (a picked `File`, a `FetchResponse`, a `Canvas.toFile()` snapshot). */
13
13
  export type FileData = string | Uint8Array | ArrayBuffer | File | FetchResponse;
14
+ /** One directory entry, as `files.ls` / `files.stat` report it. */
15
+ export type FileEntry = {
16
+ /** The last path segment. */
17
+ name: string;
18
+ /** Relative to the listed directory, `/`-separated — equal to `name` unless the listing was
19
+ * recursive. Join it onto the directory you listed to read the file. */
20
+ path: string;
21
+ kind: "file" | "dir";
22
+ /** Bytes; 0 for a directory. */
23
+ size: number;
24
+ /** Last modification, ms since the epoch (a `Date` value). */
25
+ modified: number;
26
+ };
14
27
  export declare const files: {
15
28
  /** Is there a filesystem here (desktop)? False on web/mobile, where every other call rejects. */
16
29
  readonly supported: boolean;
@@ -24,10 +37,20 @@ export declare const files: {
24
37
  readBytes(path: string): Promise<Uint8Array | null>;
25
38
  /** Delete a file, or a directory (empty, or any directory with `{ recursive: true }`). Deleting
26
39
  * something already gone is success — what's promised is that the path is free afterwards. */
27
- delete(path: string, options?: {
40
+ rm(path: string, options?: {
28
41
  recursive?: boolean;
29
42
  }): Promise<void>;
30
43
  /** Create a directory and any missing parents. An existing directory is fine. */
31
44
  mkdir(path: string): Promise<void>;
45
+ /** The entries of a directory, sorted by path — null when there is no such directory (an empty
46
+ * one is `[]`, so `(await files.ls("saves")) ?? []` covers both). `{ recursive: true }` walks
47
+ * the subfolders too; each entry's `path` is then relative to the directory listed. A path that
48
+ * is a file, not a directory, rejects. Filter in JS — `entries.filter(e => e.name.endsWith(".json"))`. */
49
+ ls(path: string, options?: {
50
+ recursive?: boolean;
51
+ }): Promise<FileEntry[] | null>;
52
+ /** What is at `path`: a file or a directory entry (`path` equal to `name`), or null when nothing
53
+ * is there — `(await files.stat(p)) !== null` is the exists check. */
54
+ stat(path: string): Promise<FileEntry | null>;
32
55
  };
33
56
  export {};
@@ -5,10 +5,12 @@ import { Scene, type SceneOptions } from "../gl/Scene";
5
5
  import { Node } from "../gl/Node";
6
6
  import { Mesh } from "../gl/Mesh";
7
7
  import { Model } from "../gl/Model";
8
- import { type TerrainLayer, type TerrainRegion, type TerrainOptions } from "../gl/Terrain";
8
+ import { type LightmapTransmit } from "../gl/Lightmap";
9
+ import { type FoliageOptions } from "../gl/Foliage";
10
+ import { type TerrainLayer, type TerrainRegion, type TerrainOptions } from "../gl/terrain/Terrain";
9
11
  import { Light, type SunOptions } from "../gl/Light";
10
12
  import { type MaterialDef } from "./material";
11
- import type { CylinderOptions, PlaneOptions, SphereOptions } from "../gl/Geometry";
13
+ import type { CapsuleOptions, CylinderOptions, PlaneOptions, SphereOptions } from "../gl/Geometry";
12
14
  export type MeshDef = {
13
15
  kind: "box";
14
16
  size?: Vec3Like | number;
@@ -17,6 +19,8 @@ export type MeshDef = {
17
19
  } & SphereOptions) | ({
18
20
  kind: "cylinder";
19
21
  } & CylinderOptions) | ({
22
+ kind: "capsule";
23
+ } & CapsuleOptions) | ({
20
24
  kind: "plane";
21
25
  } & PlaneOptions);
22
26
  export type LightDef = {
@@ -128,6 +132,12 @@ export type SceneNodeDef = {
128
132
  * Default: static unless a `Physics` aspect moves the node (`dynamic` — Physics' default — or
129
133
  * `kinematic`). Set it only to override that rule; prefab subtrees inherit the verdict. */
130
134
  lightmap?: boolean;
135
+ /** `model` nodes: vegetation — load through the FOLIAGE tier (wind, touch bending, per-copy tint; see
136
+ * `Foliage`). `{ fade: [start, end] }` also thins the cards out over that distance range (m) and drops the
137
+ * copy past `end` — for ground cover. The fade is per ASSET: every node of the same GLB shares it. */
138
+ foliage?: boolean | {
139
+ fade?: [number, number];
140
+ };
131
141
  /** Navigation (a scene with `env.navmesh`, see navmesh.md): every STATIC body is walkable by
132
142
  * default. `false` leaves this one out of the bake; `'unwalkable'` cuts its footprint out
133
143
  * (nobody stands on or crosses it). */
@@ -142,37 +152,44 @@ export type SceneCameraDef = CameraProjectionDef & {
142
152
  /** Point the camera looks at. */
143
153
  target?: Vec3Like;
144
154
  };
145
- /** `env.lightmap` — the level's baked lighting (see lightmap.md): the two files
146
- * `lecodes lightmap bake` writes, plus how the bake is applied. Absent = real-time only. */
155
+ /** `env.lightmap` — the level's baked lighting (packages/creator-bake): the files `lecodes lightmap bake`
156
+ * writes. Absent = real-time only. */
147
157
  export type SceneLightmapDef = {
148
- /** `asset('./assets/lightmap/lightmap.bake')` */
158
+ /** `asset('./assets/lightmap/level.bake')` */
149
159
  data: string;
150
- /** `asset('./assets/lightmap/lightmap.ktx2')` — or, for a bake that took more than one atlas PAGE
151
- * (`lecodes lightmap bake --pages`), every page in order: `[asset('x.ktx2'), asset('x_1.ktx2')]`. */
152
- texture: string | string[];
153
- /** `asset('./assets/lightmap/lightmap-light.ktx2')` (pages like `texture`) — the point lights' baked
154
- * irradiance the bake writes when the level holds point lights; the real-time lights it carries are
155
- * switched off once it applies. Absent = the lamps stay real-time. */
156
- light?: string | string[];
157
- /** `asset('./assets/lightmap/lightmap.volume')` — the light VOLUME for everything that moves (the
158
- * same bake writes it): dynamic nodes, and every model loaded in code while the level runs, read
159
- * the baked sun/ambient at their own position. Absent = movers stay real-time only. */
160
+ /** `asset('./assets/lightmap/level-light.ktx2')` — or, for a bake that took more than one atlas PAGE
161
+ * (`lecodes lightmap bake --pages`), every page in order: `[asset('x-light.ktx2'), asset('x-light_1.ktx2')]`. */
162
+ light: string | string[];
163
+ /** `asset('./assets/lightmap/level-aux.ktx2')` (pages like `light`) — sun / sky visibility + light direction. */
164
+ aux: string | string[];
165
+ /** DEBUG: the page sets a `lecodes lightmap bake --split` wrote (`<stem>-direct[_n].ktx2`, `<stem>-indirect[_n].ktx2`),
166
+ * pages like `light` — the `Lightmap.debug("direct" | "indirect")` views bind them in place of `light`. */
167
+ direct?: string | string[];
168
+ indirect?: string | string[];
169
+ /** `asset('./assets/lightmap/level-probes.ktx2')` — the bake's reflection probes (one prefiltered cubemap of the level's
170
+ * radiance per probe; every static reflects the nearest). Absent = statics reflect the sky's SH alone. */
171
+ probes?: string;
172
+ /** FOR THE BAKE: the radiance in cd / m² of emission 1.0 on this level (emissive factor x strength x map) - every lit
173
+ * region of an emissive surface bakes as a rectangle lamp. An imported pack's emission is a LOOK (the school's panels
174
+ * say 10); this is where the level says what they give: a 1.3 m ceiling panel of ~8000 lm is ~1600 nits. Default: what
175
+ * the camera makes of 1.0 (`1.2 x 2^EV100`). 0 = emissive surfaces light nothing. `--emissive-nits` overrides. */
176
+ emissiveNits?: number;
177
+ /** `false` = the emissive surfaces light nothing in the bake - the level is lit by its lamps (`Light.point` with a
178
+ * `bakeArea` next to every panel, the Unity packs' way) and the panels are decoration; `emissiveNits` then only says how
179
+ * bright their glow is DRAWN (scene-referred: nits through the camera's exposure). Default true. */
180
+ emissiveBake?: boolean;
181
+ /** FOR THE BAKE: materials that let light THROUGH them, by glTF material name (`"name*"` = every name with that start) →
182
+ * the share of a shadow ray that passes, tinted by the material's base colour x map: `{ mat_awning: 0.3 }` puts the
183
+ * awning's warm, patterned light on the sand. The surface stays opaque - baked, drawn as before, a shadow caster.
184
+ * `{ through, diffuse }` splits it: `through` goes straight (the picture of the map), `diffuse` is scattered by the
185
+ * fibres (the underside glows, a soft fill with no picture) - dense canvas: `{ through: 0.08, diffuse: 0.25 }`. */
186
+ transmit?: Record<string, LightmapTransmit>;
187
+ /** `asset('./assets/lightmap/level.lgrid')` — THE LIGHT GRID for movers: every node a Physics aspect or a
188
+ * CharacterController moves (and what code marks with `Lightmap.track`) takes its ambient light from the bake at the
189
+ * place it is at, whatever its shader. Absent = movers keep the scene's IBL. */
160
190
  volume?: string;
161
- /** 1 = baked sun shadows at full strength, 0 = ambient occlusion only. Default 1. */
162
- sunStrength?: number;
163
- /** Multiplier on the ambient share in the shadow math (1 = filament's own darkness). Default 1. */
164
- ambientScale?: number;
165
- /** How much of the baked ambient occlusion applies: 1 = all of it, 0 = none. An interior lit by its
166
- * ambient probe alone can want less than the geometric truth. Default 1. */
167
- aoStrength?: number;
168
- /** Multiplier on the baked point lights' irradiance (default 1) - dial the lamps up or down
169
- * without a re-bake, since their irradiance lives in the atlas. Scales the light volume with the
170
- * atlas, so movers match the statics. See `LightmapLoadOptions.lightBoost`. */
171
- lightBoost?: number;
172
- /** Multiplier on the light VOLUME's irradiance alone (default = `lightBoost`). Since the volume is an
173
- * ambient cube (2026-08-31) movers shade like the statics and the default is right; it stays as a
174
- * trim. See `LightmapLoadOptions.volumeBoost`. */
175
- volumeBoost?: number;
191
+ /** A DEBUG multiplier on the atlas' lux (default 1). */
192
+ lightScale?: number;
176
193
  };
177
194
  /** `env.navmesh` — the level's navigation mesh (see navmesh.md): the file `lecodes navmesh bake`
178
195
  * writes, the agent size it is built for, and the named areas. Absent = no navigation. */
@@ -193,6 +210,7 @@ export type SceneDef = {
193
210
  env?: SceneOptions & {
194
211
  lightmap?: SceneLightmapDef;
195
212
  navmesh?: SceneNavmeshDef;
213
+ foliage?: FoliageOptions;
196
214
  };
197
215
  camera?: SceneCameraDef;
198
216
  nodes?: Record<string, SceneNodeDef>;
@@ -256,7 +274,8 @@ export type ModelPartRow = {
256
274
  * not parts, skipped. Filtering BEFORE segment math keeps `name[i]` indices stable no matter
257
275
  * what defs are parented in. */
258
276
  export declare const modelPartRows: (root: Node) => ModelPartRow[];
259
- /** The static verdict for one node def (the editor's "Baked lighting" switch shows the same rule). */
277
+ /** The static verdict for one node def (the editor's "Baked lighting" switch shows the same rule).
278
+ * Children of a mover are movers too — buildNodes carries that down, this is the node's own verdict. */
260
279
  export declare const isLightmapStatic: (def: SceneNodeDef) => boolean;
261
280
  /** Edit mode: play the preview pose on a model node (`editor: { clip, time }`) — idempotent, the
262
281
  * harness re-applies it on inspector edits. No clip = back to the rest pose. */
@@ -1,5 +1,5 @@
1
1
  import type { ClickEvent as ClickEventType, TouchStartEvent as TouchStartEventType, LongPressEvent as LongPressEventType } from "../runtime/touch";
2
- import { type ElementStyle, type ContainerStyle, type DrawableStyle, type UIChildArg, type UIContainerBase, type UINodeChild, type ChildrenFn, type BaseStyle, type Color } from "./UINode";
2
+ import { type ElementStyle, type ContainerStyle, type DrawableStyle, type UIChildArg, type UIContainerBase, type UINodeChild, type ChildrenFn, type BaseStyle, type Color, type LayerEasing } from "./UINode";
3
3
  export type UIButtonStyle = ElementStyle & DrawableStyle & ContainerStyle & {
4
4
  rippleColor?: Color | "default";
5
5
  };
@@ -8,6 +8,8 @@ export type UIButtonStyle = ElementStyle & DrawableStyle & ContainerStyle & {
8
8
  export interface UIButton extends UIContainerBase<UIButtonStyle & {
9
9
  onPressed?: UIButtonStyle & {
10
10
  duration?: number;
11
+ delay?: number;
12
+ easing?: LayerEasing;
11
13
  };
12
14
  }, DrawableStyle & BaseStyle> {
13
15
  readonly type: "button";
@@ -1,4 +1,4 @@
1
- import { type ElementStyle, type DrawableStyle, type TextStyle, type Color, type BaseStyle, type UIElementBase } from "./UINode";
1
+ import { type ElementStyle, type DrawableStyle, type TextStyle, type Color, type BaseStyle, type UIElementBase, type LayerEasing } from "./UINode";
2
2
  type InputStyle = {
3
3
  placeholder?: string;
4
4
  placeholderColor?: Color;
@@ -50,6 +50,8 @@ type SubmitStyle = {
50
50
  export interface UIInput extends UIElementBase<UIInputStyle & SubmitStyle & {
51
51
  onFocused?: UIInputStyle & {
52
52
  duration?: number;
53
+ delay?: number;
54
+ easing?: LayerEasing;
53
55
  };
54
56
  }, DrawableStyle & BaseStyle> {
55
57
  readonly type: "input";
@@ -77,6 +79,8 @@ export interface UIInput extends UIElementBase<UIInputStyle & SubmitStyle & {
77
79
  export interface UITextArea extends UIElementBase<UIInputStyle & {
78
80
  onFocused?: UIInputStyle & {
79
81
  duration?: number;
82
+ delay?: number;
83
+ easing?: LayerEasing;
80
84
  };
81
85
  }, DrawableStyle & BaseStyle> {
82
86
  readonly type: "textarea";
@@ -1,4 +1,6 @@
1
1
  import type { FetchResponse } from "../runtime/fetch";
2
+ import type { Animation } from "../animate/tween/Animation";
3
+ import { type TweenMeta } from "../animate/tween/spec";
2
4
  export interface UINode {
3
5
  readonly type: string;
4
6
  /** Author-given semantic name (a stable selector for tests + AI review feedback). */
@@ -158,8 +160,12 @@ export type TextStyle = {
158
160
  export type Reactive<T> = {
159
161
  [K in keyof T]: NonNullable<T[K]> extends object ? T[K] : T[K] | (() => T[K]);
160
162
  };
163
+ /** The curve of a state block's swap (`$class` / `onPressed` / `onFocused` with a `duration`): a
164
+ * curve name (`outCubic`, `inOutSine`, `smoothstep` — the default — …), `cubic-bezier(x1,y1,x2,y2)`
165
+ * or `steps(n)`. A function cannot ride a style block; use `animateTo` for those. */
166
+ export type LayerEasing = string;
161
167
  /** User-defined style classes: any `$`-prefixed key in `.style()` declares a state block toggled
162
- * from code via the `el.class` proxy; `duration`/`delay` animate the swap. Class state INHERITS
168
+ * from code via the `el.class` proxy; `duration`/`delay`/`easing` animate the swap. Class state INHERITS
163
169
  * down the tree (CSS-`.dark`-on-body style): a class set on a node also activates same-name `$`
164
170
  * blocks on all descendants within the same root (docs/style-class-cascade-plan.md).
165
171
  * `$pressed`/`$focused` are reserved — hosts toggle them on press/focus (they beat other
@@ -172,6 +178,7 @@ export type ClassStyles<T> = {
172
178
  [key: `$${string}`]: T & {
173
179
  duration?: number;
174
180
  delay?: number;
181
+ easing?: LayerEasing;
175
182
  };
176
183
  };
177
184
  /** Value accepted by a `el.class` write: a boolean sets the class, a `() => boolean` binds it —
@@ -245,25 +252,15 @@ export type StyleFn<R, T extends object> = ((style: Reactive<T> & OrientationSty
245
252
  * chaining — and per-key readable/writable (`el.style.opacity = 0.5`; a `() => value` write
246
253
  * installs a reactive binding). */
247
254
  export type Style<R, T extends object> = StyleFn<R, T> & T & OrientationStyles<T> & ClassStyles<T>;
248
- /** The `animateTo`/`animateFrom` surface: target style props plus the flat meta keys below
249
- * (stripped before the commit into the element's style). */
250
- export type AnimateStyle<R, T extends object> = ((style: T & {
251
- /** Tween length in **milliseconds** (default 225). */
252
- duration?: number;
253
- /** Wait before starting, in **milliseconds**. A looping animation delays once, not per cycle. */
254
- delay?: number;
255
- /** Escape hatch: target an internal style layer (the onPressed/orientation mechanism). Not part of the supported surface. */
256
- layer?: number;
257
- /** `false` = animate without writing the targets into the element's style (e.g. a fade-out right before `.hide()`). */
258
- commit?: boolean;
259
- /** Repeat the animation: `true` = forever, a number = that many cycles. A looping animation is an
260
- * effect, not a state change — it never commits, and the element ends back at its base style.
261
- * The loop stops when a later `animateTo`/`animateFrom` runs on the element or it leaves the screen. */
262
- loop?: boolean | number;
263
- /** `"ping-pong"` (default) animates there and back each cycle — no jump. `"restart"` snaps back and
264
- * replays forward — for full-turn spinners (`transform: "rotate(360deg)"`) and shimmers. Only meaningful with `loop`. */
265
- loopMode?: "restart" | "ping-pong";
266
- }) => R);
255
+ /** Style props of an animate bag: a single value (tween from the current value) or an array of
256
+ * KEYFRAMES (`opacity: [1, 0.3, 1]`, offsets via `times`). */
257
+ export type AnimateProps<T extends object> = {
258
+ [K in keyof T]?: T[K] | T[K][];
259
+ };
260
+ /** The `animateTo`/`animateFrom` surface: target style props (single values or keyframe arrays) plus
261
+ * the flat meta keys — `duration` / `delay` / `easing` / `times` / `loop` / `loopMode` / `commit` /
262
+ * `clock`, see {@link TweenMeta}. Returns the {@link Animation} handle (seek, rate, `finished`). */
263
+ export type AnimateStyle<R, T extends object> = ((style: AnimateProps<T> & TweenMeta) => Animation);
267
264
  export type AppearStyle<R, T> = (arg: {
268
265
  from: T;
269
266
  duration?: number;
@@ -306,10 +303,13 @@ export declare class Element<T extends string> {
306
303
  constructor(type: T, style: any);
307
304
  get style(): any;
308
305
  /** Tween to the target style and commit it (unless `commit: false` or looping — a loop is an
309
- * effect, not a state change). Meta keys: `duration`/`delay`/`loop`/… — see {@link AnimateStyle}. */
310
- animateTo(style: any): this;
311
- /** Tween FROM the given style to the element's current one (entrance animations). */
312
- animateFrom(style: any): this;
306
+ * effect, not a state change). A prop value may be an array of keyframes; meta keys
307
+ * `duration`/`delay`/`easing`/`times`/`loop`/… — see {@link AnimateStyle}. Returns the
308
+ * {@link Animation} handle. */
309
+ animateTo(style: any): Animation;
310
+ /** Tween FROM the given style to the element's current one (entrance animations): the element's
311
+ * own style is the implicit last keyframe. Never modifies the stored style. */
312
+ animateFrom(style: any): Animation;
313
313
  protected _classProxy?: any;
314
314
  /** Style-class control — flips the `$`-class blocks declared in `.style()`. One proxy, same
315
315
  * contract as `el.style`: