blecsd 0.3.0 → 0.6.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 (147) hide show
  1. package/README.md +146 -147
  2. package/dist/{border-Jb7TrMob.d.ts → border-Br-Jc027.d.ts} +2 -2
  3. package/dist/{cell-DwIu2ryP.d.ts → cell-5Ty_3yMs.d.ts} +1 -1
  4. package/dist/cellRenderer-D0-DJXWl.d.ts +374 -0
  5. package/dist/chunk-4N7IFBRQ.js +4 -0
  6. package/dist/{chunk-DNRXW56C.js → chunk-4XCFTNGN.js} +1 -1
  7. package/dist/chunk-5YWRP2KG.js +3 -0
  8. package/dist/chunk-6PX5R326.js +1 -0
  9. package/dist/chunk-73Y45MLV.js +12 -0
  10. package/dist/chunk-7ZFQO3OQ.js +1 -0
  11. package/dist/chunk-A3GSH6MV.js +1 -0
  12. package/dist/chunk-A5B2BGUM.js +1 -0
  13. package/dist/chunk-AM6IDSXI.js +1 -0
  14. package/dist/chunk-EHYOVHRL.js +2 -0
  15. package/dist/chunk-EMZA6G2M.js +4 -0
  16. package/dist/chunk-EOFT3PNU.js +1 -0
  17. package/dist/chunk-ETFDYZVJ.js +1 -0
  18. package/dist/chunk-FUW7OD3H.js +1 -0
  19. package/dist/chunk-GRMSEMU7.js +1 -0
  20. package/dist/chunk-I7AUKTXE.js +1 -0
  21. package/dist/chunk-IANAVH2A.js +1 -0
  22. package/dist/chunk-JN2OGNK3.js +1 -0
  23. package/dist/chunk-JVMNMAHX.js +1 -0
  24. package/dist/chunk-K2QWNDXV.js +1 -0
  25. package/dist/chunk-KYNS3GBJ.js +2 -0
  26. package/dist/chunk-LI3ZYXUT.js +1 -0
  27. package/dist/chunk-LNEISTXM.js +1 -0
  28. package/dist/chunk-QABNK7IA.js +1 -0
  29. package/dist/chunk-QS5QXZNJ.js +1 -0
  30. package/dist/chunk-QTDRFJG2.js +1 -0
  31. package/dist/chunk-RJULLVTH.js +1 -0
  32. package/dist/chunk-SVHITP3F.js +2 -0
  33. package/dist/chunk-UKVY43V3.js +1 -0
  34. package/dist/chunk-VIT4KE6Q.js +1 -0
  35. package/dist/chunk-XG5PVDOP.js +1 -0
  36. package/dist/chunk-XH5GTWCV.js +1 -0
  37. package/dist/chunk-XYMPBCYW.js +1 -0
  38. package/dist/chunk-YRSSCEAS.js +1 -0
  39. package/dist/chunk-ZL46COQF.js +1 -0
  40. package/dist/cli/init.js +1 -1
  41. package/dist/{componentStorage-CJTh-TPO.d.ts → componentStorage-CXJvx4Lt.d.ts} +2 -2
  42. package/dist/components/index.d.ts +7209 -6691
  43. package/dist/components/index.js +5 -1
  44. package/dist/core/index.d.ts +2501 -1262
  45. package/dist/core/index.js +1 -1
  46. package/dist/debug/index.d.ts +310 -84
  47. package/dist/debug/index.js +8 -1
  48. package/dist/{dirtyTracking-C4v8MmM9.d.ts → dirtyTracking-kCS9-NVF.d.ts} +2 -2
  49. package/dist/{doubleBuffer-CKQFmlPN.d.ts → doubleBuffer-CWASihKh.d.ts} +1 -1
  50. package/dist/errors/index.js +1 -1
  51. package/dist/{inputActions-CRsUtTHM.d.ts → factories-vW7bn_He.d.ts} +21 -786
  52. package/dist/{gameLoop-C-Ez_i54.d.ts → gameLoop-C1AyRWyP.d.ts} +3 -3
  53. package/dist/index.d.ts +25 -500
  54. package/dist/index.js +1 -3
  55. package/dist/input/index.d.ts +1 -1
  56. package/dist/input/index.js +1 -1
  57. package/dist/inputStream-COARA4CP.d.ts +1182 -0
  58. package/dist/interactiveSystem-h92W9W4n.d.ts +1977 -0
  59. package/dist/{keyParser-BnHbg2iD.d.ts → keyParser-DReXe2j-.d.ts} +41 -41
  60. package/dist/{events-9ForpTfM.d.ts → mouseParser-CCqSEUVN.d.ts} +177 -2
  61. package/dist/{packedStore-BgvnEdE7.d.ts → packedStore-480t2X74.d.ts} +1 -1
  62. package/dist/panelMovement-DGzIQ8Ll.d.ts +1908 -0
  63. package/dist/{parser-iMHmQuUh.d.ts → parser-Q1YLXYpF.d.ts} +1 -1
  64. package/dist/positioning-DiUivJXa.d.ts +917 -0
  65. package/dist/{renderable-CwqGwrEV.d.ts → renderable-IbSJao5y.d.ts} +2 -2
  66. package/dist/{scheduler-CMcYew9Z.d.ts → scheduler-NbHT3-D2.d.ts} +3 -1
  67. package/dist/schemas/index.d.ts +6 -6
  68. package/dist/schemas/index.js +1 -1
  69. package/dist/systems/index.d.ts +1057 -1807
  70. package/dist/systems/index.js +1 -1
  71. package/dist/terminal/index.d.ts +7207 -2709
  72. package/dist/terminal/index.js +1 -1
  73. package/dist/terminalBuffer-BbUz27qM.d.ts +691 -0
  74. package/dist/{terminus-14-bold-HWSPRLJD.js → terminus-14-bold-ZS4IH465.js} +1 -1
  75. package/dist/{terminus-14-normal-T3SWMH4D.js → terminus-14-normal-HD5N7F5W.js} +1 -1
  76. package/dist/text/index.d.ts +263 -0
  77. package/dist/text/index.js +3 -0
  78. package/dist/textWrap-Ct2J8gO6.d.ts +761 -0
  79. package/dist/{tilemap-BirMJdbu.d.ts → tilemap-ByvTsepD.d.ts} +5 -5
  80. package/dist/{types-CPB4CpbH.d.ts → types-B8LmNkzG.d.ts} +1 -1
  81. package/dist/utils/index.d.ts +827 -780
  82. package/dist/utils/index.js +32 -1
  83. package/dist/{virtualScrollback-D9uLFe8l.d.ts → virtualScrollback-CiooIebp.d.ts} +4 -4
  84. package/dist/virtualViewport-fIlbIGPt.d.ts +657 -0
  85. package/dist/{virtualizedLineStore-DwPEvPkk.d.ts → virtualizedLineStore-DfyhojPZ.d.ts} +1 -1
  86. package/dist/widgets/bigText.d.ts +13 -13
  87. package/dist/widgets/bigText.js +1 -1
  88. package/dist/widgets/fonts/index.d.ts +1 -1
  89. package/dist/widgets/fonts/index.js +1 -1
  90. package/dist/widgets/index.d.ts +2933 -1102
  91. package/dist/widgets/index.js +24 -1
  92. package/package.json +9 -22
  93. package/dist/3d/index.d.ts +0 -5
  94. package/dist/3d/index.js +0 -1
  95. package/dist/audio/index.d.ts +0 -177
  96. package/dist/audio/index.js +0 -1
  97. package/dist/chunk-2IEMMRUO.js +0 -1
  98. package/dist/chunk-35LCBY6P.js +0 -1
  99. package/dist/chunk-3AV52GY5.js +0 -1
  100. package/dist/chunk-3LHLSY3Y.js +0 -1
  101. package/dist/chunk-3O4TQHGK.js +0 -4
  102. package/dist/chunk-3UJWZ5ZN.js +0 -1
  103. package/dist/chunk-5PELJRUQ.js +0 -1
  104. package/dist/chunk-6M2J5QUA.js +0 -1
  105. package/dist/chunk-7IQEUVGF.js +0 -1
  106. package/dist/chunk-A6M6TFBL.js +0 -1
  107. package/dist/chunk-CIK4AMUA.js +0 -1
  108. package/dist/chunk-CUEUJAHK.js +0 -3
  109. package/dist/chunk-D42Q2KKR.js +0 -1
  110. package/dist/chunk-DYEXOFUU.js +0 -2
  111. package/dist/chunk-DYU72XLL.js +0 -1
  112. package/dist/chunk-E4CJRSND.js +0 -1
  113. package/dist/chunk-EAY7B5GL.js +0 -1
  114. package/dist/chunk-FCMTWFSE.js +0 -1
  115. package/dist/chunk-FGHEFXLK.js +0 -1
  116. package/dist/chunk-FL56THSI.js +0 -25
  117. package/dist/chunk-G437VE43.js +0 -1
  118. package/dist/chunk-G7GIWWLE.js +0 -1
  119. package/dist/chunk-GGXNWT36.js +0 -8
  120. package/dist/chunk-HLFORKXS.js +0 -1
  121. package/dist/chunk-J7MBKEBY.js +0 -1
  122. package/dist/chunk-K3SX2LY5.js +0 -1
  123. package/dist/chunk-LDAFEXN5.js +0 -1
  124. package/dist/chunk-LYSK5S63.js +0 -1
  125. package/dist/chunk-MKMFUXLB.js +0 -33
  126. package/dist/chunk-MQWPHPUM.js +0 -1
  127. package/dist/chunk-MTI376CU.js +0 -5
  128. package/dist/chunk-MTV2RJZD.js +0 -1
  129. package/dist/chunk-NZ55KBM6.js +0 -1
  130. package/dist/chunk-OB66FB4F.js +0 -1
  131. package/dist/chunk-OMMJ7B5P.js +0 -1
  132. package/dist/chunk-OR3BZY7C.js +0 -1
  133. package/dist/chunk-PXXGH3BV.js +0 -1
  134. package/dist/chunk-R7AICVRN.js +0 -2
  135. package/dist/chunk-RZ7FGVI6.js +0 -1
  136. package/dist/chunk-SHUC6JWA.js +0 -1
  137. package/dist/chunk-TWSWTBYL.js +0 -1
  138. package/dist/chunk-UMGTXSQB.js +0 -11
  139. package/dist/chunk-X3Q3T2SS.js +0 -4
  140. package/dist/chunk-XZJRWFOS.js +0 -1
  141. package/dist/chunk-ZAHG7Y3X.js +0 -1
  142. package/dist/game/index.d.ts +0 -486
  143. package/dist/game/index.js +0 -1
  144. package/dist/index-DBS5Uefn.d.ts +0 -3156
  145. package/dist/mouseParser-Cfrbn3AX.d.ts +0 -177
  146. package/dist/viewport3d-xI33-_wq.d.ts +0 -182
  147. package/dist/virtualViewport-Bpv6jlKt.d.ts +0 -1856
@@ -1,23 +1,22 @@
1
- import { W as World, E as Entity, L as LoopPhase, S as System } from '../types-CPB4CpbH.js';
2
- export { U as Unsubscribe } from '../types-CPB4CpbH.js';
3
- import { a as ComponentStore } from '../componentStorage-CJTh-TPO.js';
4
- export { j as DirtyRect, D as DirtyTracker, c as clearDirtyTracking, a as createDirtyTracker, f as forceFullRedraw, g as getDirtyRegions, b as getDirtyTrackingStats, h as hasDirtyRegions, i as isCellDirty, m as markCellDirty, d as markEntityDirty, e as markRegionDirty, r as removeEntityFromTracking } from '../dirtyTracking-C4v8MmM9.js';
5
- import * as bitecs from 'bitecs';
6
- import { ComponentRef, QueryTerm, QueryResult } from 'bitecs';
7
- export { ComponentRef, QueryResult, QueryTerm } from 'bitecs';
8
- import { S as StyleData } from '../renderable-CwqGwrEV.js';
9
- import { B as BoxConfig, b as ButtonConfig, C as CheckboxConfig, F as FormConfig, c as InputConfig, L as ListConfig, P as ProgressBarConfig, R as RadioButtonConfig, f as RadioSetConfig, E as ScreenConfig, S as SelectConfig, g as SliderConfig, T as TextConfig, d as TextareaConfig, e as TextboxConfig } from '../inputActions-CRsUtTHM.js';
10
- export { A as ActionBinding, h as ActionBindingSchema, i as ActionCallback, j as ActionPresets, k as ActionState, l as BoxConfigSchema, m as ButtonConfigSchema, n as CheckboxConfigSchema, o as FormConfigSchema, p as InputActionManager, q as InputBufferStats, r as InputConfigSchema, I as InputEventBufferData, s as InputEventBufferOptions, t as InputLatencyStats, a as InputState, u as InputStateConfig, v as InputStateStats, K as KeyState, w as ListConfigSchema, M as MouseButtonState, x as MouseState, y as ProgressBarConfigSchema, z as RadioButtonConfigSchema, D as RadioSetConfigSchema, G as ScreenConfigSchema, H as SelectConfigSchema, J as SerializedBindings, N as SerializedBindingsSchema, O as SliderConfigSchema, Q as TextConfigSchema, U as TextareaConfigSchema, V as TextboxConfigSchema, W as TimestampedInputEvent, X as TimestampedKeyEvent, Y as TimestampedMouseEvent, Z as beginFrame, aq as clearBuffer, _ as createInputActionManager, $ as createInputEventBuffer, a0 as createInputState, a1 as drainAllEvents, a2 as drainKeys, a3 as drainMouse, a4 as endFrame, a5 as getLatencyStats, a6 as getMovementDirection, a7 as getPendingCount, a8 as getPendingKeyCount, a9 as getPendingMouseCount, ar as getStats, aa as globalInputBuffer, ab as hasPendingEvents, ac as isAllKeysDown, ad as isAnyKeyDown, ae as isAnyKeyPressed, af as isLatencyAcceptable, ag as isProcessingTimeAcceptable, ah as peekEvents, ai as peekKeys, aj as peekMouse, ak as pushKeyEvent, al as pushMouseEvent, am as recordLatency, an as recordLatencyBatch, ao as resetLatencyStats, ap as resetStats } from '../inputActions-CRsUtTHM.js';
11
- import { c as EventMap, G as GetEntityEventBus, a as EventBus } from '../events-9ForpTfM.js';
12
- export { E as EntityEventBusStore, b as EventHandler, S as ScreenEventMap, U as UIEventMap, d as createEntityEventBusStore, e as createEventBus } from '../events-9ForpTfM.js';
1
+ import { W as World, E as Entity, L as LoopPhase, S as System } from '../types-B8LmNkzG.js';
2
+ export { U as Unsubscribe } from '../types-B8LmNkzG.js';
3
+ import { C as ComponentStore } from '../componentStorage-CXJvx4Lt.js';
4
+ export { D as DirtyRect, a as DirtyTracker, c as clearDirtyTracking, b as createDirtyTracker, f as forceFullRedraw, g as getDirtyRegions, d as getDirtyTrackingStats, h as hasDirtyRegions, i as isCellDirty, m as markCellDirty, e as markEntityDirty, j as markRegionDirty, r as removeEntityFromTracking } from '../dirtyTracking-kCS9-NVF.js';
5
+ export { C as CleanupCallback, g as ClickableCache, D as DestroyOptions, i as HitTestOptions, H as HitTestResult, P as PositionValue, a as PositionValueSchema, b as addComponent, c as addEntity, j as centerPosition, k as clampPosition, l as clearCleanupCallbacks, m as clearDestroyQueue, n as createClickableCache, d as createWorld, o as destroyAllChildren, p as destroyEntity, e as destroyWorld, q as entityExists, s as flushDestroyQueue, t as getAllClickablesAt, u as getAllEntities, v as getAllHoverablesAt, w as getClickableAt, x as getClickableCount, y as getClickableEntities, z as getDestroyQueueSize, A as getHoverableAt, B as hasClickableAt, h as hasComponent, E as hasHoverableAt, f as hitTest, F as hitTestAll, G as hitTestDetailed, I as invalidateClickableCache, J as isCacheDirty, K as isKeywordPosition, L as isMarkedForDestruction, M as isPercentagePosition, N as parsePosition, O as parsePositionWithNegative, Q as percentOffsetPosition, R as percentPosition, S as query, T as registerCleanupCallback, U as registerComponent, V as removeComponent, r as removeEntity, W as resetDisposalState, X as resetWorld, Y as resolvePosition, Z as resolvePositionClamped, _ as updateClickableCache, $ as withStore } from '../positioning-DiUivJXa.js';
6
+ import { S as StyleData } from '../renderable-IbSJao5y.js';
7
+ export { B as BoxConfig, a as BoxConfigSchema, k as ButtonConfig, l as ButtonConfigSchema, C as CheckboxConfig, m as CheckboxConfigSchema, F as FormConfig, n as FormConfigSchema, I as InputConfig, o as InputConfigSchema, L as ListConfig, p as ListConfigSchema, P as ProgressBarConfig, q as ProgressBarConfigSchema, R as RadioButtonConfig, r as RadioButtonConfigSchema, s as RadioSetConfig, t as RadioSetConfigSchema, S as ScreenConfig, u as ScreenConfigSchema, v as SelectConfig, w as SelectConfigSchema, x as SliderConfig, y as SliderConfigSchema, T as TextConfig, b as TextConfigSchema, z as TextareaConfig, A as TextareaConfigSchema, D as TextboxConfig, E as TextboxConfigSchema, c as createBoxEntity, d as createButtonEntity, e as createCheckboxEntity, G as createFormEntity, f as createInputEntity, g as createListEntity, H as createProgressBarEntity, J as createRadioButtonEntity, K as createRadioSetEntity, h as createScreenEntity, i as createSelectEntity, M as createSliderEntity, j as createTextEntity, N as createTextareaEntity, O as createTextboxEntity } from '../factories-vW7bn_He.js';
8
+ import { E as EventMap, G as GetEntityEventBus, M as MouseEvent, a as MouseButton, b as EventBus } from '../mouseParser-CCqSEUVN.js';
9
+ export { c as EntityEventBusStore, d as EventHandler, S as ScreenEventMap, U as UIEventMap, e as createEntityEventBusStore, f as createEventBus } from '../mouseParser-CCqSEUVN.js';
13
10
  import { z } from 'zod';
14
- export { F as FixedTimestepConfig, a as FixedUpdateHook, G as GameLoop, b as GameLoopHooks, c as GameLoopOptions, I as InterpolateHook, d as LoopHook, e as LoopState, L as LoopStats, f as createGameLoop, i as isLoopPaused, g as isLoopRunning } from '../gameLoop-C-Ez_i54.js';
15
- import { K as KeyName, b as KeyEvent } from '../keyParser-BnHbg2iD.js';
11
+ export { F as FixedTimestepConfig, a as FixedUpdateHook, G as GameLoop, b as GameLoopHooks, c as GameLoopOptions, I as InterpolateHook, L as LoopHook, d as LoopState, e as LoopStats, f as createGameLoop, i as isLoopPaused, g as isLoopRunning } from '../gameLoop-C1AyRWyP.js';
12
+ import { K as KeyEvent, a as KeyName } from '../keyParser-DReXe2j-.js';
16
13
  import { K as KeyEvent$1 } from '../program-BZaKqDKH.js';
17
- export { A as AdaptiveFrameBudgetConfig, a as AdaptiveFrameBudgetStatus, F as FrameTelemetry, P as PhaseTimingData, S as Scheduler, T as TelemetryConfig, c as createScheduler, g as getDeltaTime } from '../scheduler-CMcYew9Z.js';
18
- export { a as PackedHandle, P as PackedStore, b as addToStore, c as clearStore, d as createPackedStore, f as forEachInStore, g as getFromStore, e as getStoreCapacity, h as getStoreData, i as getStoreSize, j as isValidHandle, m as mapStore, r as removeFromStore, s as setInStore } from '../packedStore-BgvnEdE7.js';
19
- import '../border-Jb7TrMob.js';
20
- import '../mouseParser-Cfrbn3AX.js';
14
+ import { S as Scheduler } from '../scheduler-NbHT3-D2.js';
15
+ export { A as AdaptiveFrameBudgetConfig, a as AdaptiveFrameBudgetStatus, F as FrameTelemetry, P as PhaseTimingData, T as TelemetryConfig, c as createScheduler, g as getDeltaTime } from '../scheduler-NbHT3-D2.js';
16
+ export { P as PackedHandle, a as PackedStore, b as addToStore, c as clearStore, d as createPackedStore, f as forEachInStore, g as getFromStore, e as getStoreCapacity, h as getStoreData, i as getStoreSize, j as isValidHandle, m as mapStore, r as removeFromStore, s as setInStore } from '../packedStore-480t2X74.js';
17
+ import { ComponentRef, QueryTerm } from 'bitecs';
18
+ export { ComponentRef, QueryResult, QueryTerm } from 'bitecs';
19
+ import '../border-Br-Jc027.js';
21
20
  import 'node:stream';
22
21
 
23
22
  /**
@@ -1242,6 +1241,445 @@ declare function isPointInEntity(world: World, eid: Entity, x: number, y: number
1242
1241
  */
1243
1242
  declare function isPointInInnerBounds(world: World, eid: Entity, x: number, y: number): boolean;
1244
1243
 
