@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.
- package/README.md +221 -30
- package/dist/animate.d.ts +60 -0
- package/dist/animate.d.ts.map +1 -0
- package/dist/animate.js +98 -0
- package/dist/animate.js.map +1 -0
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +2 -2
- package/dist/commands.js.map +1 -1
- package/dist/eventbus.d.ts +69 -0
- package/dist/eventbus.d.ts.map +1 -0
- package/dist/eventbus.js +120 -0
- package/dist/eventbus.js.map +1 -0
- package/dist/flex.d.ts +64 -0
- package/dist/flex.d.ts.map +1 -0
- package/dist/flex.js +261 -0
- package/dist/flex.js.map +1 -0
- package/dist/help.d.ts +58 -0
- package/dist/help.d.ts.map +1 -0
- package/dist/help.js +104 -0
- package/dist/help.js.map +1 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -1
- package/dist/index.js.map +1 -1
- package/dist/inputstack.d.ts +79 -0
- package/dist/inputstack.d.ts.map +1 -0
- package/dist/inputstack.js +81 -0
- package/dist/inputstack.js.map +1 -0
- package/dist/keybindings.d.ts +83 -0
- package/dist/keybindings.d.ts.map +1 -0
- package/dist/keybindings.js +184 -0
- package/dist/keybindings.js.map +1 -0
- package/dist/layout.d.ts +0 -1
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +3 -4
- package/dist/layout.js.map +1 -1
- package/dist/runtime.d.ts +3 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +24 -37
- package/dist/runtime.js.map +1 -1
- package/dist/screen.d.ts +10 -4
- package/dist/screen.d.ts.map +1 -1
- package/dist/screen.js +13 -11
- package/dist/screen.js.map +1 -1
- package/dist/spring.d.ts +139 -0
- package/dist/spring.d.ts.map +1 -0
- package/dist/spring.js +106 -0
- package/dist/spring.js.map +1 -0
- package/dist/timeline.d.ts +127 -0
- package/dist/timeline.d.ts.map +1 -0
- package/dist/timeline.js +298 -0
- package/dist/timeline.js.map +1 -0
- package/dist/types.d.ts +11 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/viewport.d.ts +64 -0
- package/dist/viewport.d.ts.map +1 -0
- package/dist/viewport.js +162 -0
- package/dist/viewport.js.map +1 -0
- 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
|
-
//
|
|
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
|
-
|
|
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
|
-
//
|
|
75
|
-
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
//
|
|
79
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
91
|
+
// Cleanup — bus disposes all I/O connections
|
|
92
|
+
bus.dispose();
|
|
106
93
|
if (useAltScreen || useHideCursor) {
|
|
107
94
|
exitScreen(ctx.io);
|
|
108
95
|
}
|
package/dist/runtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,
|
|
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
|
-
*
|
|
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
|
package/dist/screen.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
package/dist/screen.js.map
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/spring.d.ts
ADDED
|
@@ -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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,QAAQ,EAQb,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AAMrB,wEAAwE;AACxE,MAAM,MAAM,QAAQ,GAChB,MAAM,GACN,GAAG,GACH,MAAM,MAAM,EAAE,GACd,KAAK,MAAM,EAAE,GACb,KAAK,MAAM,EAAE,GACb,IAAI,MAAM,EAAE,GACZ,MAAM,CAAC;AAEX,kCAAkC;AAClC,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY,CAAC;CACxD;AAED,iCAAiC;AACjC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC;CAC1B;AAED,MAAM,MAAM,QAAQ,GAAG,cAAc,GAAG,aAAa,CAAC;AAEtD,yDAAyD;AACzD,MAAM,WAAW,aAAa;IAC5B,oCAAoC;IACpC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,iCAAiC;IACjC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;CACvD;AAED,gBAAgB;AAChB,UAAU,UAAU;IAClB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB;AAsHD,MAAM,WAAW,eAAe;IAC9B;;;;;;;OAOG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,eAAe,CAAC;IAEvE;;;;;;;OAOG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAAC;IAErC;;;;;;OAMG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,eAAe,CAAC;IAEzD,8DAA8D;IAC9D,KAAK,IAAI,QAAQ,CAAC;CACnB;AAED,uEAAuE;AACvE,MAAM,WAAW,QAAQ;IACvB,yCAAyC;IACzC,IAAI,IAAI,aAAa,CAAC;IAEtB;;;OAGG;IACH,IAAI,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,GAAG,aAAa,CAAC;IAEtD,6DAA6D;IAC7D,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAErD,yCAAyC;IACzC,IAAI,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC;IAEpC;;;OAGG;IACH,cAAc,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,GAAG,MAAM,EAAE,CAAC;IAEnE,gDAAgD;IAChD,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IAErC,uCAAuC;IACvC,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAMD;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,IAAI,eAAe,CAsB1C"}
|