@flyingrobots/bijou-tui 0.1.0 → 0.2.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 (60) hide show
  1. package/README.md +221 -30
  2. package/dist/animate.d.ts +60 -0
  3. package/dist/animate.d.ts.map +1 -0
  4. package/dist/animate.js +98 -0
  5. package/dist/animate.js.map +1 -0
  6. package/dist/commands.d.ts.map +1 -1
  7. package/dist/commands.js +2 -2
  8. package/dist/commands.js.map +1 -1
  9. package/dist/eventbus.d.ts +69 -0
  10. package/dist/eventbus.d.ts.map +1 -0
  11. package/dist/eventbus.js +120 -0
  12. package/dist/eventbus.js.map +1 -0
  13. package/dist/flex.d.ts +64 -0
  14. package/dist/flex.d.ts.map +1 -0
  15. package/dist/flex.js +261 -0
  16. package/dist/flex.js.map +1 -0
  17. package/dist/help.d.ts +58 -0
  18. package/dist/help.d.ts.map +1 -0
  19. package/dist/help.js +104 -0
  20. package/dist/help.js.map +1 -0
  21. package/dist/index.d.ts +11 -2
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +18 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/inputstack.d.ts +79 -0
  26. package/dist/inputstack.d.ts.map +1 -0
  27. package/dist/inputstack.js +81 -0
  28. package/dist/inputstack.js.map +1 -0
  29. package/dist/keybindings.d.ts +83 -0
  30. package/dist/keybindings.d.ts.map +1 -0
  31. package/dist/keybindings.js +184 -0
  32. package/dist/keybindings.js.map +1 -0
  33. package/dist/layout.d.ts +0 -1
  34. package/dist/layout.d.ts.map +1 -1
  35. package/dist/layout.js +3 -4
  36. package/dist/layout.js.map +1 -1
  37. package/dist/runtime.d.ts +3 -0
  38. package/dist/runtime.d.ts.map +1 -1
  39. package/dist/runtime.js +24 -37
  40. package/dist/runtime.js.map +1 -1
  41. package/dist/screen.d.ts +10 -4
  42. package/dist/screen.d.ts.map +1 -1
  43. package/dist/screen.js +13 -11
  44. package/dist/screen.js.map +1 -1
  45. package/dist/spring.d.ts +139 -0
  46. package/dist/spring.d.ts.map +1 -0
  47. package/dist/spring.js +106 -0
  48. package/dist/spring.js.map +1 -0
  49. package/dist/timeline.d.ts +127 -0
  50. package/dist/timeline.d.ts.map +1 -0
  51. package/dist/timeline.js +298 -0
  52. package/dist/timeline.js.map +1 -0
  53. package/dist/types.d.ts +11 -2
  54. package/dist/types.d.ts.map +1 -1
  55. package/dist/types.js.map +1 -1
  56. package/dist/viewport.d.ts +64 -0
  57. package/dist/viewport.d.ts.map +1 -0
  58. package/dist/viewport.js +162 -0
  59. package/dist/viewport.js.map +1 -0
  60. package/package.json +2 -2
package/dist/runtime.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { getDefaultContext } from '@flyingrobots/bijou';
2
- import { QUIT } from './types.js';
3
- import { parseKey } from './keys.js';
4
2
  import { enterScreen, exitScreen, renderFrame } from './screen.js';
3
+ import { createEventBus } from './eventbus.js';
5
4
  /**
6
5
  * Disable mouse reporting sequences that terminals may send.
7
6
  * Some terminals auto-enable mouse tracking in alt screen mode.
@@ -13,6 +12,9 @@ const DISABLE_MOUSE = '\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l';
13
12
  * In non-interactive modes (pipe/static/accessible), renders the initial view
14
13
  * once and exits. In interactive mode, enters the alt screen, starts the
15
14
  * keyboard event loop, and drives the model/update/view cycle.
15
+ *
16
+ * All input sources (keyboard, resize, commands) are unified through an
17
+ * internal EventBus — a single subscription drives the update cycle.
16
18
  */
