@effect-tui/core 0.1.0 → 0.1.4
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 +31 -11
- package/dist/ansi.d.ts +127 -32
- package/dist/ansi.d.ts.map +1 -1
- package/dist/ansi.js +159 -37
- package/dist/ansi.js.map +1 -1
- package/dist/colors.d.ts +139 -0
- package/dist/colors.d.ts.map +1 -0
- package/dist/colors.js +339 -0
- package/dist/colors.js.map +1 -0
- package/dist/index.d.ts +6 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -11
- package/dist/index.js.map +1 -1
- package/dist/keys.d.ts +21 -0
- package/dist/keys.d.ts.map +1 -1
- package/dist/keys.js +199 -58
- package/dist/keys.js.map +1 -1
- package/dist/layout/axis-helpers.d.ts +19 -0
- package/dist/layout/axis-helpers.d.ts.map +1 -0
- package/dist/layout/axis-helpers.js +19 -0
- package/dist/layout/axis-helpers.js.map +1 -0
- package/dist/output.d.ts +59 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +142 -0
- package/dist/output.js.map +1 -0
- package/dist/render/buffer.d.ts.map +1 -1
- package/dist/render/buffer.js +6 -25
- package/dist/render/buffer.js.map +1 -1
- package/dist/render/graphemes.d.ts +15 -0
- package/dist/render/graphemes.d.ts.map +1 -0
- package/dist/render/graphemes.js +28 -0
- package/dist/render/graphemes.js.map +1 -0
- package/dist/render/measure.d.ts +1 -0
- package/dist/render/measure.d.ts.map +1 -1
- package/dist/render/measure.js +14 -36
- package/dist/render/measure.js.map +1 -1
- package/dist/render/palette.d.ts.map +1 -1
- package/dist/render/palette.js +26 -1
- package/dist/render/palette.js.map +1 -1
- package/dist/render/segmenter.d.ts +8 -0
- package/dist/render/segmenter.d.ts.map +1 -0
- package/dist/render/segmenter.js +23 -0
- package/dist/render/segmenter.js.map +1 -0
- package/dist/render/surface.d.ts +6 -32
- package/dist/render/surface.d.ts.map +1 -1
- package/dist/render/surface.js +11 -80
- package/dist/render/surface.js.map +1 -1
- package/dist/runtime/backend_node.d.ts.map +1 -1
- package/dist/runtime/backend_node.js.map +1 -1
- package/dist/tailwind-colors.d.ts +291 -0
- package/dist/tailwind-colors.d.ts.map +1 -0
- package/dist/tailwind-colors.js +291 -0
- package/dist/tailwind-colors.js.map +1 -0
- package/dist/types.d.ts +15 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +55 -55
- package/src/ansi.ts +201 -73
- package/src/colors.ts +468 -0
- package/src/index.ts +28 -14
- package/src/keys.ts +467 -287
- package/src/layout/axis-helpers.ts +33 -0
- package/src/output.ts +175 -0
- package/src/render/buffer.ts +161 -184
- package/src/render/graphemes.ts +34 -0
- package/src/render/measure.ts +15 -38
- package/src/render/palette.ts +98 -77
- package/src/render/segmenter.ts +27 -0
- package/src/render/surface.ts +139 -225
- package/src/runtime/backend_node.ts +71 -71
- package/src/tailwind-colors.ts +295 -0
- package/src/types.ts +18 -0
- package/dist/anim.d.ts +0 -4
- package/dist/anim.d.ts.map +0 -1
- package/dist/anim.js +0 -5
- package/dist/anim.js.map +0 -1
- package/dist/layout/linearStack.d.ts +0 -17
- package/dist/layout/linearStack.d.ts.map +0 -1
- package/dist/layout/linearStack.js +0 -86
- package/dist/layout/linearStack.js.map +0 -1
- package/dist/motion-value.d.ts +0 -58
- package/dist/motion-value.d.ts.map +0 -1
- package/dist/motion-value.js +0 -250
- package/dist/motion-value.js.map +0 -1
- package/dist/present/display.d.ts +0 -58
- package/dist/present/display.d.ts.map +0 -1
- package/dist/present/display.js +0 -168
- package/dist/present/display.js.map +0 -1
- package/dist/present/writers/fullscreen.d.ts +0 -19
- package/dist/present/writers/fullscreen.d.ts.map +0 -1
- package/dist/present/writers/fullscreen.js +0 -55
- package/dist/present/writers/fullscreen.js.map +0 -1
- package/dist/present/writers/inline.d.ts +0 -20
- package/dist/present/writers/inline.d.ts.map +0 -1
- package/dist/present/writers/inline.js +0 -92
- package/dist/present/writers/inline.js.map +0 -1
- package/dist/render/color-utils.d.ts +0 -18
- package/dist/render/color-utils.d.ts.map +0 -1
- package/dist/render/color-utils.js +0 -58
- package/dist/render/color-utils.js.map +0 -1
- package/dist/render/diff.d.ts +0 -30
- package/dist/render/diff.d.ts.map +0 -1
- package/dist/render/diff.js +0 -83
- package/dist/render/diff.js.map +0 -1
- package/dist/spring-physics.d.ts +0 -36
- package/dist/spring-physics.d.ts.map +0 -1
- package/dist/spring-physics.js +0 -113
- package/dist/spring-physics.js.map +0 -1
- package/dist/spring.d.ts +0 -73
- package/dist/spring.d.ts.map +0 -1
- package/dist/spring.js +0 -136
- package/dist/spring.js.map +0 -1
- package/dist/ui/containers/canvas.d.ts +0 -13
- package/dist/ui/containers/canvas.d.ts.map +0 -1
- package/dist/ui/containers/canvas.js +0 -16
- package/dist/ui/containers/canvas.js.map +0 -1
- package/dist/ui/containers/geometry-reader.d.ts +0 -17
- package/dist/ui/containers/geometry-reader.d.ts.map +0 -1
- package/dist/ui/containers/geometry-reader.js +0 -24
- package/dist/ui/containers/geometry-reader.js.map +0 -1
- package/dist/ui/containers/hstack.d.ts +0 -12
- package/dist/ui/containers/hstack.d.ts.map +0 -1
- package/dist/ui/containers/hstack.js +0 -28
- package/dist/ui/containers/hstack.js.map +0 -1
- package/dist/ui/containers/scroll.d.ts +0 -28
- package/dist/ui/containers/scroll.d.ts.map +0 -1
- package/dist/ui/containers/scroll.js +0 -97
- package/dist/ui/containers/scroll.js.map +0 -1
- package/dist/ui/containers/shared.d.ts +0 -12
- package/dist/ui/containers/shared.d.ts.map +0 -1
- package/dist/ui/containers/shared.js +0 -19
- package/dist/ui/containers/shared.js.map +0 -1
- package/dist/ui/containers/vstack.d.ts +0 -12
- package/dist/ui/containers/vstack.d.ts.map +0 -1
- package/dist/ui/containers/vstack.js +0 -28
- package/dist/ui/containers/vstack.js.map +0 -1
- package/dist/ui/containers/zstack.d.ts +0 -14
- package/dist/ui/containers/zstack.d.ts.map +0 -1
- package/dist/ui/containers/zstack.js +0 -36
- package/dist/ui/containers/zstack.js.map +0 -1
- package/dist/ui/core/geometry-store.d.ts +0 -22
- package/dist/ui/core/geometry-store.d.ts.map +0 -1
- package/dist/ui/core/geometry-store.js +0 -29
- package/dist/ui/core/geometry-store.js.map +0 -1
- package/dist/ui/core/geometry.d.ts +0 -34
- package/dist/ui/core/geometry.d.ts.map +0 -1
- package/dist/ui/core/geometry.js +0 -14
- package/dist/ui/core/geometry.js.map +0 -1
- package/dist/ui/core/view.d.ts +0 -25
- package/dist/ui/core/view.d.ts.map +0 -1
- package/dist/ui/core/view.js +0 -34
- package/dist/ui/core/view.js.map +0 -1
- package/dist/ui/index.d.ts +0 -44
- package/dist/ui/index.d.ts.map +0 -1
- package/dist/ui/index.js +0 -39
- package/dist/ui/index.js.map +0 -1
- package/dist/ui/inlinetext.d.ts +0 -24
- package/dist/ui/inlinetext.d.ts.map +0 -1
- package/dist/ui/inlinetext.js +0 -131
- package/dist/ui/inlinetext.js.map +0 -1
- package/dist/ui/install.d.ts +0 -22
- package/dist/ui/install.d.ts.map +0 -1
- package/dist/ui/install.js +0 -66
- package/dist/ui/install.js.map +0 -1
- package/dist/ui/markdown.d.ts +0 -40
- package/dist/ui/markdown.d.ts.map +0 -1
- package/dist/ui/markdown.js +0 -351
- package/dist/ui/markdown.js.map +0 -1
- package/dist/ui/modifiers/border.d.ts +0 -33
- package/dist/ui/modifiers/border.d.ts.map +0 -1
- package/dist/ui/modifiers/border.js +0 -82
- package/dist/ui/modifiers/border.js.map +0 -1
- package/dist/ui/modifiers/fill.d.ts +0 -14
- package/dist/ui/modifiers/fill.d.ts.map +0 -1
- package/dist/ui/modifiers/fill.js +0 -25
- package/dist/ui/modifiers/fill.js.map +0 -1
- package/dist/ui/modifiers/frame.d.ts +0 -23
- package/dist/ui/modifiers/frame.d.ts.map +0 -1
- package/dist/ui/modifiers/frame.js +0 -54
- package/dist/ui/modifiers/frame.js.map +0 -1
- package/dist/ui/modifiers/offset.d.ts +0 -15
- package/dist/ui/modifiers/offset.d.ts.map +0 -1
- package/dist/ui/modifiers/offset.js +0 -21
- package/dist/ui/modifiers/offset.js.map +0 -1
- package/dist/ui/modifiers/opacity.d.ts +0 -15
- package/dist/ui/modifiers/opacity.d.ts.map +0 -1
- package/dist/ui/modifiers/opacity.js +0 -95
- package/dist/ui/modifiers/opacity.js.map +0 -1
- package/dist/ui/modifiers/padding.d.ts +0 -20
- package/dist/ui/modifiers/padding.d.ts.map +0 -1
- package/dist/ui/modifiers/padding.js +0 -36
- package/dist/ui/modifiers/padding.js.map +0 -1
- package/dist/ui/modifiers/styled.d.ts +0 -14
- package/dist/ui/modifiers/styled.d.ts.map +0 -1
- package/dist/ui/modifiers/styled.js +0 -26
- package/dist/ui/modifiers/styled.js.map +0 -1
- package/dist/ui/primitives/rectangle.d.ts +0 -15
- package/dist/ui/primitives/rectangle.d.ts.map +0 -1
- package/dist/ui/primitives/rectangle.js +0 -23
- package/dist/ui/primitives/rectangle.js.map +0 -1
- package/dist/ui/primitives/spacer.d.ts +0 -13
- package/dist/ui/primitives/spacer.d.ts.map +0 -1
- package/dist/ui/primitives/spacer.js +0 -16
- package/dist/ui/primitives/spacer.js.map +0 -1
- package/dist/ui/primitives/text.d.ts +0 -15
- package/dist/ui/primitives/text.d.ts.map +0 -1
- package/dist/ui/primitives/text.js +0 -79
- package/dist/ui/primitives/text.js.map +0 -1
- package/dist/ui/primitives/wrapped-text.d.ts +0 -30
- package/dist/ui/primitives/wrapped-text.d.ts.map +0 -1
- package/dist/ui/primitives/wrapped-text.js +0 -117
- package/dist/ui/primitives/wrapped-text.js.map +0 -1
- package/dist/ui/shinytext.d.ts +0 -66
- package/dist/ui/shinytext.d.ts.map +0 -1
- package/dist/ui/shinytext.js +0 -99
- package/dist/ui/shinytext.js.map +0 -1
- package/dist/ui/text/layout.d.ts +0 -35
- package/dist/ui/text/layout.d.ts.map +0 -1
- package/dist/ui/text/layout.js +0 -102
- package/dist/ui/text/layout.js.map +0 -1
- package/dist/ui/textinput.d.ts +0 -140
- package/dist/ui/textinput.d.ts.map +0 -1
- package/dist/ui/textinput.js +0 -402
- package/dist/ui/textinput.js.map +0 -1
- package/dist/ui/view-constructors.d.ts +0 -72
- package/dist/ui/view-constructors.d.ts.map +0 -1
- package/dist/ui/view-constructors.js +0 -74
- package/dist/ui/view-constructors.js.map +0 -1
- package/src/anim.ts +0 -5
- package/src/layout/linearStack.ts +0 -115
- package/src/motion-value.ts +0 -335
- package/src/present/display.ts +0 -206
- package/src/present/writers/fullscreen.ts +0 -58
- package/src/present/writers/inline.ts +0 -101
- package/src/render/color-utils.ts +0 -60
- package/src/render/diff.ts +0 -95
- package/src/spring-physics.ts +0 -151
- package/src/spring.ts +0 -234
- package/src/ui/__snapshots__/wrappedtext.test.ts.snap +0 -57
- package/src/ui/containers/canvas.ts +0 -18
- package/src/ui/containers/geometry-reader.ts +0 -32
- package/src/ui/containers/hstack.ts +0 -33
- package/src/ui/containers/scroll.ts +0 -106
- package/src/ui/containers/shared.ts +0 -27
- package/src/ui/containers/vstack.ts +0 -34
- package/src/ui/containers/zstack.ts +0 -37
- package/src/ui/core/geometry-store.ts +0 -42
- package/src/ui/core/geometry.ts +0 -30
- package/src/ui/core/view.ts +0 -49
- package/src/ui/index.ts +0 -84
- package/src/ui/inlinetext.ts +0 -135
- package/src/ui/install.ts +0 -110
- package/src/ui/markdown.test.ts +0 -74
- package/src/ui/markdown.ts +0 -388
- package/src/ui/modifiers/border.ts +0 -100
- package/src/ui/modifiers/fill.ts +0 -28
- package/src/ui/modifiers/frame.ts +0 -74
- package/src/ui/modifiers/offset.ts +0 -23
- package/src/ui/modifiers/opacity.ts +0 -93
- package/src/ui/modifiers/padding.ts +0 -53
- package/src/ui/modifiers/styled.ts +0 -31
- package/src/ui/primitives/rectangle.ts +0 -25
- package/src/ui/primitives/spacer.ts +0 -18
- package/src/ui/primitives/text.ts +0 -85
- package/src/ui/primitives/wrapped-text.ts +0 -131
- package/src/ui/shinytext.ts +0 -159
- package/src/ui/text/layout.ts +0 -119
- package/src/ui/textinput.ts +0 -496
- package/src/ui/view-constructors.ts +0 -96
- package/src/ui/wrappedtext.test.ts +0 -138
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import type { RunWriter } from "../../render/diff.js"
|
|
2
|
-
import type { Palette } from "../../render/surface.js"
|
|
3
|
-
import { ANSI } from "../../ansi.js"
|
|
4
|
-
|
|
5
|
-
type Seg = { col: number; style: number; text: string }
|
|
6
|
-
|
|
7
|
-
export class LineDiffWriter implements RunWriter {
|
|
8
|
-
private shadow = new Map<number, Seg[]>()
|
|
9
|
-
private printed = new Map<number, Seg[]>() // canonical content per row
|
|
10
|
-
private rows = 0
|
|
11
|
-
private baseRow = 0
|
|
12
|
-
|
|
13
|
-
constructor(private palette: Palette) {}
|
|
14
|
-
|
|
15
|
-
begin(vp: { cols: number; rows: number }) {
|
|
16
|
-
this.rows = vp.rows
|
|
17
|
-
this.shadow.clear()
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
setBaseRow(n: number) {
|
|
21
|
-
this.baseRow = Math.max(0, n | 0)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
run(row: number, col: number, style: number, text: string) {
|
|
25
|
-
if (!text) return
|
|
26
|
-
const list = this.shadow.get(row) ?? []
|
|
27
|
-
list.push({ col, style, text })
|
|
28
|
-
this.shadow.set(row, list)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
end() {
|
|
32
|
-
/* no-op */
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
private sameRow(a?: Seg[], b?: Seg[]): boolean {
|
|
36
|
-
if (!a && !b) return true
|
|
37
|
-
if (!a || !b || a.length !== b.length) return false
|
|
38
|
-
for (let i = 0; i < a.length; i++) {
|
|
39
|
-
const A = a[i],
|
|
40
|
-
B = b[i]
|
|
41
|
-
if (A.col !== B.col || A.style !== B.style || A.text !== B.text) return false
|
|
42
|
-
}
|
|
43
|
-
return true
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
flush(): string {
|
|
47
|
-
// Only process rows that the diff reported as changed.
|
|
48
|
-
// Unchanged rows are left intact to avoid accidental clearing.
|
|
49
|
-
const rowsToProcess = Array.from(this.shadow.keys()).sort((a, b) => a - b)
|
|
50
|
-
|
|
51
|
-
let out = ""
|
|
52
|
-
|
|
53
|
-
for (const row of rowsToProcess) {
|
|
54
|
-
const segs = (this.shadow.get(row) ?? []).sort((a, b) => a.col - b.col)
|
|
55
|
-
const prev = this.printed.get(row)
|
|
56
|
-
if (this.sameRow(prev, segs)) continue // nothing changed effectively
|
|
57
|
-
|
|
58
|
-
// coalesce adjacent segments with same style (keep this optimization)
|
|
59
|
-
const merged: Seg[] = []
|
|
60
|
-
for (const s of segs) {
|
|
61
|
-
const last = merged[merged.length - 1]
|
|
62
|
-
if (last && last.style === s.style && last.col + last.text.length === s.col) {
|
|
63
|
-
last.text += s.text
|
|
64
|
-
} else {
|
|
65
|
-
merged.push({ ...s })
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Move to absolute row (1-based) and place segments in-place.
|
|
70
|
-
// Avoid clearing the entire line; we only overwrite changed cells.
|
|
71
|
-
const termRow1 = this.baseRow + row + 1
|
|
72
|
-
out += ANSI.cursor.to(termRow1, 1)
|
|
73
|
-
|
|
74
|
-
let curStyle = -1
|
|
75
|
-
for (const s of merged) {
|
|
76
|
-
// Move to column (1-based) within the current row
|
|
77
|
-
out += ANSI.cursor.toCol(s.col + 1)
|
|
78
|
-
if (curStyle !== s.style) {
|
|
79
|
-
out += this.palette.sgr(s.style)
|
|
80
|
-
curStyle = s.style
|
|
81
|
-
}
|
|
82
|
-
out += s.text
|
|
83
|
-
}
|
|
84
|
-
if (curStyle !== -1) out += ANSI.reset
|
|
85
|
-
this.printed.set(row, merged)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Determine printed height (last row index with any segments + 1)
|
|
89
|
-
let lastRow = -1
|
|
90
|
-
for (const [r, segs] of this.printed) {
|
|
91
|
-
if (segs.length > 0 && r > lastRow) lastRow = r
|
|
92
|
-
}
|
|
93
|
-
const printedHeight = lastRow + 1
|
|
94
|
-
|
|
95
|
-
// Park cursor at bottom-left of our region (clamped to viewport)
|
|
96
|
-
const parkRow = Math.min(this.rows, Math.max(1, this.baseRow + (printedHeight > 0 ? printedHeight : 1)))
|
|
97
|
-
out += ANSI.cursor.to(parkRow, 1)
|
|
98
|
-
|
|
99
|
-
return out
|
|
100
|
-
}
|
|
101
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
// color-utils.ts - Shared color conversion utilities
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Clamp a number to the 0-255 range and convert to integer.
|
|
5
|
-
*/
|
|
6
|
-
export function clamp255(n: number): number {
|
|
7
|
-
return n < 0 ? 0 : n > 255 ? 255 : n | 0
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Convert a 256-color index to RGB values (approximate xterm palette).
|
|
12
|
-
*
|
|
13
|
-
* The 256-color palette is divided into:
|
|
14
|
-
* - 0-15: Standard ANSI colors (system colors)
|
|
15
|
-
* - 16-231: 6x6x6 RGB cube
|
|
16
|
-
* - 232-255: Grayscale ramp (24 shades)
|
|
17
|
-
*/
|
|
18
|
-
export function idxToRGB(idx: number): { r: number; g: number; b: number } {
|
|
19
|
-
if (idx < 0) idx = 0
|
|
20
|
-
if (idx > 255) idx = 255
|
|
21
|
-
|
|
22
|
-
// Standard ANSI colors (0-15)
|
|
23
|
-
if (idx < 16) {
|
|
24
|
-
const base: Array<[number, number, number]> = [
|
|
25
|
-
[0x00, 0x00, 0x00], // 0: black
|
|
26
|
-
[0x80, 0x00, 0x00], // 1: red
|
|
27
|
-
[0x00, 0x80, 0x00], // 2: green
|
|
28
|
-
[0x80, 0x80, 0x00], // 3: yellow
|
|
29
|
-
[0x00, 0x00, 0x80], // 4: blue
|
|
30
|
-
[0x80, 0x00, 0x80], // 5: magenta
|
|
31
|
-
[0x00, 0x80, 0x80], // 6: cyan
|
|
32
|
-
[0xc0, 0xc0, 0xc0], // 7: white
|
|
33
|
-
[0x80, 0x80, 0x80], // 8: bright black (gray)
|
|
34
|
-
[0xff, 0x00, 0x00], // 9: bright red
|
|
35
|
-
[0x00, 0xff, 0x00], // 10: bright green
|
|
36
|
-
[0xff, 0xff, 0x00], // 11: bright yellow
|
|
37
|
-
[0x00, 0x00, 0xff], // 12: bright blue
|
|
38
|
-
[0xff, 0x00, 0xff], // 13: bright magenta
|
|
39
|
-
[0x00, 0xff, 0xff], // 14: bright cyan
|
|
40
|
-
[0xff, 0xff, 0xff], // 15: bright white
|
|
41
|
-
]
|
|
42
|
-
const [r, g, b] = base[idx] ?? [0, 0, 0]
|
|
43
|
-
return { r, g, b }
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Grayscale ramp (232-255)
|
|
47
|
-
if (idx >= 232) {
|
|
48
|
-
const n = idx - 232 // 0..23
|
|
49
|
-
const v = 8 + n * 10
|
|
50
|
-
return { r: v, g: v, b: v }
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// 6x6x6 RGB cube (16-231)
|
|
54
|
-
const n = idx - 16 // 0..215
|
|
55
|
-
const r = Math.floor(n / 36) % 6
|
|
56
|
-
const g = Math.floor(n / 6) % 6
|
|
57
|
-
const b = n % 6
|
|
58
|
-
const steps = [0, 95, 135, 175, 215, 255]
|
|
59
|
-
return { r: steps[r] ?? 0, g: steps[g] ?? 0, b: steps[b] ?? 0 }
|
|
60
|
-
}
|
package/src/render/diff.ts
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import type { Surface } from "./surface.js"
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Writer that consumes style runs produced by diffing two frames.
|
|
5
|
-
* - Coordinates are 0-based: `row` in [0..rows), `col` in [0..cols).
|
|
6
|
-
* - `begin` is called once with the viewport size, then `run` zero or more times,
|
|
7
|
-
* then `end`. Implementations may buffer and expose the result via `flush`.
|
|
8
|
-
*/
|
|
9
|
-
export interface RunWriter {
|
|
10
|
-
begin(viewport: { cols: number; rows: number }): void
|
|
11
|
-
run(row: number, col: number, styleId: number, text: string): void
|
|
12
|
-
end(): void
|
|
13
|
-
flush(): string
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Stream a minimal set of style-runs for changes from prev → next.
|
|
18
|
-
*
|
|
19
|
-
* Algorithm (row-wise):
|
|
20
|
-
* - For each row, compute a tight change window [left, right]. Skip entirely if equal.
|
|
21
|
-
* - Within the window, emit contiguous runs grouped by style. A new run begins when:
|
|
22
|
-
* - A cell changes, and
|
|
23
|
-
* - Its style differs from the current run (or the run just started).
|
|
24
|
-
* - Wide glyph continuation cells (cw=0) are skipped so they are not reprinted.
|
|
25
|
-
*
|
|
26
|
-
* Assumes both Surfaces are same size and compares their back buffers (B).
|
|
27
|
-
*/
|
|
28
|
-
export function diffFrames(prev: Surface, next: Surface, w: RunWriter): void {
|
|
29
|
-
const width = next.w
|
|
30
|
-
const height = next.h
|
|
31
|
-
// Access back buffers (like Surface.flush does). B is private on Surface, so use `any`.
|
|
32
|
-
const gA = (prev as any).B.g as Uint32Array
|
|
33
|
-
const sA = (prev as any).B.s as Uint32Array
|
|
34
|
-
const gB = (next as any).B.g as Uint32Array
|
|
35
|
-
const sB = (next as any).B.s as Uint32Array
|
|
36
|
-
const cwB = (next as any).B.cw as Uint8Array
|
|
37
|
-
|
|
38
|
-
w.begin({ cols: width, rows: height })
|
|
39
|
-
|
|
40
|
-
for (let y = 0; y < height; y++) {
|
|
41
|
-
const row = y * width
|
|
42
|
-
|
|
43
|
-
// Fast skip window: [left..right] bounds that changed
|
|
44
|
-
let left = 0,
|
|
45
|
-
right = width - 1
|
|
46
|
-
while (left <= right) {
|
|
47
|
-
const i = row + left
|
|
48
|
-
if (gA[i] === gB[i] && sA[i] === sB[i]) left++
|
|
49
|
-
else break
|
|
50
|
-
}
|
|
51
|
-
if (left > right) continue
|
|
52
|
-
|
|
53
|
-
while (right >= left) {
|
|
54
|
-
const i = row + right
|
|
55
|
-
if (gA[i] === gB[i] && sA[i] === sB[i]) right--
|
|
56
|
-
else break
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Emit contiguous style runs within [left..right]
|
|
60
|
-
let x = left
|
|
61
|
-
while (x <= right) {
|
|
62
|
-
// Skip unchanged within window
|
|
63
|
-
while (x <= right) {
|
|
64
|
-
const i = row + x
|
|
65
|
-
if (gA[i] !== gB[i] || sA[i] !== sB[i]) break
|
|
66
|
-
x++
|
|
67
|
-
}
|
|
68
|
-
if (x > right) break
|
|
69
|
-
|
|
70
|
-
const style = sB[row + x]
|
|
71
|
-
const runX = x
|
|
72
|
-
let text = ""
|
|
73
|
-
|
|
74
|
-
while (x <= right) {
|
|
75
|
-
const i = row + x
|
|
76
|
-
if (sB[i] !== style) break
|
|
77
|
-
const cp = gB[i]
|
|
78
|
-
const ww = cwB[i]
|
|
79
|
-
if (ww !== 0) text += cp === 32 ? " " : String.fromCodePoint(cp)
|
|
80
|
-
x++
|
|
81
|
-
// skip continuation cells of wide glyphs from the next buffer
|
|
82
|
-
while (x <= right && cwB[row + x] === 0) x++
|
|
83
|
-
|
|
84
|
-
// Stop run if next cell is unchanged; diff window will reposition
|
|
85
|
-
if (x <= right) {
|
|
86
|
-
const j = row + x
|
|
87
|
-
if (gA[j] === gB[j] && sA[j] === sB[j]) break
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (text.length > 0) w.run(y, runX, style, text)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
w.end()
|
|
95
|
-
}
|
package/src/spring-physics.ts
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
// spring-physics.ts - Core spring physics primitives
|
|
2
|
-
|
|
3
|
-
export type SpringState = {
|
|
4
|
-
// current normalized position (e.g., 0..1)
|
|
5
|
-
x: number
|
|
6
|
-
// current velocity in units per second
|
|
7
|
-
v: number
|
|
8
|
-
// target normalized position
|
|
9
|
-
target: number
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export type SpringConfig = {
|
|
13
|
-
// natural frequency in Hz (cycles per second). e.g., 3 = fairly snappy
|
|
14
|
-
frequency?: number
|
|
15
|
-
// damping ratio (1 = critical damping, < 1 underdamped, > 1 overdamped)
|
|
16
|
-
dampingRatio?: number
|
|
17
|
-
// when |x-target| and |v| fall below epsilon, consider settled
|
|
18
|
-
epsilon?: number
|
|
19
|
-
// Optional UX-style controls akin to Motion libraries:
|
|
20
|
-
// approximate settle time in seconds (to ~2% band)
|
|
21
|
-
duration?: number
|
|
22
|
-
// bounce amount (0..1), maps to overshoot ratio; default 0 = no overshoot
|
|
23
|
-
bounce?: number
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const DEFAULTS: Required<Omit<SpringConfig, "duration">> & { duration?: number } = {
|
|
27
|
-
frequency: 3,
|
|
28
|
-
dampingRatio: 1,
|
|
29
|
-
epsilon: 0.0005,
|
|
30
|
-
duration: undefined,
|
|
31
|
-
bounce: 0,
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function fromDurationBounce(durationSec: number, bounce = 0): { frequency: number; dampingRatio: number } {
|
|
35
|
-
const d = Math.max(0.05, durationSec)
|
|
36
|
-
let zeta: number
|
|
37
|
-
const b = Math.max(0, Math.min(0.999, bounce))
|
|
38
|
-
if (b <= 0) {
|
|
39
|
-
zeta = 1 // critically damped, no overshoot
|
|
40
|
-
} else {
|
|
41
|
-
// Map overshoot ratio M_p=b to damping ratio via standard 2nd-order step response
|
|
42
|
-
const lnMp = Math.log(Math.max(1e-6, b))
|
|
43
|
-
zeta = Math.sqrt((lnMp * lnMp) / (Math.PI * Math.PI + lnMp * lnMp))
|
|
44
|
-
zeta = Math.max(0.02, Math.min(0.999, zeta))
|
|
45
|
-
}
|
|
46
|
-
// Use ~2% settling time approximation Ts ≈ 4/(ζ ω_n)
|
|
47
|
-
const omega = 4 / (Math.max(0.02, zeta) * d)
|
|
48
|
-
const freq = omega / (2 * Math.PI)
|
|
49
|
-
return { frequency: freq, dampingRatio: zeta }
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Advance a 1D damped spring towards its target using a semi-implicit Euler step.
|
|
54
|
-
* - Stable enough for small dt (<= ~1/30s) typical of frame ticks.
|
|
55
|
-
* - Uses frequency/dampingRatio to feel similar across frame rates.
|
|
56
|
-
*/
|
|
57
|
-
export function advanceSpring(
|
|
58
|
-
state: SpringState,
|
|
59
|
-
dtSeconds: number,
|
|
60
|
-
config?: SpringConfig,
|
|
61
|
-
): SpringState & { settled: boolean } {
|
|
62
|
-
let { frequency, dampingRatio, epsilon } = { ...DEFAULTS, ...(config ?? {}) }
|
|
63
|
-
const bounce = config?.bounce ?? DEFAULTS.bounce
|
|
64
|
-
// If duration is provided, derive frequency/dampingRatio from it and bounce
|
|
65
|
-
if (config?.duration != null) {
|
|
66
|
-
const derived = fromDurationBounce(config.duration, config.bounce ?? DEFAULTS.bounce)
|
|
67
|
-
frequency = derived.frequency
|
|
68
|
-
dampingRatio = derived.dampingRatio
|
|
69
|
-
}
|
|
70
|
-
const omega = Math.PI * 2 * Math.max(0.001, frequency)
|
|
71
|
-
const k = omega * omega // stiffness
|
|
72
|
-
const c = 2 * dampingRatio * omega // damping
|
|
73
|
-
|
|
74
|
-
let { x, v, target } = state
|
|
75
|
-
const prevX = x
|
|
76
|
-
if (dtSeconds <= 0)
|
|
77
|
-
return {
|
|
78
|
-
x,
|
|
79
|
-
v,
|
|
80
|
-
target,
|
|
81
|
-
settled: Math.abs(x - target) < epsilon && Math.abs(v) < epsilon,
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// semi-implicit Euler integration
|
|
85
|
-
const a = k * (target - x) - c * v
|
|
86
|
-
v += a * dtSeconds
|
|
87
|
-
x += v * dtSeconds
|
|
88
|
-
|
|
89
|
-
// With bounce=0 users expect a monotonic approach; clamp if we cross the target.
|
|
90
|
-
if (bounce <= 0) {
|
|
91
|
-
const crossed = (prevX - target) * (x - target) <= 0
|
|
92
|
-
if (crossed) {
|
|
93
|
-
x = target
|
|
94
|
-
v = 0
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// snap to target when sufficiently close to avoid long tails
|
|
99
|
-
const settled = Math.abs(x - target) < epsilon && Math.abs(v) < epsilon
|
|
100
|
-
if (settled) {
|
|
101
|
-
x = target
|
|
102
|
-
v = 0
|
|
103
|
-
}
|
|
104
|
-
return { x, v, target, settled }
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Internal exact spring step for 1D channel (frame-rate independent).
|
|
109
|
-
* Uses closed-form analytical solution for underdamped/critically-damped/overdamped cases.
|
|
110
|
-
*/
|
|
111
|
-
export function stepSpring1D(
|
|
112
|
-
x: number,
|
|
113
|
-
v: number,
|
|
114
|
-
target: number,
|
|
115
|
-
dt: number,
|
|
116
|
-
freq: number,
|
|
117
|
-
zeta: number,
|
|
118
|
-
): { x: number; v: number } {
|
|
119
|
-
const omega = Math.max(0.001, freq) * 2 * Math.PI
|
|
120
|
-
const A0 = x - target
|
|
121
|
-
const exp = Math.exp(-zeta * omega * dt)
|
|
122
|
-
if (zeta < 1) {
|
|
123
|
-
// Underdamped
|
|
124
|
-
const wd = omega * Math.sqrt(1 - zeta * zeta)
|
|
125
|
-
const cos = Math.cos(wd * dt)
|
|
126
|
-
const sin = Math.sin(wd * dt)
|
|
127
|
-
const B0 = v + zeta * omega * A0
|
|
128
|
-
const A = exp * (A0 * cos + (B0 / wd) * sin)
|
|
129
|
-
const dA = exp * (-zeta * omega * (A0 * cos + (B0 / wd) * sin) + (-A0 * wd * sin + B0 * cos))
|
|
130
|
-
return { x: target + A, v: dA }
|
|
131
|
-
} else if (zeta === 1) {
|
|
132
|
-
// Critically damped
|
|
133
|
-
const t = dt
|
|
134
|
-
const C2 = v + omega * A0
|
|
135
|
-
const A = exp * (A0 + C2 * t)
|
|
136
|
-
const dA = exp * (C2 - omega * (A0 + C2 * t))
|
|
137
|
-
return { x: target + A, v: dA }
|
|
138
|
-
} else {
|
|
139
|
-
// Overdamped
|
|
140
|
-
const s = Math.sqrt(zeta * zeta - 1)
|
|
141
|
-
const r1 = -omega * (zeta - s)
|
|
142
|
-
const r2 = -omega * (zeta + s)
|
|
143
|
-
const C1 = (v - r2 * A0) / (r1 - r2)
|
|
144
|
-
const C2 = A0 - C1
|
|
145
|
-
const e1 = Math.exp(r1 * dt)
|
|
146
|
-
const e2 = Math.exp(r2 * dt)
|
|
147
|
-
const A = C1 * e1 + C2 * e2
|
|
148
|
-
const dA = C1 * r1 * e1 + C2 * r2 * e2
|
|
149
|
-
return { x: target + A, v: dA }
|
|
150
|
-
}
|
|
151
|
-
}
|
package/src/spring.ts
DELETED
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
// spring.ts - Higher-level spring API for number and color values
|
|
2
|
-
|
|
3
|
-
import type { ColorLike, ColorValue } from "./render/surface.js"
|
|
4
|
-
import { parseColor } from "./render/surface.js"
|
|
5
|
-
import { idxToRGB } from "./render/color-utils.js"
|
|
6
|
-
import { advanceSpring, DEFAULTS, type SpringConfig, type SpringState } from "./spring-physics.js"
|
|
7
|
-
|
|
8
|
-
// ---- Types ----
|
|
9
|
-
|
|
10
|
-
export type SpringValue = SpringState & {
|
|
11
|
-
/** last timestamp in ms used for dt; hidden from callers */
|
|
12
|
-
_last?: number
|
|
13
|
-
/** default config for ticks (optional) */
|
|
14
|
-
_config?: SpringConfig
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export type SpringColorValue = {
|
|
18
|
-
x: { r: number; g: number; b: number }
|
|
19
|
-
v: { r: number; g: number; b: number }
|
|
20
|
-
target: { r: number; g: number; b: number }
|
|
21
|
-
_last?: number
|
|
22
|
-
_config?: SpringConfig
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export type AnySpringValue = SpringValue | SpringColorValue
|
|
26
|
-
|
|
27
|
-
export type SpringAutoOptions = SpringConfig & {
|
|
28
|
-
as?: "auto" | "number" | "color"
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// ---- Helpers ----
|
|
32
|
-
|
|
33
|
-
export function colorLikeToRGB(c: ColorLike): { r: number; g: number; b: number } {
|
|
34
|
-
const v = parseColor(c)
|
|
35
|
-
if (typeof v === "number") return idxToRGB(v)
|
|
36
|
-
return { r: v.r | 0, g: v.g | 0, b: v.b | 0 }
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function isColorSpring(s: AnySpringValue): s is SpringColorValue {
|
|
40
|
-
return typeof (s as any).x === "object"
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// ---- Number spring primitives ----
|
|
44
|
-
|
|
45
|
-
function initNumber(initial: number, cfg?: SpringConfig): SpringValue {
|
|
46
|
-
return {
|
|
47
|
-
x: initial,
|
|
48
|
-
v: 0,
|
|
49
|
-
target: initial,
|
|
50
|
-
...(cfg && { _config: cfg }),
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function tickNumber(s: SpringValue, nowMs: number, cfg?: SpringConfig): SpringValue {
|
|
55
|
-
const last = s._last ?? nowMs
|
|
56
|
-
const dt = Math.max(0, Math.min(0.1, (nowMs - last) / 1000))
|
|
57
|
-
const stepped = advanceSpring(s, dt, cfg ?? s._config)
|
|
58
|
-
return {
|
|
59
|
-
...s,
|
|
60
|
-
x: stepped.x,
|
|
61
|
-
v: stepped.v,
|
|
62
|
-
target: stepped.target,
|
|
63
|
-
_last: nowMs,
|
|
64
|
-
...(cfg || s._config ? { _config: cfg ?? s._config } : {}),
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function toNumber(s: SpringValue, target: number): SpringValue {
|
|
69
|
-
return { ...s, target }
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function valueNumber(s: SpringValue): number {
|
|
73
|
-
return s.x
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function settledNumber(s: SpringValue, eps = DEFAULTS.epsilon): boolean {
|
|
77
|
-
return Math.abs(s.x - s.target) < eps && Math.abs(s.v) < eps
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// ---- Color spring primitives ----
|
|
81
|
-
|
|
82
|
-
const SpringColor = {
|
|
83
|
-
init(initial: ColorLike, cfg?: SpringConfig): SpringColorValue {
|
|
84
|
-
const rgb = colorLikeToRGB(initial)
|
|
85
|
-
return {
|
|
86
|
-
x: { ...rgb },
|
|
87
|
-
v: { r: 0, g: 0, b: 0 },
|
|
88
|
-
target: { ...rgb },
|
|
89
|
-
...(cfg && { _config: cfg }),
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
to(s: SpringColorValue, target: ColorLike): SpringColorValue {
|
|
93
|
-
const rgb = colorLikeToRGB(target)
|
|
94
|
-
return { ...s, target: { ...rgb } }
|
|
95
|
-
},
|
|
96
|
-
tick(s: SpringColorValue, nowMs: number, cfg?: SpringConfig): SpringColorValue {
|
|
97
|
-
const last = s._last ?? nowMs
|
|
98
|
-
const dt = Math.max(0, Math.min(0.1, (nowMs - last) / 1000))
|
|
99
|
-
const rx = advanceSpring({ x: s.x.r, v: s.v.r, target: s.target.r }, dt, cfg ?? s._config)
|
|
100
|
-
const gx = advanceSpring({ x: s.x.g, v: s.v.g, target: s.target.g }, dt, cfg ?? s._config)
|
|
101
|
-
const bx = advanceSpring({ x: s.x.b, v: s.v.b, target: s.target.b }, dt, cfg ?? s._config)
|
|
102
|
-
return {
|
|
103
|
-
x: { r: rx.x, g: gx.x, b: bx.x },
|
|
104
|
-
v: { r: rx.v, g: gx.v, b: bx.v },
|
|
105
|
-
target: { r: s.target.r, g: s.target.g, b: s.target.b },
|
|
106
|
-
_last: nowMs,
|
|
107
|
-
...(cfg || s._config ? { _config: cfg ?? s._config } : {}),
|
|
108
|
-
}
|
|
109
|
-
},
|
|
110
|
-
value(s: SpringColorValue): ColorValue {
|
|
111
|
-
const clamp = (n: number) => (n < 0 ? 0 : n > 255 ? 255 : n | 0)
|
|
112
|
-
return { r: clamp(s.x.r), g: clamp(s.x.g), b: clamp(s.x.b) }
|
|
113
|
-
},
|
|
114
|
-
settled(s: SpringColorValue, eps = DEFAULTS.epsilon): boolean {
|
|
115
|
-
const dx = Math.abs(s.x.r - s.target.r) + Math.abs(s.x.g - s.target.g) + Math.abs(s.x.b - s.target.b)
|
|
116
|
-
const dv = Math.abs(s.v.r) + Math.abs(s.v.g) + Math.abs(s.v.b)
|
|
117
|
-
return dx < eps * 3 && dv < eps * 3
|
|
118
|
-
},
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// ---- Unified, type-agnostic helpers ----
|
|
122
|
-
|
|
123
|
-
// Overloads for the ergonomic, single-API surface
|
|
124
|
-
function create(initial: number, cfg?: SpringAutoOptions): SpringValue
|
|
125
|
-
function create(initial: ColorLike, cfg?: SpringAutoOptions): SpringColorValue
|
|
126
|
-
function create(initial: number | ColorLike, cfg?: SpringAutoOptions): AnySpringValue {
|
|
127
|
-
const as = cfg?.as ?? (typeof initial === "number" ? "number" : "color")
|
|
128
|
-
return as === "number" ? initNumber(initial as number, cfg) : SpringColor.init(initial as ColorLike, cfg)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function to(s: SpringValue, target: number): SpringValue
|
|
132
|
-
function to(s: SpringColorValue, target: ColorLike): SpringColorValue
|
|
133
|
-
function to(s: AnySpringValue, target: number | ColorLike): AnySpringValue {
|
|
134
|
-
return isColorSpring(s) ? SpringColor.to(s, target as ColorLike) : toNumber(s as SpringValue, target as number)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function tick(s: SpringValue, nowMs: number, cfg?: SpringConfig): SpringValue
|
|
138
|
-
function tick(s: SpringColorValue, nowMs: number, cfg?: SpringConfig): SpringColorValue
|
|
139
|
-
function tick(s: AnySpringValue, nowMs: number, cfg?: SpringConfig): AnySpringValue {
|
|
140
|
-
return isColorSpring(s) ? SpringColor.tick(s, nowMs, cfg) : tickNumber(s as SpringValue, nowMs, cfg)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function read(s: SpringValue): number
|
|
144
|
-
function read(s: SpringColorValue): ColorValue
|
|
145
|
-
function read(s: AnySpringValue): number | ColorValue {
|
|
146
|
-
return isColorSpring(s) ? SpringColor.value(s) : valueNumber(s as SpringValue)
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function isSettled(s: SpringValue, eps?: number): boolean
|
|
150
|
-
function isSettled(s: SpringColorValue, eps?: number): boolean
|
|
151
|
-
function isSettled(s: AnySpringValue, eps = DEFAULTS.epsilon): boolean {
|
|
152
|
-
return isColorSpring(s) ? SpringColor.settled(s, eps) : settledNumber(s as SpringValue, eps)
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// ---- Public Spring API ----
|
|
156
|
-
|
|
157
|
-
export type SpringAPI = {
|
|
158
|
-
// number
|
|
159
|
-
init(initial: number, cfg?: SpringConfig): SpringValue
|
|
160
|
-
to(s: SpringValue, target: number): SpringValue
|
|
161
|
-
tick(s: SpringValue, nowMs: number, cfg?: SpringConfig): SpringValue
|
|
162
|
-
value(s: SpringValue): number
|
|
163
|
-
settled(s: SpringValue, eps?: number): boolean
|
|
164
|
-
|
|
165
|
-
// color (legacy explicit path kept for convenience)
|
|
166
|
-
color: {
|
|
167
|
-
init(initial: ColorLike, cfg?: SpringConfig): SpringColorValue
|
|
168
|
-
to(s: SpringColorValue, target: ColorLike): SpringColorValue
|
|
169
|
-
tick(s: SpringColorValue, nowMs: number, cfg?: SpringConfig): SpringColorValue
|
|
170
|
-
value(s: SpringColorValue): ColorValue
|
|
171
|
-
settled(s: SpringColorValue, eps?: number): boolean
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// unified ergonomic API (overloads)
|
|
175
|
-
create(initial: number, cfg?: SpringAutoOptions): SpringValue
|
|
176
|
-
create(initial: ColorLike, cfg?: SpringAutoOptions): SpringColorValue
|
|
177
|
-
to(s: SpringColorValue, target: ColorLike): SpringColorValue // overload of number variant
|
|
178
|
-
tick(s: SpringColorValue, nowMs: number, cfg?: SpringConfig): SpringColorValue // overload of number variant
|
|
179
|
-
advance(s: SpringValue, nowMs: number, cfg?: SpringConfig): SpringValue
|
|
180
|
-
advance(s: SpringColorValue, nowMs: number, cfg?: SpringConfig): SpringColorValue
|
|
181
|
-
read(s: SpringValue): number
|
|
182
|
-
read(s: SpringColorValue): ColorValue
|
|
183
|
-
isSettled(s: SpringValue, eps?: number): boolean
|
|
184
|
-
isSettled(s: SpringColorValue, eps?: number): boolean
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export const Spring: SpringAPI = {
|
|
188
|
-
// number
|
|
189
|
-
init: initNumber,
|
|
190
|
-
to,
|
|
191
|
-
tick,
|
|
192
|
-
value: valueNumber,
|
|
193
|
-
settled: settledNumber,
|
|
194
|
-
|
|
195
|
-
// color (explicit path)
|
|
196
|
-
color: SpringColor,
|
|
197
|
-
|
|
198
|
-
// unified ergonomic API
|
|
199
|
-
create,
|
|
200
|
-
advance: tick,
|
|
201
|
-
read,
|
|
202
|
-
isSettled,
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// ---- Stepper (frame-based discrete animation) ----
|
|
206
|
-
|
|
207
|
-
export type Stepper = {
|
|
208
|
-
index: number
|
|
209
|
-
length: number
|
|
210
|
-
intervalMs: number
|
|
211
|
-
_accumMs: number
|
|
212
|
-
_last?: number
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
export const Step = {
|
|
216
|
-
init(length: number, intervalMs: number, startIndex = 0): Stepper {
|
|
217
|
-
return {
|
|
218
|
-
index: ((startIndex % length) + length) % length,
|
|
219
|
-
length: Math.max(1, length | 0),
|
|
220
|
-
intervalMs: Math.max(1, intervalMs | 0),
|
|
221
|
-
_accumMs: 0,
|
|
222
|
-
}
|
|
223
|
-
},
|
|
224
|
-
tick(s: Stepper, nowMs: number): Stepper {
|
|
225
|
-
const last = s._last ?? nowMs
|
|
226
|
-
let accum = s._accumMs + Math.max(0, nowMs - last)
|
|
227
|
-
let idx = s.index
|
|
228
|
-
while (accum >= s.intervalMs) {
|
|
229
|
-
accum -= s.intervalMs
|
|
230
|
-
idx = (idx + 1) % s.length
|
|
231
|
-
}
|
|
232
|
-
return { ...s, index: idx, _accumMs: accum, _last: nowMs }
|
|
233
|
-
},
|
|
234
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
-
|
|
3
|
-
exports[`WrappedText > basic word wrapping > should handle single words longer than width 1`] = `
|
|
4
|
-
"verylo
|
|
5
|
-
ngword
|
|
6
|
-
"
|
|
7
|
-
`;
|
|
8
|
-
|
|
9
|
-
exports[`WrappedText > basic word wrapping > should preserve explicit line breaks 1`] = `
|
|
10
|
-
"line1
|
|
11
|
-
line2
|
|
12
|
-
line3
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
`;
|
|
16
|
-
|
|
17
|
-
exports[`WrappedText > basic word wrapping > should wrap text at word boundaries 1`] = `
|
|
18
|
-
"hello
|
|
19
|
-
world
|
|
20
|
-
test"
|
|
21
|
-
`;
|
|
22
|
-
|
|
23
|
-
exports[`WrappedText > wrapping strategies > should break words when breakWords is enabled 1`] = `
|
|
24
|
-
"supercalif
|
|
25
|
-
ragilistic
|
|
26
|
-
expialidoc
|
|
27
|
-
ious
|
|
28
|
-
"
|
|
29
|
-
`;
|
|
30
|
-
|
|
31
|
-
exports[`WrappedText basic word wrapping should wrap text at word boundaries 1`] = `
|
|
32
|
-
"hello
|
|
33
|
-
world
|
|
34
|
-
test"
|
|
35
|
-
`;
|
|
36
|
-
|
|
37
|
-
exports[`WrappedText basic word wrapping should handle single words longer than width 1`] = `
|
|
38
|
-
"verylo
|
|
39
|
-
ngword
|
|
40
|
-
"
|
|
41
|
-
`;
|
|
42
|
-
|
|
43
|
-
exports[`WrappedText basic word wrapping should preserve explicit line breaks 1`] = `
|
|
44
|
-
"line1
|
|
45
|
-
line2
|
|
46
|
-
line3
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
`;
|
|
50
|
-
|
|
51
|
-
exports[`WrappedText wrapping strategies should break words when breakWords is enabled 1`] = `
|
|
52
|
-
"supercalif
|
|
53
|
-
ragilistic
|
|
54
|
-
expialidoc
|
|
55
|
-
ious
|
|
56
|
-
"
|
|
57
|
-
`;
|