1244
+ /**
1245
+ * Signal primitives for reactive state management.
1246
+ * Provides automatic dependency tracking and efficient recomputation.
1247
+ * @module core/signals
1248
+ */
1249
+
1250
+ /**
1251
+ * Signal getter function.
1252
+ * Returns the current value of the signal.
1253
+ */
1254
+ type SignalGetter<T> = () => T;
1255
+ /**
1256
+ * Signal setter function.
1257
+ * Updates the signal value and notifies dependents.
1258
+ */
1259
+ type SignalSetter<T> = (value: T | ((prev: T) => T)) => void;
1260
+ /**
1261
+ * A signal is a tuple of [getter, setter].
1262
+ */
1263
+ type Signal<T> = readonly [SignalGetter<T>, SignalSetter<T>];
1264
+ /**
1265
+ * Computed signal getter.
1266
+ * Returns the current computed value.
1267
+ */
1268
+ type ComputedGetter<T> = SignalGetter<T>;
1269
+ /**
1270
+ * Alias for computed signal getter used by reactive sources.
1271
+ */
1272
+ type ComputedSignal<T> = SignalGetter<T>;
1273
+ /**
1274
+ * Creates a reactive signal with automatic dependency tracking.
1275
+ *
1276
+ * Returns a tuple of [getter, setter]. The getter returns the current value
1277
+ * and tracks dependencies. The setter updates the value and notifies dependents.
1278
+ *
1279
+ * @param initialValue - Initial value of the signal
1280
+ * @returns Tuple of [getter, setter]
1281
+ *
1282
+ * @example
1283
+ * ```typescript
1284
+ * import { createSignal } from 'blecsd';
1285
+ *
1286
+ * const [count, setCount] = createSignal(0);
1287
+ * console.log(count()); // 0
1288
+ * setCount(1);
1289
+ * console.log(count()); // 1
1290
+ *
1291
+ * // Functional update
1292
+ * setCount(prev => prev + 1);
1293
+ * console.log(count()); // 2
1294
+ * ```
1295
+ */
1296
+ declare function createSignal<T>(initialValue: T): Signal<T>;
1297
+ /**
1298
+ * Creates a computed signal that automatically tracks dependencies.
1299
+ *
1300
+ * The function is executed once immediately, and any signals read during
1301
+ * execution become dependencies. The computed value is cached and only
1302
+ * recomputed when dependencies change.
1303
+ *
1304
+ * Handles diamond dependencies correctly (A depends on B and C, both depend on D -
1305
+ * when D changes, A recomputes only once).
1306
+ *
1307
+ * @param fn - Function that computes the value
1308
+ * @returns Getter function for the computed value
1309
+ *
1310
+ * @example
1311
+ * ```typescript
1312
+ * import { createSignal, createComputed } from 'blecsd';
1313
+ *
1314
+ * const [count, setCount] = createSignal(0);
1315
+ * const doubled = createComputed(() => count() * 2);
1316
+ *
1317
+ * console.log(doubled()); // 0
1318
+ * setCount(5);
1319
+ * console.log(doubled()); // 10
1320
+ * ```
1321
+ */
1322
+ declare function createComputed<T>(fn: () => T): ComputedGetter<T>;
1323
+ /**
1324
+ * Batches multiple signal updates together.
1325
+ *
1326
+ * All signal updates within the batch function are deferred, and
1327
+ * computed signals only recompute once after all updates complete.
1328
+ *
1329
+ * @param fn - Function containing batched updates
1330
+ *
1331
+ * @example
1332
+ * ```typescript
1333
+ * import { createSignal, createComputed, createBatch } from 'blecsd';
1334
+ *
1335
+ * const [a, setA] = createSignal(0);
1336
+ * const [b, setB] = createSignal(0);
1337
+ * const sum = createComputed(() => a() + b());
1338
+ *
1339
+ * createBatch(() => {
1340
+ * setA(1);
1341
+ * setB(2);
1342
+ * });
1343
+ * // sum recomputes only once, not twice
1344
+ * console.log(sum()); // 3
1345
+ * ```
1346
+ */
1347
+ declare function createBatch(fn: () => void): void;
1348
+ /**
1349
+ * Creates an entity-scoped signal that automatically marks the entity dirty.
1350
+ *
1351
+ * When the signal changes, `markDirty(world, eid)` is called automatically,
1352
+ * integrating with the rendering system's dirty tracking.
1353
+ *
1354
+ * @param world - The ECS world
1355
+ * @param eid - The entity ID
1356
+ * @param initialValue - Initial value of the signal
1357
+ * @returns Tuple of [getter, setter]
1358
+ *
1359
+ * @example
1360
+ * ```typescript
1361
+ * import { createEntitySignal } from 'blecsd';
1362
+ *
1363
+ * const [health, setHealth] = createEntitySignal(world, playerEntity, 100);
1364
+ * setHealth(80); // Automatically marks playerEntity as dirty
1365
+ * ```
1366
+ */
1367
+ declare function createEntitySignal<T>(world: World, eid: Entity, initialValue: T): Signal<T>;
1368
+ /**
1369
+ * Disposes a signal or computed, removing all subscriptions.
1370
+ *
1371
+ * For computed signals, this unsubscribes from all dependencies.
1372
+ * Call this when a signal is no longer needed to prevent memory leaks.
1373
+ *
1374
+ * Note: This takes a getter function, not the full signal tuple.
1375
+ *
1376
+ * @param getter - Signal or computed getter function
1377
+ *
1378
+ * @example
1379
+ * ```typescript
1380
+ * import { createSignal, createComputed, disposeSignal } from 'blecsd';
1381
+ *
1382
+ * const [count] = createSignal(0);
1383
+ * const doubled = createComputed(() => count() * 2);
1384
+ *
1385
+ * // When done with the computed
1386
+ * disposeSignal(doubled);
1387
+ * ```
1388
+ */
1389
+ declare function disposeSignal<T>(getter: SignalGetter<T>): void;
1390
+ /**
1391
+ * Runs a function while tracking signal dependencies.
1392
+ * When any tracked signal changes, the callback is invoked.
1393
+ *
1394
+ * Used internally by the effect system.
1395
+ *
1396
+ * @param fn - Function to run while tracking reads
1397
+ * @param onDependencyChange - Called when any tracked signal changes
1398
+ * @returns Cleanup function that stops tracking
1399
+ * @internal
1400
+ */
1401
+ declare function trackDependencies(fn: () => void, onDependencyChange: () => void): () => void;
1402
+
1403
+ /**
1404
+ * Declarative widget composition API.
1405
+ *
1406
+ * Instead of imperative entity creation and component attachment,
1407
+ * this module lets users describe UI structure as functional composition.
1408
+ * Reactive signal bindings are set up automatically.
1409
+ *
1410
+ * @module core/declarative
1411
+ *
1412
+ * @example
1413
+ * ```typescript
1414
+ * import { createSignal, el, mount, vbox } from 'blecsd';
1415
+ *
1416
+ * const [title, setTitle] = createSignal('Dashboard');
1417
+ * const [cpu, setCpu] = createSignal(42);
1418
+ *
1419
+ * const tree = mount(world, vbox({}, [
1420
+ * el('text', { content: title }),
1421
+ * el('gauge', { value: cpu, label: 'CPU' }),
1422
+ * ]));
1423
+ *
1424
+ * setTitle('Updated!'); // text widget re-renders automatically
1425
+ * setCpu(87); // gauge widget re-renders automatically
1426
+ *
1427
+ * tree.dispose(); // cleanup all entities and reactive bindings
1428
+ * ```
1429
+ */
1430
+
1431
+ /**
1432
+ * Supported widget types for declarative composition.
1433
+ */
1434
+ type WidgetType = 'box' | 'text' | 'gauge' | 'list' | 'progressbar';
1435
+ /**
1436
+ * A value that may be reactive (a signal getter) or static.
1437
+ */
1438
+ type ReactiveProp<T> = T | SignalGetter<T>;
1439
+ /**
1440
+ * Configuration for a declarative widget.
1441
+ * Any property can be a static value or a reactive signal getter.
1442
+ */
1443
+ interface ReactiveConfig {
1444
+ readonly [key: string]: ReactiveProp<unknown>;
1445
+ }
1446
+ /**
1447
+ * Describes a widget to be created in the UI tree.
1448
+ */
1449
+ interface WidgetDescriptor {
1450
+ readonly type: WidgetType;
1451
+ readonly config: ReactiveConfig;
1452
+ readonly children: readonly WidgetDescriptor[];
1453
+ readonly ref?: (eid: Entity) => void;
1454
+ }
1455
+ /**
1456
+ * Result of mounting a descriptor tree into actual entities.
1457
+ */
1458
+ interface MountedTree {
1459
+ readonly root: Entity;
1460
+ readonly entities: readonly Entity[];
1461
+ readonly dispose: () => void;
1462
+ }
1463
+ /**
1464
+ * Cleanup handle from a reactive binding.
1465
+ */
1466
+ interface BindingCleanup {
1467
+ readonly dispose: () => void;
1468
+ }
1469
+ /**
1470
+ * Factory function type for creating entities from config.
1471
+ */
1472
+ type WidgetFactory = (world: World, config: Record<string, unknown>) => Entity;
1473
+ /**
1474
+ * Registry of property setters for widget types.
1475
+ * Maps (widgetType, propName) to a setter function.
1476
+ */
1477
+ type PropSetter = (world: World, eid: Entity, value: unknown) => void;
1478
+ /**
1479
+ * Registers a widget factory for declarative composition.
1480
+ *
1481
+ * @param type - Widget type name
1482
+ * @param factory - Factory function that creates an entity from config
1483
+ *
1484
+ * @example
1485
+ * ```typescript
1486
+ * import { registerWidgetFactory, createBoxEntity } from 'blecsd';
1487
+ *
1488
+ * registerWidgetFactory('box', (world, config) => createBoxEntity(world, config));
1489
+ * ```
1490
+ */
1491
+ declare function registerWidgetFactory(type: WidgetType, factory: WidgetFactory): void;
1492
+ /**
1493
+ * Registers a property setter for a widget type.
1494
+ * Used to update a specific property on an entity when a reactive value changes.
1495
+ *
1496
+ * @param type - Widget type name
1497
+ * @param prop - Property name
1498
+ * @param setter - Function that applies the property value to an entity
1499
+ *
1500
+ * @example
1501
+ * ```typescript
1502
+ * import { registerPropSetter, setContent } from 'blecsd';
1503
+ *
1504
+ * registerPropSetter('text', 'content', (world, eid, value) => {
1505
+ * setContent(world, eid, value as string);
1506
+ * });
1507
+ * ```
1508
+ */
1509
+ declare function registerPropSetter(type: WidgetType, prop: string, setter: PropSetter): void;
1510
+ /**
1511
+ * Creates a widget descriptor for declarative composition.
1512
+ *
1513
+ * @param type - Widget type ('box', 'text', 'gauge', etc.)
1514
+ * @param config - Configuration object; values can be static or signal getters
1515
+ * @param children - Optional child descriptors
1516
+ * @returns Widget descriptor
1517
+ *
1518
+ * @example
1519
+ * ```typescript
1520
+ * import { el, createSignal } from 'blecsd';
1521
+ *
1522
+ * const [count, setCount] = createSignal(0);
1523
+ *
1524
+ * const desc = el('text', { content: count });
1525
+ * ```
1526
+ */
1527
+ declare function el(type: WidgetType, config?: ReactiveConfig, children?: WidgetDescriptor[]): WidgetDescriptor;
1528
+ /**
1529
+ * Creates a widget descriptor with a ref callback.
1530
+ *
1531
+ * @param type - Widget type
1532
+ * @param config - Configuration object
1533
+ * @param ref - Callback receiving the created entity ID
1534
+ * @param children - Optional child descriptors
1535
+ * @returns Widget descriptor
1536
+ *
1537
+ * @example
1538
+ * ```typescript
1539
+ * import { elRef } from 'blecsd';
1540
+ *
1541
+ * let myEntity: Entity;
1542
+ * const desc = elRef('box', { width: 20 }, (eid) => { myEntity = eid; });
1543
+ * ```
1544
+ */
1545
+ declare function elRef(type: WidgetType, config: ReactiveConfig, ref: (eid: Entity) => void, children?: WidgetDescriptor[]): WidgetDescriptor;
1546
+ /**
1547
+ * Creates a vertical box layout descriptor.
1548
+ *
1549
+ * @param config - Box configuration
1550
+ * @param children - Child widget descriptors
1551
+ * @returns Box descriptor with vertical layout
1552
+ *
1553
+ * @example
1554
+ * ```typescript
1555
+ * import { vbox, el } from 'blecsd';
1556
+ *
1557
+ * const layout = vbox({}, [
1558
+ * el('text', { content: 'Header' }),
1559
+ * el('text', { content: 'Body' }),
1560
+ * ]);
1561
+ * ```
1562
+ */
1563
+ declare function vbox(config?: ReactiveConfig, children?: WidgetDescriptor[]): WidgetDescriptor;
1564
+ /**
1565
+ * Creates a horizontal box layout descriptor.
1566
+ *
1567
+ * @param config - Box configuration
1568
+ * @param children - Child widget descriptors
1569
+ * @returns Box descriptor with horizontal layout
1570
+ *
1571
+ * @example
1572
+ * ```typescript
1573
+ * import { hbox, el } from 'blecsd';
1574
+ *
1575
+ * const layout = hbox({}, [
1576
+ * el('text', { content: 'Left' }),
1577
+ * el('text', { content: 'Right' }),
1578
+ * ]);
1579
+ * ```
1580
+ */
1581
+ declare function hbox(config?: ReactiveConfig, children?: WidgetDescriptor[]): WidgetDescriptor;
1582
+ /**
1583
+ * Mounts a widget descriptor tree, creating entities and setting up reactive bindings.
1584
+ *
1585
+ * For each descriptor node:
1586
+ * 1. Creates the entity using the registered factory
1587
+ * 2. Sets static properties immediately
1588
+ * 3. Creates reactive effects for signal-backed properties
1589
+ * 4. Recursively mounts children and establishes parent-child relationships
1590
+ *
1591
+ * @param world - The ECS world
1592
+ * @param descriptor - Root widget descriptor
1593
+ * @param parent - Optional parent entity
1594
+ * @returns Mounted tree with root entity, all entities, and dispose function
1595
+ *
1596
+ * @example
1597
+ * ```typescript
1598
+ * import { mount, el, createSignal } from 'blecsd';
1599
+ *
1600
+ * const [status, setStatus] = createSignal('Ready');
1601
+ *
1602
+ * const tree = mount(world, el('text', { content: status }));
1603
+ * setStatus('Running'); // widget updates automatically
1604
+ *
1605
+ * tree.dispose(); // cleanup
1606
+ * ```
1607
+ */
1608
+ declare function mount(world: World, descriptor: WidgetDescriptor, parent?: Entity): MountedTree;
1609
+ /**
1610
+ * Convenience function to unmount a tree.
1611
+ * Disposes all reactive bindings. Entity cleanup should be done
1612
+ * separately using the entity disposal system.
1613
+ *
1614
+ * @param tree - The mounted tree to unmount
1615
+ *
1616
+ * @example
1617
+ * ```typescript
1618
+ * import { mount, unmount, el } from 'blecsd';
1619
+ *
1620
+ * const tree = mount(world, el('box', {}));
1621
+ * // ... later ...
1622
+ * unmount(tree); // clean up reactive bindings
1623
+ * ```
1624
+ */
1625
+ declare function unmount(tree: MountedTree): void;
1626
+ /**
1627
+ * Creates a reactive binding between a signal and an entity property.
1628
+ * The setter is called immediately with the current value, and again
1629
+ * whenever the signal changes.
1630
+ *
1631
+ * @param world - The ECS world
1632
+ * @param eid - The entity to bind to
1633
+ * @param signal - Signal getter providing the value
1634
+ * @param setter - Function that applies the value to the entity
1635
+ * @returns Cleanup handle
1636
+ *
1637
+ * @example
1638
+ * ```typescript
1639
+ * import { bind, createSignal, setContent } from 'blecsd';
1640
+ *
1641
+ * const [text, setText] = createSignal('Hello');
1642
+ *
1643
+ * const cleanup = bind(world, myEntity, text, (world, eid, value) => {
1644
+ * setContent(world, eid, value);
1645
+ * });
1646
+ *
1647
+ * setText('World'); // entity content updates automatically
1648
+ * cleanup.dispose(); // stop watching
1649
+ * ```
1650
+ */
1651
+ declare function bind<T>(world: World, eid: Entity, signal: SignalGetter<T>, setter: (world: World, eid: Entity, value: T) => void): BindingCleanup;
1652
+ /**
1653
+ * Registers the default property setters for common widget properties.
1654
+ * Call this once during application setup to enable reactive bindings
1655
+ * for standard properties like content, width, height.
1656
+ *
1657
+ * @param deps - Dependencies injected to avoid circular imports
1658
+ *
1659
+ * @example
1660
+ * ```typescript
1661
+ * import { registerDefaultPropSetters, setContent } from 'blecsd';
1662
+ *
1663
+ * registerDefaultPropSetters({
1664
+ * setContent: (world, eid, value) => setContent(world, eid, value as string),
1665
+ * setWidth: (world, eid, value) => setDimensions(world, eid, { width: value as number }),
1666
+ * setHeight: (world, eid, value) => setDimensions(world, eid, { height: value as number }),
1667
+ * });
1668
+ * ```
1669
+ */
1670
+ declare function registerDefaultPropSetters(deps: {
1671
+ readonly setContent: PropSetter;
1672
+ readonly setWidth: PropSetter;
1673
+ readonly setHeight: PropSetter;
1674
+ }): void;
1675
+ /**
1676
+ * Clears all widget factory and prop setter registrations.
1677
+ * Used for testing.
1678
+ *
1679
+ * @internal
1680
+ */
1681
+ declare function resetDeclarativeRegistrations(): void;
1682
+
1245
1683
  /**
1246
1684
  * Dirty Rectangle Tracking System
1247
1685
  *
@@ -1548,447 +1986,34 @@ declare function regionIntersectsDirty(tracker: DirtyTrackerData, x: number, y:
1548
1986
  declare function getDirtyRegionsInViewport(tracker: DirtyTrackerData, viewX: number, viewY: number, viewWidth: number, viewHeight: number): DirtyRect[];
1549
1987
 
1550
1988
  /**
1551
- * Entity disposal and cleanup system.
1552
- *
1553
- * Handles proper cleanup of entities including:
1554
- * - Deferred destruction (mark for destruction, process at frame end)
1555
- * - Hierarchy cleanup (remove from parent, destroy children)
1556
- * - Lifecycle event emission
1557
- * - Store cleanup
1558
- * - Entity recycling
1559
- *
1560
- * @module core/disposal
1561
- *
1562
- * @example
1563
- * ```typescript
1564
- * import { destroyEntity, destroyAllChildren, flushDestroyQueue } from 'blecsd';
1565
- *
1566
- * // Mark entity for destruction (deferred)
1567
- * destroyEntity(world, entity);
1568
- *
1569
- * // At end of frame, process all pending destructions
1570
- * flushDestroyQueue(world);
1571
- *
1572
- * // Or destroy immediately
1573
- * destroyEntity(world, entity, { immediate: true });
1574
- * ```
1989
+ * Focus and hover effects system for dynamic styling.
1990
+ * Manages effect application, removal, and style restoration.
1991
+ * @module core/effects
1575
1992
  */
1576
1993
 
1577
1994
  /**
1578
- * Options for entity destruction.
1579
- */
1580
- interface DestroyOptions {
1581
- /**
1582
- * If true, destroy immediately instead of deferring to end of frame.
1583
- * Use with caution as this may cause issues during iteration.
1584
- */
1585
- immediate?: boolean;
1586
- /**
1587
- * If true, also destroy all children recursively.
1588
- * Defaults to true.
1589
- */
1590
- destroyChildren?: boolean;
1591
- /**
1592
- * If true, emit destroy event before cleanup.
1593
- * Defaults to true.
1594
- */
1595
- emitEvent?: boolean;
1596
- }
1597
- /**
1598
- * Callback for custom cleanup when an entity is destroyed.
1995
+ * A style value that can be static or computed dynamically.
1599
1996
  */
1600
- type CleanupCallback = (world: World, entity: Entity) => void;
1997
+ type DynamicValue<T> = T | ((world: World, entity: Entity) => T);
1601
1998
  /**
1602
- * Registers a cleanup callback that runs when any entity is destroyed.
1603
- *
1604
- * Use this to register store cleanup functions.
1605
- *
1606
- * @param callback - Function to call during entity cleanup
1607
- * @returns Function to unregister the callback
1999
+ * Effect configuration with support for dynamic values.
2000
+ * Values can be static or functions that compute the value at render time.
1608
2001
  *
1609
2002
  * @example
1610
2003
  * ```typescript
1611
- * import { registerCleanupCallback } from 'blecsd';
2004
+ * import { EffectConfig } from 'blecsd';
1612
2005
  *
1613
- * // Register cleanup for a custom store
1614
- * const unregister = registerCleanupCallback((world, entity) => {
1615
- * myCustomStore.delete(entity);
1616
- * });
2006
+ * // Static effect
2007
+ * const staticEffect: EffectConfig = {
2008
+ * fg: 0xff0000ff,
2009
+ * bold: true,
2010
+ * };
1617
2011
  *
1618
- * // Later, unregister if needed
1619
- * unregister();
1620
- * ```
1621
- */
1622
- declare function registerCleanupCallback(callback: CleanupCallback): () => void;
1623
- /**
1624
- * Clears all registered cleanup callbacks.
1625
- * Primarily for testing.
1626
- */
1627
- declare function clearCleanupCallbacks(): void;
1628
- /**
1629
- * Marks an entity for destruction.
1630
- *
1631
- * By default, destruction is deferred to the end of the frame via
1632
- * `flushDestroyQueue()`. This prevents issues when destroying entities
1633
- * during iteration.
1634
- *
1635
- * @param world - The ECS world
1636
- * @param entity - The entity to destroy
1637
- * @param options - Destruction options
1638
- *
1639
- * @example
1640
- * ```typescript
1641
- * import { destroyEntity } from 'blecsd';
1642
- *
1643
- * // Deferred destruction (recommended)
1644
- * destroyEntity(world, entity);
1645
- *
1646
- * // Immediate destruction
1647
- * destroyEntity(world, entity, { immediate: true });
1648
- *
1649
- * // Don't destroy children
1650
- * destroyEntity(world, entity, { destroyChildren: false });
1651
- * ```
1652
- */
1653
- declare function destroyEntity(world: World, entity: Entity, options?: DestroyOptions): void;
1654
- /**
1655
- * Destroys all children of an entity without destroying the parent.
1656
- *
1657
- * @param world - The ECS world
1658
- * @param parent - The parent entity
1659
- * @param options - Destruction options (immediate applies to all children)
1660
- *
1661
- * @example
1662
- * ```typescript
1663
- * import { destroyAllChildren } from 'blecsd';
1664
- *
1665
- * // Clear all children from a container
1666
- * destroyAllChildren(world, container);
1667
- * ```
1668
- */
1669
- declare function destroyAllChildren(world: World, parent: Entity, options?: DestroyOptions): void;
1670
- /**
1671
- * Checks if an entity is marked for destruction.
1672
- *
1673
- * @param entity - The entity to check
1674
- * @returns true if entity is queued for destruction
1675
- */
1676
- declare function isMarkedForDestruction(entity: Entity): boolean;
1677
- /**
1678
- * Processes all entities marked for destruction.
1679
- *
1680
- * Should be called at the end of each frame, typically in POST_RENDER phase.
1681
- *
1682
- * @param world - The ECS world
1683
- * @returns Number of entities destroyed
1684
- *
1685
- * @example
1686
- * ```typescript
1687
- * import { flushDestroyQueue } from 'blecsd';
1688
- *
1689
- * // In your game loop's post-render phase:
1690
- * const destroyed = flushDestroyQueue(world);
1691
- * ```
1692
- */
1693
- declare function flushDestroyQueue(world: World): number;
1694
- /**
1695
- * Destroys all entities in a world.
1696
- *
1697
- * This performs immediate destruction of all entities.
1698
- *
1699
- * @param world - The ECS world to clear
1700
- *
1701
- * @example
1702
- * ```typescript
1703
- * import { destroyWorld } from 'blecsd';
1704
- *
1705
- * // Clean up everything before resetting
1706
- * destroyWorld(world);
1707
- * ```
1708
- */
1709
- declare function destroyWorld(world: World): void;
1710
- /**
1711
- * Gets the number of entities currently queued for destruction.
1712
- *
1713
- * @param world - Optional world to check (if not provided, returns global count)
1714
- * @returns Number of entities in destroy queue
1715
- */
1716
- declare function getDestroyQueueSize(world?: World): number;
1717
- /**
1718
- * Clears the destruction queue without destroying entities.
1719
- *
1720
- * Use with caution - entities will remain but won't be destroyed.
1721
- *
1722
- * @param world - The ECS world
1723
- */
1724
- declare function clearDestroyQueue(world: World): void;
1725
- /**
1726
- * Resets all disposal state.
1727
- * Primarily for testing.
1728
- */
1729
- declare function resetDisposalState(): void;
1730
-
1731
- /**
1732
- * ECS World creation and management
1733
- * @module core/world
1734
- */
1735
-
1736
- /**
1737
- * Creates a new ECS world for the game.
1738
- *
1739
- * @returns A new World instance
1740
- *
1741
- * @example
1742
- * ```typescript
1743
- * import { createWorld } from 'blecsd';
1744
- *
1745
- * const world = createWorld();
1746
- * ```
1747
- */
1748
- declare function createWorld(): World;
1749
- /**
1750
- * Resets an existing world, removing all entities and resetting component data.
1751
- * Useful for level reloading or game restart.
1752
- *
1753
- * @param world - The world to reset
1754
- *
1755
- * @example
1756
- * ```typescript
1757
- * import { createWorld, resetWorld } from 'blecsd';
1758
- *
1759
- * const world = createWorld();
1760
- * // ... game runs ...
1761
- * resetWorld(world); // Clear everything for new game
1762
- * ```
1763
- */
1764
- declare function resetWorld(world: World): void;
1765
-
1766
- /**
1767
- * Creates a new entity in the world.
1768
- *
1769
- * @param world - The ECS world to add the entity to
1770
- * @returns The newly created entity ID
1771
- *
1772
- * @example
1773
- * ```typescript
1774
- * import { createWorld, addEntity } from 'blecsd';
1775
- *
1776
- * const world = createWorld();
1777
- * const player = addEntity(world);
1778
- * const enemy = addEntity(world);
1779
- * ```
1780
- */
1781
- declare function addEntity(world: World): Entity;
1782
- /**
1783
- * Removes an entity and all its components from the world.
1784
- *
1785
- * @param world - The ECS world containing the entity
1786
- * @param eid - The entity ID to remove
1787
- *
1788
- * @example
1789
- * ```typescript
1790
- * import { createWorld, addEntity, removeEntity } from 'blecsd';
1791
- *
1792
- * const world = createWorld();
1793
- * const entity = addEntity(world);
1794
- * // ... use entity ...
1795
- * removeEntity(world, entity); // Clean up when done
1796
- * ```
1797
- */
1798
- declare function removeEntity(world: World, eid: Entity): void;
1799
- /**
1800
- * Checks if an entity exists in the world.
1801
- *
1802
- * @param world - The ECS world to check
1803
- * @param eid - The entity ID to check
1804
- * @returns True if the entity exists, false otherwise
1805
- *
1806
- * @example
1807
- * ```typescript
1808
- * import { createWorld, addEntity, removeEntity, entityExists } from 'blecsd';
1809
- *
1810
- * const world = createWorld();
1811
- * const entity = addEntity(world);
1812
- *
1813
- * console.log(entityExists(world, entity)); // true
1814
- * removeEntity(world, entity);
1815
- * console.log(entityExists(world, entity)); // false
1816
- * ```
1817
- */
1818
- declare function entityExists(world: World, eid: Entity): boolean;
1819
- /**
1820
- * Gets all entity IDs currently in the world.
1821
- *
1822
- * @param world - The ECS world to query
1823
- * @returns Array of all entity IDs in the world
1824
- *
1825
- * @example
1826
- * ```typescript
1827
- * import { createWorld, addEntity, getAllEntities } from 'blecsd';
1828
- *
1829
- * const world = createWorld();
1830
- * addEntity(world);
1831
- * addEntity(world);
1832
- * addEntity(world);
1833
- *
1834
- * const entities = getAllEntities(world);
1835
- * console.log(entities.length); // 3
1836
- * ```
1837
- */
1838
- declare function getAllEntities(world: World): readonly Entity[];
1839
- /**
1840
- * Adds a component to an entity.
1841
- *
1842
- * @param world - The ECS world
1843
- * @param eid - The entity to add the component to
1844
- * @param component - The component to add
1845
- *
1846
- * @example
1847
- * ```typescript
1848
- * import { createWorld, addEntity, addComponent, Position } from 'blecsd';
1849
- *
1850
- * const world = createWorld();
1851
- * const entity = addEntity(world);
1852
- * addComponent(world, entity, Position);
1853
- * Position.x[entity] = 100;
1854
- * Position.y[entity] = 50;
1855
- * ```
1856
- */
1857
- declare function addComponent(world: World, eid: Entity, component: ComponentRef): void;
1858
- /**
1859
- * Checks if an entity has a specific component.
1860
- *
1861
- * @param world - The ECS world
1862
- * @param eid - The entity to check
1863
- * @param component - The component to check for
1864
- * @returns True if the entity has the component, false otherwise
1865
- *
1866
- * @example
1867
- * ```typescript
1868
- * import { createWorld, addEntity, addComponent, hasComponent, Position } from 'blecsd';
1869
- *
1870
- * const world = createWorld();
1871
- * const entity = addEntity(world);
1872
- *
1873
- * console.log(hasComponent(world, entity, Position)); // false
1874
- * addComponent(world, entity, Position);
1875
- * console.log(hasComponent(world, entity, Position)); // true
1876
- * ```
1877
- */
1878
- declare function hasComponent(world: World, eid: Entity, component: ComponentRef): boolean;
1879
- /**
1880
- * Removes a component from an entity.
1881
- *
1882
- * @param world - The ECS world
1883
- * @param eid - The entity to remove the component from
1884
- * @param component - The component to remove
1885
- *
1886
- * @example
1887
- * ```typescript
1888
- * import { createWorld, addEntity, addComponent, removeComponent, hasComponent, Position } from 'blecsd';
1889
- *
1890
- * const world = createWorld();
1891
- * const entity = addEntity(world);
1892
- * addComponent(world, entity, Position);
1893
- *
1894
- * console.log(hasComponent(world, entity, Position)); // true
1895
- * removeComponent(world, entity, Position);
1896
- * console.log(hasComponent(world, entity, Position)); // false
1897
- * ```
1898
- */
1899
- declare function removeComponent(world: World, eid: Entity, component: ComponentRef): void;
1900
- /**
1901
- * Queries the world for entities that have all specified components.
1902
- *
1903
- * @param world - The ECS world to query
1904
- * @param components - Array of components that entities must have
1905
- * @returns Array of entity IDs that match the query
1906
- *
1907
- * @example
1908
- * ```typescript
1909
- * import { createWorld, addEntity, addComponent, query, Position, Velocity } from 'blecsd';
1910
- *
1911
- * const world = createWorld();
1912
- *
1913
- * // Create entities with different component combinations
1914
- * const staticEntity = addEntity(world);
1915
- * addComponent(world, staticEntity, Position);
1916
- *
1917
- * const movingEntity = addEntity(world);
1918
- * addComponent(world, movingEntity, Position);
1919
- * addComponent(world, movingEntity, Velocity);
1920
- *
1921
- * // Query for entities with both Position and Velocity
1922
- * const movingEntities = query(world, [Position, Velocity]);
1923
- * console.log(movingEntities.length); // 1 (only movingEntity)
1924
- * ```
1925
- */
1926
- declare function query(world: World, components: QueryTerm[]): QueryResult;
1927
- /**
1928
- * Registers a component with the world. This is typically called automatically
1929
- * when components are first used, but can be useful for advanced scenarios.
1930
- *
1931
- * @param world - The ECS world
1932
- * @param component - The component to register
1933
- *
1934
- * @example
1935
- * ```typescript
1936
- * import { createWorld, registerComponent, Position } from 'blecsd';
1937
- *
1938
- * const world = createWorld();
1939
- * registerComponent(world, Position);
1940
- * ```
1941
- */
1942
- declare const registerComponent: (world: bitecs.World, component: ComponentRef) => bitecs.ComponentData;
1943
- /**
1944
- * Creates a component with a custom backing store. Useful for components that
1945
- * need special memory layouts or interop with external systems.
1946
- *
1947
- * @param store - Custom store object with typed arrays
1948
- * @returns A component that uses the provided store
1949
- *
1950
- * @example
1951
- * ```typescript
1952
- * import { withStore } from 'blecsd';
1953
- *
1954
- * // Create a component backed by custom Float32Arrays
1955
- * const CustomPosition = withStore({
1956
- * x: new Float32Array(10000),
1957
- * y: new Float32Array(10000),
1958
- * });
1959
- * ```
1960
- */
1961
- declare const withStore: <T>(createStore: (eid: bitecs.EntityId) => T) => (relation: bitecs.Relation<T>) => bitecs.Relation<T>;
1962
-
1963
- /**
1964
- * Focus and hover effects system for dynamic styling.
1965
- * Manages effect application, removal, and style restoration.
1966
- * @module core/effects
1967
- */
1968
-
1969
- /**
1970
- * A style value that can be static or computed dynamically.
1971
- */
1972
- type DynamicValue<T> = T | ((world: World, entity: Entity) => T);
1973
- /**
1974
- * Effect configuration with support for dynamic values.
1975
- * Values can be static or functions that compute the value at render time.
1976
- *
1977
- * @example
1978
- * ```typescript
1979
- * import { EffectConfig } from 'blecsd';
1980
- *
1981
- * // Static effect
1982
- * const staticEffect: EffectConfig = {
1983
- * fg: 0xff0000ff,
1984
- * bold: true,
1985
- * };
1986
- *
1987
- * // Dynamic effect based on entity state
1988
- * const dynamicEffect: EffectConfig = {
1989
- * fg: (world, eid) => isActive(world, eid) ? 0x00ff00ff : 0xff0000ff,
1990
- * bold: (world, eid) => hasHighPriority(world, eid),
1991
- * };
2012
+ * // Dynamic effect based on entity state
2013
+ * const dynamicEffect: EffectConfig = {
2014
+ * fg: (world, eid) => isActive(world, eid) ? 0x00ff00ff : 0xff0000ff,
2015
+ * bold: (world, eid) => hasHighPriority(world, eid),
2016
+ * };
1992
2017
  * ```
1993
2018
  */