17
19
  export async function run(app, options) {
18
20
  const ctx = options?.ctx ?? getDefaultContext();
@@ -29,6 +31,7 @@ export async function run(app, options) {
29
31
  let running = true;
30
32
  let lastCtrlC = 0;
31
33
  let resolveQuit = null;
34
+ const bus = createEventBus();
32
35
  function shutdown() {
33
36
  if (!running)
34
37
  return;
@@ -40,7 +43,6 @@ export async function run(app, options) {
40
43
  if (useAltScreen || useHideCursor) {
41
44
  enterScreen(ctx.io);
42
45
  }
43
- // Disable mouse reporting to avoid garbage from mouse events
44
46
  ctx.io.write(DISABLE_MOUSE);
45
47
  // Render helper
46
48
  function render() {
@@ -48,43 +50,23 @@ export async function run(app, options) {
48
50
  return;
49
51
  renderFrame(ctx.io, app.view(model));
50
52
  }
51
- // Message handler
52
- function handleMsg(msg) {
53
- if (!running)
54
- return;
55
- const [newModel, cmds] = app.update(msg, model);
56
- model = newModel;
57
- render();
58
- executeCommands(cmds);
59
- }
60
- // Execute commands, feeding results back as messages
53
+ // Execute commands through the bus
61
54
  function executeCommands(cmds) {
62
55
  for (const cmd of cmds) {
63
- void cmd().then((result) => {
64
- if (result === QUIT) {
65
- shutdown();
66
- return;
67
- }
68
- if (result !== undefined) {
69
- handleMsg(result);
70
- }
71
- });
56
+ bus.runCmd(cmd);
72
57
  }
73
58
  }
74
- // Initial render
75
- render();
76
- // Execute startup commands
77
- executeCommands(initCmds);
78
- // Start keyboard listener
79
- const inputHandle = ctx.io.rawInput((raw) => {
59
+ // Connect I/O sources — keyboard + resize, parsed and unified
60
+ bus.connectIO(ctx.io);
61
+ // Handle quit signals from commands
62
+ bus.onQuit(shutdown);
63
+ // Single subscription drives the entire update cycle
64
+ bus.on((msg) => {
80
65
  if (!running)
81
66
  return;
82
- const keyMsg = parseKey(raw);
83
- // Skip unknown sequences (mouse events, etc.)
84
- if (keyMsg.key === 'unknown')
85
- return;
86
67
  // Double Ctrl+C force-quit
87
- if (keyMsg.key === 'c' && keyMsg.ctrl) {
68
+ const keyMsg = msg;
69
+ if (keyMsg.type === 'key' && keyMsg.key === 'c' && keyMsg.ctrl) {
88
70
  const now = Date.now();
89
71
  if (now - lastCtrlC < 1000) {
90
72
  shutdown();
@@ -92,17 +74,22 @@ export async function run(app, options) {
92
74
  }
93
75
  lastCtrlC = now;
94
76
  }
95
- handleMsg(keyMsg);
77
+ const [newModel, cmds] = app.update(msg, model);
78
+ model = newModel;
79
+ render();
80
+ executeCommands(cmds);
96
81
  });
82
+ // Initial render + startup commands
83
+ render();
84
+ executeCommands(initCmds);
97
85
  // Wait for quit signal
98
86
  await new Promise((resolve) => {
99
87
  resolveQuit = resolve;
100
- // In case shutdown was already called before we got here
101
88
  if (!running)
102
89
  resolve();
103
90
  });
104
- // Cleanup
105
- inputHandle.dispose();
91
+ // Cleanup — bus disposes all I/O connections
92
+ bus.dispose();
106
93
  if (useAltScreen || useHideCursor) {
107
94
  exitScreen(ctx.io);
108
95
  }
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
package/dist/screen.d.ts CHANGED
@@ -3,19 +3,25 @@ export declare const ENTER_ALT_SCREEN = "\u001B[?1049h";
3
3
  export declare const EXIT_ALT_SCREEN = "\u001B[?1049l";
4
4
  export declare const HIDE_CURSOR = "\u001B[?25l";
5
5
  export declare const SHOW_CURSOR = "\u001B[?25h";
6
+ export declare const WRAP_DISABLE = "\u001B[?7l";
7
+ export declare const WRAP_ENABLE = "\u001B[?7h";
6
8
  export declare const CLEAR_SCREEN = "\u001B[2J";
7
9
  export declare const CLEAR_TO_END = "\u001B[J";
10
+ export declare const CLEAR_LINE_TO_END = "\u001B[K";
8
11
  export declare const HOME = "\u001B[H";
9
12
  export declare const CLEAR_LINE = "\u001B[2K";
10
- /** Enter alt screen buffer, hide cursor, and clear screen. */
13
+ /** Enter alt screen buffer, hide cursor, disable wrap, and clear screen. */
11
14
  export declare function enterScreen(io: IOPort): void;
12
- /** Show cursor and exit alt screen buffer. */
15
+ /** Show cursor, enable wrap, and exit alt screen buffer. */
13
16
  export declare function exitScreen(io: IOPort): void;
14
17
  /** Clear screen and move cursor to home position. */
15
18
  export declare function clearAndHome(io: IOPort): void;
16
19
  /**
17
- * Flicker-free render: move cursor home, write content line-by-line
18
- * (clearing each line first), then erase everything below.
20
+ * Flicker-free render: move cursor home, write content line-by-line,
21
+ * clearing from the end of each line to the terminal edge.
22
+ *
23
+ * Disabling wrap in enterScreen() ensures the terminal won't scroll
24
+ * if we write to the bottom-right cell.
19
25
  */
20
26
  export declare function renderFrame(io: IOPort, content: string): void;
21
27
  //# sourceMappingURL=screen.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"screen.d.ts","sourceRoot":"","sources":["../src/screen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD,eAAO,MAAM,gBAAgB,kBAAgB,CAAC;AAC9C,eAAO,MAAM,eAAe,kBAAgB,CAAC;AAC7C,eAAO,MAAM,WAAW,gBAAc,CAAC;AACvC,eAAO,MAAM,WAAW,gBAAc,CAAC;AACvC,eAAO,MAAM,YAAY,cAAY,CAAC;AACtC,eAAO,MAAM,YAAY,aAAW,CAAC;AACrC,eAAO,MAAM,IAAI,aAAW,CAAC;AAC7B,eAAO,MAAM,UAAU,cAAY,CAAC;AAEpC,8DAA8D;AAC9D,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE5C;AAED,8CAA8C;AAC9C,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED,qDAAqD;AACrD,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAQ7D"}
1
+ {"version":3,"file":"screen.d.ts","sourceRoot":"","sources":["../src/screen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD,eAAO,MAAM,gBAAgB,kBAAgB,CAAC;AAC9C,eAAO,MAAM,eAAe,kBAAgB,CAAC;AAC7C,eAAO,MAAM,WAAW,gBAAc,CAAC;AACvC,eAAO,MAAM,WAAW,gBAAc,CAAC;AACvC,eAAO,MAAM,YAAY,eAAa,CAAC;AACvC,eAAO,MAAM,WAAW,eAAa,CAAC;AACtC,eAAO,MAAM,YAAY,cAAY,CAAC;AACtC,eAAO,MAAM,YAAY,aAAW,CAAC;AACrC,eAAO,MAAM,iBAAiB,aAAW,CAAC;AAC1C,eAAO,MAAM,IAAI,aAAW,CAAC;AAC7B,eAAO,MAAM,UAAU,cAAY,CAAC;AAEpC,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE5C;AAED,4DAA4D;AAC5D,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED,qDAAqD;AACrD,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAI7D"}
package/dist/screen.js CHANGED
@@ -2,33 +2,35 @@ export const ENTER_ALT_SCREEN = '\x1b[?1049h';
2
2
  export const EXIT_ALT_SCREEN = '\x1b[?1049l';
3
3
  export const HIDE_CURSOR = '\x1b[?25l';
4
4
  export const SHOW_CURSOR = '\x1b[?25h';
5
+ export const WRAP_DISABLE = '\x1b[?7l';
6
+ export const WRAP_ENABLE = '\x1b[?7h';
5
7
  export const CLEAR_SCREEN = '\x1b[2J';
6
8
  export const CLEAR_TO_END = '\x1b[J';
9
+ export const CLEAR_LINE_TO_END = '\x1b[K';
7
10
  export const HOME = '\x1b[H';
8
11
  export const CLEAR_LINE = '\x1b[2K';
9
- /** Enter alt screen buffer, hide cursor, and clear screen. */
12
+ /** Enter alt screen buffer, hide cursor, disable wrap, and clear screen. */
10
13
  export function enterScreen(io) {
11
- io.write(ENTER_ALT_SCREEN + HIDE_CURSOR + CLEAR_SCREEN + HOME);
14
+ io.write(ENTER_ALT_SCREEN + HIDE_CURSOR + WRAP_DISABLE + CLEAR_SCREEN + HOME);
12
15
  }
13
- /** Show cursor and exit alt screen buffer. */
16
+ /** Show cursor, enable wrap, and exit alt screen buffer. */
14
17
  export function exitScreen(io) {
15
- io.write(SHOW_CURSOR + EXIT_ALT_SCREEN);
18
+ io.write(SHOW_CURSOR + WRAP_ENABLE + EXIT_ALT_SCREEN);
16
19
  }
17
20
  /** Clear screen and move cursor to home position. */
18
21
  export function clearAndHome(io) {
19
22
  io.write(CLEAR_SCREEN + HOME);
20
23
  }
21
24
  /**
22
- * Flicker-free render: move cursor home, write content line-by-line
23
- * (clearing each line first), then erase everything below.
25
+ * Flicker-free render: move cursor home, write content line-by-line,
26
+ * clearing from the end of each line to the terminal edge.
27
+ *
28
+ * Disabling wrap in enterScreen() ensures the terminal won't scroll
29
+ * if we write to the bottom-right cell.
24
30
  */
25
31
  export function renderFrame(io, content) {
26
32
  const lines = content.split('\n');
27
- let frame = HOME;
28
- for (const line of lines) {
29
- frame += CLEAR_LINE + line + '\n';
30
- }
31
- frame += CLEAR_TO_END;
33
+ const frame = HOME + lines.map((line) => line + CLEAR_LINE_TO_END).join('\n') + CLEAR_TO_END;
32
34
  io.write(frame);
33
35
  }
34
36
  //# sourceMappingURL=screen.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"screen.js","sourceRoot":"","sources":["../src/screen.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAG,aAAa,CAAC;AAC9C,MAAM,CAAC,MAAM,eAAe,GAAG,aAAa,CAAC;AAC7C,MAAM,CAAC,MAAM,WAAW,GAAG,WAAW,CAAC;AACvC,MAAM,CAAC,MAAM,WAAW,GAAG,WAAW,CAAC;AACvC,MAAM,CAAC,MAAM,YAAY,GAAG,SAAS,CAAC;AACtC,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;AACrC,MAAM,CAAC,MAAM,IAAI,GAAG,QAAQ,CAAC;AAC7B,MAAM,CAAC,MAAM,UAAU,GAAG,SAAS,CAAC;AAEpC,8DAA8D;AAC9D,MAAM,UAAU,WAAW,CAAC,EAAU;IACpC,EAAE,CAAC,KAAK,CAAC,gBAAgB,GAAG,WAAW,GAAG,YAAY,GAAG,IAAI,CAAC,CAAC;AACjE,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,UAAU,CAAC,EAAU;IACnC,EAAE,CAAC,KAAK,CAAC,WAAW,GAAG,eAAe,CAAC,CAAC;AAC1C,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,YAAY,CAAC,EAAU;IACrC,EAAE,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;AAChC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,EAAU,EAAE,OAAe;IACrD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,KAAK,GAAG,IAAI,CAAC;IACjB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,IAAI,UAAU,GAAG,IAAI,GAAG,IAAI,CAAC;IACpC,CAAC;IACD,KAAK,IAAI,YAAY,CAAC;IACtB,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"screen.js","sourceRoot":"","sources":["../src/screen.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAG,aAAa,CAAC;AAC9C,MAAM,CAAC,MAAM,eAAe,GAAG,aAAa,CAAC;AAC7C,MAAM,CAAC,MAAM,WAAW,GAAG,WAAW,CAAC;AACvC,MAAM,CAAC,MAAM,WAAW,GAAG,WAAW,CAAC;AACvC,MAAM,CAAC,MAAM,YAAY,GAAG,UAAU,CAAC;AACvC,MAAM,CAAC,MAAM,WAAW,GAAG,UAAU,CAAC;AACtC,MAAM,CAAC,MAAM,YAAY,GAAG,SAAS,CAAC;AACtC,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;AACrC,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAC1C,MAAM,CAAC,MAAM,IAAI,GAAG,QAAQ,CAAC;AAC7B,MAAM,CAAC,MAAM,UAAU,GAAG,SAAS,CAAC;AAEpC,4EAA4E;AAC5E,MAAM,UAAU,WAAW,CAAC,EAAU;IACpC,EAAE,CAAC,KAAK,CAAC,gBAAgB,GAAG,WAAW,GAAG,YAAY,GAAG,YAAY,GAAG,IAAI,CAAC,CAAC;AAChF,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,UAAU,CAAC,EAAU;IACnC,EAAE,CAAC,KAAK,CAAC,WAAW,GAAG,WAAW,GAAG,eAAe,CAAC,CAAC;AACxD,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,YAAY,CAAC,EAAU;IACrC,EAAE,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;AAChC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,EAAU,EAAE,OAAe;IACrD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,iBAAiB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC;IAC7F,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAClB,CAAC"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Physics-based spring animation engine.
3
+ *
4
+ * Uses a damped harmonic oscillator model:
5
+ * F = -stiffness * displacement - damping * velocity
6
+ * acceleration = F / mass
7
+ *
8
+ * Inspired by react-spring and GSAP spring physics.
9
+ */
10
+ export interface SpringConfig {
11
+ /** Spring stiffness (tension). Higher = snappier. Default: 170 */
12
+ readonly stiffness: number;
13
+ /** Damping (friction). Higher = less oscillation. Default: 26 */
14
+ readonly damping: number;
15
+ /** Mass of the object. Higher = more inertia. Default: 1 */
16
+ readonly mass: number;
17
+ /** Value threshold to consider the spring settled. Default: 0.01 */
18
+ readonly precision: number;
19
+ }
20
+ export declare const SPRING_PRESETS: {
21
+ /** Balanced default — smooth with a hint of bounce */
22
+ readonly default: {
23
+ readonly stiffness: 170;
24
+ readonly damping: 26;
25
+ readonly mass: 1;
26
+ readonly precision: 0.01;
27
+ };
28
+ /** Soft entrance — slow start, gentle settle */
29
+ readonly gentle: {
30
+ readonly stiffness: 120;
31
+ readonly damping: 14;
32
+ readonly mass: 1;
33
+ readonly precision: 0.01;
34
+ };
35
+ /** Playful — noticeable overshoot and bounce */
36
+ readonly wobbly: {
37
+ readonly stiffness: 180;
38
+ readonly damping: 12;
39
+ readonly mass: 1;
40
+ readonly precision: 0.01;
41
+ };
42
+ /** Quick snap — fast with minimal overshoot */
43
+ readonly stiff: {
44
+ readonly stiffness: 210;
45
+ readonly damping: 20;
46
+ readonly mass: 1;
47
+ readonly precision: 0.01;
48
+ };
49
+ /** Heavy — deliberate, weighty motion */
50
+ readonly slow: {
51
+ readonly stiffness: 280;
52
+ readonly damping: 60;
53
+ readonly mass: 1;
54
+ readonly precision: 0.01;
55
+ };
56
+ /** Very heavy — thick, viscous feel */
57
+ readonly molasses: {
58
+ readonly stiffness: 280;
59
+ readonly damping: 120;
60
+ readonly mass: 1;
61
+ readonly precision: 0.01;
62
+ };
63
+ };
64
+ export type SpringPreset = keyof typeof SPRING_PRESETS;
65
+ export interface SpringState {
66
+ /** Current animated value */
67
+ readonly value: number;
68
+ /** Current velocity */
69
+ readonly velocity: number;
70
+ /** Whether the spring has settled at the target */
71
+ readonly done: boolean;
72
+ }
73
+ /**
74
+ * Advance a spring by one time step using semi-implicit Euler integration.
75
+ *
76
+ * @param state Current spring state
77
+ * @param target Target value the spring is approaching
78
+ * @param config Spring physics parameters
79
+ * @param dt Time step in seconds (e.g. 1/60 for 60fps)
80
+ */
81
+ export declare function springStep(state: SpringState, target: number, config: SpringConfig, dt: number): SpringState;
82
+ /**
83
+ * Create a fresh spring state at a given starting value.
84
+ */
85
+ export declare function createSpringState(value: number): SpringState;
86
+ /**
87
+ * Resolve a preset name or config object to a full SpringConfig.
88
+ */
89
+ export declare function resolveSpringConfig(config?: Partial<SpringConfig> | SpringPreset): SpringConfig;
90
+ export type EasingFn = (t: number) => number;
91
+ export declare const EASINGS: {
92
+ readonly linear: (t: number) => number;
93
+ readonly easeIn: (t: number) => number;
94
+ readonly easeOut: (t: number) => number;
95
+ readonly easeInOut: (t: number) => number;
96
+ readonly easeInCubic: (t: number) => number;
97
+ readonly easeOutCubic: (t: number) => number;
98
+ readonly easeInOutCubic: (t: number) => number;
99
+ readonly easeInQuart: (t: number) => number;
100
+ readonly easeOutQuart: (t: number) => number;
101
+ readonly easeInOutQuart: (t: number) => number;
102
+ };
103
+ export interface TweenConfig {
104
+ /** Start value. Default: 0 */
105
+ readonly from: number;
106
+ /** End value. Default: 1 */
107
+ readonly to: number;
108
+ /** Duration in milliseconds */
109
+ readonly duration: number;
110
+ /** Easing function. Default: easeOutCubic */
111
+ readonly ease: EasingFn;
112
+ }
113
+ export interface TweenState {
114
+ /** Current animated value */
115
+ readonly value: number;
116
+ /** Elapsed time in milliseconds */
117
+ readonly elapsed: number;
118
+ /** Whether the tween has completed */
119
+ readonly done: boolean;
120
+ }
121
+ /**
122
+ * Advance a tween by a time step.
123
+ *
124
+ * @param state Current tween state
125
+ * @param config Tween parameters
126
+ * @param dtMs Time step in milliseconds
127
+ */
128
+ export declare function tweenStep(state: TweenState, config: TweenConfig, dtMs: number): TweenState;
129
+ /**
130
+ * Create a fresh tween state.
131
+ */
132
+ export declare function createTweenState(from: number): TweenState;
133
+ /**
134
+ * Resolve partial tween config to full config with defaults.
135
+ */
136
+ export declare function resolveTweenConfig(config: Partial<TweenConfig> & {
137
+ duration: number;
138
+ }): TweenConfig;
139
+ //# sourceMappingURL=spring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spring.d.ts","sourceRoot":"","sources":["../src/spring.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,MAAM,WAAW,YAAY;IAC3B,kEAAkE;IAClE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,cAAc;IACzB,sDAAsD;;;;;;;IAEtD,gDAAgD;;;;;;;IAEhD,gDAAgD;;;;;;;IAEhD,+CAA+C;;;;;;;IAE/C,yCAAyC;;;;;;;IAEzC,uCAAuC;;;;;;;CAEQ,CAAC;AAElD,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,cAAc,CAAC;AAMvD,MAAM,WAAW,WAAW;IAC1B,6BAA6B;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,uBAAuB;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB;AAMD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,EACpB,EAAE,EAAE,MAAM,GACT,WAAW,CAeb;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAE5D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY,GAC5C,YAAY,CAId;AAMD,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;AAE7C,eAAO,MAAM,OAAO;yBACN,MAAM;yBAEN,MAAM;0BACL,MAAM;4BACJ,MAAM;8BAEJ,MAAM;+BACL,MAAM;iCACJ,MAAM;8BAGT,MAAM;+BACL,MAAM;iCACJ,MAAM;CAEiB,CAAC;AAM9C,MAAM,WAAW,WAAW;IAC1B,8BAA8B;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4BAA4B;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,6BAA6B;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,mCAAmC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,MAAM,GACX,UAAU,CAQZ;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAEzD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAClD,WAAW,CAOb"}
package/dist/spring.js ADDED
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Physics-based spring animation engine.
3
+ *
4
+ * Uses a damped harmonic oscillator model:
5
+ * F = -stiffness * displacement - damping * velocity
6
+ * acceleration = F / mass
7
+ *
8
+ * Inspired by react-spring and GSAP spring physics.
9
+ */
10
+ export const SPRING_PRESETS = {
11
+ /** Balanced default — smooth with a hint of bounce */
12
+ default: { stiffness: 170, damping: 26, mass: 1, precision: 0.01 },
13
+ /** Soft entrance — slow start, gentle settle */
14
+ gentle: { stiffness: 120, damping: 14, mass: 1, precision: 0.01 },
15
+ /** Playful — noticeable overshoot and bounce */
16
+ wobbly: { stiffness: 180, damping: 12, mass: 1, precision: 0.01 },
17
+ /** Quick snap — fast with minimal overshoot */
18
+ stiff: { stiffness: 210, damping: 20, mass: 1, precision: 0.01 },
19
+ /** Heavy — deliberate, weighty motion */
20
+ slow: { stiffness: 280, damping: 60, mass: 1, precision: 0.01 },
21
+ /** Very heavy — thick, viscous feel */
22
+ molasses: { stiffness: 280, damping: 120, mass: 1, precision: 0.01 },
23
+ };
24
+ // ---------------------------------------------------------------------------
25
+ // Core simulation
26
+ // ---------------------------------------------------------------------------
27
+ /**
28
+ * Advance a spring by one time step using semi-implicit Euler integration.
29
+ *
30
+ * @param state Current spring state
31
+ * @param target Target value the spring is approaching
32
+ * @param config Spring physics parameters
33
+ * @param dt Time step in seconds (e.g. 1/60 for 60fps)
34
+ */
35
+ export function springStep(state, target, config, dt) {
36
+ const { stiffness, damping, mass, precision } = config;
37
+ const displacement = state.value - target;
38
+ const springForce = -stiffness * displacement;
39
+ const dampingForce = -damping * state.velocity;
40
+ const acceleration = (springForce + dampingForce) / mass;
41
+ const velocity = state.velocity + acceleration * dt;
42
+ const value = state.value + velocity * dt;
43
+ const done = Math.abs(velocity) < precision && Math.abs(value - target) < precision;
44
+ return { value: done ? target : value, velocity: done ? 0 : velocity, done };
45
+ }
46
+ /**
47
+ * Create a fresh spring state at a given starting value.
48
+ */
49
+ export function createSpringState(value) {
50
+ return { value, velocity: 0, done: false };
51
+ }
52
+ /**
53
+ * Resolve a preset name or config object to a full SpringConfig.
54
+ */
55
+ export function resolveSpringConfig(config) {
56
+ if (config === undefined)
57
+ return SPRING_PRESETS.default;
58
+ if (typeof config === 'string')
59
+ return SPRING_PRESETS[config];
60
+ return { ...SPRING_PRESETS.default, ...config };
61
+ }
62
+ export const EASINGS = {
63
+ linear: (t) => t,
64
+ easeIn: (t) => t * t,
65
+ easeOut: (t) => t * (2 - t),
66
+ easeInOut: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),
67
+ easeInCubic: (t) => t * t * t,
68
+ easeOutCubic: (t) => --t * t * t + 1,
69
+ easeInOutCubic: (t) => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1,
70
+ easeInQuart: (t) => t * t * t * t,
71
+ easeOutQuart: (t) => 1 - --t * t * t * t,
72
+ easeInOutQuart: (t) => t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t,
73
+ };
74
+ /**
75
+ * Advance a tween by a time step.
76
+ *
77
+ * @param state Current tween state
78
+ * @param config Tween parameters
79
+ * @param dtMs Time step in milliseconds
80
+ */
81
+ export function tweenStep(state, config, dtMs) {
82
+ const elapsed = Math.min(state.elapsed + dtMs, config.duration);
83
+ const t = config.duration > 0 ? elapsed / config.duration : 1;
84
+ const eased = config.ease(t);
85
+ const value = config.from + (config.to - config.from) * eased;
86
+ const done = elapsed >= config.duration;
87
+ return { value: done ? config.to : value, elapsed, done };
88
+ }
89
+ /**
90
+ * Create a fresh tween state.
91
+ */
92
+ export function createTweenState(from) {
93
+ return { value: from, elapsed: 0, done: false };
94
+ }
95
+ /**
96
+ * Resolve partial tween config to full config with defaults.
97
+ */
98
+ export function resolveTweenConfig(config) {
99
+ return {
100
+ from: config.from ?? 0,
101
+ to: config.to ?? 1,
102
+ duration: config.duration,
103
+ ease: config.ease ?? EASINGS.easeOutCubic,
104
+ };
105
+ }
106
+ //# sourceMappingURL=spring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spring.js","sourceRoot":"","sources":["../src/spring.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * GSAP-style Timeline — orchestrate multiple animations with position-based timing.
3
+ *
4
+ * The timeline is a **pure state machine**: build a timeline definition,
5
+ * then step it forward in your TEA update function. No internal timers,
6
+ * no mutation, fully testable.
7
+ *
8
+ * ```ts
9
+ * const tl = timeline()
10
+ * .add('x', { from: 0, to: 100, spring: 'wobbly' })
11
+ * .add('opacity', { type: 'tween', from: 0, to: 1, duration: 300 }, '<')
12
+ * .label('reveal')
13
+ * .add('y', { from: 0, to: 50, spring: 'gentle' }, 'reveal')
14
+ * .call('onReveal', 'reveal+=100')
15
+ * .build();
16
+ *
17
+ * // TEA init:
18
+ * const state = tl.init();
19
+ *
20
+ * // TEA update (on each animation frame):
21
+ * const next = tl.step(state, 1/60);
22
+ * const { x, opacity, y } = tl.values(next);
23
+ * const fired = tl.firedCallbacks(state, next);
24
+ * if (tl.done(next)) { ... }
25
+ * ```
26
+ */
27
+ import { type SpringConfig, type SpringPreset, type SpringState, type EasingFn, type TweenState } from './spring.js';
28
+ /** Position in the timeline — controls when a track/callback starts. */
29
+ export type Position = number | '<' | `<+=${string}` | `+=${string}` | `-=${string}` | `>${string}` | string;
30
+ /** Spring track configuration. */
31
+ export interface SpringTrackDef {
32
+ readonly type?: 'spring';
33
+ readonly from: number;
34
+ readonly to: number;
35
+ readonly spring?: Partial<SpringConfig> | SpringPreset;
36
+ }
37
+ /** Tween track configuration. */
38
+ export interface TweenTrackDef {
39
+ readonly type: 'tween';
40
+ readonly from: number;
41
+ readonly to: number;
42
+ readonly duration: number;
43
+ readonly ease?: EasingFn;
44
+ }
45
+ export type TrackDef = SpringTrackDef | TweenTrackDef;
46
+ /** Opaque timeline state — pass between step() calls. */
47
+ export interface TimelineState {
48
+ /** Elapsed time in milliseconds. */
49
+ readonly elapsedMs: number;
50
+ /** Per-track animation state. */
51
+ readonly tracks: Readonly<Record<string, TrackState>>;
52
+ }
53
+ /** @internal */
54
+ interface TrackState {
55
+ readonly type: 'spring' | 'tween';
56
+ readonly started: boolean;
57
+ readonly spring?: SpringState;
58
+ readonly tween?: TweenState;
59
+ readonly currentValue: number;
60
+ readonly done: boolean;
61
+ }
62
+ export interface TimelineBuilder {
63
+ /**
64
+ * Add an animation track.
65
+ *
66
+ * ```ts
67
+ * .add('x', { from: 0, to: 100, spring: 'wobbly' })
68
+ * .add('opacity', { type: 'tween', from: 0, to: 1, duration: 300 }, '<')
69
+ * ```
70
+ */
71
+ add(name: string, def: TrackDef, position?: Position): TimelineBuilder;
72
+ /**
73
+ * Add a named label at the current position (end of previous item).
74
+ *
75
+ * ```ts
76
+ * .label('reveal')
77
+ * .add('scale', { type: 'tween', from: 0, to: 1, duration: 200 }, 'reveal')
78
+ * ```
79
+ */
80
+ label(name: string): TimelineBuilder;
81
+ /**
82
+ * Add a named callback trigger at a position.
83
+ *
84
+ * ```ts
85
+ * .call('onHalfway', '+=500')
86
+ * ```
87
+ */
88
+ call(name: string, position?: Position): TimelineBuilder;
89
+ /** Compile the timeline into an immutable Timeline object. */
90
+ build(): Timeline;
91
+ }
92
+ /** Compiled timeline — step forward, read values, check completion. */
93
+ export interface Timeline {
94
+ /** Create the initial timeline state. */
95
+ init(): TimelineState;
96
+ /**
97
+ * Advance the timeline by `dt` seconds.
98
+ * Returns a new state (pure — no mutation).
99
+ */
100
+ step(state: TimelineState, dt: number): TimelineState;
101
+ /** Read all current track values as a name→number record. */
102
+ values(state: TimelineState): Record<string, number>;
103
+ /** True when every track has settled. */
104
+ done(state: TimelineState): boolean;
105
+ /**
106
+ * Return callback names that fired between `prev` and `next` states.
107
+ * A callback fires when the timeline's elapsed time crosses its trigger point.
108
+ */
109
+ firedCallbacks(prev: TimelineState, next: TimelineState): string[];
110
+ /** Total estimated duration in milliseconds. */
111
+ readonly estimatedDurationMs: number;
112
+ /** All track names in the timeline. */
113
+ readonly trackNames: readonly string[];
114
+ }
115
+ /**
116
+ * Create a new timeline builder.
117
+ *
118
+ * ```ts
119
+ * const tl = timeline()
120
+ * .add('x', { from: 0, to: 100, spring: 'wobbly' })
121
+ * .add('opacity', { type: 'tween', from: 0, to: 1, duration: 300 }, '<')
122
+ * .build();
123
+ * ```
124
+ */
125
+ export declare function timeline(): TimelineBuilder;
126
+ export {};
127
+ //# sourceMappingURL=timeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../src/timeline.ts"],"names":[],"mappings":"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"}