1994
2019
  interface EffectConfig {
@@ -2053,21 +2078,21 @@ declare function resolveEffectConfig(world: World, eid: Entity, config: EffectCo
2053
2078
  * @param eid - The entity ID
2054
2079
  * @returns Stored style data or undefined
2055
2080
  */
2056
- declare function getStoredStyle(eid: Entity): StoredStyle | undefined;
2081
+ declare function getStoredStyle(_world: World, eid: Entity): StoredStyle | undefined;
2057
2082
  /**
2058
2083
  * Checks if an entity has stored style data.
2059
2084
  *
2060
2085
  * @param eid - The entity ID
2061
2086
  * @returns true if entity has stored style
2062
2087
  */
2063
- declare function hasStoredStyle(eid: Entity): boolean;
2088
+ declare function hasStoredStyle(_world: World, eid: Entity): boolean;
2064
2089
  /**
2065
2090
  * Clears stored style for an entity.
2066
2091
  * Call this when an entity is destroyed.
2067
2092
  *
2068
2093
  * @param eid - The entity ID
2069
2094
  */
2070
- declare function clearStoredStyle(eid: Entity): void;
2095
+ declare function clearStoredStyle(_world: World, eid: Entity): void;
2071
2096
  /**
2072
2097
  * Clears all stored styles.
2073
2098
  * Primarily for testing.
@@ -2121,7 +2146,7 @@ declare function removeFocusEffect(world: World, eid: Entity): void;
2121
2146
  * @param eid - The entity ID
2122
2147
  * @returns true if focus effect is applied
2123
2148
  */
2124
- declare function hasFocusEffectApplied(eid: Entity): boolean;
2149
+ declare function hasFocusEffectApplied(_world: World, eid: Entity): boolean;
2125
2150
  /**
2126
2151
  * Applies hover effect to an entity.
2127
2152
  * Uses the entity's hoverEffectFg and hoverEffectBg from the Interactive component.
@@ -2170,7 +2195,7 @@ declare function removeHoverEffect(world: World, eid: Entity): void;
2170
2195
  * @param eid - The entity ID
2171
2196
  * @returns true if hover effect is applied
2172
2197
  */
2173
- declare function hasHoverEffectApplied(eid: Entity): boolean;
2198
+ declare function hasHoverEffectApplied(_world: World, eid: Entity): boolean;
2174
2199
  /**
2175
2200
  * Applies a custom effect configuration to an entity.
2176
2201
  * Stores the original style for later restoration.
@@ -2311,7 +2336,7 @@ declare function setEffects(world: World, eid: Entity, config: EffectsConfig): v
2311
2336
  * @param eid - The entity ID
2312
2337
  * @returns The effects configuration or undefined
2313
2338
  */
2314
- declare function getEffects(eid: Entity): EffectsConfig | undefined;
2339
+ declare function getEffects(_world: World, eid: Entity): EffectsConfig | undefined;
2315
2340
  /**
2316
2341
  * Clears the effects configuration for an entity.
2317
2342
  *
@@ -2379,28 +2404,28 @@ declare function removeDisabledEffect(world: World, eid: Entity): void;
2379
2404
  * @param eid - The entity ID
2380
2405
  * @returns true if press effect is applied
2381
2406
  */
2382
- declare function hasPressEffectApplied(eid: Entity): boolean;
2407
+ declare function hasPressEffectApplied(_world: World, eid: Entity): boolean;
2383
2408
  /**
2384
2409
  * Checks if disabled effect is currently applied.
2385
2410
  *
2386
2411
  * @param eid - The entity ID
2387
2412
  * @returns true if disabled effect is applied
2388
2413
  */
2389
- declare function hasDisabledEffectApplied(eid: Entity): boolean;
2414
+ declare function hasDisabledEffectApplied(_world: World, eid: Entity): boolean;
2390
2415
  /**
2391
2416
  * Checks if any effect is currently applied to an entity.
2392
2417
  *
2393
2418
  * @param eid - The entity ID
2394
2419
  * @returns true if any effect is active
2395
2420
  */
2396
- declare function hasAnyEffectApplied(eid: Entity): boolean;
2421
+ declare function hasAnyEffectApplied(_world: World, eid: Entity): boolean;
2397
2422
  /**
2398
2423
  * Gets the current effect state for an entity.
2399
2424
  *
2400
2425
  * @param eid - The entity ID
2401
2426
  * @returns Object describing which effects are applied
2402
2427
  */
2403
- declare function getEffectState(eid: Entity): {
2428
+ declare function getEffectState(_world: World, eid: Entity): {
2404
2429
  focus: boolean;
2405
2430
  hover: boolean;
2406
2431
  press: boolean;
@@ -2412,35 +2437,13 @@ declare function getEffectState(eid: Entity): {
2412
2437
  *
2413
2438
  * @param eid - The entity ID
2414
2439
  */
2415
- declare function clearEffectState(eid: Entity): void;
2440
+ declare function clearEffectState(_world: World, eid: Entity): void;
2416
2441
  /**
2417
2442
  * Clears all effect configs.
2418
2443
  * Primarily for testing.
2419
2444
  */
2420
2445
  declare function clearAllEffectConfigs(): void;
2421
2446
 
2422
- /**
2423
- * Entity factory functions for creating common entity types.
2424
- * These factories combine components and helpers to create fully-configured entities.
2425
- * @module core/entities/factories
2426
- */
2427
-
2428
- declare function createBoxEntity(world: World, config?: BoxConfig): Entity;
2429
- declare function createTextEntity(world: World, config?: TextConfig): Entity;
2430
- declare function createButtonEntity(world: World, config?: ButtonConfig): Entity;
2431
- declare function createScreenEntity(world: World, config: ScreenConfig): Entity;
2432
- declare function createInputEntity(world: World, config?: InputConfig): Entity;
2433
- declare function createListEntity(world: World, config?: ListConfig): Entity;
2434
- declare function createCheckboxEntity(world: World, config?: CheckboxConfig): Entity;
2435
- declare function createTextboxEntity(world: World, config?: TextboxConfig): Entity;
2436
- declare function createTextareaEntity(world: World, config?: TextareaConfig): Entity;
2437
- declare function createSelectEntity(world: World, config?: SelectConfig): Entity;
2438
- declare function createSliderEntity(world: World, config?: SliderConfig): Entity;
2439
- declare function createFormEntity(world: World, config?: FormConfig): Entity;
2440
- declare function createProgressBarEntity(world: World, config?: ProgressBarConfig): Entity;
2441
- declare function createRadioSetEntity(world: World, config?: RadioSetConfig): Entity;
2442
- declare function createRadioButtonEntity(world: World, config?: RadioButtonConfig): Entity;
2443
-
2444
2447
  /**
2445
2448
  * Entity data storage for arbitrary key-value pairs.
2446
2449
  *
@@ -2482,7 +2485,7 @@ type EntityDataMap = Map<string, DataValue>;
2482
2485
  * console.log(health); // 100 (default, since not set)
2483
2486
  * ```
2484
2487
  */
2485
- declare function getEntityData<T = DataValue>(eid: Entity, key: string, defaultValue?: T): T;
2488
+ declare function getEntityData<T = DataValue>(_world: World, eid: Entity, key: string, defaultValue?: T): T;
2486
2489
  /**
2487
2490
  * Sets a value on an entity.
2488
2491
  *
@@ -2506,7 +2509,7 @@ declare function getEntityData<T = DataValue>(eid: Entity, key: string, defaultV
2506
2509
  * setEntityData(entity, 'onDeath', () => console.log('Game over'));
2507
2510
  * ```
2508
2511
  */
2509
- declare function setEntityData(eid: Entity, key: string, value: DataValue): void;
2512
+ declare function setEntityData(_world: World, eid: Entity, key: string, value: DataValue): void;
2510
2513
  /**
2511
2514
  * Checks if an entity has data stored for a specific key.
2512
2515
  *
@@ -2524,7 +2527,7 @@ declare function setEntityData(eid: Entity, key: string, value: DataValue): void
2524
2527
  * }
2525
2528
  * ```
2526
2529
  */
2527
- declare function hasEntityData(eid: Entity, key: string): boolean;
2530
+ declare function hasEntityData(_world: World, eid: Entity, key: string): boolean;
2528
2531
  /**
2529
2532
  * Deletes a specific key from an entity's data.
2530
2533
  *
@@ -2541,7 +2544,7 @@ declare function hasEntityData(eid: Entity, key: string): boolean;
2541
2544
  * deleteEntityData(entity, 'temporaryBuff');
2542
2545
  * ```
2543
2546
  */
2544
- declare function deleteEntityData(eid: Entity, key: string): boolean;
2547
+ declare function deleteEntityData(_world: World, eid: Entity, key: string): boolean;
2545
2548
  /**
2546
2549
  * Gets all keys stored on an entity.
2547
2550
  *
@@ -2559,7 +2562,7 @@ declare function deleteEntityData(eid: Entity, key: string): boolean;
2559
2562
  * console.log(keys); // ['name', 'score']
2560
2563
  * ```
2561
2564
  */
2562
- declare function getEntityDataKeys(eid: Entity): string[];
2565
+ declare function getEntityDataKeys(_world: World, eid: Entity): string[];
2563
2566
  /**
2564
2567
  * Gets all data stored on an entity as a plain object.
2565
2568
  *
@@ -2577,7 +2580,7 @@ declare function getEntityDataKeys(eid: Entity): string[];
2577
2580
  * console.log(allData); // { name: 'Player', score: 100 }
2578
2581
  * ```
2579
2582
  */
2580
- declare function getAllEntityData(eid: Entity): Record<string, DataValue>;
2583
+ declare function getAllEntityData(_world: World, eid: Entity): Record<string, DataValue>;
2581
2584
  /**
2582
2585
  * Sets multiple values on an entity at once.
2583
2586
  *
@@ -2596,7 +2599,7 @@ declare function getAllEntityData(eid: Entity): Record<string, DataValue>;
2596
2599
  * });
2597
2600
  * ```
2598
2601
  */
2599
- declare function setEntityDataBulk(eid: Entity, data: Record<string, DataValue>): void;
2602
+ declare function setEntityDataBulk(_world: World, eid: Entity, data: Record<string, DataValue>): void;
2600
2603
  /**
2601
2604
  * Clears all data stored on an entity.
2602
2605
  *
@@ -2610,7 +2613,7 @@ declare function setEntityDataBulk(eid: Entity, data: Record<string, DataValue>)
2610
2613
  * clearEntityData(entity);
2611
2614
  * ```
2612
2615
  */
2613
- declare function clearEntityData(eid: Entity): void;
2616
+ declare function clearEntityData(_world: World, eid: Entity): void;
2614
2617
  /**
2615
2618
  * Clears all entity data from the store.
2616
2619
  * Useful for testing or resetting game state.
@@ -2652,7 +2655,7 @@ declare function getEntityDataCount(): number;
2652
2655
  * }
2653
2656
  * ```
2654
2657
  */
2655
- declare function hasAnyEntityData(eid: Entity): boolean;
2658
+ declare function hasAnyEntityData(_world: World, eid: Entity): boolean;
2656
2659
  /**
2657
2660
  * Updates a value on an entity using a transform function.
2658
2661
  * If the key doesn't exist, the transform receives undefined.
@@ -2677,7 +2680,7 @@ declare function hasAnyEntityData(eid: Entity): boolean;
2677
2680
  * updateEntityData(entity, 'items', (current) => [...(current ?? []), newItem]);
2678
2681
  * ```
2679
2682
  */
2680
- declare function updateEntityData<T = DataValue>(eid: Entity, key: string, transform: (current: T | undefined) => T): void;
2683
+ declare function updateEntityData<T = DataValue>(_world: World, eid: Entity, key: string, transform: (current: T | undefined) => T): void;
2681
2684
 
2682
2685
  /**
2683
2686
  * Event bubbling system for hierarchical event propagation.
@@ -2916,263 +2919,790 @@ declare const EmitDescendantsOptionsSchema: z.ZodObject<{
2916
2919
  declare function emitDescendants<T extends EventMap, K extends keyof T>(world: World, eid: Entity, eventName: K, eventData: T[K], getEventBus: GetEntityEventBus<T>, options?: EmitDescendantsOptions): EmitDescendantsResult;
2917
2920
 
2918
2921
  /**
2919
- * Hit Test System with Z-Order Aware Clickable Sorting
2920
- *
2921
- * Provides efficient hit testing for mouse interactions, sorting clickable
2922
- * elements by z-index so the topmost element receives events first.
2923
- *
2924
- * @module core/hitTest
2925
- *
2926
- * @example
2927
- * ```typescript
2928
- * import {
2929
- * createClickableCache,
2930
- * hitTest,
2931
- * hitTestAll,
2932
- * invalidateClickableCache,
2933
- * } from 'blecsd';
2922
+ * Input event buffer for frame-independent input handling.
2934
2923
  *
2935
- * // Create cache for efficient hit testing
2936
- * const cache = createClickableCache();
2924
+ * Buffers keyboard and mouse events between frames so no input is lost.
2925
+ * Events are collected asynchronously from stdin and drained synchronously
2926
+ * each frame by the game loop.
2937
2927
  *
2938
- * // Hit test at mouse position - returns topmost entity
2939
- * const topEntity = hitTest(world, mouseX, mouseY, cache);
2928
+ * HARD REQUIREMENT: No input events should ever be lost or delayed.
2940
2929
  *
2941
- * // Get all entities under point, sorted by z-index (highest first)
2942
- * const allEntities = hitTestAll(world, mouseX, mouseY, cache);
2943
- *
2944
- * // Invalidate cache when hierarchy changes
2945
- * invalidateClickableCache(cache);
2946
- * ```
2930
+ * @module core/inputEventBuffer
2947
2931
  */
2948
2932
 
2949
2933
  /**
2950
- * Cache for clickable element sorting.
2951
- *
2952
- * Maintains a sorted list of clickable/hoverable entities for efficient
2953
- * hit testing. The cache is invalidated when the hierarchy changes.
2934
+ * A timestamped keyboard event.
2954
2935
  */
2955
- interface ClickableCache {
2956
- /** Sorted entities (highest z-index first) */
2957
- entities: Entity[];
2958
- /** Whether cache needs rebuilding */
2959
- dirty: boolean;
2960
- /** Last known count for quick dirty check */
2961
- lastCount: number;
2936
+ interface TimestampedKeyEvent {
2937
+ readonly type: 'key';
2938
+ readonly event: KeyEvent;
2939
+ readonly timestamp: number;
2962
2940
  }
2963
2941
  /**
2964
- * Result of a hit test operation.
2942
+ * A timestamped mouse event.
2965
2943
  */
2966
- interface HitTestResult {
2967
- /** The entity under the point (topmost if multiple) */
2968
- readonly entity: Entity;
2969
- /** Z-index of the entity */
2970
- readonly zIndex: number;
2944
+ interface TimestampedMouseEvent {
2945
+ readonly type: 'mouse';
2946
+ readonly event: MouseEvent;
2947
+ readonly timestamp: number;
2971
2948
  }
2972
2949
  /**
2973
- * Options for hit testing.
2974
- */
2975
- interface HitTestOptions {
2976
- /** Use cached positions for faster testing (default: true) */
2977
- useCachedPositions?: boolean;
2978
- /** Only test clickable entities (default: true) */
2979
- clickableOnly?: boolean;
2980
- /** Only test hoverable entities (default: false) */
2981
- hoverableOnly?: boolean;
2982
- /** Test both clickable and hoverable (default: false) */
2983
- interactiveOnly?: boolean;
2950
+ * Union of all timestamped input events.
2951
+ */
2952
+ type TimestampedInputEvent = TimestampedKeyEvent | TimestampedMouseEvent;
2953
+ /**
2954
+ * Statistics about the input buffer.
2955
+ */
2956
+ interface InputBufferStats {
2957
+ /** Total key events pushed since creation/reset */
2958
+ readonly totalKeyEvents: number;
2959
+ /** Total mouse events pushed since creation/reset */
2960
+ readonly totalMouseEvents: number;
2961
+ /** Current key events in buffer */
2962
+ readonly pendingKeyEvents: number;
2963
+ /** Current mouse events in buffer */
2964
+ readonly pendingMouseEvents: number;
2965
+ /** Number of events dropped due to overflow */
2966
+ readonly droppedEvents: number;
2967
+ /** Maximum buffer size */
2968
+ readonly maxBufferSize: number;
2984
2969
  }
2985
2970
  /**
2986
- * Creates a new clickable cache.
2987
- *
2988
- * The cache stores a z-index sorted list of interactive entities
2989
- * for efficient hit testing.
2971
+ * Latency statistics for input processing.
2972
+ * All values in milliseconds.
2973
+ */
2974
+ interface InputLatencyStats {
2975
+ /** Minimum latency observed */
2976
+ readonly min: number;
2977
+ /** Maximum latency observed */
2978
+ readonly max: number;
2979
+ /** Average latency */
2980
+ readonly avg: number;
2981
+ /** 95th percentile latency */
2982
+ readonly p95: number;
2983
+ /** 99th percentile latency */
2984
+ readonly p99: number;
2985
+ /** Number of samples in the window */
2986
+ readonly sampleCount: number;
2987
+ /** Last frame processing time in ms */
2988
+ readonly lastFrameProcessingTime: number;
2989
+ /** Average frame processing time */
2990
+ readonly avgFrameProcessingTime: number;
2991
+ }
2992
+ /**
2993
+ * Configuration options for the input event buffer.
2994
+ */
2995
+ interface InputEventBufferOptions {
2996
+ /**
2997
+ * Maximum number of events to buffer before dropping oldest.
2998
+ * Set to 0 for unlimited (not recommended).
2999
+ * @default 1000
3000
+ */
3001
+ readonly maxBufferSize?: number;
3002
+ /**
3003
+ * Whether to emit a warning when buffer overflows.
3004
+ * @default true
3005
+ */
3006
+ readonly warnOnOverflow?: boolean;
3007
+ /**
3008
+ * Custom warning handler for overflow events.
3009
+ * @default console.warn
3010
+ */
3011
+ readonly onOverflow?: (droppedCount: number) => void;
3012
+ /**
3013
+ * Maximum number of latency samples to keep for statistics.
3014
+ * @default 1000
3015
+ */
3016
+ readonly maxLatencySamples?: number;
3017
+ /**
3018
+ * Maximum number of frame processing time samples to keep.
3019
+ * @default 100
3020
+ */
3021
+ readonly maxFrameSamples?: number;
3022
+ }
3023
+ /**
3024
+ * Input event buffer data structure.
3025
+ * All state is stored in plain arrays for functional manipulation.
3026
+ */
3027
+ interface InputEventBufferData {
3028
+ /** Pending key events */
3029
+ keyEvents: TimestampedKeyEvent[];
3030
+ /** Pending mouse events */
3031
+ mouseEvents: TimestampedMouseEvent[];
3032
+ /** Latency samples for statistics */
3033
+ latencySamples: number[];
3034
+ /** Frame processing time samples */
3035
+ frameProcessingTimes: number[];
3036
+ /** Frame start timestamp */
3037
+ frameStartTime: number;
3038
+ /** Total key events pushed since creation/reset */
3039
+ totalKeyEvents: number;
3040
+ /** Total mouse events pushed since creation/reset */
3041
+ totalMouseEvents: number;
3042
+ /** Number of events dropped due to overflow */
3043
+ droppedEvents: number;
3044
+ /** Configuration */
3045
+ readonly config: {
3046
+ readonly maxBufferSize: number;
3047
+ readonly maxLatencySamples: number;
3048
+ readonly maxFrameSamples: number;
3049
+ readonly warnOnOverflow: boolean;
3050
+ readonly onOverflow: (droppedCount: number) => void;
3051
+ };
3052
+ }
3053
+ /**
3054
+ * Creates a new input event buffer.
2990
3055
  *
2991
- * @returns A new ClickableCache
3056
+ * @param options - Buffer configuration options
3057
+ * @returns A new InputEventBufferData
2992
3058
  *
2993
3059
  * @example
2994
3060
  * ```typescript
2995
- * import { createClickableCache } from 'blecsd';
3061
+ * import { createInputEventBuffer, pushKeyEvent, drainKeys } from 'blecsd';
2996
3062
  *
2997
- * const cache = createClickableCache();
3063
+ * const buffer = createInputEventBuffer({ maxBufferSize: 500 });
3064
+ *
3065
+ * // Push events from stdin
3066
+ * pushKeyEvent(buffer, keyEvent);
3067
+ *
3068
+ * // Drain in game loop
3069
+ * const keys = drainKeys(buffer);
2998
3070
  * ```
2999
3071
  */
3000
- declare function createClickableCache(): ClickableCache;
3072
+ declare function createInputEventBuffer(options?: InputEventBufferOptions): InputEventBufferData;
3001
3073
  /**
3002
- * Marks the clickable cache as needing rebuild.
3074
+ * Pushes a keyboard event to the buffer.
3003
3075
  *
3004
- * Call this when:
3005
- * - Entities are added or removed
3006
- * - Z-index values change
3007
- * - Interactive state changes (clickable/hoverable toggled)
3008
- *
3009
- * @param cache - The clickable cache
3076
+ * @param buffer - The input event buffer
3077
+ * @param event - The keyboard event to buffer
3078
+ * @param timestamp - Optional timestamp (default: now)
3010
3079
  *
3011
3080
  * @example
3012
3081
  * ```typescript
3013
- * import { invalidateClickableCache } from 'blecsd';
3014
- *
3015
- * // After adding a new clickable entity
3016
- * invalidateClickableCache(cache);
3082
+ * pushKeyEvent(buffer, { name: 'a', ctrl: false, meta: false, shift: false, sequence: 'a' });
3017
3083
  * ```
3018
3084
  */
3019
- declare function invalidateClickableCache(cache: ClickableCache): void;
3085
+ declare function pushKeyEvent(buffer: InputEventBufferData, event: KeyEvent, timestamp?: number): void;
3020
3086
  /**
3021
- * Checks if the cache needs rebuilding.
3087
+ * Pushes a mouse event to the buffer.
3022
3088
  *
3023
- * @param cache - The clickable cache
3024
- * @returns true if cache is dirty
3089
+ * @param buffer - The input event buffer
3090
+ * @param event - The mouse event to buffer
3091
+ * @param timestamp - Optional timestamp (default: now)
3092
+ *
3093
+ * @example
3094
+ * ```typescript
3095
+ * pushMouseEvent(buffer, { x: 10, y: 20, button: 'left', action: 'mousedown', ctrl: false, meta: false, shift: false });
3096
+ * ```
3025
3097
  */
3026
- declare function isCacheDirty(cache: ClickableCache): boolean;
3098
+ declare function pushMouseEvent(buffer: InputEventBufferData, event: MouseEvent, timestamp?: number): void;
3027
3099
  /**
3028
- * Rebuilds the clickable cache if needed.
3100
+ * Drains all keyboard events from the buffer.
3101
+ * Returns events in order (oldest first) and clears the buffer.
3029
3102
  *
3030
- * Queries for all interactive entities and sorts them by z-index
3031
- * in descending order (highest z-index first for hit testing).
3103
+ * @param buffer - The input event buffer
3104
+ * @returns Array of timestamped key events
3032
3105
  *
3033
- * @param world - The ECS world
3034
- * @param cache - The clickable cache
3106
+ * @example
3107
+ * ```typescript
3108
+ * const keys = drainKeys(buffer);
3109
+ * for (const { event, timestamp } of keys) {
3110
+ * console.log(`Key: ${event.name} at ${timestamp}`);
3111
+ * }
3112
+ * ```
3113
+ */
3114
+ declare function drainKeys(buffer: InputEventBufferData): TimestampedKeyEvent[];
3115
+ /**
3116
+ * Drains all mouse events from the buffer.
3117
+ * Returns events in order (oldest first) and clears the buffer.
3118
+ *
3119
+ * @param buffer - The input event buffer
3120
+ * @returns Array of timestamped mouse events
3035
3121
  *
3036
3122
  * @example
3037
3123
  * ```typescript
3038
- * import { updateClickableCache } from 'blecsd';
3124
+ * const mouse = drainMouse(buffer);
3125
+ * for (const { event, timestamp } of mouse) {
3126
+ * console.log(`Mouse: ${event.action} at ${event.x},${event.y}`);
3127
+ * }
3128
+ * ```
3129
+ */
3130
+ declare function drainMouse(buffer: InputEventBufferData): TimestampedMouseEvent[];
3131
+ /**
3132
+ * Drains all events (keys and mouse) from the buffer.
3133
+ * Returns events in chronological order by timestamp.
3039
3134
  *
3040
- * // Rebuild cache before hit testing
3041
- * updateClickableCache(world, cache);
3135
+ * @param buffer - The input event buffer
3136
+ * @returns Array of all timestamped events sorted by timestamp
3137
+ *
3138
+ * @example
3139
+ * ```typescript
3140
+ * const events = drainAllEvents(buffer);
3141
+ * for (const event of events) {
3142
+ * if (event.type === 'key') {
3143
+ * handleKey(event.event);
3144
+ * } else {
3145
+ * handleMouse(event.event);
3146
+ * }
3147
+ * }
3042
3148
  * ```
3043
3149
  */
3044
- declare function updateClickableCache(world: World, cache: ClickableCache): void;
3150
+ declare function drainAllEvents(buffer: InputEventBufferData): TimestampedInputEvent[];
3045
3151
  /**
3046
- * Gets the current sorted clickable entities from cache.
3152
+ * Peeks at all pending events without removing them.
3047
3153
  *
3048
- * @param world - The ECS world
3049
- * @param cache - The clickable cache
3050
- * @returns Array of entities sorted by z-index (highest first)
3154
+ * @param buffer - The input event buffer
3155
+ * @returns Array of all pending events sorted by timestamp
3051
3156
  */
3052
- declare function getClickableEntities(world: World, cache: ClickableCache): readonly Entity[];
3157
+ declare function peekEvents(buffer: InputEventBufferData): TimestampedInputEvent[];
3053
3158
  /**
3054
- * Gets the count of clickable entities.
3159
+ * Peeks at pending key events without removing them.
3055
3160
  *
3056
- * @param world - The ECS world
3057
- * @param cache - The clickable cache
3058
- * @returns Number of clickable/hoverable entities
3161
+ * @param buffer - The input event buffer
3162
+ * @returns Array of pending key events
3059
3163
  */
3060
- declare function getClickableCount(world: World, cache: ClickableCache): number;
3061
- declare function hitTest(world: World, x: number, y: number, cache?: ClickableCache, options?: HitTestOptions): Entity | null;
3164
+ declare function peekKeys(buffer: InputEventBufferData): readonly TimestampedKeyEvent[];
3062
3165
  /**
3063
- * Performs a hit test and returns all entities at the point.
3166
+ * Peeks at pending mouse events without removing them.
3064
3167
  *
3065
- * Returns entities sorted by z-index (highest first).
3168
+ * @param buffer - The input event buffer
3169
+ * @returns Array of pending mouse events
3170
+ */
3171
+ declare function peekMouse(buffer: InputEventBufferData): readonly TimestampedMouseEvent[];
3172
+ /**
3173
+ * Clears all pending events from the buffer.
3066
3174
  *
3067
- * @param world - The ECS world
3068
- * @param x - Screen X coordinate
3069
- * @param y - Screen Y coordinate
3070
- * @param cache - Optional clickable cache for efficiency
3071
- * @param options - Hit test options
3072
- * @returns Array of entities at the point, sorted by z-index (highest first)
3175
+ * @param buffer - The input event buffer
3176
+ */
3177
+ declare function clearBuffer(buffer: InputEventBufferData): void;
3178
+ /**
3179
+ * Gets the number of pending key events.
3180
+ *
3181
+ * @param buffer - The input event buffer
3182
+ */
3183
+ declare function getPendingKeyCount(buffer: InputEventBufferData): number;
3184
+ /**
3185
+ * Gets the number of pending mouse events.
3186
+ *
3187
+ * @param buffer - The input event buffer
3188
+ */
3189
+ declare function getPendingMouseCount(buffer: InputEventBufferData): number;
3190
+ /**
3191
+ * Gets the total number of pending events.
3192
+ *
3193
+ * @param buffer - The input event buffer
3194
+ */
3195
+ declare function getPendingCount(buffer: InputEventBufferData): number;
3196
+ /**
3197
+ * Checks if there are any pending events.
3198
+ *
3199
+ * @param buffer - The input event buffer
3200
+ */
3201
+ declare function hasPendingEvents(buffer: InputEventBufferData): boolean;
3202
+ /**
3203
+ * Gets buffer statistics for debugging.
3204
+ *
3205
+ * @param buffer - The input event buffer
3206
+ * @returns Statistics about the buffer
3073
3207
  *
3074
3208
  * @example
3075
3209
  * ```typescript
3076
- * import { hitTestAll, createClickableCache } from 'blecsd';
3210
+ * const stats = getStats(buffer);
3211
+ * console.log(`Total events: ${stats.totalKeyEvents + stats.totalMouseEvents}`);
3212
+ * console.log(`Dropped: ${stats.droppedEvents}`);
3213
+ * ```
3214
+ */
3215
+ declare function getStats(buffer: InputEventBufferData): InputBufferStats;
3216
+ /**
3217
+ * Resets buffer statistics.
3077
3218
  *
3078
- * const cache = createClickableCache();
3219
+ * @param buffer - The input event buffer
3220
+ */
3221
+ declare function resetStats(buffer: InputEventBufferData): void;
3222
+ /**
3223
+ * Marks the start of frame processing.
3224
+ * Call this at the beginning of your input processing phase.
3079
3225
  *
3080
- * // Get all entities under mouse position
3081
- * const entities = hitTestAll(world, mouseX, mouseY, cache);
3226
+ * @param buffer - The input event buffer
3082
3227
  *
3083
- * for (const eid of entities) {
3084
- * console.log(`Entity ${eid} at z=${getZIndex(world, eid)}`);
3085
- * }
3228
+ * @example
3229
+ * ```typescript
3230
+ * beginFrame(buffer);
3231
+ * const keys = drainKeys(buffer);
3232
+ * // process keys...
3233
+ * endFrame(buffer);
3086
3234
  * ```
3087
3235
  */
3088
- declare function hitTestAll(world: World, x: number, y: number, cache?: ClickableCache, options?: HitTestOptions): Entity[];
3236
+ declare function beginFrame(buffer: InputEventBufferData): void;
3089
3237
  /**
3090
- * Performs a hit test with detailed results.
3238
+ * Marks the end of frame processing and records the processing time.
3239
+ * Call this after all input events have been processed.
3091
3240
  *
3092
- * @param world - The ECS world
3093
- * @param x - Screen X coordinate
3094
- * @param y - Screen Y coordinate
3095
- * @param cache - Optional clickable cache for efficiency
3096
- * @param options - Hit test options
3097
- * @returns Array of HitTestResults with entity and z-index
3241
+ * @param buffer - The input event buffer
3242
+ * @returns The frame processing time in milliseconds
3243
+ */
3244
+ declare function endFrame(buffer: InputEventBufferData): number;
3245
+ /**
3246
+ * Records latency for a processed event.
3247
+ * Call this when an event has been fully processed to track input latency.
3248
+ *
3249
+ * @param buffer - The input event buffer
3250
+ * @param latencyMs - The latency in milliseconds
3098
3251
  *
3099
3252
  * @example
3100
3253
  * ```typescript
3101
- * import { hitTestDetailed, createClickableCache } from 'blecsd';
3254
+ * for (const { event, timestamp } of drainKeys(buffer)) {
3255
+ * handleKey(event);
3256
+ * const latency = performance.now() - timestamp;
3257
+ * recordLatency(buffer, latency);
3258
+ * }
3259
+ * ```
3260
+ */
3261
+ declare function recordLatency(buffer: InputEventBufferData, latencyMs: number): void;
3262
+ /**
3263
+ * Records latency for multiple events at once.
3264
+ * More efficient than calling recordLatency for each event.
3265
+ *
3266
+ * @param buffer - The input event buffer
3267
+ * @param avgLatencyMs - Average latency for all events
3268
+ * @param eventCount - Number of events processed
3269
+ */
3270
+ declare function recordLatencyBatch(buffer: InputEventBufferData, avgLatencyMs: number, eventCount: number): void;
3271
+ /**
3272
+ * Gets latency statistics for input processing.
3273
+ * Returns min, max, average, and percentile latencies.
3102
3274
  *
3103
- * const cache = createClickableCache();
3275
+ * @param buffer - The input event buffer
3276
+ * @returns Latency statistics in milliseconds
3104
3277
  *
3105
- * const results = hitTestDetailed(world, mouseX, mouseY, cache);
3106
- * for (const { entity, zIndex } of results) {
3107
- * console.log(`Entity ${entity} at z=${zIndex}`);
3278
+ * @example
3279
+ * ```typescript
3280
+ * const stats = getLatencyStats(buffer);
3281
+ * console.log(`Avg latency: ${stats.avg.toFixed(2)}ms`);
3282
+ * console.log(`P95 latency: ${stats.p95.toFixed(2)}ms`);
3283
+ * if (stats.max > 16) {
3284
+ * console.warn('Input latency exceeds frame budget!');
3108
3285
  * }
3109
3286
  * ```
3110
3287
  */
3111
- declare function hitTestDetailed(world: World, x: number, y: number, cache?: ClickableCache, options?: HitTestOptions): HitTestResult[];
3288
+ declare function getLatencyStats(buffer: InputEventBufferData): InputLatencyStats;
3112
3289
  /**
3113
- * Checks if any clickable entity is under the point.
3290
+ * Resets latency statistics.
3114
3291
  *
3115
- * @param world - The ECS world
3116
- * @param x - Screen X coordinate
3117
- * @param y - Screen Y coordinate
3118
- * @param cache - Optional clickable cache
3119
- * @returns true if any clickable entity is under the point
3292
+ * @param buffer - The input event buffer
3120
3293
  */
3121
- declare function hasClickableAt(world: World, x: number, y: number, cache?: ClickableCache): boolean;
3294
+ declare function resetLatencyStats(buffer: InputEventBufferData): void;
3122
3295
  /**
3123
- * Checks if any hoverable entity is under the point.
3296
+ * Checks if the current latency is within acceptable bounds.
3297
+ * By default, checks if p95 latency is under 16ms (one frame at 60fps).
3124
3298
  *
3125
- * @param world - The ECS world
3126
- * @param x - Screen X coordinate
3127
- * @param y - Screen Y coordinate
3128
- * @param cache - Optional clickable cache
3129
- * @returns true if any hoverable entity is under the point
3299
+ * @param buffer - The input event buffer
3300
+ * @param maxLatencyMs - Maximum acceptable p95 latency in milliseconds
3301
+ * @returns True if latency is acceptable
3130
3302
  */
3131
- declare function hasHoverableAt(world: World, x: number, y: number, cache?: ClickableCache): boolean;
3303
+ declare function isLatencyAcceptable(buffer: InputEventBufferData, maxLatencyMs?: number): boolean;
3132
3304
  /**
3133
- * Gets the topmost clickable entity at a point.
3305
+ * Checks if frame processing time is within budget.
3306
+ * By default, checks if average processing time is under 1ms.
3134
3307
  *
3135
- * Convenience wrapper for hitTest with clickableOnly=true.
3308
+ * @param buffer - The input event buffer
3309
+ * @param maxProcessingTimeMs - Maximum acceptable processing time in milliseconds
3310
+ * @returns True if processing time is acceptable
3311
+ */
3312
+ declare function isProcessingTimeAcceptable(buffer: InputEventBufferData, maxProcessingTimeMs?: number): boolean;
3313
+ /**
3314
+ * Global shared input buffer for simple use cases.
3136
3315
  *
3137
- * @param world - The ECS world
3138
- * @param x - Screen X coordinate
3139
- * @param y - Screen Y coordinate
3140
- * @param cache - Optional clickable cache
3141
- * @returns Topmost clickable entity or null
3316
+ * For more complex scenarios (multiple input sources, custom overflow handling),
3317
+ * create your own buffer with createInputEventBuffer().
3318
+ *
3319
+ * @example
3320
+ * ```typescript
3321
+ * import { globalInputBuffer, pushKeyEvent, drainAllEvents } from 'blecsd';
3322
+ *
3323
+ * // Push from stdin handler
3324
+ * pushKeyEvent(globalInputBuffer, event);
3325
+ *
3326
+ * // Drain in game loop
3327
+ * const events = drainAllEvents(globalInputBuffer);
3328
+ * ```
3142
3329
  */
3143
- declare function getClickableAt(world: World, x: number, y: number, cache?: ClickableCache): Entity | null;
3330
+ declare const globalInputBuffer: InputEventBufferData;
3331
+
3144
3332
  /**
3145
- * Gets the topmost hoverable entity at a point.
3333
+ * Input state tracking for keyboard and mouse.
3146
3334
  *
3147
- * Convenience wrapper for hitTest with hoverableOnly=true.
3335
+ * Provides frame-aware input queries like isKeyPressed (just this frame),
3336
+ * isKeyReleased, key hold time, and repeat handling.
3148
3337
  *
3149
- * @param world - The ECS world
3150
- * @param x - Screen X coordinate
3151
- * @param y - Screen Y coordinate
3152
- * @param cache - Optional clickable cache
3153
- * @returns Topmost hoverable entity or null
3338
+ * @module core/inputState
3339
+ */
3340
+
3341
+ /**
3342
+ * State of a single key.
3343
+ */
3344
+ interface KeyState {
3345
+ /** Key is currently pressed down */
3346
+ readonly pressed: boolean;
3347
+ /** Key was pressed this frame (transitioned from up to down) */
3348
+ readonly justPressed: boolean;
3349
+ /** Key was released this frame (transitioned from down to up) */
3350
+ readonly justReleased: boolean;
3351
+ /** Time the key has been held in milliseconds */
3352
+ readonly heldTime: number;
3353
+ /** Number of auto-repeat events received while held */
3354
+ readonly repeatCount: number;
3355
+ /** Last event timestamp */
3356
+ readonly lastEventTime: number;
3357
+ }
3358
+ /**
3359
+ * State of a mouse button.
3360
+ */
3361
+ interface MouseButtonState {
3362
+ /** Button is currently pressed */
3363
+ readonly pressed: boolean;
3364
+ /** Button was pressed this frame */
3365
+ readonly justPressed: boolean;
3366
+ /** Button was released this frame */
3367
+ readonly justReleased: boolean;
3368
+ /** Time the button has been held in milliseconds */
3369
+ readonly heldTime: number;
3370
+ /** Last event timestamp */
3371
+ readonly lastEventTime: number;
3372
+ }
3373
+ /**
3374
+ * Current mouse position and state.
3375
+ */
3376
+ interface MouseState {
3377
+ /** Current X position */
3378
+ readonly x: number;
3379
+ /** Current Y position */
3380
+ readonly y: number;
3381
+ /** X movement since last frame */
3382
+ readonly deltaX: number;
3383
+ /** Y movement since last frame */
3384
+ readonly deltaY: number;
3385
+ /** Scroll wheel delta since last frame (positive = up) */
3386
+ readonly wheelDelta: number;
3387
+ /** State of each button */
3388
+ readonly buttons: Readonly<Record<MouseButton, MouseButtonState>>;
3389
+ }
3390
+ /**
3391
+ * Input state statistics.
3392
+ */
3393
+ interface InputStateStats {
3394
+ /** Number of keys currently held down */
3395
+ readonly keysDown: number;
3396
+ /** Number of keys pressed this frame */
3397
+ readonly keysPressed: number;
3398
+ /** Number of keys released this frame */
3399
+ readonly keysReleased: number;
3400
+ /** Total key events processed this frame */
3401
+ readonly keyEventsThisFrame: number;
3402
+ /** Total mouse events processed this frame */
3403
+ readonly mouseEventsThisFrame: number;
3404
+ /** Current frame number */
3405
+ readonly frameCount: number;
3406
+ }
3407
+ /**
3408
+ * Configuration for input state tracking.
3154
3409
  */
3155
- declare function getHoverableAt(world: World, x: number, y: number, cache?: ClickableCache): Entity | null;
3410
+ interface InputStateConfig {
3411
+ /**
3412
+ * Whether to track OS key repeats separately.
3413
+ * When true, repeatCount increments for each repeat event.
3414
+ * When false, repeats are ignored after initial press.
3415
+ * @default true
3416
+ */
3417
+ readonly trackRepeats?: boolean;
3418
+ /**
3419
+ * Minimum time (ms) between key events to consider them separate presses.
3420
+ * Helps filter out very fast unintentional double-presses.
3421
+ * @default 0 (no debouncing)
3422
+ */
3423
+ readonly debounceTime?: number;
3424
+ /**
3425
+ * Custom repeat rate in ms. When set, overrides OS key repeat.
3426
+ * InputState will generate synthetic repeat events at this rate.
3427
+ * @default undefined (use OS repeat)
3428
+ */
3429
+ readonly customRepeatRate?: number;
3430
+ /**
3431
+ * Initial delay before custom repeat starts (ms).
3432
+ * @default 500
3433
+ */
3434
+ readonly customRepeatDelay?: number;
3435
+ }
3436
+ /**
3437
+ * InputState interface for type-safe access.
3438
+ *
3439
+ * Tracks input state across frames.
3440
+ * Call `update()` at the start of each frame with input events from the buffer.
3441
+ * Then use query methods like `isKeyDown()`, `isKeyPressed()`, etc.
3442
+ */
3443
+ interface InputState {
3444
+ update(keyEvents: readonly TimestampedKeyEvent[], mouseEvents: readonly TimestampedMouseEvent[], deltaTime: number): void;
3445
+ isKeyDown(key: KeyName | string): boolean;
3446
+ isKeyPressed(key: KeyName | string): boolean;
3447
+ isKeyReleased(key: KeyName | string): boolean;
3448
+ getKeyHeldTime(key: KeyName | string): number;
3449
+ getKeyState(key: KeyName | string): KeyState;
3450
+ getKeyRepeatCount(key: KeyName | string): number;
3451
+ getPressedKeys(): string[];
3452
+ getJustPressedKeys(): string[];
3453
+ getJustReleasedKeys(): string[];
3454
+ isCtrlDown(): boolean;
3455
+ isAltDown(): boolean;
3456
+ isShiftDown(): boolean;
3457
+ hasModifier(): boolean;
3458
+ isMouseButtonDown(button: MouseButton): boolean;
3459
+ isMouseButtonPressed(button: MouseButton): boolean;
3460
+ isMouseButtonReleased(button: MouseButton): boolean;
3461
+ getMouseX(): number;
3462
+ getMouseY(): number;
3463
+ getMousePosition(): {
3464
+ x: number;
3465
+ y: number;
3466
+ };
3467
+ getMouseDelta(): {
3468
+ deltaX: number;
3469
+ deltaY: number;
3470
+ };
3471
+ getWheelDelta(): number;
3472
+ getMouseState(): MouseState;
3473
+ releaseKey(key: KeyName | string): void;
3474
+ releaseAllKeys(): void;
3475
+ releaseAllMouseButtons(): void;
3476
+ releaseAll(): void;
3477
+ getStats(): InputStateStats;
3478
+ getFrameCount(): number;
3479
+ reset(): void;
3480
+ }
3156
3481
  /**
3157
- * Gets all clickable entities at a point.
3482
+ * Creates a new InputState tracker.
3158
3483
  *
3159
- * @param world - The ECS world
3160
- * @param x - Screen X coordinate
3161
- * @param y - Screen Y coordinate
3162
- * @param cache - Optional clickable cache
3163
- * @returns Array of clickable entities (highest z-index first)
3484
+ * @param config - Configuration options
3485
+ * @returns A new InputState instance
3486
+ *
3487
+ * @example
3488
+ * ```typescript
3489
+ * import { createInputState } from 'blecsd';
3490
+ *
3491
+ * const inputState = createInputState({
3492
+ * trackRepeats: true,
3493
+ * debounceTime: 50, // Ignore inputs within 50ms
3494
+ * });
3495
+ * ```
3164
3496
  */
3165
- declare function getAllClickablesAt(world: World, x: number, y: number, cache?: ClickableCache): Entity[];
3497
+ declare function createInputState(config?: InputStateConfig): InputState;
3166
3498
  /**
3167
- * Gets all hoverable entities at a point.
3499
+ * Checks if any of the specified keys are pressed.
3168
3500
  *
3169
- * @param world - The ECS world
3170
- * @param x - Screen X coordinate
3171
- * @param y - Screen Y coordinate
3172
- * @param cache - Optional clickable cache
3173
- * @returns Array of hoverable entities (highest z-index first)
3501
+ * @param inputState - The input state to check
3502
+ * @param keys - Keys to check
3503
+ * @returns true if any key is currently pressed
3504
+ *
3505
+ * @example
3506
+ * ```typescript
3507
+ * if (isAnyKeyDown(inputState, ['w', 'up'])) {
3508
+ * moveForward();
3509
+ * }
3510
+ * ```
3511
+ */
3512
+ declare function isAnyKeyDown(inputState: InputState, keys: readonly (KeyName | string)[]): boolean;
3513
+ /**
3514
+ * Checks if all specified keys are pressed.
3515
+ *
3516
+ * @param inputState - The input state to check
3517
+ * @param keys - Keys to check
3518
+ * @returns true if all keys are currently pressed
3519
+ *
3520
+ * @example
3521
+ * ```typescript
3522
+ * if (isAllKeysDown(inputState, ['ctrl', 's'])) {
3523
+ * save();
3524
+ * }
3525
+ * ```
3526
+ */
3527
+ declare function isAllKeysDown(inputState: InputState, keys: readonly (KeyName | string)[]): boolean;
3528
+ /**
3529
+ * Checks if any of the specified keys were just pressed this frame.
3530
+ *
3531
+ * @param inputState - The input state to check
3532
+ * @param keys - Keys to check
3533
+ * @returns true if any key was just pressed
3534
+ */
3535
+ declare function isAnyKeyPressed(inputState: InputState, keys: readonly (KeyName | string)[]): boolean;
3536
+ /**
3537
+ * Gets the direction vector from WASD or arrow keys.
3538
+ *
3539
+ * @param inputState - The input state to check
3540
+ * @returns Object with x (-1, 0, or 1) and y (-1, 0, or 1)
3541
+ *
3542
+ * @example
3543
+ * ```typescript
3544
+ * const dir = getMovementDirection(inputState);
3545
+ * player.x += dir.x * speed;
3546
+ * player.y += dir.y * speed;
3547
+ * ```
3548
+ */
3549
+ declare function getMovementDirection(inputState: InputState): {
3550
+ x: number;
3551
+ y: number;
3552
+ };
3553
+
3554
+ /**
3555
+ * Input action mapping system for game controls.
3556
+ *
3557
+ * Maps physical inputs (keys, mouse buttons) to logical game actions.
3558
+ * Supports multiple bindings per action, runtime rebinding, and save/load.
3559
+ *
3560
+ * @module core/inputActions
3561
+ */
3562
+
3563
+ /**
3564
+ * Configuration for a single action binding.
3565
+ */
3566
+ interface ActionBinding {
3567
+ /** Unique action identifier (e.g., 'jump', 'attack', 'move_left') */
3568
+ readonly action: string;
3569
+ /** Keys that activate this action */
3570
+ readonly keys: readonly string[];
3571
+ /** Mouse buttons that activate this action */
3572
+ readonly mouseButtons?: readonly MouseButton[] | undefined;
3573
+ /** Whether action fires continuously while held (default: false) */
3574
+ readonly continuous?: boolean | undefined;
3575
+ /** Deadzone for analog inputs (0-1, default: 0.1) */
3576
+ readonly deadzone?: number | undefined;
3577
+ }
3578
+ /**
3579
+ * Runtime state of an action.
3580
+ */
3581
+ interface ActionState {
3582
+ /** Action is currently active (input is held) */
3583
+ readonly active: boolean;
3584
+ /** Action was just activated this frame */
3585
+ readonly justActivated: boolean;
3586
+ /** Action was just deactivated this frame */
3587
+ readonly justDeactivated: boolean;
3588
+ /** How long the action has been active (ms) */
3589
+ readonly activeTime: number;
3590
+ /** Analog value (0-1), 1 when digital input is pressed */
3591
+ readonly value: number;
3592
+ }
3593
+ /**
3594
+ * Serialized action bindings for save/load.
3595
+ */
3596
+ interface SerializedBindings {
3597
+ readonly version: number;
3598
+ readonly bindings: readonly {
3599
+ readonly action: string;
3600
+ readonly keys: readonly string[];
3601
+ readonly mouseButtons?: readonly string[] | undefined;
3602
+ readonly continuous?: boolean | undefined;
3603
+ }[];
3604
+ }
3605
+ /**
3606
+ * Callback for action state changes.
3607
+ */
3608
+ type ActionCallback = (action: string, state: ActionState, inputState: InputState) => void;
3609
+ /**
3610
+ * Zod schema for action binding validation.
3611
+ */
3612
+ declare const ActionBindingSchema: z.ZodObject<{
3613
+ action: z.ZodString;
3614
+ keys: z.ZodDefault<z.ZodArray<z.ZodString>>;
3615
+ mouseButtons: z.ZodOptional<z.ZodArray<z.ZodEnum<{
3616
+ unknown: "unknown";
3617
+ left: "left";
3618
+ right: "right";
3619
+ middle: "middle";
3620
+ wheelUp: "wheelUp";
3621
+ wheelDown: "wheelDown";
3622
+ }>>>;
3623
+ continuous: z.ZodDefault<z.ZodBoolean>;
3624
+ deadzone: z.ZodDefault<z.ZodNumber>;
3625
+ }, z.core.$strip>;
3626
+ /**
3627
+ * Zod schema for serialized bindings.
3628
+ */
3629
+ declare const SerializedBindingsSchema: z.ZodObject<{
3630
+ version: z.ZodNumber;
3631
+ bindings: z.ZodArray<z.ZodObject<{
3632
+ action: z.ZodString;
3633
+ keys: z.ZodArray<z.ZodString>;
3634
+ mouseButtons: z.ZodOptional<z.ZodArray<z.ZodString>>;
3635
+ continuous: z.ZodOptional<z.ZodBoolean>;
3636
+ }, z.core.$strip>>;
3637
+ }, z.core.$strip>;
3638
+ /**
3639
+ * InputActionManager interface for type-safe access.
3640
+ */
3641
+ interface InputActionManager {
3642
+ register(binding: ActionBinding): InputActionManager;
3643
+ registerAll(bindings: readonly ActionBinding[]): InputActionManager;
3644
+ unregister(action: string): boolean;
3645
+ hasAction(action: string): boolean;
3646
+ getActions(): string[];
3647
+ getBinding(action: string): ActionBinding | undefined;
3648
+ update(inputState: InputState, deltaTime: number): void;
3649
+ isActive(action: string): boolean;
3650
+ isJustActivated(action: string): boolean;
3651
+ isJustDeactivated(action: string): boolean;
3652
+ getValue(action: string): number;
3653
+ getActiveTime(action: string): number;
3654
+ getState(action: string): ActionState;
3655
+ getActiveActions(): string[];
3656
+ rebindKeys(action: string, keys: readonly string[]): boolean;
3657
+ rebindMouseButtons(action: string, buttons: readonly MouseButton[]): boolean;
3658
+ addKey(action: string, key: string): boolean;
3659
+ removeKey(action: string, key: string): boolean;
3660
+ getKeysForAction(action: string): string[];
3661
+ getMouseButtonsForAction(action: string): MouseButton[];
3662
+ getActionsForKey(key: string): string[];
3663
+ onAction(action: string, callback: ActionCallback): () => void;
3664
+ onAnyAction(callback: ActionCallback): () => void;
3665
+ saveBindings(): SerializedBindings;
3666
+ loadBindings(data: unknown): void;
3667
+ toJSON(pretty?: boolean): string;
3668
+ fromJSON(json: string): void;
3669
+ resetStates(): void;
3670
+ clear(): void;
3671
+ }
3672
+ /**
3673
+ * Creates a new InputActionManager.
3674
+ *
3675
+ * @param initialBindings - Optional initial bindings to register
3676
+ * @returns A new InputActionManager instance
3677
+ *
3678
+ * @example
3679
+ * ```typescript
3680
+ * import { createInputActionManager } from 'blecsd';
3681
+ *
3682
+ * const actions = createInputActionManager([
3683
+ * { action: 'jump', keys: ['space'] },
3684
+ * { action: 'attack', keys: ['j'], mouseButtons: ['left'] },
3685
+ * ]);
3686
+ * ```
3174
3687
  */
3175
- declare function getAllHoverablesAt(world: World, x: number, y: number, cache?: ClickableCache): Entity[];
3688
+ declare function createInputActionManager(initialBindings?: readonly ActionBinding[]): InputActionManager;
3689
+ /**
3690
+ * Common action presets for quick setup.
3691
+ */
3692
+ declare const ActionPresets: {
3693
+ /**
3694
+ * Standard platformer controls.
3695
+ */
3696
+ readonly platformer: readonly ActionBinding[];
3697
+ /**
3698
+ * Standard top-down controls.
3699
+ */
3700
+ readonly topDown: readonly ActionBinding[];
3701
+ /**
3702
+ * Menu navigation controls.
3703
+ */
3704
+ readonly menu: readonly ActionBinding[];
3705
+ };
3176
3706
 
3177
3707
  /**
3178
3708
  * Configurable key binding system.
@@ -4262,506 +4792,783 @@ declare function createPhaseManager(): PhaseManager;
4262
4792
  declare const defaultPhaseManager: PhaseManager;
4263
4793
 
4264
4794
  /**
4265
- * Position caching for render optimization.
4266
- * Caches computed positions to avoid expensive recalculations.
4267
- * @module core/positionCache
4268
- */
4269
-
4270
- /**
4271
- * Last computed position data.
4272
- * Used to cache resolved positions and skip recalculation when unchanged.
4795
+ * Plugin/Module System
4273
4796
  *
4274
- * - `xi`: Inner X start (left edge after border/padding)
4275
- * - `xl`: Inner X end (right edge before border/padding)
4276
- * - `yi`: Inner Y start (top edge after border/padding)
4277
- * - `yl`: Inner Y end (bottom edge before border/padding)
4278
- * - `base`: Scroll base offset
4279
- * - `valid`: Cache validity flag (1 = valid, 0 = invalid)
4797
+ * Provides a plugin interface for extending blECSd with reusable modules
4798
+ * that bundle components, systems, and lifecycle hooks. Plugins declare
4799
+ * which scheduler phase their systems belong to, enabling modular
4800
+ * composition of functionality.
4801
+ *
4802
+ * @module core/plugins
4280
4803
  *
4281
4804
  * @example
4282
4805
  * ```typescript
4283
- * import { setPositionCache, getPositionCache, invalidatePositionCache } from 'blecsd';
4284
- *
4285
- * // Set cached position after computing
4286
- * setPositionCache(world, entity, { xi: 5, xl: 85, yi: 3, yl: 23, base: 0 });
4806
+ * import { createPluginRegistry, registerPlugin, LoopPhase } from 'blecsd';
4287
4807
  *
4288
- * // Get cached position (returns undefined if invalid)
4289
- * const cached = getPositionCache(world, entity);
4290
- * if (cached) {
4291
- * // Use cached values instead of recalculating
4292
- * console.log(`Inner bounds: ${cached.xi},${cached.yi} to ${cached.xl},${cached.yl}`);
4293
- * }
4808
+ * const registry = createPluginRegistry();
4809
+ * const plugin = {
4810
+ * name: 'physics',
4811
+ * version: '1.0.0',
4812
+ * systems: [
4813
+ * { system: gravitySystem, phase: LoopPhase.ANIMATION, priority: 0 },
4814
+ * ],
4815
+ * };
4294
4816
  *
4295
- * // Invalidate when position changes
4296
- * invalidatePositionCache(world, entity);
4817
+ * registerPlugin(registry, scheduler, world, plugin);
4297
4818
  * ```
4298
4819
  */
4299
- declare const PositionCache: {
4300
- /** Inner X start (left edge after border/padding) */
4301
- xi: Float32Array<ArrayBuffer>;
4302
- /** Inner X end (right edge before border/padding) */
4303
- xl: Float32Array<ArrayBuffer>;
4304
- /** Inner Y start (top edge after border/padding) */
4305
- yi: Float32Array<ArrayBuffer>;
4306
- /** Inner Y end (bottom edge before border/padding) */
4307
- yl: Float32Array<ArrayBuffer>;
4308
- /** Scroll base offset */
4309
- base: Float32Array<ArrayBuffer>;
4310
- /** Cache validity flag (1 = valid, 0 = invalid) */
4311
- valid: Uint8Array<ArrayBuffer>;
4312
- };
4820
+
4313
4821
  /**
4314
- * Cached position data returned by getPositionCache.
4822
+ * A component definition that a plugin provides.
4823
+ * Stores metadata about the component for registration tracking.
4315
4824
  */
4316
- interface CachedPosition {
4317
- /** Inner X start (left edge after border/padding) */
4318
- readonly xi: number;
4319
- /** Inner X end (right edge before border/padding) */
4320
- readonly xl: number;
4321
- /** Inner Y start (top edge after border/padding) */
4322
- readonly yi: number;
4323
- /** Inner Y end (bottom edge before border/padding) */
4324
- readonly yl: number;
4325
- /** Scroll base offset */
4326
- readonly base: number;
4825
+ interface PluginComponent {
4826
+ /** Unique name identifying this component */
4827
+ readonly name: string;
4828
+ /** The actual component store (SoA typed arrays or marker) */
4829
+ readonly store: unknown;
4327
4830
  }
4328
4831
  /**
4329
- * Options for setting position cache.
4832
+ * A system definition that a plugin provides,
4833
+ * including which scheduler phase it should run in.
4330
4834
  */
4331
- interface SetPositionCacheOptions {
4332
- /** Inner X start (left edge after border/padding) */
4333
- readonly xi: number;
4334
- /** Inner X end (right edge before border/padding) */
4335
- readonly xl: number;
4336
- /** Inner Y start (top edge after border/padding) */
4337
- readonly yi: number;
4338
- /** Inner Y end (bottom edge before border/padding) */
4339
- readonly yl: number;
4340
- /** Scroll base offset */
4341
- readonly base: number;
4835
+ interface PluginSystem {
4836
+ /** The system function */
4837
+ readonly system: System;
4838
+ /** The scheduler phase this system should be registered in */
4839
+ readonly phase: LoopPhase;
4840
+ /** Priority within the phase (lower = earlier, default: 0) */
4841
+ readonly priority?: number;
4342
4842
  }
4343
4843
  /**
4344
- * Sets the cached position for an entity.
4345
- * Marks the cache as valid.
4844
+ * A widget declaration that a plugin provides.
4845
+ * Used to register widgets when the plugin is installed.
4846
+ */
4847
+ interface PluginWidgetDeclaration {
4848
+ /** Widget type name (e.g., 'bar-chart', 'pie-chart') */
4849
+ readonly name: string;
4850
+ /** Human-readable description */
4851
+ readonly description?: string;
4852
+ /** Factory function for creating the widget entity */
4853
+ readonly factory: (world: World, config: Record<string, unknown>) => number;
4854
+ /** Tags for categorization */
4855
+ readonly tags?: readonly string[];
4856
+ }
4857
+ /**
4858
+ * A theme declaration that a plugin provides.
4859
+ * Used to register themes when the plugin is installed.
4860
+ */
4861
+ interface PluginThemeDeclaration {
4862
+ /** Theme name */
4863
+ readonly name: string;
4864
+ /** Theme data object */
4865
+ readonly theme: Record<string, unknown>;
4866
+ }
4867
+ /**
4868
+ * Plugin interface for extending blECSd with reusable functionality.
4346
4869
  *
4347
- * @param _world - The ECS world (unused, for API consistency)
4348
- * @param eid - The entity ID
4349
- * @param options - Position values to cache
4870
+ * Plugins bundle components, systems, and lifecycle hooks into a
4871
+ * single installable unit. They declare dependencies and can
4872
+ * initialize/cleanup world state.
4350
4873
  *
4351
4874
  * @example
4352
4875
  * ```typescript
4353
- * import { setPositionCache } from 'blecsd';
4876
+ * import type { Plugin } from 'blecsd';
4877
+ * import { LoopPhase } from 'blecsd';
4354
4878
  *
4355
- * // After computing positions during render
4356
- * setPositionCache(world, entity, {
4357
- * xi: 5, // Inner left
4358
- * xl: 85, // Inner right
4359
- * yi: 3, // Inner top
4360
- * yl: 23, // Inner bottom
4361
- * base: 0, // Scroll offset
4362
- * });
4879
+ * const myPlugin: Plugin = {
4880
+ * name: 'my-plugin',
4881
+ * version: '1.0.0',
4882
+ * dependencies: ['core-renderer'],
4883
+ * components: [
4884
+ * { name: 'MyComponent', store: MyComponentStore },
4885
+ * ],
4886
+ * systems: [
4887
+ * { system: myUpdateSystem, phase: LoopPhase.UPDATE },
4888
+ * { system: myRenderSystem, phase: LoopPhase.RENDER, priority: 10 },
4889
+ * ],
4890
+ * init: (world) => {
4891
+ * // Set up initial world state
4892
+ * return world;
4893
+ * },
4894
+ * cleanup: (world) => {
4895
+ * // Tear down plugin state
4896
+ * return world;
4897
+ * },
4898
+ * };
4363
4899
  * ```
4364
4900
  */
4365
- declare function setPositionCache(_world: World, eid: Entity, options: SetPositionCacheOptions): void;
4901
+ interface Plugin {
4902
+ /** Unique plugin name */
4903
+ readonly name: string;
4904
+ /** Semantic version string */
4905
+ readonly version: string;
4906
+ /** Names of plugins this one depends on (must be registered first) */
4907
+ readonly dependencies?: readonly string[];
4908
+ /** Components provided by this plugin */
4909
+ readonly components?: readonly PluginComponent[];
4910
+ /** Systems provided by this plugin, with phase assignments */
4911
+ readonly systems?: readonly PluginSystem[];
4912
+ /** Widget declarations provided by this plugin */
4913
+ readonly widgets?: readonly PluginWidgetDeclaration[];
4914
+ /** Theme declarations provided by this plugin */
4915
+ readonly themes?: readonly PluginThemeDeclaration[];
4916
+ /** Zod schema for plugin configuration validation */
4917
+ readonly configSchema?: z.ZodType;
4918
+ /** Called when the plugin is registered. Can set up initial world state. */
4919
+ readonly init?: (world: World) => World;
4920
+ /** Called when the plugin is activated (starts processing). */
4921
+ readonly onActivate?: (world: World) => void;
4922
+ /** Called when the plugin is deactivated (stops processing). */
4923
+ readonly onDeactivate?: (world: World) => void;
4924
+ /** Called when the plugin is unregistered. Can clean up world state. */
4925
+ readonly cleanup?: (world: World) => World;
4926
+ }
4366
4927
  /**
4367
- * Gets the cached position for an entity.
4368
- * Returns undefined if the cache is invalid.
4369
- *
4370
- * @param _world - The ECS world (unused, for API consistency)
4371
- * @param eid - The entity ID
4372
- * @returns Cached position data or undefined if invalid
4928
+ * Info about a registered plugin, returned by getPlugins().
4929
+ */
4930
+ interface PluginInfo {
4931
+ /** Plugin name */
4932
+ readonly name: string;
4933
+ /** Plugin version */
4934
+ readonly version: string;
4935
+ /** Names of dependencies */
4936
+ readonly dependencies: readonly string[];
4937
+ /** Number of components provided */
4938
+ readonly componentCount: number;
4939
+ /** Number of systems provided */
4940
+ readonly systemCount: number;
4941
+ /** Number of widgets provided */
4942
+ readonly widgetCount: number;
4943
+ /** Number of themes provided */
4944
+ readonly themeCount: number;
4945
+ /** Whether the plugin is currently active */
4946
+ readonly active: boolean;
4947
+ }
4948
+ /**
4949
+ * Result of a plugin registration operation.
4950
+ */
4951
+ interface PluginRegistrationResult {
4952
+ /** Whether registration succeeded */
4953
+ readonly success: boolean;
4954
+ /** Error message if registration failed */
4955
+ readonly error?: string;
4956
+ /** Number of systems registered with the scheduler */
4957
+ readonly systemsRegistered: number;
4958
+ /** Number of components registered */
4959
+ readonly componentsRegistered: number;
4960
+ }
4961
+ /**
4962
+ * Registry that tracks all registered plugins and their state.
4963
+ */
4964
+ interface PluginRegistry {
4965
+ /** Internal storage, not for direct access */
4966
+ readonly _plugins: ReadonlyMap<string, PluginRegistryEntry>;
4967
+ }
4968
+ /**
4969
+ * Internal entry for a registered plugin.
4970
+ * @internal
4971
+ */
4972
+ interface PluginRegistryEntry {
4973
+ readonly plugin: Plugin;
4974
+ readonly registeredSystems: readonly PluginSystem[];
4975
+ active: boolean;
4976
+ }
4977
+ /**
4978
+ * Zod schema for PluginComponent validation.
4373
4979
  *
4374
4980
  * @example
4375
4981
  * ```typescript
4376
- * import { getPositionCache } from 'blecsd';
4982
+ * import { PluginComponentSchema } from 'blecsd';
4377
4983
  *
4378
- * const cached = getPositionCache(world, entity);
4379
- * if (cached) {
4380
- * // Use cached values
4381
- * const innerWidth = cached.xl - cached.xi;
4382
- * const innerHeight = cached.yl - cached.yi;
4383
- * }
4984
+ * const validated = PluginComponentSchema.parse({
4985
+ * name: 'MyComponent',
4986
+ * store: myStore,
4987
+ * });
4384
4988
  * ```
4385
4989
  */
4386
- declare function getPositionCache(_world: World, eid: Entity): CachedPosition | undefined;
4990
+ declare const PluginComponentSchema: z.ZodObject<{
4991
+ name: z.ZodString;
4992
+ store: z.ZodUnknown;
4993
+ }, z.core.$strip>;
4387
4994
  /**
4388
- * Checks if the position cache is valid for an entity.
4995
+ * Zod schema for PluginSystem validation.
4389
4996
  *
4390
- * @param _world - The ECS world (unused, for API consistency)
4391
- * @param eid - The entity ID
4392
- * @returns true if cache is valid
4997
+ * @example
4998
+ * ```typescript
4999
+ * import { PluginSystemSchema } from 'blecsd';
5000
+ *
5001
+ * const validated = PluginSystemSchema.parse({
5002
+ * system: mySystem,
5003
+ * phase: 2, // LoopPhase.UPDATE
5004
+ * priority: 0,
5005
+ * });
5006
+ * ```
5007
+ */
5008
+ declare const PluginSystemSchema: z.ZodObject<{
5009
+ system: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
5010
+ phase: z.ZodEnum<typeof LoopPhase>;
5011
+ priority: z.ZodOptional<z.ZodNumber>;
5012
+ }, z.core.$strip>;
5013
+ /**
5014
+ * Zod schema for Plugin manifest validation.
5015
+ * Validates the structure of a plugin before registration.
4393
5016
  *
4394
5017
  * @example
4395
5018
  * ```typescript
4396
- * import { hasValidPositionCache } from 'blecsd';
5019
+ * import { PluginSchema } from 'blecsd';
4397
5020
  *
4398
- * if (hasValidPositionCache(world, entity)) {
4399
- * // Skip expensive position calculation
5021
+ * const result = PluginSchema.safeParse(pluginCandidate);
5022
+ * if (result.success) {
5023
+ * registerPlugin(registry, scheduler, world, result.data);
4400
5024
  * }
4401
5025
  * ```
4402
5026
  */
4403
- declare function hasValidPositionCache(_world: World, eid: Entity): boolean;
4404
5027
  /**
4405
- * Invalidates the position cache for an entity.
4406
- * Call this when position, size, or parent changes.
5028
+ * Zod schema for PluginWidgetDeclaration validation.
5029
+ */
5030
+ declare const PluginWidgetDeclarationSchema: z.ZodObject<{
5031
+ name: z.ZodString;
5032
+ description: z.ZodOptional<z.ZodString>;
5033
+ factory: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
5034
+ tags: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
5035
+ }, z.core.$strip>;
5036
+ /**
5037
+ * Zod schema for PluginThemeDeclaration validation.
5038
+ */
5039
+ declare const PluginThemeDeclarationSchema: z.ZodObject<{
5040
+ name: z.ZodString;
5041
+ theme: z.ZodRecord<z.ZodString, z.ZodUnknown>;
5042
+ }, z.core.$strip>;
5043
+ declare const PluginSchema: z.ZodObject<{
5044
+ name: z.ZodString;
5045
+ version: z.ZodString;
5046
+ dependencies: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
5047
+ components: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodObject<{
5048
+ name: z.ZodString;
5049
+ store: z.ZodUnknown;
5050
+ }, z.core.$strip>>>>;
5051
+ systems: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodObject<{
5052
+ system: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
5053
+ phase: z.ZodEnum<typeof LoopPhase>;
5054
+ priority: z.ZodOptional<z.ZodNumber>;
5055
+ }, z.core.$strip>>>>;
5056
+ widgets: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodObject<{
5057
+ name: z.ZodString;
5058
+ description: z.ZodOptional<z.ZodString>;
5059
+ factory: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
5060
+ tags: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
5061
+ }, z.core.$strip>>>>;
5062
+ themes: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodObject<{
5063
+ name: z.ZodString;
5064
+ theme: z.ZodRecord<z.ZodString, z.ZodUnknown>;
5065
+ }, z.core.$strip>>>>;
5066
+ configSchema: z.ZodOptional<z.ZodUnknown>;
5067
+ init: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
5068
+ onActivate: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
5069
+ onDeactivate: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
5070
+ cleanup: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
5071
+ }, z.core.$strip>;
5072
+ /**
5073
+ * Creates a new, empty plugin registry.
4407
5074
  *
4408
- * @param _world - The ECS world (unused, for API consistency)
4409
- * @param eid - The entity ID
5075
+ * @returns A new PluginRegistry
4410
5076
  *
4411
5077
  * @example
4412
5078
  * ```typescript
4413
- * import { invalidatePositionCache } from 'blecsd';
5079
+ * import { createPluginRegistry } from 'blecsd';
4414
5080
  *
4415
- * // Invalidate when entity moves
4416
- * setPosition(world, entity, 10, 20);
4417
- * invalidatePositionCache(world, entity);
5081
+ * const registry = createPluginRegistry();
4418
5082
  * ```
4419
5083
  */
4420
- declare function invalidatePositionCache(_world: World, eid: Entity): void;
5084
+ declare function createPluginRegistry(): PluginRegistry;
4421
5085
  /**
4422
- * Invalidates the position cache for an entity and all its descendants.
4423
- * Call this when a parent changes position, as all children need recalculation.
5086
+ * Registers a plugin with the given registry and scheduler.
4424
5087
  *
4425
- * @param world - The ECS world
4426
- * @param eid - The entity ID
4427
- * @param getDescendants - Function to get descendant entities
5088
+ * Validates the plugin manifest, checks dependencies, registers systems
5089
+ * with the scheduler, and calls the plugin's init hook if present.
5090
+ *
5091
+ * @param registry - The plugin registry to register with
5092
+ * @param scheduler - The scheduler to register systems with
5093
+ * @param world - The ECS world (passed to init hook)
5094
+ * @param plugin - The plugin to register
5095
+ * @returns Registration result with success status and details
4428
5096
  *
4429
5097
  * @example
4430
5098
  * ```typescript
4431
- * import { invalidatePositionCacheTree, getChildren } from 'blecsd';
5099
+ * import { createPluginRegistry, createScheduler, registerPlugin, LoopPhase } from 'blecsd';
4432
5100
  *
4433
- * // Invalidate entire subtree when parent moves
4434
- * invalidatePositionCacheTree(world, parentEntity, (w, e) => {
4435
- * // Return all descendants recursively
4436
- * return getAllDescendants(w, e);
5101
+ * const registry = createPluginRegistry();
5102
+ * const scheduler = createScheduler();
5103
+ * const world = createWorld();
5104
+ *
5105
+ * const result = registerPlugin(registry, scheduler, world, {
5106
+ * name: 'physics',
5107
+ * version: '1.0.0',
5108
+ * systems: [
5109
+ * { system: gravitySystem, phase: LoopPhase.ANIMATION },
5110
+ * ],
4437
5111
  * });
5112
+ *
5113
+ * if (!result.success) {
5114
+ * console.error('Plugin registration failed:', result.error);
5115
+ * }
4438
5116
  * ```
4439
5117
  */
4440
- declare function invalidatePositionCacheTree(world: World, eid: Entity, getDescendants: (world: World, eid: Entity) => Entity[]): void;
5118
+ declare function registerPlugin(registry: PluginRegistry, scheduler: Scheduler, world: World, plugin: Plugin): PluginRegistrationResult;
4441
5119
  /**
4442
- * Clears all position caches.
4443
- * Useful for forcing a complete recalculation after major changes.
5120
+ * Unregisters a plugin, removing its systems from the scheduler
5121
+ * and calling the cleanup hook.
4444
5122
  *
4445
- * @param _world - The ECS world (unused, for API consistency)
5123
+ * @param registry - The plugin registry
5124
+ * @param scheduler - The scheduler to unregister systems from
5125
+ * @param world - The ECS world (passed to cleanup hook)
5126
+ * @param pluginName - The name of the plugin to unregister
5127
+ * @returns true if the plugin was found and unregistered
4446
5128
  *
4447
5129
  * @example
4448
5130
  * ```typescript
4449
- * import { clearAllPositionCaches } from 'blecsd';
5131
+ * import { unregisterPlugin } from 'blecsd';
4450
5132
  *
4451
- * // After screen resize
4452
- * clearAllPositionCaches(world);
5133
+ * const removed = unregisterPlugin(registry, scheduler, world, 'physics');
5134
+ * if (!removed) {
5135
+ * console.warn('Plugin not found');
5136
+ * }
4453
5137
  * ```
4454
5138
  */
4455
- declare function clearAllPositionCaches(_world: World): void;
5139
+ declare function unregisterPlugin(registry: PluginRegistry, scheduler: Scheduler, world: World, pluginName: string): boolean;
4456
5140
  /**
4457
- * Gets inner width from cached position.
4458
- * Returns 0 if cache is invalid.
5141
+ * Gets information about all registered plugins.
4459
5142
  *
4460
- * @param _world - The ECS world (unused, for API consistency)
4461
- * @param eid - The entity ID
4462
- * @returns Inner width or 0 if invalid
5143
+ * @param registry - The plugin registry
5144
+ * @returns Array of plugin info objects
4463
5145
  *
4464
5146
  * @example
4465
5147
  * ```typescript
4466
- * import { getCachedInnerWidth } from 'blecsd';
5148
+ * import { getPlugins } from 'blecsd';
4467
5149
  *
4468
- * const innerWidth = getCachedInnerWidth(world, entity);
5150
+ * const plugins = getPlugins(registry);
5151
+ * for (const info of plugins) {
5152
+ * console.log(`${info.name} v${info.version} (${info.systemCount} systems)`);
5153
+ * }
4469
5154
  * ```
4470
5155
  */
4471
- declare function getCachedInnerWidth(_world: World, eid: Entity): number;
5156
+ declare function getPlugins(registry: PluginRegistry): readonly PluginInfo[];
4472
5157
  /**
4473
- * Gets inner height from cached position.
4474
- * Returns 0 if cache is invalid.
5158
+ * Checks if a plugin is registered.
4475
5159
  *
4476
- * @param _world - The ECS world (unused, for API consistency)
4477
- * @param eid - The entity ID
4478
- * @returns Inner height or 0 if invalid
5160
+ * @param registry - The plugin registry
5161
+ * @param pluginName - The plugin name to check
5162
+ * @returns true if the plugin is registered
4479
5163
  *
4480
5164
  * @example
4481
5165
  * ```typescript
4482
- * import { getCachedInnerHeight } from 'blecsd';
5166
+ * import { hasPlugin } from 'blecsd';
4483
5167
  *
4484
- * const innerHeight = getCachedInnerHeight(world, entity);
5168
+ * if (hasPlugin(registry, 'physics')) {
5169
+ * console.log('Physics plugin is loaded');
5170
+ * }
4485
5171
  * ```
4486
5172
  */
4487
- declare function getCachedInnerHeight(_world: World, eid: Entity): number;
5173
+ declare function hasPlugin(registry: PluginRegistry, pluginName: string): boolean;
4488
5174
  /**
4489
- * Updates just the scroll base in the cache.
4490
- * Useful when only scroll position changes but layout is the same.
5175
+ * Gets the count of registered plugins.
4491
5176
  *
4492
- * @param _world - The ECS world (unused, for API consistency)
4493
- * @param eid - The entity ID
4494
- * @param base - New scroll base value
5177
+ * @param registry - The plugin registry
5178
+ * @returns Number of registered plugins
4495
5179
  *
4496
5180
  * @example
4497
5181
  * ```typescript
4498
- * import { updateCachedScrollBase } from 'blecsd';
5182
+ * import { getPluginCount } from 'blecsd';
4499
5183
  *
4500
- * // After scrolling (layout unchanged, just scroll offset)
4501
- * updateCachedScrollBase(world, entity, newScrollY);
5184
+ * console.log(`${getPluginCount(registry)} plugins loaded`);
4502
5185
  * ```
4503
5186
  */
4504
- declare function updateCachedScrollBase(_world: World, eid: Entity, base: number): void;
5187
+ declare function getPluginCount(registry: PluginRegistry): number;
4505
5188
  /**
4506
- * Checks if a point is within the cached inner bounds.
4507
- * Returns false if cache is invalid.
5189
+ * Unregisters all plugins in reverse registration order,
5190
+ * calling cleanup hooks and removing systems.
4508
5191
  *
4509
- * @param _world - The ECS world (unused, for API consistency)
4510
- * @param eid - The entity ID
4511
- * @param x - X coordinate to test
4512
- * @param y - Y coordinate to test
4513
- * @returns true if point is within inner bounds
5192
+ * @param registry - The plugin registry
5193
+ * @param scheduler - The scheduler to unregister systems from
5194
+ * @param world - The ECS world (passed to cleanup hooks)
4514
5195
  *
4515
5196
  * @example
4516
5197
  * ```typescript
4517
- * import { isPointInCachedBounds } from 'blecsd';
5198
+ * import { clearPlugins } from 'blecsd';
4518
5199
  *
4519
- * // Efficient hit testing using cached positions
4520
- * if (isPointInCachedBounds(world, entity, mouseX, mouseY)) {
4521
- * // Point is inside the element's inner area
4522
- * }
5200
+ * clearPlugins(registry, scheduler, world);
4523
5201
  * ```
4524
5202
  */
4525
- declare function isPointInCachedBounds(_world: World, eid: Entity, x: number, y: number): boolean;
4526
-
5203
+ declare function clearPlugins(registry: PluginRegistry, scheduler: Scheduler, world: World): void;
4527
5204
  /**
4528
- * Advanced positioning system with percentage and expression support.
4529
- * Resolves position values to absolute coordinates.
4530
- * @module core/positioning
5205
+ * Creates a plugin definition with sensible defaults.
5206
+ * A convenience function that makes plugin creation more concise.
5207
+ *
5208
+ * @param config - Plugin configuration
5209
+ * @returns A fully-formed Plugin object
5210
+ *
5211
+ * @example
5212
+ * ```typescript
5213
+ * import { definePlugin, LoopPhase } from 'blecsd';
5214
+ *
5215
+ * const myPlugin = definePlugin({
5216
+ * name: 'my-charts',
5217
+ * version: '1.0.0',
5218
+ * widgets: [
5219
+ * { name: 'bar-chart', factory: createBarChart, tags: ['chart', 'data'] },
5220
+ * { name: 'pie-chart', factory: createPieChart, tags: ['chart', 'data'] },
5221
+ * ],
5222
+ * systems: [
5223
+ * { system: chartAnimationSystem, phase: LoopPhase.ANIMATION },
5224
+ * ],
5225
+ * });
5226
+ * ```
4531
5227
  */
4532
-
5228
+ declare function definePlugin(config: Plugin): Plugin;
4533
5229
  /**
4534
- * Position value types supported by the positioning system.
4535
- * - number: absolute position in cells
4536
- * - string: percentage ('50%'), expression ('50%-5'), or keyword ('center', 'half')
5230
+ * Activates a previously deactivated plugin.
5231
+ * Re-registers its systems with the scheduler and calls onActivate.
5232
+ *
5233
+ * @param registry - The plugin registry
5234
+ * @param scheduler - The scheduler to register systems with
5235
+ * @param world - The ECS world
5236
+ * @param pluginName - The name of the plugin to activate
5237
+ * @returns true if the plugin was found and activated
5238
+ *
5239
+ * @example
5240
+ * ```typescript
5241
+ * import { activatePlugin } from 'blecsd';
5242
+ *
5243
+ * activatePlugin(registry, scheduler, world, 'physics');
5244
+ * ```
4537
5245
  */
4538
- type PositionValue = number | string;
5246
+ declare function activatePlugin(registry: PluginRegistry, scheduler: Scheduler, world: World, pluginName: string): boolean;
4539
5247
  /**
4540
- * Schema for validating position values.
4541
- * Accepts numbers, percentage strings, expressions, and keywords.
5248
+ * Deactivates a plugin without fully unregistering it.
5249
+ * Removes its systems from the scheduler and calls onDeactivate,
5250
+ * but keeps the plugin in the registry for later reactivation.
5251
+ *
5252
+ * @param registry - The plugin registry
5253
+ * @param scheduler - The scheduler to unregister systems from
5254
+ * @param world - The ECS world
5255
+ * @param pluginName - The name of the plugin to deactivate
5256
+ * @returns true if the plugin was found and deactivated
4542
5257
  *
4543
5258
  * @example
4544
5259
  * ```typescript
4545
- * import { PositionValueSchema } from 'blecsd';
5260
+ * import { deactivatePlugin } from 'blecsd';
4546
5261
  *
4547
- * PositionValueSchema.parse(10); // Valid: absolute
4548
- * PositionValueSchema.parse('50%'); // Valid: percentage
4549
- * PositionValueSchema.parse('50%-5'); // Valid: expression
4550
- * PositionValueSchema.parse('center'); // Valid: keyword
5262
+ * deactivatePlugin(registry, scheduler, world, 'physics');
5263
+ * // Plugin is still registered but not running
5264
+ * // Re-activate later with activatePlugin()
4551
5265
  * ```
4552
5266
  */
4553
- declare const PositionValueSchema: z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>;
5267
+ declare function deactivatePlugin(registry: PluginRegistry, scheduler: Scheduler, world: World, pluginName: string): boolean;
4554
5268
  /**
4555
- * Resolves a position value to an absolute coordinate.
4556
- *
4557
- * Supports:
4558
- * - Numbers: returned as-is
4559
- * - Percentages: '50%' → parentSize * 0.5
4560
- * - Expressions: '50%-5' → (parentSize * 0.5) - 5
4561
- * - Keywords:
4562
- * - 'center': (parentSize - elementSize) / 2
4563
- * - 'half': parentSize / 2
4564
- * - 'left'/'top': 0
4565
- * - 'right'/'bottom': parentSize - elementSize
5269
+ * Checks if a plugin is currently active.
4566
5270
  *
4567
- * @param value - Position value to resolve
4568
- * @param parentSize - Size of the parent container
4569
- * @param elementSize - Size of the element (for centering)
4570
- * @returns Resolved absolute position
5271
+ * @param registry - The plugin registry
5272
+ * @param pluginName - The name of the plugin to check
5273
+ * @returns true if the plugin is registered and active
4571
5274
  *
4572
5275
  * @example
4573
5276
  * ```typescript
4574
- * import { parsePosition } from 'blecsd';
5277
+ * import { isPluginActive } from 'blecsd';
4575
5278
  *
4576
- * // Absolute position
4577
- * parsePosition(10, 100, 20); // 10
5279
+ * if (isPluginActive(registry, 'physics')) {
5280
+ * console.log('Physics is running');
5281
+ * }
5282
+ * ```
5283
+ */
5284
+ declare function isPluginActive(registry: PluginRegistry, pluginName: string): boolean;
5285
+ /**
5286
+ * Validates plugin configuration against its declared config schema.
5287
+ * Returns the validated config or throws if invalid.
5288
+ *
5289
+ * @param plugin - The plugin with a configSchema
5290
+ * @param config - The configuration to validate
5291
+ * @returns The validated configuration
5292
+ * @throws If the config is invalid according to the plugin's schema
4578
5293
  *
4579
- * // Percentage
4580
- * parsePosition('50%', 100, 20); // 50
5294
+ * @example
5295
+ * ```typescript
5296
+ * import { validatePluginConfig, definePlugin } from 'blecsd';
5297
+ * import { z } from 'zod';
4581
5298
  *
4582
- * // Expression
4583
- * parsePosition('50%-5', 100, 20); // 45
4584
- * parsePosition('100%-10', 100, 20); // 90
5299
+ * const plugin = definePlugin({
5300
+ * name: 'my-plugin',
5301
+ * version: '1.0.0',
5302
+ * configSchema: z.object({ maxItems: z.number().positive() }),
5303
+ * });
4585
5304
  *
4586
- * // Keywords
4587
- * parsePosition('center', 100, 20); // 40 ((100-20)/2)
4588
- * parsePosition('half', 100, 20); // 50 (100/2)
4589
- * parsePosition('right', 100, 20); // 80 (100-20)
5305
+ * const config = validatePluginConfig(plugin, { maxItems: 10 });
4590
5306
  * ```
4591
5307
  */
4592
- declare function parsePosition(value: PositionValue, parentSize: number, elementSize?: number): number;
5308
+ declare function validatePluginConfig(plugin: Plugin, config: unknown): unknown;
5309
+
5310
+ /**
5311
+ * Position caching for render optimization.
5312
+ * Caches computed positions to avoid expensive recalculations.
5313
+ * @module core/positionCache
5314
+ */
5315
+
4593
5316
  /**
4594
- * Resolves a position value that may be negative (from right/bottom edge).
4595
- * Negative values are converted to offsets from the opposite edge.
5317
+ * Last computed position data.
5318
+ * Used to cache resolved positions and skip recalculation when unchanged.
4596
5319
  *
4597
- * @param value - Position value to resolve
4598
- * @param parentSize - Size of the parent container
4599
- * @param elementSize - Size of the element
4600
- * @returns Resolved absolute position
5320
+ * - `xi`: Inner X start (left edge after border/padding)
5321
+ * - `xl`: Inner X end (right edge before border/padding)
5322
+ * - `yi`: Inner Y start (top edge after border/padding)
5323
+ * - `yl`: Inner Y end (bottom edge before border/padding)
5324
+ * - `base`: Scroll base offset
5325
+ * - `valid`: Cache validity flag (1 = valid, 0 = invalid)
4601
5326
  *
4602
5327
  * @example
4603
5328
  * ```typescript
4604
- * import { parsePositionWithNegative } from 'blecsd';
5329
+ * import { setPositionCache, getPositionCache, invalidatePositionCache } from 'blecsd';
4605
5330
  *
4606
- * // Positive values work normally
4607
- * parsePositionWithNegative(10, 100, 20); // 10
5331
+ * // Set cached position after computing
5332
+ * setPositionCache(world, entity, { xi: 5, xl: 85, yi: 3, yl: 23, base: 0 });
4608
5333
  *
4609
- * // Negative values are from the right/bottom
4610
- * parsePositionWithNegative(-10, 100, 20); // 70 (100 - 10 - 20)
4611
- * parsePositionWithNegative(-5, 100, 10); // 85 (100 - 5 - 10)
5334
+ * // Get cached position (returns undefined if invalid)
5335
+ * const cached = getPositionCache(world, entity);
5336
+ * if (cached) {
5337
+ * // Use cached values instead of recalculating
5338
+ * console.log(`Inner bounds: ${cached.xi},${cached.yi} to ${cached.xl},${cached.yl}`);
5339
+ * }
5340
+ *
5341
+ * // Invalidate when position changes
5342
+ * invalidatePositionCache(world, entity);
4612
5343
  * ```
4613
5344
  */
4614
- declare function parsePositionWithNegative(value: PositionValue, parentSize: number, elementSize?: number): number;
5345
+ declare const PositionCache: {
5346
+ /** Inner X start (left edge after border/padding) */
5347
+ xi: Float32Array<ArrayBuffer>;
5348
+ /** Inner X end (right edge before border/padding) */
5349
+ xl: Float32Array<ArrayBuffer>;
5350
+ /** Inner Y start (top edge after border/padding) */
5351
+ yi: Float32Array<ArrayBuffer>;
5352
+ /** Inner Y end (bottom edge before border/padding) */
5353
+ yl: Float32Array<ArrayBuffer>;
5354
+ /** Scroll base offset */
5355
+ base: Float32Array<ArrayBuffer>;
5356
+ /** Cache validity flag (1 = valid, 0 = invalid) */
5357
+ valid: Uint8Array<ArrayBuffer>;
5358
+ };
5359
+ /**
5360
+ * Cached position data returned by getPositionCache.
5361
+ */
5362
+ interface CachedPosition {
5363
+ /** Inner X start (left edge after border/padding) */
5364
+ readonly xi: number;
5365
+ /** Inner X end (right edge before border/padding) */
5366
+ readonly xl: number;
5367
+ /** Inner Y start (top edge after border/padding) */
5368
+ readonly yi: number;
5369
+ /** Inner Y end (bottom edge before border/padding) */
5370
+ readonly yl: number;
5371
+ /** Scroll base offset */
5372
+ readonly base: number;
5373
+ }
4615
5374
  /**
4616
- * Validates that a position value is within valid bounds.
5375
+ * Options for setting position cache.
5376
+ */
5377
+ interface SetPositionCacheOptions {
5378
+ /** Inner X start (left edge after border/padding) */
5379
+ readonly xi: number;
5380
+ /** Inner X end (right edge before border/padding) */
5381
+ readonly xl: number;
5382
+ /** Inner Y start (top edge after border/padding) */
5383
+ readonly yi: number;
5384
+ /** Inner Y end (bottom edge before border/padding) */
5385
+ readonly yl: number;
5386
+ /** Scroll base offset */
5387
+ readonly base: number;
5388
+ }
5389
+ /**
5390
+ * Sets the cached position for an entity.
5391
+ * Marks the cache as valid.
4617
5392
  *
4618
- * @param value - Resolved position value
4619
- * @param parentSize - Size of the parent container
4620
- * @param elementSize - Size of the element
4621
- * @returns Clamped position value within bounds
5393
+ * @param _world - The ECS world (unused, for API consistency)
5394
+ * @param eid - The entity ID
5395
+ * @param options - Position values to cache
4622
5396
  *
4623
5397
  * @example
4624
5398
  * ```typescript
4625
- * import { clampPosition } from 'blecsd';
5399
+ * import { setPositionCache } from 'blecsd';
4626
5400
  *
4627
- * clampPosition(-10, 100, 20); // 0 (can't be negative)
4628
- * clampPosition(90, 100, 20); // 80 (element must fit)
4629
- * clampPosition(50, 100, 20); // 50 (within bounds)
5401
+ * // After computing positions during render
5402
+ * setPositionCache(world, entity, {
5403
+ * xi: 5, // Inner left
5404
+ * xl: 85, // Inner right
5405
+ * yi: 3, // Inner top
5406
+ * yl: 23, // Inner bottom
5407
+ * base: 0, // Scroll offset
5408
+ * });
4630
5409
  * ```
4631
5410
  */
4632
- declare function clampPosition(value: number, parentSize: number, elementSize?: number): number;
5411
+ declare function setPositionCache(_world: World, eid: Entity, options: SetPositionCacheOptions): void;
4633
5412
  /**
4634
- * Checks if a position value is a percentage or expression containing a percentage.
5413
+ * Gets the cached position for an entity.
5414
+ * Returns undefined if the cache is invalid.
4635
5415
  *
4636
- * @param value - Position value to check
4637
- * @returns true if the value contains a percentage
5416
+ * @param _world - The ECS world (unused, for API consistency)
5417
+ * @param eid - The entity ID
5418
+ * @returns Cached position data or undefined if invalid
4638
5419
  *
4639
5420
  * @example
4640
5421
  * ```typescript
4641
- * import { isPercentagePosition } from 'blecsd';
5422
+ * import { getPositionCache } from 'blecsd';
4642
5423
  *
4643
- * isPercentagePosition('50%'); // true
4644
- * isPercentagePosition('50%-5'); // true
4645
- * isPercentagePosition(50); // false
4646
- * isPercentagePosition('center'); // false
5424
+ * const cached = getPositionCache(world, entity);
5425
+ * if (cached) {
5426
+ * // Use cached values
5427
+ * const innerWidth = cached.xl - cached.xi;
5428
+ * const innerHeight = cached.yl - cached.yi;
5429
+ * }
4647
5430
  * ```
4648
5431
  */
4649
- declare function isPercentagePosition(value: PositionValue): boolean;
5432
+ declare function getPositionCache(_world: World, eid: Entity): CachedPosition | undefined;
4650
5433
  /**
4651
- * Checks if a position value is a keyword.
5434
+ * Checks if the position cache is valid for an entity.
4652
5435
  *
4653
- * @param value - Position value to check
4654
- * @returns true if the value is a position keyword
5436
+ * @param _world - The ECS world (unused, for API consistency)
5437
+ * @param eid - The entity ID
5438
+ * @returns true if cache is valid
4655
5439
  *
4656
5440
  * @example
4657
5441
  * ```typescript
4658
- * import { isKeywordPosition } from 'blecsd';
5442
+ * import { hasValidPositionCache } from 'blecsd';
4659
5443
  *
4660
- * isKeywordPosition('center'); // true
4661
- * isKeywordPosition('half'); // true
4662
- * isKeywordPosition('50%'); // false
4663
- * isKeywordPosition(50); // false
5444
+ * if (hasValidPositionCache(world, entity)) {
5445
+ * // Skip expensive position calculation
5446
+ * }
4664
5447
  * ```
4665
5448
  */
4666
- declare function isKeywordPosition(value: PositionValue): boolean;
5449
+ declare function hasValidPositionCache(_world: World, eid: Entity): boolean;
4667
5450
  /**
4668
- * Creates a position value that centers an element.
5451
+ * Invalidates the position cache for an entity.
5452
+ * Call this when position, size, or parent changes.
4669
5453
  *
4670
- * @returns 'center' keyword
5454
+ * @param _world - The ECS world (unused, for API consistency)
5455
+ * @param eid - The entity ID
4671
5456
  *
4672
5457
  * @example
4673
5458
  * ```typescript
4674
- * import { centerPosition, parsePosition } from 'blecsd';
5459
+ * import { invalidatePositionCache } from 'blecsd';
4675
5460
  *
4676
- * const pos = centerPosition();
4677
- * const resolved = parsePosition(pos, 100, 20); // 40
5461
+ * // Invalidate when entity moves
5462
+ * setPosition(world, entity, 10, 20);
5463
+ * invalidatePositionCache(world, entity);
4678
5464
  * ```
4679
5465
  */
4680
- declare function centerPosition(): 'center';
5466
+ declare function invalidatePositionCache(_world: World, eid: Entity): void;
4681
5467
  /**
4682
- * Creates a percentage position value.
5468
+ * Invalidates the position cache for an entity and all its descendants.
5469
+ * Call this when a parent changes position, as all children need recalculation.
4683
5470
  *
4684
- * @param percent - Percentage value (0-100)
4685
- * @returns Percentage string
5471
+ * @param world - The ECS world
5472
+ * @param eid - The entity ID
5473
+ * @param getDescendants - Function to get descendant entities
4686
5474
  *
4687
5475
  * @example
4688
5476
  * ```typescript
4689
- * import { percentPosition, parsePosition } from 'blecsd';
5477
+ * import { invalidatePositionCacheTree, getChildren } from 'blecsd';
4690
5478
  *
4691
- * const pos = percentPosition(50);
4692
- * const resolved = parsePosition(pos, 100, 0); // 50
5479
+ * // Invalidate entire subtree when parent moves
5480
+ * invalidatePositionCacheTree(world, parentEntity, (w, e) => {
5481
+ * // Return all descendants recursively
5482
+ * return getAllDescendants(w, e);
5483
+ * });
4693
5484
  * ```
4694
5485
  */
4695
- declare function percentPosition(percent: number): string;
5486
+ declare function invalidatePositionCacheTree(world: World, eid: Entity, getDescendants: (world: World, eid: Entity) => Entity[]): void;
4696
5487
  /**
4697
- * Creates a percentage position with an offset.
5488
+ * Clears all position caches.
5489
+ * Useful for forcing a complete recalculation after major changes.
4698
5490
  *
4699
- * @param percent - Percentage value (0-100)
4700
- * @param offset - Offset to add (negative to subtract)
4701
- * @returns Expression string
5491
+ * @param _world - The ECS world (unused, for API consistency)
4702
5492
  *
4703
5493
  * @example
4704
5494
  * ```typescript
4705
- * import { percentOffsetPosition, parsePosition } from 'blecsd';
5495
+ * import { clearAllPositionCaches } from 'blecsd';
4706
5496
  *
4707
- * const pos = percentOffsetPosition(50, -5);
4708
- * const resolved = parsePosition(pos, 100, 0); // 45
5497
+ * // After screen resize
5498
+ * clearAllPositionCaches(world);
5499
+ * ```
5500
+ */
5501
+ declare function clearAllPositionCaches(_world: World): void;
5502
+ /**
5503
+ * Gets inner width from cached position.
5504
+ * Returns 0 if cache is invalid.
5505
+ *
5506
+ * @param _world - The ECS world (unused, for API consistency)
5507
+ * @param eid - The entity ID
5508
+ * @returns Inner width or 0 if invalid
5509
+ *
5510
+ * @example
5511
+ * ```typescript
5512
+ * import { getCachedInnerWidth } from 'blecsd';
4709
5513
  *
4710
- * const pos2 = percentOffsetPosition(100, -10);
4711
- * const resolved2 = parsePosition(pos2, 100, 0); // 90
5514
+ * const innerWidth = getCachedInnerWidth(world, entity);
4712
5515
  * ```
4713
5516
  */
4714
- declare function percentOffsetPosition(percent: number, offset: number): string;
5517
+ declare function getCachedInnerWidth(_world: World, eid: Entity): number;
4715
5518
  /**
4716
- * Resolves both X and Y position values at once.
5519
+ * Gets inner height from cached position.
5520
+ * Returns 0 if cache is invalid.
4717
5521
  *
4718
- * @param x - X position value
4719
- * @param y - Y position value
4720
- * @param parentWidth - Parent container width
4721
- * @param parentHeight - Parent container height
4722
- * @param elementWidth - Element width (for centering)
4723
- * @param elementHeight - Element height (for centering)
4724
- * @returns Resolved { x, y } coordinates
5522
+ * @param _world - The ECS world (unused, for API consistency)
5523
+ * @param eid - The entity ID
5524
+ * @returns Inner height or 0 if invalid
4725
5525
  *
4726
5526
  * @example
4727
5527
  * ```typescript
4728
- * import { resolvePosition } from 'blecsd';
5528
+ * import { getCachedInnerHeight } from 'blecsd';
4729
5529
  *
4730
- * const pos = resolvePosition('center', 'center', 100, 80, 20, 10);
4731
- * // pos = { x: 40, y: 35 }
5530
+ * const innerHeight = getCachedInnerHeight(world, entity);
5531
+ * ```
5532
+ */
5533
+ declare function getCachedInnerHeight(_world: World, eid: Entity): number;
5534
+ /**
5535
+ * Updates just the scroll base in the cache.
5536
+ * Useful when only scroll position changes but layout is the same.
5537
+ *
5538
+ * @param _world - The ECS world (unused, for API consistency)
5539
+ * @param eid - The entity ID
5540
+ * @param base - New scroll base value
5541
+ *
5542
+ * @example
5543
+ * ```typescript
5544
+ * import { updateCachedScrollBase } from 'blecsd';
4732
5545
  *
4733
- * const pos2 = resolvePosition('50%-5', '100%-10', 100, 80, 0, 0);
4734
- * // pos2 = { x: 45, y: 70 }
5546
+ * // After scrolling (layout unchanged, just scroll offset)
5547
+ * updateCachedScrollBase(world, entity, newScrollY);
4735
5548
  * ```
4736
5549
  */
4737
- declare function resolvePosition(x: PositionValue, y: PositionValue, parentWidth: number, parentHeight: number, elementWidth?: number, elementHeight?: number): {
4738
- x: number;
4739
- y: number;
4740
- };
5550
+ declare function updateCachedScrollBase(_world: World, eid: Entity, base: number): void;
4741
5551
  /**
4742
- * Resolves position values and clamps them to valid bounds.
5552
+ * Checks if a point is within the cached inner bounds.
5553
+ * Returns false if cache is invalid.
4743
5554
  *
4744
- * @param x - X position value
4745
- * @param y - Y position value
4746
- * @param parentWidth - Parent container width
4747
- * @param parentHeight - Parent container height
4748
- * @param elementWidth - Element width
4749
- * @param elementHeight - Element height
4750
- * @returns Resolved and clamped { x, y } coordinates
5555
+ * @param _world - The ECS world (unused, for API consistency)
5556
+ * @param eid - The entity ID
5557
+ * @param x - X coordinate to test
5558
+ * @param y - Y coordinate to test
5559
+ * @returns true if point is within inner bounds
4751
5560
  *
4752
5561
  * @example
4753
5562
  * ```typescript
4754
- * import { resolvePositionClamped } from 'blecsd';
5563
+ * import { isPointInCachedBounds } from 'blecsd';
4755
5564
  *
4756
- * // Position that would go off-screen is clamped
4757
- * const pos = resolvePositionClamped('100%', '100%', 100, 80, 20, 10);
4758
- * // pos = { x: 80, y: 70 } (clamped so element fits)
5565
+ * // Efficient hit testing using cached positions
5566
+ * if (isPointInCachedBounds(world, entity, mouseX, mouseY)) {
5567
+ * // Point is inside the element's inner area
5568
+ * }
4759
5569
  * ```
4760
5570
  */
4761
- declare function resolvePositionClamped(x: PositionValue, y: PositionValue, parentWidth: number, parentHeight: number, elementWidth?: number, elementHeight?: number): {
4762
- x: number;
4763
- y: number;
4764
- };
5571
+ declare function isPointInCachedBounds(_world: World, eid: Entity, x: number, y: number): boolean;
4765
5572
 
4766
5573
  /**
4767
5574
  * Common ECS queries for filtering and selecting entities.
@@ -5079,140 +5886,548 @@ declare function getChildEntities(_world: World, parent: Entity): Entity[];
5079
5886
  *
5080
5887
  * @example
5081
5888
  * ```typescript
5082
- * import { createWorld, createBoxEntity, getDescendantEntities } from 'blecsd';
5889
+ * import { createWorld, createBoxEntity, getDescendantEntities } from 'blecsd';
5890
+ *
5891
+ * const world = createWorld();
5892
+ * const root = createBoxEntity(world, { x: 0, y: 0 });
5893
+ * const child = createBoxEntity(world, { parent: root, x: 1, y: 1 });
5894
+ * const grandchild = createBoxEntity(world, { parent: child, x: 2, y: 2 });
5895
+ *
5896
+ * const descendants = getDescendantEntities(world, root);
5897
+ * // descendants = [child, grandchild]
5898
+ * ```
5899
+ */
5900
+ declare function getDescendantEntities(world: World, parent: Entity): Entity[];
5901
+ /**
5902
+ * Gets all root entities (entities with no parent).
5903
+ *
5904
+ * Filters queryHierarchy to entities where parent === 0 (NULL_ENTITY).
5905
+ *
5906
+ * @param world - The ECS world
5907
+ * @returns Array of root entity IDs
5908
+ *
5909
+ * @example
5910
+ * ```typescript
5911
+ * import { createWorld, createBoxEntity, getRootEntities } from 'blecsd';
5912
+ *
5913
+ * const world = createWorld();
5914
+ * const root1 = createBoxEntity(world, { x: 0, y: 0 });
5915
+ * const root2 = createBoxEntity(world, { x: 10, y: 0 });
5916
+ * const child = createBoxEntity(world, { parent: root1, x: 1, y: 1 });
5917
+ *
5918
+ * const roots = getRootEntities(world);
5919
+ * // roots = [root1, root2]
5920
+ * ```
5921
+ */
5922
+ declare function getRootEntities(world: World): Entity[];
5923
+ /**
5924
+ * Filters entities to only those that are focusable and enabled.
5925
+ *
5926
+ * Returns entities where Focusable.focusable === 1.
5927
+ *
5928
+ * @param world - The ECS world
5929
+ * @param entities - Array of entity IDs to filter
5930
+ * @returns Filtered array of focusable entity IDs
5931
+ *
5932
+ * @example
5933
+ * ```typescript
5934
+ * import { createWorld, queryFocusable, filterFocusable } from 'blecsd';
5935
+ *
5936
+ * const world = createWorld();
5937
+ * // ... create focusable entities, some disabled ...
5938
+ *
5939
+ * const allFocusables = queryFocusable(world);
5940
+ * const enabledFocusables = filterFocusable(world, allFocusables);
5941
+ *
5942
+ * for (const eid of enabledFocusables) {
5943
+ * // Process only enabled focusable entities
5944
+ * }
5945
+ * ```
5946
+ */
5947
+ declare function filterFocusable(_world: World, entities: readonly Entity[]): Entity[];
5948
+ /**
5949
+ * Filters entities to only those that are clickable.
5950
+ *
5951
+ * Returns entities where Interactive.clickable === 1.
5952
+ *
5953
+ * @param world - The ECS world
5954
+ * @param entities - Array of entity IDs to filter
5955
+ * @returns Filtered array of clickable entity IDs
5956
+ *
5957
+ * @example
5958
+ * ```typescript
5959
+ * import { createWorld, queryInteractive, filterClickable } from 'blecsd';
5960
+ *
5961
+ * const world = createWorld();
5962
+ * // ... create interactive entities ...
5963
+ *
5964
+ * const allInteractives = queryInteractive(world);
5965
+ * const clickables = filterClickable(world, allInteractives);
5966
+ *
5967
+ * for (const eid of clickables) {
5968
+ * // Process only clickable entities
5969
+ * }
5970
+ * ```
5971
+ */
5972
+ declare function filterClickable(_world: World, entities: readonly Entity[]): Entity[];
5973
+ /**
5974
+ * Sorts entities by tab index for focus navigation.
5975
+ *
5976
+ * Entities with lower tabIndex are focused first. Entities with tabIndex 0
5977
+ * follow DOM-like ordering.
5978
+ *
5979
+ * @param world - The ECS world
5980
+ * @param entities - Array of entity IDs to sort
5981
+ * @returns New array of entity IDs sorted by tab index
5982
+ *
5983
+ * @example
5984
+ * ```typescript
5985
+ * import { createWorld, queryFocusable, sortByTabIndex, filterFocusable } from 'blecsd';
5986
+ *
5987
+ * const world = createWorld();
5988
+ * // ... create focusable entities with different tab indices ...
5989
+ *
5990
+ * const focusables = queryFocusable(world);
5991
+ * const enabled = filterFocusable(world, focusables);
5992
+ * const tabOrder = sortByTabIndex(world, enabled);
5993
+ *
5994
+ * // tabOrder is now in correct focus navigation order
5995
+ * ```
5996
+ */
5997
+ declare function sortByTabIndex(_world: World, entities: readonly Entity[]): Entity[];
5998
+ /**
5999
+ * Sorts entities by depth in the hierarchy (ancestors first).
6000
+ *
6001
+ * Useful for processing entities in parent-to-child order.
6002
+ *
6003
+ * @param world - The ECS world
6004
+ * @param entities - Array of entity IDs to sort
6005
+ * @returns New array of entity IDs sorted by depth (ascending)
6006
+ *
6007
+ * @example
6008
+ * ```typescript
6009
+ * import { createWorld, queryHierarchy, sortByDepth } from 'blecsd';
6010
+ *
6011
+ * const world = createWorld();
6012
+ * // ... create hierarchical entities ...
6013
+ *
6014
+ * const hierarchical = queryHierarchy(world);
6015
+ * const topDown = sortByDepth(world, hierarchical);
6016
+ *
6017
+ * for (const eid of topDown) {
6018
+ * // Process parent before children
6019
+ * }
6020
+ * ```
6021
+ */
6022
+ declare function sortByDepth(_world: World, entities: readonly Entity[]): Entity[];
6023
+
6024
+ /**
6025
+ * Reactive effect system for blECSd.
6026
+ * Effects automatically re-run when their dependencies change.
6027
+ *
6028
+ * @module core/reactiveEffects
6029
+ */
6030
+
6031
+ /**
6032
+ * Effect handle for disposal.
6033
+ */
6034
+ interface EffectHandle {
6035
+ readonly dispose: () => void;
6036
+ }
6037
+ /**
6038
+ * Effect function that may optionally return a cleanup function.
6039
+ * The function can return nothing (void/undefined) or a cleanup function.
6040
+ */
6041
+ type EffectFunction = () => unknown;
6042
+ /**
6043
+ * Creates an immediate effect that runs when dependencies change.
6044
+ * The effect runs immediately on creation and re-runs whenever any signal
6045
+ * read inside the effect changes.
6046
+ *
6047
+ * @param fn - Effect function to run
6048
+ * @returns Effect handle for disposal
6049
+ *
6050
+ * @example
6051
+ * ```typescript
6052
+ * import { createSignal, createEffect } from 'blecsd';
6053
+ *
6054
+ * const [count, setCount] = createSignal(0);
6055
+ *
6056
+ * const effect = createEffect(() => {
6057
+ * console.log(`Count is now: ${count()}`);
6058
+ * return () => console.log('Cleaning up effect');
6059
+ * });
6060
+ *
6061
+ * setCount(5); // Logs: "Cleaning up effect", then "Count is now: 5"
6062
+ *
6063
+ * effect.dispose(); // Stop the effect
6064
+ * ```
6065
+ */
6066
+ declare function createEffect(fn: EffectFunction): EffectHandle;
6067
+ /**
6068
+ * Creates a scheduled effect that defers execution to a specific ECS loop phase.
6069
+ * Unlike immediate effects, scheduled effects batch their updates and run during
6070
+ * the specified phase of the game loop.
6071
+ *
6072
+ * @param fn - Effect function to run
6073
+ * @param phase - Loop phase to run in (default: LATE_UPDATE)
6074
+ * @returns Effect handle for disposal
6075
+ *
6076
+ * @example
6077
+ * ```typescript
6078
+ * import { createSignal, createScheduledEffect, LoopPhase } from 'blecsd';
6079
+ *
6080
+ * const [position, setPosition] = createSignal({ x: 0, y: 0 });
6081
+ *
6082
+ * // Effect runs during LAYOUT phase
6083
+ * const effect = createScheduledEffect(() => {
6084
+ * const pos = position();
6085
+ * console.log(`Position updated: ${pos.x}, ${pos.y}`);
6086
+ * }, LoopPhase.LAYOUT);
6087
+ *
6088
+ * setPosition({ x: 10, y: 20 }); // Marked dirty, waits for LAYOUT phase
6089
+ *
6090
+ * effect.dispose(); // Stop the effect
6091
+ * ```
6092
+ */
6093
+ declare function createScheduledEffect(fn: EffectFunction, phase?: LoopPhase): EffectHandle;
6094
+ /**
6095
+ * Disposes an effect, stopping it from running and cleaning up.
6096
+ * This is a convenience function equivalent to calling effect.dispose().
6097
+ *
6098
+ * @param handle - Effect handle to dispose
6099
+ *
6100
+ * @example
6101
+ * ```typescript
6102
+ * import { createEffect, disposeEffect } from 'blecsd';
6103
+ *
6104
+ * const effect = createEffect(() => {
6105
+ * console.log('Effect running');
6106
+ * });
6107
+ *
6108
+ * disposeEffect(effect); // Same as effect.dispose()
6109
+ * ```
6110
+ */
6111
+ declare function disposeEffect(handle: EffectHandle): void;
6112
+ /**
6113
+ * Flushes all scheduled effects for a specific phase.
6114
+ * This is called by the reactive system during each loop phase.
6115
+ *
6116
+ * @param phase - Loop phase to flush effects for
6117
+ * @internal
6118
+ */
6119
+ declare function flushScheduledEffects(phase: LoopPhase): void;
6120
+ /**
6121
+ * Gets the count of scheduled effects for a specific phase.
6122
+ * Useful for debugging and testing.
6123
+ *
6124
+ * @param phase - Loop phase to count effects for
6125
+ * @returns Number of scheduled effects in the phase
6126
+ *
6127
+ * @example
6128
+ * ```typescript
6129
+ * import { createScheduledEffect, getScheduledEffectCount, LoopPhase } from 'blecsd';
6130
+ *
6131
+ * createScheduledEffect(() => {}, LoopPhase.UPDATE);
6132
+ * createScheduledEffect(() => {}, LoopPhase.UPDATE);
6133
+ *
6134
+ * console.log(getScheduledEffectCount(LoopPhase.UPDATE)); // 2
6135
+ * ```
6136
+ */
6137
+ declare function getScheduledEffectCount(phase: LoopPhase): number;
6138
+ /**
6139
+ * Gets the total count of all scheduled effects across all phases.
6140
+ *
6141
+ * @returns Total number of scheduled effects
6142
+ *
6143
+ * @example
6144
+ * ```typescript
6145
+ * import { createScheduledEffect, getTotalScheduledEffectCount, LoopPhase } from 'blecsd';
6146
+ *
6147
+ * createScheduledEffect(() => {}, LoopPhase.UPDATE);
6148
+ * createScheduledEffect(() => {}, LoopPhase.RENDER);
6149
+ *
6150
+ * console.log(getTotalScheduledEffectCount()); // 2
6151
+ * ```
6152
+ */
6153
+ declare function getTotalScheduledEffectCount(): number;
6154
+
6155
+ /**
6156
+ * Reactive data source adapters for blECSd.
6157
+ * Provides signal-based wrappers for common data sources like intervals, timers, and reducers.
6158
+ *
6159
+ * @module core/reactiveSource
6160
+ */
6161
+
6162
+ /**
6163
+ * Dispose function for reactive sources.
6164
+ */
6165
+ type Dispose = () => void;
6166
+ /**
6167
+ * Creates a signal that updates at a regular interval.
6168
+ * The signal value is computed by calling the provided function.
6169
+ *
6170
+ * @param fn - Function to call on each interval
6171
+ * @param intervalMs - Interval in milliseconds
6172
+ * @returns Tuple of [getter, dispose]
6173
+ *
6174
+ * @example
6175
+ * ```typescript
6176
+ * import { createIntervalSignal } from 'blecsd';
6177
+ *
6178
+ * const [time, dispose] = createIntervalSignal(() => Date.now(), 1000);
6179
+ *
6180
+ * console.log(time()); // Current timestamp
6181
+ * // ... wait 1 second ...
6182
+ * console.log(time()); // Updated timestamp
6183
+ *
6184
+ * dispose(); // Stop the interval
6185
+ * ```
6186
+ */
6187
+ declare function createIntervalSignal<T>(fn: () => T, intervalMs: number): [SignalGetter<T>, Dispose];
6188
+ /**
6189
+ * Creates a signal that tracks elapsed time as a progress value from 0 to 1.
6190
+ * Useful for animations, countdowns, and timed transitions.
6191
+ *
6192
+ * @param durationMs - Duration in milliseconds
6193
+ * @returns Tuple of [progress getter (0-1), dispose]
6194
+ *
6195
+ * @example
6196
+ * ```typescript
6197
+ * import { createTimerSignal } from 'blecsd';
6198
+ *
6199
+ * const [progress, dispose] = createTimerSignal(5000); // 5 second timer
6200
+ *
6201
+ * console.log(progress()); // 0.0
6202
+ * // ... wait 2.5 seconds ...
6203
+ * console.log(progress()); // ~0.5
6204
+ * // ... wait 2.5 more seconds ...
6205
+ * console.log(progress()); // 1.0
6206
+ *
6207
+ * dispose(); // Stop the timer
6208
+ * ```
6209
+ */
6210
+ declare function createTimerSignal(durationMs: number): [SignalGetter<number>, Dispose];
6211
+ /**
6212
+ * Dispatch function for reducer signal.
6213
+ */
6214
+ type Dispatch<A> = (action: A) => void;
6215
+ /**
6216
+ * Reducer function type.
6217
+ */
6218
+ type Reducer<T, A> = (state: T, action: A) => T;
6219
+ /**
6220
+ * Creates a signal with a reducer-style update pattern.
6221
+ * Similar to React's useReducer, but as a reactive signal.
6222
+ *
6223
+ * @param reducer - Reducer function (state, action) => newState
6224
+ * @param initial - Initial state value
6225
+ * @returns Tuple of [getter, dispatch]
6226
+ *
6227
+ * @example
6228
+ * ```typescript
6229
+ * import { createReducerSignal } from 'blecsd';
5083
6230
  *
5084
- * const world = createWorld();
5085
- * const root = createBoxEntity(world, { x: 0, y: 0 });
5086
- * const child = createBoxEntity(world, { parent: root, x: 1, y: 1 });
5087
- * const grandchild = createBoxEntity(world, { parent: child, x: 2, y: 2 });
6231
+ * type CounterAction = { type: 'increment' } | { type: 'decrement' } | { type: 'reset' };
5088
6232
  *
5089
- * const descendants = getDescendantEntities(world, root);
5090
- * // descendants = [child, grandchild]
6233
+ * const [count, dispatch] = createReducerSignal<number, CounterAction>(
6234
+ * (state, action) => {
6235
+ * switch (action.type) {
6236
+ * case 'increment': return state + 1;
6237
+ * case 'decrement': return state - 1;
6238
+ * case 'reset': return 0;
6239
+ * }
6240
+ * },
6241
+ * 0
6242
+ * );
6243
+ *
6244
+ * console.log(count()); // 0
6245
+ * dispatch({ type: 'increment' });
6246
+ * console.log(count()); // 1
6247
+ * dispatch({ type: 'increment' });
6248
+ * console.log(count()); // 2
6249
+ * dispatch({ type: 'reset' });
6250
+ * console.log(count()); // 0
5091
6251
  * ```
5092
6252
  */
5093
- declare function getDescendantEntities(world: World, parent: Entity): Entity[];
6253
+ declare function createReducerSignal<T, A>(reducer: Reducer<T, A>, initial: T): [SignalGetter<T>, Dispatch<A>];
5094
6254
  /**
5095
- * Gets all root entities (entities with no parent).
5096
- *
5097
- * Filters queryHierarchy to entities where parent === 0 (NULL_ENTITY).
6255
+ * Combiner function type for derived signals.
6256
+ */
6257
+ type Combiner<T, R> = (...values: T[]) => R;
6258
+ /**
6259
+ * Creates a derived signal that combines multiple signal values.
6260
+ * The combiner function is called whenever any input signal changes.
5098
6261
  *
5099
- * @param world - The ECS world
5100
- * @returns Array of root entity IDs
6262
+ * @param signals - Array of signal getters to combine
6263
+ * @param combiner - Function to combine signal values
6264
+ * @returns Computed signal with combined value
5101
6265
  *
5102
6266
  * @example
5103
6267
  * ```typescript
5104
- * import { createWorld, createBoxEntity, getRootEntities } from 'blecsd';
6268
+ * import { createSignal, createDerivedSignal } from 'blecsd';
5105
6269
  *
5106
- * const world = createWorld();
5107
- * const root1 = createBoxEntity(world, { x: 0, y: 0 });
5108
- * const root2 = createBoxEntity(world, { x: 10, y: 0 });
5109
- * const child = createBoxEntity(world, { parent: root1, x: 1, y: 1 });
6270
+ * const [firstName, setFirstName] = createSignal('John');
6271
+ * const [lastName, setLastName] = createSignal('Doe');
5110
6272
  *
5111
- * const roots = getRootEntities(world);
5112
- * // roots = [root1, root2]
6273
+ * const fullName = createDerivedSignal(
6274
+ * [firstName, lastName],
6275
+ * (first, last) => `${first} ${last}`
6276
+ * );
6277
+ *
6278
+ * console.log(fullName()); // "John Doe"
6279
+ * setFirstName('Jane');
6280
+ * console.log(fullName()); // "Jane Doe"
5113
6281
  * ```
5114
6282
  */
5115
- declare function getRootEntities(world: World): Entity[];
6283
+ declare function createDerivedSignal<T, R>(signals: ReadonlyArray<SignalGetter<T>>, combiner: Combiner<T, R>): ComputedSignal<R>;
5116
6284
  /**
5117
- * Filters entities to only those that are focusable and enabled.
5118
- *
5119
- * Returns entities where Focusable.focusable === 1.
6285
+ * Minimal interface for a readable stream.
6286
+ */
6287
+ interface ReadableStream {
6288
+ on(event: string, listener: (...args: unknown[]) => void): void;
6289
+ removeListener(event: string, listener: (...args: unknown[]) => void): void;
6290
+ destroy?(): void;
6291
+ }
6292
+ /**
6293
+ * Options for stream signal creation.
6294
+ */
6295
+ interface StreamSignalOptions<T> {
6296
+ /**
6297
+ * Transform function to convert Buffer chunks to signal values.
6298
+ * Defaults to converting Buffer to string.
6299
+ */
6300
+ transform?: (chunk: Buffer) => T;
6301
+ /**
6302
+ * Initial value for the signal.
6303
+ */
6304
+ initialValue: T;
6305
+ /**
6306
+ * Whether to destroy the stream on dispose. Defaults to false.
6307
+ */
6308
+ destroyOnDispose?: boolean;
6309
+ }
6310
+ /**
6311
+ * Creates a signal from a Node.js Readable stream.
6312
+ * Updates on each 'data' event with the transformed chunk value.
5120
6313
  *
5121
- * @param world - The ECS world
5122
- * @param entities - Array of entity IDs to filter
5123
- * @returns Filtered array of focusable entity IDs
6314
+ * @param readable - A readable stream (Node.js Readable or compatible)
6315
+ * @param options - Stream signal options
6316
+ * @returns Tuple of [getter, dispose]
5124
6317
  *
5125
6318
  * @example
5126
6319
  * ```typescript
5127
- * import { createWorld, queryFocusable, filterFocusable } from 'blecsd';
6320
+ * import { createStreamSignal } from 'blecsd';
6321
+ * import { createReadStream } from 'fs';
5128
6322
  *
5129
- * const world = createWorld();
5130
- * // ... create focusable entities, some disabled ...
6323
+ * const stream = createReadStream('./data.txt', { encoding: 'utf8' });
6324
+ * const [data, dispose] = createStreamSignal(stream, {
6325
+ * initialValue: '',
6326
+ * transform: (chunk) => chunk.toString('utf8')
6327
+ * });
5131
6328
  *
5132
- * const allFocusables = queryFocusable(world);
5133
- * const enabledFocusables = filterFocusable(world, allFocusables);
6329
+ * console.log(data()); // Latest chunk from stream
5134
6330
  *
5135
- * for (const eid of enabledFocusables) {
5136
- * // Process only enabled focusable entities
5137
- * }
6331
+ * dispose(); // Clean up listeners
5138
6332
  * ```
5139
6333
  */
5140
- declare function filterFocusable(_world: World, entities: readonly Entity[]): Entity[];
6334
+ declare function createStreamSignal<T>(readable: ReadableStream, options: StreamSignalOptions<T>): [SignalGetter<T>, Dispose];
5141
6335
  /**
5142
- * Filters entities to only those that are clickable.
5143
- *
5144
- * Returns entities where Interactive.clickable === 1.
6336
+ * Subscribe function type for callback signal.
6337
+ * Takes a callback and returns an unsubscribe function.
6338
+ */
6339
+ type Subscribe<T> = (callback: (value: T) => void) => () => void;
6340
+ /**
6341
+ * Creates a signal from a generic subscribe/unsubscribe pattern.
6342
+ * This is a universal adapter for WebSocket, EventEmitter, and other callback-based sources.
5145
6343
  *
5146
- * @param world - The ECS world
5147
- * @param entities - Array of entity IDs to filter
5148
- * @returns Filtered array of clickable entity IDs
6344
+ * @param subscribe - Function that takes a callback and returns an unsubscribe function
6345
+ * @param initialValue - Initial value for the signal
6346
+ * @returns Tuple of [getter, dispose]
5149
6347
  *
5150
6348
  * @example
5151
6349
  * ```typescript
5152
- * import { createWorld, queryInteractive, filterClickable } from 'blecsd';
6350
+ * import { createCallbackSignal } from 'blecsd';
5153
6351
  *
5154
- * const world = createWorld();
5155
- * // ... create interactive entities ...
6352
+ * // WebSocket example
6353
+ * const ws = new WebSocket('ws://localhost:8080');
6354
+ * const [message, dispose] = createCallbackSignal<string>(
6355
+ * (callback) => {
6356
+ * const handler = (event: MessageEvent) => callback(event.data);
6357
+ * ws.addEventListener('message', handler);
6358
+ * return () => ws.removeEventListener('message', handler);
6359
+ * },
6360
+ * ''
6361
+ * );
5156
6362
  *
5157
- * const allInteractives = queryInteractive(world);
5158
- * const clickables = filterClickable(world, allInteractives);
6363
+ * console.log(message()); // Latest WebSocket message
5159
6364
  *
5160
- * for (const eid of clickables) {
5161
- * // Process only clickable entities
5162
- * }
6365
+ * dispose(); // Unsubscribe
5163
6366
  * ```
5164
6367
  */
5165
- declare function filterClickable(_world: World, entities: readonly Entity[]): Entity[];
6368
+ declare function createCallbackSignal<T>(subscribe: Subscribe<T>, initialValue: T): [SignalGetter<T>, Dispose];
5166
6369
  /**
5167
- * Sorts entities by tab index for focus navigation.
5168
- *
5169
- * Entities with lower tabIndex are focused first. Entities with tabIndex 0
5170
- * follow DOM-like ordering.
6370
+ * Creates a signal that polls an async function at regular intervals.
6371
+ * Handles promises and errors gracefully.
5171
6372
  *
5172
- * @param world - The ECS world
5173
- * @param entities - Array of entity IDs to sort
5174
- * @returns New array of entity IDs sorted by tab index
6373
+ * @param fn - Async function to poll
6374
+ * @param intervalMs - Polling interval in milliseconds
6375
+ * @param initialValue - Initial value for the signal
6376
+ * @returns Tuple of [getter, dispose]
5175
6377
  *
5176
6378
  * @example
5177
6379
  * ```typescript
5178
- * import { createWorld, queryFocusable, sortByTabIndex, filterFocusable } from 'blecsd';
6380
+ * import { createPollingSignal } from 'blecsd';
5179
6381
  *
5180
- * const world = createWorld();
5181
- * // ... create focusable entities with different tab indices ...
6382
+ * const [apiData, dispose] = createPollingSignal(
6383
+ * async () => {
6384
+ * const response = await fetch('https://api.example.com/status');
6385
+ * return response.json();
6386
+ * },
6387
+ * 5000, // Poll every 5 seconds
6388
+ * { status: 'unknown' }
6389
+ * );
5182
6390
  *
5183
- * const focusables = queryFocusable(world);
5184
- * const enabled = filterFocusable(world, focusables);
5185
- * const tabOrder = sortByTabIndex(world, enabled);
6391
+ * console.log(apiData()); // Latest API response
5186
6392
  *
5187
- * // tabOrder is now in correct focus navigation order
6393
+ * dispose(); // Stop polling
5188
6394
  * ```
5189
6395
  */
5190
- declare function sortByTabIndex(_world: World, entities: readonly Entity[]): Entity[];
6396
+ declare function createPollingSignal<T>(fn: () => Promise<T>, intervalMs: number, initialValue: T): [SignalGetter<T>, Dispose];
5191
6397
  /**
5192
- * Sorts entities by depth in the hierarchy (ancestors first).
5193
- *
5194
- * Useful for processing entities in parent-to-child order.
6398
+ * Minimal interface for an event emitter.
6399
+ */
6400
+ interface EventEmitter {
6401
+ on(eventName: string, listener: (...args: unknown[]) => void): void;
6402
+ removeListener(eventName: string, listener: (...args: unknown[]) => void): void;
6403
+ }
6404
+ /**
6405
+ * Creates a signal from a Node.js EventEmitter event.
6406
+ * Updates whenever the specified event is emitted.
5195
6407
  *
5196
- * @param world - The ECS world
5197
- * @param entities - Array of entity IDs to sort
5198
- * @returns New array of entity IDs sorted by depth (ascending)
6408
+ * @param emitter - Event emitter instance
6409
+ * @param eventName - Name of the event to listen to
6410
+ * @param initialValue - Initial value for the signal (optional)
6411
+ * @returns Tuple of [getter, dispose]
5199
6412
  *
5200
6413
  * @example
5201
6414
  * ```typescript
5202
- * import { createWorld, queryHierarchy, sortByDepth } from 'blecsd';
6415
+ * import { createEventSignal } from 'blecsd';
6416
+ * import { EventEmitter } from 'events';
5203
6417
  *
5204
- * const world = createWorld();
5205
- * // ... create hierarchical entities ...
6418
+ * const emitter = new EventEmitter();
6419
+ * const [message, dispose] = createEventSignal<string>(emitter, 'message', '');
5206
6420
  *
5207
- * const hierarchical = queryHierarchy(world);
5208
- * const topDown = sortByDepth(world, hierarchical);
6421
+ * emitter.emit('message', 'Hello');
6422
+ * console.log(message()); // "Hello"
5209
6423
  *
5210
- * for (const eid of topDown) {
5211
- * // Process parent before children
5212
- * }
6424
+ * emitter.emit('message', 'World');
6425
+ * console.log(message()); // "World"
6426
+ *
6427
+ * dispose(); // Remove listener
5213
6428
  * ```
5214
6429
  */
5215
- declare function sortByDepth(_world: World, entities: readonly Entity[]): Entity[];
6430
+ declare function createEventSignal<T>(emitter: EventEmitter, eventName: string, initialValue?: T): [SignalGetter<T | undefined>, Dispose];
5216
6431
 
5217
6432
  /**
5218
6433
  * Scene management for game screens.
@@ -5426,283 +6641,156 @@ declare function createSlideTransition(duration: number, direction: 'left' | 'ri
5426
6641
  declare function createSceneSystem(sceneManager: SceneManager, getDelta: () => number): System;
5427
6642
 
5428
6643
  /**
5429
- * ECS world state serialization and deserialization.
5430
- *
5431
- * Provides functions to serialize ECS world state to JSON and restore it,
5432
- * supporting component data, entity relationships, and custom serializers
5433
- * for complex (non-typed-array) data.
5434
- *
6644
+ * ECS world state serialization with delta compression
5435
6645
  * @module core/serialization
5436
6646
  */
5437
6647
 
5438
6648
  /**
5439
- * A registered component descriptor for serialization.
5440
- */
5441
- interface ComponentDescriptor {
5442
- /** Unique name for the component (used as key in serialized data) */
5443
- readonly name: string;
5444
- /** The component store object (SoA typed arrays) */
5445
- readonly store: Record<string, any>;
5446
- /** Optional custom serializer for non-typed-array data */
5447
- readonly serialize?: (eid: Entity) => unknown;
5448
- /** Optional custom deserializer for non-typed-array data */
5449
- readonly deserialize?: (eid: Entity, data: unknown) => void;
5450
- }
5451
- /**
5452
- * Serialized entity data.
6649
+ * Component field data - maps field names to their value arrays
5453
6650
  */
5454
- interface SerializedEntity {
5455
- /** Original entity ID (for relationship mapping) */
5456
- readonly id: number;
5457
- /** Map of component name to serialized component data */
5458
- readonly components: Record<string, SerializedComponentData>;
6651
+ interface ComponentFieldData {
6652
+ readonly [fieldName: string]: readonly number[];
5459
6653
  }
5460
6654
  /**
5461
- * Serialized component data for a single entity.
6655
+ * Serialized component data
5462
6656
  */
5463
- interface SerializedComponentData {
5464
- /** Typed array field values (field name -> value) */
5465
- readonly fields: Record<string, number>;
5466
- /** Optional custom data from serialize callback */
5467
- readonly custom?: unknown;
6657
+ interface ComponentData {
6658
+ readonly name: string;
6659
+ readonly entities: readonly number[];
6660
+ readonly values: ComponentFieldData;
5468
6661
  }
5469
6662
  /**
5470
- * Complete serialized world snapshot.
6663
+ * Complete world snapshot
5471
6664
  */
5472
- interface SerializedWorld {
5473
- /** Version for forward compatibility */
6665
+ interface WorldSnapshot {
5474
6666
  readonly version: number;
5475
- /** Timestamp of serialization */
5476
6667
  readonly timestamp: number;
5477
- /** Array of serialized entities */
5478
- readonly entities: readonly SerializedEntity[];
5479
- /** Optional metadata attached by user */
5480
- readonly metadata?: Record<string, unknown> | undefined;
5481
- }
5482
- /**
5483
- * Options for serialization.
5484
- */
5485
- interface SerializeOptions {
5486
- /** Only serialize entities with these IDs (default: all entities) */
5487
- readonly entityFilter?: readonly Entity[] | undefined;
5488
- /** Only serialize these components by name (default: all registered) */
5489
- readonly componentFilter?: readonly string[] | undefined;
5490
- /** Metadata to include in the snapshot */
5491
- readonly metadata?: Record<string, unknown> | undefined;
6668
+ readonly entityCount: number;
6669
+ readonly components: readonly ComponentData[];
5492
6670
  }
5493
6671
  /**
5494
- * Options for deserialization.
6672
+ * Delta between two world snapshots
5495
6673
  */
5496
- interface DeserializeOptions {
5497
- /** If true, clear the world before loading (default: false) */
5498
- readonly clearWorld?: boolean | undefined;
5499
- /** If true, create a new world instead of modifying the given one (default: false) */
5500
- readonly createNew?: boolean | undefined;
6674
+ interface WorldDelta {
6675
+ readonly baseTimestamp: number;
6676
+ readonly timestamp: number;
6677
+ readonly addedEntities: readonly number[];
6678
+ readonly removedEntities: readonly number[];
6679
+ readonly changedComponents: readonly ComponentData[];
5501
6680
  }
5502
6681
  /**
5503
- * Result of deserialization.
6682
+ * Component registration for serialization
5504
6683
  */
5505
- interface DeserializeResult {
5506
- /** The world that was deserialized into */
5507
- readonly world: World;
5508
- /** Map from old entity IDs to new entity IDs */
5509
- readonly entityMap: ReadonlyMap<number, Entity>;
5510
- /** Count of entities restored */
5511
- readonly entityCount: number;
5512
- /** Count of components restored */
5513
- readonly componentCount: number;
6684
+ interface ComponentRegistration {
6685
+ readonly name: string;
6686
+ readonly component: any;
6687
+ readonly fields: readonly string[];
5514
6688
  }
5515
6689
  /**
5516
- * Registers a component for serialization.
5517
- *
5518
- * Components must be registered before they can be serialized or deserialized.
5519
- * The descriptor tells the serializer how to read/write the component's data.
5520
- *
5521
- * @param descriptor - Component descriptor with name, store, and optional custom serializers
5522
- *
5523
- * @example
5524
- * ```typescript
5525
- * import { registerSerializable, Position } from 'blecsd';
5526
- *
5527
- * registerSerializable({
5528
- * name: 'Position',
5529
- * store: Position,
5530
- * });
5531
- * ```
5532
- */
5533
- declare function registerSerializable(descriptor: ComponentDescriptor): void;
5534
- /**
5535
- * Unregisters a component from serialization.
5536
- *
5537
- * @param name - The component name to unregister
5538
- * @returns True if the component was found and removed
5539
- *
5540
- * @example
5541
- * ```typescript
5542
- * import { unregisterSerializable } from 'blecsd';
5543
- *
5544
- * unregisterSerializable('Position');
5545
- * ```
5546
- */
5547
- declare function unregisterSerializable(name: string): boolean;
5548
- /**
5549
- * Gets a registered component descriptor by name.
6690
+ * Registers components for serialization.
6691
+ * Must be called before serializing/deserializing world state.
5550
6692
  *
5551
- * @param name - The component name
5552
- * @returns The descriptor, or undefined if not registered
6693
+ * @param registrations - Array of component registrations
5553
6694
  *
5554
6695
  * @example
5555
6696
  * ```typescript
5556
- * import { getSerializable } from 'blecsd';
6697
+ * import { registerComponents, Position, Velocity } from 'blecsd';
5557
6698
  *
5558
- * const desc = getSerializable('Position');
5559
- * if (desc) {
5560
- * console.log(Object.keys(desc.store)); // ['x', 'y', 'z', 'absolute']
5561
- * }
6699
+ * registerComponents([
6700
+ * { name: 'Position', component: Position, fields: ['x', 'y', 'z', 'absolute'] },
6701
+ * { name: 'Velocity', component: Velocity, fields: ['x', 'y', 'maxSpeed', 'friction'] },
6702
+ * ]);
5562
6703
  * ```
5563
6704
  */
5564
- declare function getSerializable(name: string): ComponentDescriptor | undefined;
6705
+ declare function registerComponents(registrations: readonly ComponentRegistration[]): void;
5565
6706
  /**
5566
- * Gets all registered component names.
6707
+ * Gets the currently registered components.
5567
6708
  *
5568
- * @returns Array of registered component names
6709
+ * @returns Array of component registrations
5569
6710
  *
5570
6711
  * @example
5571
6712
  * ```typescript
5572
6713
  * import { getRegisteredComponents } from 'blecsd';
5573
6714
  *
5574
- * const names = getRegisteredComponents();
5575
- * console.log(names); // ['Position', 'Velocity', 'Renderable']
5576
- * ```
5577
- */
5578
- declare function getRegisteredComponents(): readonly string[];
5579
- /**
5580
- * Clears all registered serializable components.
5581
- * Useful for testing.
5582
- *
5583
- * @example
5584
- * ```typescript
5585
- * import { clearSerializableRegistry } from 'blecsd';
5586
- *
5587
- * clearSerializableRegistry();
5588
- * ```
5589
- */
5590
- declare function clearSerializableRegistry(): void;
5591
- /**
5592
- * Serializes ECS world state to a snapshot object.
5593
- *
5594
- * Only components registered via `registerSerializable` are included.
5595
- * The snapshot can be converted to JSON with `JSON.stringify`.
5596
- *
5597
- * @param world - The world to serialize
5598
- * @param options - Optional serialization options
5599
- * @returns A serialized world snapshot
5600
- *
5601
- * @example
5602
- * ```typescript
5603
- * import { createWorld, addEntity, setPosition, serializeWorld, registerSerializable, Position } from 'blecsd';
5604
- *
5605
- * registerSerializable({ name: 'Position', store: Position });
5606
- *
5607
- * const world = createWorld();
5608
- * const eid = addEntity(world);
5609
- * setPosition(world, eid, 10, 20);
5610
- *
5611
- * const snapshot = serializeWorld(world);
5612
- * const json = JSON.stringify(snapshot);
6715
+ * const components = getRegisteredComponents();
6716
+ * console.log(components.map(c => c.name));
5613
6717
  * ```
5614
6718
  */
5615
- declare function serializeWorld(world: World, options?: SerializeOptions): SerializedWorld;
6719
+ declare function getRegisteredComponents(): readonly ComponentRegistration[];
5616
6720
  /**
5617
- * Serializes ECS world state to a JSON string.
6721
+ * Serializes a world to a snapshot.
5618
6722
  *
5619
- * Convenience wrapper around `serializeWorld` + `JSON.stringify`.
5620
- *
5621
- * @param world - The world to serialize
5622
- * @param options - Optional serialization options
5623
- * @returns JSON string of the world state
6723
+ * @param world - The ECS world to serialize
6724
+ * @param components - Array of component registrations to serialize
6725
+ * @returns World snapshot
5624
6726
  *
5625
6727
  * @example
5626
6728
  * ```typescript
5627
- * import { createWorld, addEntity, setPosition, serializeWorldToJSON, registerSerializable, Position } from 'blecsd';
5628
- *
5629
- * registerSerializable({ name: 'Position', store: Position });
6729
+ * import { createWorld, addEntity, setPosition, serializeWorld } from 'blecsd';
5630
6730
  *
5631
6731
  * const world = createWorld();
5632
- * const eid = addEntity(world);
5633
- * setPosition(world, eid, 10, 20);
6732
+ * const entity = addEntity(world);
6733
+ * setPosition(world, entity, 10, 5);
5634
6734
  *
5635
- * const json = serializeWorldToJSON(world);
5636
- * // Save to file, send over network, etc.
6735
+ * const snapshot = serializeWorld(world, [
6736
+ * { name: 'Position', component: Position, fields: ['x', 'y', 'z', 'absolute'] },
6737
+ * ]);
5637
6738
  * ```
5638
6739
  */
5639
- declare function serializeWorldToJSON(world: World, options?: SerializeOptions): string;
6740
+ declare function serializeWorld(world: World, components: readonly ComponentRegistration[]): WorldSnapshot;
5640
6741
  /**
5641
- * Deserializes a world snapshot back into an ECS world.
5642
- *
5643
- * Creates new entities and restores their component data from the snapshot.
5644
- * Returns a mapping from old entity IDs to new ones for relationship fixup.
6742
+ * Deserializes a snapshot into a new world.
5645
6743
  *
5646
- * @param snapshot - The serialized world snapshot
5647
- * @param world - The world to deserialize into
5648
- * @param options - Optional deserialization options
5649
- * @returns Result with the world, entity map, and statistics
6744
+ * @param snapshot - World snapshot to deserialize
6745
+ * @returns A new world with the snapshot data
5650
6746
  *
5651
6747
  * @example
5652
6748
  * ```typescript
5653
- * import { createWorld, deserializeWorld, registerSerializable, Position } from 'blecsd';
5654
- *
5655
- * registerSerializable({ name: 'Position', store: Position });
5656
- *
5657
- * const world = createWorld();
5658
- * const result = deserializeWorld(snapshot, world);
6749
+ * import { deserializeWorld, getAllEntities } from 'blecsd';
5659
6750
  *
5660
- * console.log(result.entityCount); // number of entities restored
5661
- * console.log(result.entityMap); // Map<oldId, newId>
6751
+ * const world = deserializeWorld(snapshot);
6752
+ * const entities = getAllEntities(world);
6753
+ * console.log(`Restored ${entities.length} entities`);
5662
6754
  * ```
5663
6755
  */
5664
- declare function deserializeWorld(snapshot: SerializedWorld, world: World, options?: DeserializeOptions): DeserializeResult;
6756
+ declare function deserializeWorld(snapshot: WorldSnapshot): World;
5665
6757
  /**
5666
- * Deserializes a JSON string back into an ECS world.
6758
+ * Creates a delta between two snapshots.
5667
6759
  *
5668
- * Convenience wrapper around JSON.parse + `deserializeWorld`.
5669
- *
5670
- * @param json - The JSON string to parse
5671
- * @param world - The world to deserialize into
5672
- * @param options - Optional deserialization options
5673
- * @returns Result with the world, entity map, and statistics
6760
+ * @param prev - Previous snapshot
6761
+ * @param current - Current snapshot
6762
+ * @returns Delta containing changes between snapshots
5674
6763
  *
5675
6764
  * @example
5676
6765
  * ```typescript
5677
- * import { createWorld, deserializeWorldFromJSON, registerSerializable, Position } from 'blecsd';
5678
- *
5679
- * registerSerializable({ name: 'Position', store: Position });
6766
+ * import { serializeWorld, createWorldDelta } from 'blecsd';
5680
6767
  *
5681
- * const world = createWorld();
5682
- * const result = deserializeWorldFromJSON(jsonString, world);
6768
+ * const snapshot1 = serializeWorld(world, components);
6769
+ * // ... modify world ...
6770
+ * const snapshot2 = serializeWorld(world, components);
5683
6771
  *
5684
- * console.log(result.entityCount);
6772
+ * const delta = createWorldDelta(snapshot1, snapshot2);
6773
+ * console.log(`Added: ${delta.addedEntities.length}, Removed: ${delta.removedEntities.length}`);
5685
6774
  * ```
5686
6775
  */
5687
- declare function deserializeWorldFromJSON(json: string, world: World, options?: DeserializeOptions): DeserializeResult;
6776
+ declare function createWorldDelta(prev: WorldSnapshot, current: WorldSnapshot): WorldDelta;
5688
6777
  /**
5689
- * Creates a deep clone of a serialized world snapshot.
5690
- * Useful for creating save slots or undo history.
6778
+ * Applies a delta to a world.
5691
6779
  *
5692
- * @param snapshot - The snapshot to clone
5693
- * @returns A new snapshot with identical data
6780
+ * @param world - The world to apply the delta to
6781
+ * @param delta - The delta to apply
6782
+ * @param components - Component registrations
6783
+ * @returns The modified world
5694
6784
  *
5695
6785
  * @example
5696
6786
  * ```typescript
5697
- * import { serializeWorld, cloneSnapshot } from 'blecsd';
6787
+ * import { applyWorldDelta } from 'blecsd';
5698
6788
  *
5699
- * const snapshot = serializeWorld(world);
5700
- * const backup = cloneSnapshot(snapshot);
6789
+ * const world = deserializeWorld(baseSnapshot);
6790
+ * applyWorldDelta(world, delta, components);
5701
6791
  * ```
5702
6792
  */
5703
- declare function cloneSnapshot(snapshot: SerializedWorld): SerializedWorld;
5704
- /** Current serialization format version */
5705
- declare const SERIALIZATION_VERSION = 1;
6793
+ declare function applyWorldDelta(world: World, delta: WorldDelta, components: readonly ComponentRegistration[]): World;
5706
6794
 
5707
6795
  /**
5708
6796
  * Shrink-to-content calculation utilities.
@@ -6367,11 +7455,41 @@ declare function getComputedStyles(world: World, entities: readonly Entity[]): M
6367
7455
  */
6368
7456
 
6369
7457
  /**
6370
- * Error thrown when entity validation fails.
7458
+ * Creates an entity validation error with the given message.
7459
+ *
7460
+ * @param message - The error message
7461
+ * @returns An Error with name set to 'EntityValidationError'
7462
+ *
7463
+ * @example
7464
+ * ```typescript
7465
+ * import { createEntityValidationError, isEntityValidationError } from 'blecsd';
7466
+ *
7467
+ * const error = createEntityValidationError('Entity 5 is missing Position');
7468
+ * console.log(error.name); // 'EntityValidationError'
7469
+ * console.log(isEntityValidationError(error)); // true
7470
+ * ```
6371
7471
  */
6372
- declare class EntityValidationError extends Error {
6373
- constructor(message: string);
6374
- }
7472
+ declare function createEntityValidationError(message: string): Error;
7473
+ /**
7474
+ * Type guard to check if an error is an entity validation error.
7475
+ *
7476
+ * @param error - The value to check
7477
+ * @returns True if the error is an EntityValidationError
7478
+ *
7479
+ * @example
7480
+ * ```typescript
7481
+ * import { isEntityValidationError } from 'blecsd';
7482
+ *
7483
+ * try {
7484
+ * validateEntity(world, eid, [Position], 'test');
7485
+ * } catch (error) {
7486
+ * if (isEntityValidationError(error)) {
7487
+ * console.log('Validation failed:', error.message);
7488
+ * }
7489
+ * }
7490
+ * ```
7491
+ */
7492
+ declare function isEntityValidationError(error: unknown): error is Error;
6375
7493
  /**
6376
7494
  * Registers a component name for better error messages.
6377
7495
  * This is optional but recommended for clearer validation errors.
@@ -6396,7 +7514,7 @@ declare function registerComponentName(component: ComponentRef, name: string): v
6396
7514
  * @param eid - The entity ID to validate
6397
7515
  * @param requiredComponents - Array of components the entity must have
6398
7516
  * @param context - Context string describing where this validation is happening (e.g., "layoutSystem", "createBox")
6399
- * @throws {EntityValidationError} If entity doesn't exist or is missing required components
7517
+ * @throws {Error} An EntityValidationError if entity doesn't exist or is missing required components
6400
7518
  *
6401
7519
  * @example
6402
7520
  * ```typescript
@@ -6816,6 +7934,8 @@ interface PackedQueryAdapterConfig {
6816
7934
  readonly queries: readonly PackedQueryRegistration[];
6817
7935
  /** Initial capacity for each PackedStore (default: 64) */
6818
7936
  readonly initialCapacity?: number;
7937
+ /** Synchronization strategy for scheduler integration (default: 'all') */
7938
+ readonly syncMode?: 'all' | 'render_only';
6819
7939
  }
6820
7940
  /**
6821
7941
  * Extended adapter backed by PackedStores for cache-friendly iteration.
@@ -6876,6 +7996,8 @@ interface PackedQueryAdapter extends WorldAdapter {
6876
7996
  * @returns Frozen array of query names
6877
7997
  */
6878
7998
  readonly getRegisteredQueries: () => readonly string[];
7999
+ /** Scheduler sync mode hint. */
8000
+ readonly syncMode: 'all' | 'render_only';
6879
8001
  }
6880
8002
  /**
6881
8003
  * Zod schema for PackedQueryRegistration.
@@ -6917,6 +8039,10 @@ declare const PackedQueryAdapterConfigSchema: z.ZodObject<{
6917
8039
  components: z.ZodArray<z.ZodUnknown>;
6918
8040
  }, z.core.$strip>>;
6919
8041
  initialCapacity: z.ZodOptional<z.ZodNumber>;
8042
+ syncMode: z.ZodOptional<z.ZodEnum<{
8043
+ all: "all";
8044
+ render_only: "render_only";
8045
+ }>>;
6920
8046
  }, z.core.$strip>;
6921
8047
  /**
6922
8048
  * Default adapter used when no custom adapter is registered.
@@ -6980,6 +8106,23 @@ declare function setWorldAdapter(world: World, adapter: WorldAdapter): void;
6980
8106
  * ```
6981
8107
  */
6982
8108
  declare function getWorldAdapter(world: World): WorldAdapter;
8109
+ /**
8110
+ * Synchronizes the registered world adapter if it is PackedStore-backed.
8111
+ *
8112
+ * Safe to call unconditionally each frame/system tick. For non-packed
8113
+ * adapters this is a no-op.
8114
+ *
8115
+ * @param world - The ECS world
8116
+ *
8117
+ * @example
8118
+ * ```typescript
8119
+ * import { syncWorldAdapter } from 'blecsd';
8120
+ *
8121
+ * // Call before systems that read adapter-backed query data
8122
+ * syncWorldAdapter(world);
8123
+ * ```
8124
+ */
8125
+ declare function syncWorldAdapter(world: World): void;
6983
8126
  /**
6984
8127
  * Clears any custom adapter for a world.
6985
8128
  *
@@ -7036,6 +8179,13 @@ declare function clearWorldAdapter(world: World): void;
7036
8179
  * ```
7037
8180
  */
7038
8181
  declare function createPackedQueryAdapter(config: PackedQueryAdapterConfig): PackedQueryAdapter;
8182
+ /**
8183
+ * Creates the default PackedQueryAdapter used for TUI acceleration.
8184
+ *
8185
+ * @param _initialCapacity - Deprecated, kept for API compatibility. No longer used.
8186
+ * @returns Packed query adapter with common TUI query registrations
8187
+ */
8188
+ declare function createDefaultPackedQueryAdapter(_initialCapacity?: number): PackedQueryAdapter;
7039
8189
  /**
7040
8190
  * Type guard to check if an adapter is a PackedQueryAdapter.
7041
8191
  *
@@ -7055,6 +8205,95 @@ declare function createPackedQueryAdapter(config: PackedQueryAdapterConfig): Pac
7055
8205
  */
7056
8206
  declare function isPackedQueryAdapter(adapter: WorldAdapter): adapter is PackedQueryAdapter;
7057
8207
 
8208
+ /**
8209
+ * WorldStore - Utilities for world-scoped storage
8210
+ *
8211
+ * Provides a pattern for storing entity-associated data on the world object
8212
+ * rather than in module-level Map singletons. This ensures:
8213
+ * - World isolation: Each world has its own data
8214
+ * - Memory safety: Data is cleaned up when world is destroyed
8215
+ * - Library-first: Users can create multiple worlds without conflicts
8216
+ * - Testability: No global state between tests
8217
+ *
8218
+ * @module core/worldStore
8219
+ */
8220
+
8221
+ /**
8222
+ * Gets or creates a world-scoped store.
8223
+ * Stores are Maps that live on the world object, not as module singletons.
8224
+ *
8225
+ * @param world - The ECS world
8226
+ * @param key - Unique string key for this store (e.g., 'select:options')
8227
+ * @returns A Map scoped to this world
8228
+ *
8229
+ * @example
8230
+ * ```typescript
8231
+ * // Instead of module-level singleton:
8232
+ * // const optionsStore = new Map<Entity, SelectOption[]>();
8233
+ *
8234
+ * // Use world-scoped store:
8235
+ * function getOptionsStore(world: World): Map<Entity, SelectOption[]> {
8236
+ * return getWorldStore(world, 'select:options');
8237
+ * }
8238
+ *
8239
+ * // Usage
8240
+ * const store = getOptionsStore(world);
8241
+ * store.set(entity, options);
8242
+ * ```
8243
+ */
8244
+ declare function getWorldStore<K, V>(world: World, key: string): Map<K, V>;
8245
+ /**
8246
+ * Gets or creates a world-scoped Set.
8247
+ *
8248
+ * @param world - The ECS world
8249
+ * @param key - Unique string key for this set
8250
+ * @returns A Set scoped to this world
8251
+ *
8252
+ * @example
8253
+ * ```typescript
8254
+ * function getActiveSpinners(world: World): Set<Entity> {
8255
+ * return getWorldSet(world, 'spinner:active');
8256
+ * }
8257
+ * ```
8258
+ */
8259
+ declare function getWorldSet<T>(world: World, key: string): Set<T>;
8260
+ /**
8261
+ * Cleans up all data for an entity across all world stores.
8262
+ * Call this when an entity is removed to prevent memory leaks.
8263
+ *
8264
+ * @param world - The ECS world
8265
+ * @param entity - The entity being removed
8266
+ *
8267
+ * @example
8268
+ * ```typescript
8269
+ * export function removeEntity(world: World, entity: Entity): void {
8270
+ * // ... remove from components ...
8271
+ * cleanupEntityStores(world, entity);
8272
+ * }
8273
+ * ```
8274
+ */
8275
+ declare function cleanupEntityStores(world: World, entity: Entity): void;
8276
+ /**
8277
+ * Destroys all stores for a world.
8278
+ * Call this when destroying a world to free memory.
8279
+ *
8280
+ * @param world - The ECS world
8281
+ *
8282
+ * @example
8283
+ * ```typescript
8284
+ * export function destroyWorld(world: World): void {
8285
+ * clearWorldStores(world);
8286
+ * // ... other cleanup ...
8287
+ * }
8288
+ * ```
8289
+ */
8290
+ declare function clearWorldStores(world: World): void;
8291
+ /**
8292
+ * Gets all store keys for debugging/inspection.
8293
+ * @internal
8294
+ */
8295
+ declare function getStoreKeys(world: World): string[];
8296
+
7058
8297
  /**
7059
8298
  * Z-order management for entity layering.
7060
8299
  *
@@ -7295,4 +8534,4 @@ declare function normalizeZIndices(world: World, parent: Entity): void;
7295
8534
  */
7296
8535
  declare function resetZOrder(entity: Entity): void;
7297
8536
 
7298
- export { type AbsolutePosition, type AdoptEvent, type ArchetypeDefinition, type ArchetypePoolConfig, type ArchetypePoolStats, type AttachEvent, type AutoPaddingData, BUILTIN_PHASE_NAMES, type BindingMatch, type BorderDockingContext, type BorderDockingOptions, type BorderEdge, type BorderStyleType, BoxConfig, type BubbleResult, type BubbleableEvent, type BubbleableEventOptions, ButtonConfig, type CachedPosition, CheckboxConfig, type CleanupCallback, type ClickableCache, type ClipRect, type ClipStack, Clipping, type ClippingData, type ClippingOptions, type ComponentDescriptor, type ComponentResetFn, type ComputedPositionData, type ConditionContext, type ConnectionFlags, DEFAULT_NAV_BINDINGS, DEFAULT_TEXT_BINDINGS, DEFAULT_WORLD_ADAPTER, DEFAULT_Z_INDEX, type DataValue, type DeprecatedAPIMetadata, DeprecatedAPIMetadataSchema, type DeserializeOptions, type DeserializeResult, type DestroyEvent, type DestroyOptions, type DetachEvent, type DirtyStats, type DirtyTrackerData, type DockingBuffer, type DockingCell, type DynamicValue, type EffectConfig, type EffectivePaddingData, type EffectsConfig, type EmitDescendantsOptions, EmitDescendantsOptionsSchema, type EmitDescendantsResult, EmitDescendantsResultSchema, Entity, type EntityDataMap, type EntityHandle, type EntityPool, EntityValidationError, EventBus, EventMap, FormConfig, GetEntityEventBus, type HitTestOptions, type HitTestResult, INHERITING_PROPERTIES, InitPriority, type InitPriorityLevel, type InnerDimensions, type InnerPosition, InputConfig, JUNCTION_ASCII, JUNCTION_BOLD, JUNCTION_DOUBLE, JUNCTION_SINGLE, type Junction, type JunctionCharset, type KeyBinding, type KeyBindingRegistry, KeyBindingSchema, KeyBindingsArraySchema, type KeyLockFilter, type KeyLockOptions, type KeyLockState, type LazyInitFn, type LazyValue, type DirtyRect as LegacyDirtyRect, type LifecycleEvent, type LifecycleEventMap, type LifecycleEventName, ListConfig, LoopPhase, MAX_Z_INDEX, MIN_Z_INDEX, NON_INHERITING_PROPERTIES, Overflow, type OverflowValue, type PackedQueryAdapter, type PackedQueryAdapterConfig, PackedQueryAdapterConfigSchema, type PackedQueryRegistration, PackedQueryRegistrationSchema, type ParsedKey, type PerformanceIssueMetadata, PerformanceIssueMetadataSchema, type PhaseId, type PhaseManager, PositionCache, type PositionValue, PositionValueSchema, ProgressBarConfig, RadioButtonConfig, RadioSetConfig, type RecyclingSystemStats, type RelativePosition, type RemoveEvent, type ReparentEvent, type ResolvedEffect, SERIALIZATION_VERSION, type Scene, type SceneManager, type SceneTransition, ScreenConfig, SelectConfig, type SerializeOptions, type SerializedComponentData, type SerializedEntity, type SerializedWorld, type SetPositionCacheOptions, type ShrinkBox, SliderConfig, type StartupReport, type SubsystemEntry, System, type TerminalCapabilities, type TerminalTooSmallMetadata, TerminalTooSmallMetadataSchema, TextConfig, TextareaConfig, TextboxConfig, type TotalPadding, type TransitionState, type UnsupportedCapabilityMetadata, UnsupportedCapabilityMetadataSchema, type WarningEmitter, type WarningEvent, type WarningEventMap, WarningEventSchema, type WarningMetadata, WarningType, type WarningTypeValue, World, type WorldAdapter, type WorldAdapterType, ZOrder, acquireEntity, addComponent, addEntity, addIgnoredKeys, allocateEntity, applyCustomEffect, applyDisabledEffect, applyFocusEffect, applyHoverEffect, applyJunctions, applyKeyLockOptions, applyPressEffect, applyShrink, areAllKeysLocked, assertEntityAlive, bubbleEvent, calculateShrinkSize, centerPosition, clampPosition, clampToClipRect, clearAllArchetypePools, clearAllEffectConfigs, clearAllEntityData, clearAllPositionCaches, clearAllStoredStyles, clearArchetypePool, clearCapabilityCache, clearCleanupCallbacks, clearDestroyQueue, clearDockingContext, clearEffectState, clearEffects, clearEntityData, clearIgnoredKeys, clearLifecycleEventBuses, clearSerializableRegistry, clearStoredStyle, clearStyleCache, clearWorldAdapter, cloneSnapshot, computeInheritedStyle, createBorderDockingContext, createBoxEntity, createBubbleableEvent, createButtonEntity, createCheckboxEntity, createClickableCache, createClipRect, createClipStack, createEntityPool, createFadeTransition, createFormEntity, createInfiniteClipRect, createInputEntity, createKeyBindingRegistry, createKeyLockScope, createKeyLockState, createListEntity, createPackedQueryAdapter, createPhaseManager, createProgressBarEntity, createRadioButtonEntity, createRadioSetEntity, createSceneManager, createSceneSystem, createScreenEntity, createSelectEntity, createSlideTransition, createSliderEntity, createTextEntity, createTextareaEntity, createTextboxEntity, createWarningEmitter, createWorld, createWorldAdapter, deallocateEntity, defaultPhaseManager, deleteEntityData, deserializeWorld, deserializeWorldFromJSON, destroyAllChildren, destroyEntity, destroyWorld, detectAllJunctions, detectBorderStyle, detectCapabilities, detectJunctions, doesPropertyInherit, emitAdopt, emitAttach, emitDeprecatedAPIWarning, emitDescendants, emitDestroy, emitDetach, emitPerformanceWarning, emitRemove, emitReparent, emitTerminalTooSmallWarning, emitUnsupportedCapabilityWarning, entityExists, evaluateCondition, filterClickable, filterDirty, filterFocusable, filterVisible, filterVisibleDirty, findPropertySource, flushDestroyQueue, forceFullRedrawFlag, formatKey, formatKeyEvent, formatStartupReport, getAbsoluteEdges, getAbsolutePosition, getAllClickablesAt, getAllEntities, getAllEntityData, getAllHoverablesAt, getArchetypePoolStats, getAutoPadding, getBindingForAction, getBindingsForKey, getCacheGeneration, getCachedInnerHeight, getCachedInnerWidth, getChildEntities, getChildrenByZIndex, getClickableAt, getClickableCount, getClickableEntities, getClipRect, getClipRectHeight, getClipRectToAncestor, getClipRectWidth, getClipping, getComputedEffectStyle, getComputedPosition, getComputedStyles, getConnectionFlags, getCurrentClip, getDefaultStyle, getDescendantEntities, getDestroyQueueSize, getDirtyEntities, getDirtyRegionsInViewport, getDirtyStats, getEdgeCount, getEdgesAt, getEffectState, getEffectivePadding, getEffects, getEntityCount, getEntityData, getEntityDataCount, getEntityDataKeys, getEntityPoolCapacity, getGrabbedKeys, getHoverableAt, getIgnoredKeys, getInheritedProperty, getInnerDimensions, getInnerPosition, getJunctionChar, getJunctionCharset, getJunctionRenderData, getKeyLockFilter, getKeyLockState, getLifecycleEventBus, getLocalStyle, getLocalZ, getOriginalStyle, getOverflow, getPositionCache, getRecyclingStats, getRegisteredComponents, getRelativePosition, getRootEntities, getSerializable, getShrinkBox, getShrinkHeight, getShrinkWidth, getStartupReport, getStoredStyle, getTotalEffectivePadding, getTotalPadding, getWorldAdapter, getZIndex, grabKeys, hasAnyEffectApplied, hasAnyEntityData, hasAutoPadding, hasClickableAt, hasClipping, hasComponent, hasDirtyEntities, hasDisabledEffectApplied, hasEntityAutoPadding, hasEntityData, hasFocusEffectApplied, hasHoverEffectApplied, hasHoverableAt, hasPressEffectApplied, hasStoredStyle, hasValidPositionCache, hasValidStyleCache, hasZOrder, hitTest, hitTestAll, hitTestDetailed, initSubsystem, initSubsystemsUpTo, intersectClipRects, invalidateAllStyleCaches, invalidateClickableCache, invalidatePositionCache, invalidatePositionCacheTree, invalidateStyleCache, isBorderChar, isBuiltinPhase, isCacheDirty, isClipRectEmpty, isDefaultColor, isEntityAlive, isEntityDirty, isEntityValid, isJunctionChar, isKeyGrabbed, isKeyIgnored, isKeyLocked, isKeywordPosition, isMarkedForDestruction, isPackedQueryAdapter, isPercentagePosition, isPointInCachedBounds, isPointInEntity, isPointInInnerBounds, isPointVisible, isRectVisible, lazy, clearDirtyTracking as legacyClearDirtyTracking, createDirtyTracker as legacyCreateDirtyTracker, getDirtyRegions as legacyGetDirtyRegions, isCellDirty as legacyIsCellDirty, markCellDirty as legacyMarkCellDirty, markEntityDirty as legacyMarkEntityDirty, markRegionDirty as legacyMarkRegionDirty, removeEntityFromTracking as legacyRemoveEntityFromTracking, listBindings, lockAllKeys, markAllEntitiesDirty, matchEvent, matchesKey, mergeStyles, moveDown, moveUp, needsFullRedraw, normalizeZIndices, onAdopt, onAttach, onDestroy, onDetach, onRemove, onReparent, parseKeyString, parsePosition, parsePositionWithNegative, percentOffsetPosition, percentPosition, popClipRect, preallocateEntities, precomputeStyles, pushClipRect, query, queryBorder, queryContent, queryFocusable, queryHierarchy, queryInteractive, queryPadding, queryRenderable, queryScrollable, regionIntersectsDirty, registerArchetype, registerBinding, registerBindings, registerCleanupCallback, registerComponent, registerComponentName, registerEdge, registerRectBorder, registerSerializable, registerSubsystem, releaseAllGrabbedKeys, releaseEntity, releaseKeys, removeAllEffects, removeComponent, removeDisabledEffect, removeEntity, removeFocusEffect, removeHoverEffect, removeIgnoredKeys, removeLifecycleEventBus, removePressEffect, resetDisposalState, resetEntityPool, resetKeyLockState, resetSubsystems, resetWorld, resetZOrder, resizeDirtyTracker, resizeDockingContext, resolveEffectConfig, resolvePosition, resolvePositionClamped, resolveStyle, serializeWorld, serializeWorldToJSON, setAbsoluteBottom, setAbsoluteEdges, setAbsoluteLeft, setAbsolutePosition, setAbsoluteRight, setAbsoluteTop, setBack, setEffects, setEntityData, setEntityDataBulk, setFront, setIgnoredKeys, setKeyLockFilter, setLocalZ, setOverflow, setPositionCache, setRelativePosition, setWorldAdapter, setZIndex, shouldBlockKeyEvent, shouldClipContent, sortByDepth, sortByTabIndex, sortByZIndex, syncEffects, unlockAllKeys, unregisterArchetype, unregisterBinding, unregisterSerializable, updateCachedScrollBase, updateClickableCache, updateEntityBounds, updateEntityData, validateEntity, withStore };
8537
+ export { type AbsolutePosition, type ActionBinding, ActionBindingSchema, type ActionCallback, ActionPresets, type ActionState, type AdoptEvent, type ArchetypeDefinition, type ArchetypePoolConfig, type ArchetypePoolStats, type AttachEvent, type AutoPaddingData, BUILTIN_PHASE_NAMES, type BindingMatch, type BorderDockingContext, type BorderDockingOptions, type BorderEdge, type BorderStyleType, type BubbleResult, type BubbleableEvent, type BubbleableEventOptions, type CachedPosition, type ClipRect, type ClipStack, Clipping, type ClippingData, type ClippingOptions, type ComponentData, type ComponentFieldData, type ComponentRegistration, type ComponentResetFn, type ComputedGetter, type ComputedPositionData, type ComputedSignal, type ConditionContext, type ConnectionFlags, DEFAULT_NAV_BINDINGS, DEFAULT_TEXT_BINDINGS, DEFAULT_WORLD_ADAPTER, DEFAULT_Z_INDEX, type DataValue, type DeprecatedAPIMetadata, DeprecatedAPIMetadataSchema, type DestroyEvent, type DetachEvent, type DirtyStats, type DirtyTrackerData, type DockingBuffer, type DockingCell, type DynamicValue, type EffectConfig, type EffectHandle, type EffectivePaddingData, type EffectsConfig, type EmitDescendantsOptions, EmitDescendantsOptionsSchema, type EmitDescendantsResult, EmitDescendantsResultSchema, Entity, type EntityDataMap, type EntityHandle, type EntityPool, EventBus, EventMap, GetEntityEventBus, INHERITING_PROPERTIES, InitPriority, type InitPriorityLevel, type InnerDimensions, type InnerPosition, type InputActionManager, type InputBufferStats, type InputEventBufferData, type InputEventBufferOptions, type InputLatencyStats, type InputState, type InputStateConfig, type InputStateStats, JUNCTION_ASCII, JUNCTION_BOLD, JUNCTION_DOUBLE, JUNCTION_SINGLE, type Junction, type JunctionCharset, type KeyBinding, type KeyBindingRegistry, KeyBindingSchema, KeyBindingsArraySchema, type KeyLockFilter, type KeyLockOptions, type KeyLockState, type KeyState, type LazyInitFn, type LazyValue, type DirtyRect as LegacyDirtyRect, type LifecycleEvent, type LifecycleEventMap, type LifecycleEventName, LoopPhase, MAX_Z_INDEX, MIN_Z_INDEX, type MountedTree, type MouseButtonState, type MouseState, NON_INHERITING_PROPERTIES, Overflow, type OverflowValue, type PackedQueryAdapter, type PackedQueryAdapterConfig, PackedQueryAdapterConfigSchema, type PackedQueryRegistration, PackedQueryRegistrationSchema, type ParsedKey, type PerformanceIssueMetadata, PerformanceIssueMetadataSchema, type PhaseId, type PhaseManager, type Plugin, type PluginComponent, PluginComponentSchema, type PluginInfo, type PluginRegistrationResult, type PluginRegistry, PluginSchema, type PluginSystem, PluginSystemSchema, type PluginThemeDeclaration, PluginThemeDeclarationSchema, type PluginWidgetDeclaration, PluginWidgetDeclarationSchema, PositionCache, type ReactiveConfig, type ReactiveProp, type RecyclingSystemStats, type RelativePosition, type RemoveEvent, type ReparentEvent, type ResolvedEffect, type Scene, type SceneManager, type SceneTransition, Scheduler, type SerializedBindings, SerializedBindingsSchema, type SetPositionCacheOptions, type ShrinkBox, type Signal, type SignalGetter, type SignalSetter, type StartupReport, type SubsystemEntry, System, type TerminalCapabilities, type TerminalTooSmallMetadata, TerminalTooSmallMetadataSchema, type TimestampedInputEvent, type TimestampedKeyEvent, type TimestampedMouseEvent, type TotalPadding, type TransitionState, type UnsupportedCapabilityMetadata, UnsupportedCapabilityMetadataSchema, type WarningEmitter, type WarningEvent, type WarningEventMap, WarningEventSchema, type WarningMetadata, WarningType, type WarningTypeValue, type WidgetDescriptor, type WidgetType, World, type WorldAdapter, type WorldAdapterType, type WorldDelta, type WorldSnapshot, ZOrder, acquireEntity, activatePlugin, addIgnoredKeys, allocateEntity, applyCustomEffect, applyDisabledEffect, applyFocusEffect, applyHoverEffect, applyJunctions, applyKeyLockOptions, applyPressEffect, applyShrink, applyWorldDelta, areAllKeysLocked, assertEntityAlive, beginFrame, bind, bubbleEvent, calculateShrinkSize, clampToClipRect, cleanupEntityStores, clearAllArchetypePools, clearAllEffectConfigs, clearAllEntityData, clearAllPositionCaches, clearAllStoredStyles, clearArchetypePool, clearBuffer, clearCapabilityCache, clearDockingContext, clearEffectState, clearEffects, clearEntityData, clearIgnoredKeys, clearLifecycleEventBuses, clearPlugins, clearStoredStyle, clearStyleCache, clearWorldAdapter, clearWorldStores, computeInheritedStyle, createBatch, createBorderDockingContext, createBubbleableEvent, createCallbackSignal, createClipRect, createClipStack, createComputed, createDefaultPackedQueryAdapter, createDerivedSignal, createEffect, createEntityPool, createEntitySignal, createEntityValidationError, createEventSignal, createFadeTransition, createInfiniteClipRect, createInputActionManager, createInputEventBuffer, createInputState, createIntervalSignal, createKeyBindingRegistry, createKeyLockScope, createKeyLockState, createPackedQueryAdapter, createPhaseManager, createPluginRegistry, createPollingSignal, createReducerSignal, createSceneManager, createSceneSystem, createScheduledEffect, createSignal, createSlideTransition, createStreamSignal, createTimerSignal, createWarningEmitter, createWorldAdapter, createWorldDelta, deactivatePlugin, deallocateEntity, defaultPhaseManager, definePlugin, deleteEntityData, deserializeWorld, detectAllJunctions, detectBorderStyle, detectCapabilities, detectJunctions, disposeEffect, disposeSignal, doesPropertyInherit, drainAllEvents, drainKeys, drainMouse, el, elRef, emitAdopt, emitAttach, emitDeprecatedAPIWarning, emitDescendants, emitDestroy, emitDetach, emitPerformanceWarning, emitRemove, emitReparent, emitTerminalTooSmallWarning, emitUnsupportedCapabilityWarning, endFrame, evaluateCondition, filterClickable, filterDirty, filterFocusable, filterVisible, filterVisibleDirty, findPropertySource, flushScheduledEffects, forceFullRedrawFlag, formatKey, formatKeyEvent, formatStartupReport, getAbsoluteEdges, getAbsolutePosition, getAllEntityData, getArchetypePoolStats, getAutoPadding, getBindingForAction, getBindingsForKey, getCacheGeneration, getCachedInnerHeight, getCachedInnerWidth, getChildEntities, getChildrenByZIndex, getClipRect, getClipRectHeight, getClipRectToAncestor, getClipRectWidth, getClipping, getComputedEffectStyle, getComputedPosition, getComputedStyles, getConnectionFlags, getCurrentClip, getDefaultStyle, getDescendantEntities, getDirtyEntities, getDirtyRegionsInViewport, getDirtyStats, getEdgeCount, getEdgesAt, getEffectState, getEffectivePadding, getEffects, getEntityCount, getEntityData, getEntityDataCount, getEntityDataKeys, getEntityPoolCapacity, getGrabbedKeys, getIgnoredKeys, getInheritedProperty, getInnerDimensions, getInnerPosition, getJunctionChar, getJunctionCharset, getJunctionRenderData, getKeyLockFilter, getKeyLockState, getLatencyStats, getLifecycleEventBus, getLocalStyle, getLocalZ, getMovementDirection, getOriginalStyle, getOverflow, getPendingCount, getPendingKeyCount, getPendingMouseCount, getPluginCount, getPlugins, getPositionCache, getRecyclingStats, getRegisteredComponents, getRelativePosition, getRootEntities, getScheduledEffectCount, getShrinkBox, getShrinkHeight, getShrinkWidth, getStartupReport, getStats, getStoreKeys, getStoredStyle, getTotalEffectivePadding, getTotalPadding, getTotalScheduledEffectCount, getWorldAdapter, getWorldSet, getWorldStore, getZIndex, globalInputBuffer, grabKeys, hasAnyEffectApplied, hasAnyEntityData, hasAutoPadding, hasClipping, hasDirtyEntities, hasDisabledEffectApplied, hasEntityAutoPadding, hasEntityData, hasFocusEffectApplied, hasHoverEffectApplied, hasPendingEvents, hasPlugin, hasPressEffectApplied, hasStoredStyle, hasValidPositionCache, hasValidStyleCache, hasZOrder, hbox, initSubsystem, initSubsystemsUpTo, intersectClipRects, invalidateAllStyleCaches, invalidatePositionCache, invalidatePositionCacheTree, invalidateStyleCache, isAllKeysDown, isAnyKeyDown, isAnyKeyPressed, isBorderChar, isBuiltinPhase, isClipRectEmpty, isDefaultColor, isEntityAlive, isEntityDirty, isEntityValid, isEntityValidationError, isJunctionChar, isKeyGrabbed, isKeyIgnored, isKeyLocked, isLatencyAcceptable, isPackedQueryAdapter, isPluginActive, isPointInCachedBounds, isPointInEntity, isPointInInnerBounds, isPointVisible, isProcessingTimeAcceptable, isRectVisible, lazy, clearDirtyTracking as legacyClearDirtyTracking, createDirtyTracker as legacyCreateDirtyTracker, getDirtyRegions as legacyGetDirtyRegions, isCellDirty as legacyIsCellDirty, markCellDirty as legacyMarkCellDirty, markEntityDirty as legacyMarkEntityDirty, markRegionDirty as legacyMarkRegionDirty, removeEntityFromTracking as legacyRemoveEntityFromTracking, listBindings, lockAllKeys, markAllEntitiesDirty, matchEvent, matchesKey, mergeStyles, mount, moveDown, moveUp, needsFullRedraw, normalizeZIndices, onAdopt, onAttach, onDestroy, onDetach, onRemove, onReparent, parseKeyString, peekEvents, peekKeys, peekMouse, popClipRect, preallocateEntities, precomputeStyles, pushClipRect, pushKeyEvent, pushMouseEvent, queryBorder, queryContent, queryFocusable, queryHierarchy, queryInteractive, queryPadding, queryRenderable, queryScrollable, recordLatency, recordLatencyBatch, regionIntersectsDirty, registerArchetype, registerBinding, registerBindings, registerComponentName, registerComponents, registerDefaultPropSetters, registerEdge, registerPlugin, registerPropSetter, registerRectBorder, registerSubsystem, registerWidgetFactory, releaseAllGrabbedKeys, releaseEntity, releaseKeys, removeAllEffects, removeDisabledEffect, removeFocusEffect, removeHoverEffect, removeIgnoredKeys, removeLifecycleEventBus, removePressEffect, resetDeclarativeRegistrations, resetEntityPool, resetKeyLockState, resetLatencyStats, resetStats, resetSubsystems, resetZOrder, resizeDirtyTracker, resizeDockingContext, resolveEffectConfig, resolveStyle, serializeWorld, setAbsoluteBottom, setAbsoluteEdges, setAbsoluteLeft, setAbsolutePosition, setAbsoluteRight, setAbsoluteTop, setBack, setEffects, setEntityData, setEntityDataBulk, setFront, setIgnoredKeys, setKeyLockFilter, setLocalZ, setOverflow, setPositionCache, setRelativePosition, setWorldAdapter, setZIndex, shouldBlockKeyEvent, shouldClipContent, sortByDepth, sortByTabIndex, sortByZIndex, syncEffects, syncWorldAdapter, trackDependencies, unlockAllKeys, unmount, unregisterArchetype, unregisterBinding, unregisterPlugin, updateCachedScrollBase, updateEntityBounds, updateEntityData, validateEntity, validatePluginConfig, vbox };