@vsuryav/agent-sim 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -0
- package/bin/agent-sim.js +25 -0
- package/package.json +72 -0
- package/src/app-paths.ts +29 -0
- package/src/app-sync.test.ts +75 -0
- package/src/app-sync.ts +110 -0
- package/src/cli.ts +129 -0
- package/src/collector/claude-code.test.ts +102 -0
- package/src/collector/claude-code.ts +133 -0
- package/src/collector/codex-cli.test.ts +116 -0
- package/src/collector/codex-cli.ts +149 -0
- package/src/collector/db.test.ts +59 -0
- package/src/collector/db.ts +125 -0
- package/src/collector/names.test.ts +21 -0
- package/src/collector/names.ts +28 -0
- package/src/collector/personality.test.ts +40 -0
- package/src/collector/personality.ts +46 -0
- package/src/collector/remote-sync.test.ts +31 -0
- package/src/collector/remote-sync.ts +171 -0
- package/src/collector/sync.test.ts +67 -0
- package/src/collector/sync.ts +148 -0
- package/src/collector/types.ts +1 -0
- package/src/engine/bootstrap/state.ts +3 -0
- package/src/engine/buddy/CompanionSprite.tsx +371 -0
- package/src/engine/buddy/companion.ts +133 -0
- package/src/engine/buddy/prompt.ts +36 -0
- package/src/engine/buddy/sprites.ts +514 -0
- package/src/engine/buddy/types.ts +148 -0
- package/src/engine/buddy/useBuddyNotification.tsx +98 -0
- package/src/engine/ink/Ansi.tsx +292 -0
- package/src/engine/ink/bidi.ts +139 -0
- package/src/engine/ink/clearTerminal.ts +74 -0
- package/src/engine/ink/colorize.ts +231 -0
- package/src/engine/ink/components/AlternateScreen.tsx +80 -0
- package/src/engine/ink/components/App.tsx +658 -0
- package/src/engine/ink/components/AppContext.ts +21 -0
- package/src/engine/ink/components/Box.tsx +214 -0
- package/src/engine/ink/components/Button.tsx +192 -0
- package/src/engine/ink/components/ClockContext.tsx +112 -0
- package/src/engine/ink/components/CursorDeclarationContext.ts +32 -0
- package/src/engine/ink/components/ErrorOverview.tsx +109 -0
- package/src/engine/ink/components/Link.tsx +42 -0
- package/src/engine/ink/components/Newline.tsx +39 -0
- package/src/engine/ink/components/NoSelect.tsx +68 -0
- package/src/engine/ink/components/RawAnsi.tsx +57 -0
- package/src/engine/ink/components/ScrollBox.tsx +237 -0
- package/src/engine/ink/components/Spacer.tsx +20 -0
- package/src/engine/ink/components/StdinContext.ts +49 -0
- package/src/engine/ink/components/TerminalFocusContext.tsx +52 -0
- package/src/engine/ink/components/TerminalSizeContext.tsx +7 -0
- package/src/engine/ink/components/Text.tsx +254 -0
- package/src/engine/ink/constants.ts +2 -0
- package/src/engine/ink/dom.ts +484 -0
- package/src/engine/ink/events/click-event.ts +38 -0
- package/src/engine/ink/events/dispatcher.ts +233 -0
- package/src/engine/ink/events/emitter.ts +39 -0
- package/src/engine/ink/events/event-handlers.ts +73 -0
- package/src/engine/ink/events/event.ts +11 -0
- package/src/engine/ink/events/focus-event.ts +21 -0
- package/src/engine/ink/events/input-event.ts +205 -0
- package/src/engine/ink/events/keyboard-event.ts +51 -0
- package/src/engine/ink/events/terminal-event.ts +107 -0
- package/src/engine/ink/events/terminal-focus-event.ts +19 -0
- package/src/engine/ink/focus.ts +181 -0
- package/src/engine/ink/frame.ts +124 -0
- package/src/engine/ink/get-max-width.ts +27 -0
- package/src/engine/ink/global.d.ts +18 -0
- package/src/engine/ink/hit-test.ts +130 -0
- package/src/engine/ink/hooks/use-animation-frame.ts +57 -0
- package/src/engine/ink/hooks/use-app.ts +8 -0
- package/src/engine/ink/hooks/use-declared-cursor.ts +73 -0
- package/src/engine/ink/hooks/use-input.ts +92 -0
- package/src/engine/ink/hooks/use-interval.ts +67 -0
- package/src/engine/ink/hooks/use-search-highlight.ts +53 -0
- package/src/engine/ink/hooks/use-selection.ts +104 -0
- package/src/engine/ink/hooks/use-stdin.ts +8 -0
- package/src/engine/ink/hooks/use-tab-status.ts +72 -0
- package/src/engine/ink/hooks/use-terminal-focus.ts +16 -0
- package/src/engine/ink/hooks/use-terminal-title.ts +31 -0
- package/src/engine/ink/hooks/use-terminal-viewport.ts +96 -0
- package/src/engine/ink/ink.tsx +1723 -0
- package/src/engine/ink/instances.ts +10 -0
- package/src/engine/ink/layout/engine.ts +6 -0
- package/src/engine/ink/layout/geometry.ts +97 -0
- package/src/engine/ink/layout/node.ts +152 -0
- package/src/engine/ink/layout/yoga.ts +308 -0
- package/src/engine/ink/line-width-cache.ts +24 -0
- package/src/engine/ink/log-update.ts +773 -0
- package/src/engine/ink/measure-element.ts +23 -0
- package/src/engine/ink/measure-text.ts +47 -0
- package/src/engine/ink/node-cache.ts +54 -0
- package/src/engine/ink/optimizer.ts +93 -0
- package/src/engine/ink/output.ts +797 -0
- package/src/engine/ink/parse-keypress.ts +801 -0
- package/src/engine/ink/reconciler.ts +512 -0
- package/src/engine/ink/render-border.ts +231 -0
- package/src/engine/ink/render-node-to-output.ts +1462 -0
- package/src/engine/ink/render-to-screen.ts +231 -0
- package/src/engine/ink/renderer.ts +178 -0
- package/src/engine/ink/root.ts +184 -0
- package/src/engine/ink/screen.ts +1486 -0
- package/src/engine/ink/searchHighlight.ts +93 -0
- package/src/engine/ink/selection.ts +917 -0
- package/src/engine/ink/squash-text-nodes.ts +92 -0
- package/src/engine/ink/stringWidth.ts +222 -0
- package/src/engine/ink/styles.ts +771 -0
- package/src/engine/ink/supports-hyperlinks.ts +57 -0
- package/src/engine/ink/tabstops.ts +46 -0
- package/src/engine/ink/terminal-focus-state.ts +47 -0
- package/src/engine/ink/terminal-querier.ts +212 -0
- package/src/engine/ink/terminal.ts +248 -0
- package/src/engine/ink/termio/ansi.ts +75 -0
- package/src/engine/ink/termio/csi.ts +319 -0
- package/src/engine/ink/termio/dec.ts +60 -0
- package/src/engine/ink/termio/esc.ts +67 -0
- package/src/engine/ink/termio/osc.ts +493 -0
- package/src/engine/ink/termio/parser.ts +394 -0
- package/src/engine/ink/termio/sgr.ts +308 -0
- package/src/engine/ink/termio/tokenize.ts +319 -0
- package/src/engine/ink/termio/types.ts +236 -0
- package/src/engine/ink/useTerminalNotification.ts +126 -0
- package/src/engine/ink/warn.ts +9 -0
- package/src/engine/ink/widest-line.ts +19 -0
- package/src/engine/ink/wrap-text.ts +74 -0
- package/src/engine/ink/wrapAnsi.ts +20 -0
- package/src/engine/native-ts/yoga-layout/enums.ts +134 -0
- package/src/engine/native-ts/yoga-layout/index.ts +2578 -0
- package/src/engine/stubs/bootstrap-state.ts +4 -0
- package/src/engine/stubs/debug.ts +6 -0
- package/src/engine/stubs/log.ts +4 -0
- package/src/engine/utils/debug.ts +5 -0
- package/src/engine/utils/earlyInput.ts +4 -0
- package/src/engine/utils/env.ts +15 -0
- package/src/engine/utils/envUtils.ts +4 -0
- package/src/engine/utils/execFileNoThrow.ts +24 -0
- package/src/engine/utils/fullscreen.ts +4 -0
- package/src/engine/utils/intl.ts +9 -0
- package/src/engine/utils/log.ts +3 -0
- package/src/engine/utils/semver.ts +13 -0
- package/src/engine/utils/sliceAnsi.ts +10 -0
- package/src/engine/utils/theme.ts +17 -0
- package/src/game/App.tsx +141 -0
- package/src/game/agents/behavior.ts +249 -0
- package/src/game/agents/speech.ts +57 -0
- package/src/game/canvas.ts +98 -0
- package/src/game/launch.ts +36 -0
- package/src/game/ship/ShipView.tsx +145 -0
- package/src/game/ship/ship-map.ts +172 -0
- package/src/game/ui/AgentBio.tsx +72 -0
- package/src/game/ui/HUD.tsx +63 -0
- package/src/game/ui/StatusBar.tsx +49 -0
- package/src/game/useKeyboard.ts +62 -0
- package/src/main.tsx +22 -0
- package/src/run-interactive.ts +74 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Store all instances of Ink (instance.js) to ensure that consecutive render() calls
|
|
2
|
+
// use the same instance of Ink and don't create a new one
|
|
3
|
+
//
|
|
4
|
+
// This map has to be stored in a separate file, because render.js creates instances,
|
|
5
|
+
// but instance.js should delete itself from the map on unmount
|
|
6
|
+
|
|
7
|
+
import type Ink from './ink.js'
|
|
8
|
+
|
|
9
|
+
const instances = new Map<NodeJS.WriteStream, Ink>()
|
|
10
|
+
export default instances
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
export type Point = {
|
|
2
|
+
x: number
|
|
3
|
+
y: number
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export type Size = {
|
|
7
|
+
width: number
|
|
8
|
+
height: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type Rectangle = Point & Size
|
|
12
|
+
|
|
13
|
+
/** Edge insets (padding, margin, border) */
|
|
14
|
+
export type Edges = {
|
|
15
|
+
top: number
|
|
16
|
+
right: number
|
|
17
|
+
bottom: number
|
|
18
|
+
left: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Create uniform edges */
|
|
22
|
+
export function edges(all: number): Edges
|
|
23
|
+
export function edges(vertical: number, horizontal: number): Edges
|
|
24
|
+
export function edges(
|
|
25
|
+
top: number,
|
|
26
|
+
right: number,
|
|
27
|
+
bottom: number,
|
|
28
|
+
left: number,
|
|
29
|
+
): Edges
|
|
30
|
+
export function edges(a: number, b?: number, c?: number, d?: number): Edges {
|
|
31
|
+
if (b === undefined) {
|
|
32
|
+
return { top: a, right: a, bottom: a, left: a }
|
|
33
|
+
}
|
|
34
|
+
if (c === undefined) {
|
|
35
|
+
return { top: a, right: b, bottom: a, left: b }
|
|
36
|
+
}
|
|
37
|
+
return { top: a, right: b, bottom: c, left: d! }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Add two edge values */
|
|
41
|
+
export function addEdges(a: Edges, b: Edges): Edges {
|
|
42
|
+
return {
|
|
43
|
+
top: a.top + b.top,
|
|
44
|
+
right: a.right + b.right,
|
|
45
|
+
bottom: a.bottom + b.bottom,
|
|
46
|
+
left: a.left + b.left,
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Zero edges constant */
|
|
51
|
+
export const ZERO_EDGES: Edges = { top: 0, right: 0, bottom: 0, left: 0 }
|
|
52
|
+
|
|
53
|
+
/** Convert partial edges to full edges with defaults */
|
|
54
|
+
export function resolveEdges(partial?: Partial<Edges>): Edges {
|
|
55
|
+
return {
|
|
56
|
+
top: partial?.top ?? 0,
|
|
57
|
+
right: partial?.right ?? 0,
|
|
58
|
+
bottom: partial?.bottom ?? 0,
|
|
59
|
+
left: partial?.left ?? 0,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function unionRect(a: Rectangle, b: Rectangle): Rectangle {
|
|
64
|
+
const minX = Math.min(a.x, b.x)
|
|
65
|
+
const minY = Math.min(a.y, b.y)
|
|
66
|
+
const maxX = Math.max(a.x + a.width, b.x + b.width)
|
|
67
|
+
const maxY = Math.max(a.y + a.height, b.y + b.height)
|
|
68
|
+
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function clampRect(rect: Rectangle, size: Size): Rectangle {
|
|
72
|
+
const minX = Math.max(0, rect.x)
|
|
73
|
+
const minY = Math.max(0, rect.y)
|
|
74
|
+
const maxX = Math.min(size.width - 1, rect.x + rect.width - 1)
|
|
75
|
+
const maxY = Math.min(size.height - 1, rect.y + rect.height - 1)
|
|
76
|
+
return {
|
|
77
|
+
x: minX,
|
|
78
|
+
y: minY,
|
|
79
|
+
width: Math.max(0, maxX - minX + 1),
|
|
80
|
+
height: Math.max(0, maxY - minY + 1),
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function withinBounds(size: Size, point: Point): boolean {
|
|
85
|
+
return (
|
|
86
|
+
point.x >= 0 &&
|
|
87
|
+
point.y >= 0 &&
|
|
88
|
+
point.x < size.width &&
|
|
89
|
+
point.y < size.height
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function clamp(value: number, min?: number, max?: number): number {
|
|
94
|
+
if (min !== undefined && value < min) return min
|
|
95
|
+
if (max !== undefined && value > max) return max
|
|
96
|
+
return value
|
|
97
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// --
|
|
2
|
+
// Adapter interface for the layout engine (Yoga)
|
|
3
|
+
|
|
4
|
+
export const LayoutEdge = {
|
|
5
|
+
All: 'all',
|
|
6
|
+
Horizontal: 'horizontal',
|
|
7
|
+
Vertical: 'vertical',
|
|
8
|
+
Left: 'left',
|
|
9
|
+
Right: 'right',
|
|
10
|
+
Top: 'top',
|
|
11
|
+
Bottom: 'bottom',
|
|
12
|
+
Start: 'start',
|
|
13
|
+
End: 'end',
|
|
14
|
+
} as const
|
|
15
|
+
export type LayoutEdge = (typeof LayoutEdge)[keyof typeof LayoutEdge]
|
|
16
|
+
|
|
17
|
+
export const LayoutGutter = {
|
|
18
|
+
All: 'all',
|
|
19
|
+
Column: 'column',
|
|
20
|
+
Row: 'row',
|
|
21
|
+
} as const
|
|
22
|
+
export type LayoutGutter = (typeof LayoutGutter)[keyof typeof LayoutGutter]
|
|
23
|
+
|
|
24
|
+
export const LayoutDisplay = {
|
|
25
|
+
Flex: 'flex',
|
|
26
|
+
None: 'none',
|
|
27
|
+
} as const
|
|
28
|
+
export type LayoutDisplay = (typeof LayoutDisplay)[keyof typeof LayoutDisplay]
|
|
29
|
+
|
|
30
|
+
export const LayoutFlexDirection = {
|
|
31
|
+
Row: 'row',
|
|
32
|
+
RowReverse: 'row-reverse',
|
|
33
|
+
Column: 'column',
|
|
34
|
+
ColumnReverse: 'column-reverse',
|
|
35
|
+
} as const
|
|
36
|
+
export type LayoutFlexDirection =
|
|
37
|
+
(typeof LayoutFlexDirection)[keyof typeof LayoutFlexDirection]
|
|
38
|
+
|
|
39
|
+
export const LayoutAlign = {
|
|
40
|
+
Auto: 'auto',
|
|
41
|
+
Stretch: 'stretch',
|
|
42
|
+
FlexStart: 'flex-start',
|
|
43
|
+
Center: 'center',
|
|
44
|
+
FlexEnd: 'flex-end',
|
|
45
|
+
} as const
|
|
46
|
+
export type LayoutAlign = (typeof LayoutAlign)[keyof typeof LayoutAlign]
|
|
47
|
+
|
|
48
|
+
export const LayoutJustify = {
|
|
49
|
+
FlexStart: 'flex-start',
|
|
50
|
+
Center: 'center',
|
|
51
|
+
FlexEnd: 'flex-end',
|
|
52
|
+
SpaceBetween: 'space-between',
|
|
53
|
+
SpaceAround: 'space-around',
|
|
54
|
+
SpaceEvenly: 'space-evenly',
|
|
55
|
+
} as const
|
|
56
|
+
export type LayoutJustify = (typeof LayoutJustify)[keyof typeof LayoutJustify]
|
|
57
|
+
|
|
58
|
+
export const LayoutWrap = {
|
|
59
|
+
NoWrap: 'nowrap',
|
|
60
|
+
Wrap: 'wrap',
|
|
61
|
+
WrapReverse: 'wrap-reverse',
|
|
62
|
+
} as const
|
|
63
|
+
export type LayoutWrap = (typeof LayoutWrap)[keyof typeof LayoutWrap]
|
|
64
|
+
|
|
65
|
+
export const LayoutPositionType = {
|
|
66
|
+
Relative: 'relative',
|
|
67
|
+
Absolute: 'absolute',
|
|
68
|
+
} as const
|
|
69
|
+
export type LayoutPositionType =
|
|
70
|
+
(typeof LayoutPositionType)[keyof typeof LayoutPositionType]
|
|
71
|
+
|
|
72
|
+
export const LayoutOverflow = {
|
|
73
|
+
Visible: 'visible',
|
|
74
|
+
Hidden: 'hidden',
|
|
75
|
+
Scroll: 'scroll',
|
|
76
|
+
} as const
|
|
77
|
+
export type LayoutOverflow =
|
|
78
|
+
(typeof LayoutOverflow)[keyof typeof LayoutOverflow]
|
|
79
|
+
|
|
80
|
+
export type LayoutMeasureFunc = (
|
|
81
|
+
width: number,
|
|
82
|
+
widthMode: LayoutMeasureMode,
|
|
83
|
+
) => { width: number; height: number }
|
|
84
|
+
|
|
85
|
+
export const LayoutMeasureMode = {
|
|
86
|
+
Undefined: 'undefined',
|
|
87
|
+
Exactly: 'exactly',
|
|
88
|
+
AtMost: 'at-most',
|
|
89
|
+
} as const
|
|
90
|
+
export type LayoutMeasureMode =
|
|
91
|
+
(typeof LayoutMeasureMode)[keyof typeof LayoutMeasureMode]
|
|
92
|
+
|
|
93
|
+
export type LayoutNode = {
|
|
94
|
+
// Tree
|
|
95
|
+
insertChild(child: LayoutNode, index: number): void
|
|
96
|
+
removeChild(child: LayoutNode): void
|
|
97
|
+
getChildCount(): number
|
|
98
|
+
getParent(): LayoutNode | null
|
|
99
|
+
|
|
100
|
+
// Layout computation
|
|
101
|
+
calculateLayout(width?: number, height?: number): void
|
|
102
|
+
setMeasureFunc(fn: LayoutMeasureFunc): void
|
|
103
|
+
unsetMeasureFunc(): void
|
|
104
|
+
markDirty(): void
|
|
105
|
+
|
|
106
|
+
// Layout reading (post-layout)
|
|
107
|
+
getComputedLeft(): number
|
|
108
|
+
getComputedTop(): number
|
|
109
|
+
getComputedWidth(): number
|
|
110
|
+
getComputedHeight(): number
|
|
111
|
+
getComputedBorder(edge: LayoutEdge): number
|
|
112
|
+
getComputedPadding(edge: LayoutEdge): number
|
|
113
|
+
|
|
114
|
+
// Style setters
|
|
115
|
+
setWidth(value: number): void
|
|
116
|
+
setWidthPercent(value: number): void
|
|
117
|
+
setWidthAuto(): void
|
|
118
|
+
setHeight(value: number): void
|
|
119
|
+
setHeightPercent(value: number): void
|
|
120
|
+
setHeightAuto(): void
|
|
121
|
+
setMinWidth(value: number): void
|
|
122
|
+
setMinWidthPercent(value: number): void
|
|
123
|
+
setMinHeight(value: number): void
|
|
124
|
+
setMinHeightPercent(value: number): void
|
|
125
|
+
setMaxWidth(value: number): void
|
|
126
|
+
setMaxWidthPercent(value: number): void
|
|
127
|
+
setMaxHeight(value: number): void
|
|
128
|
+
setMaxHeightPercent(value: number): void
|
|
129
|
+
setFlexDirection(dir: LayoutFlexDirection): void
|
|
130
|
+
setFlexGrow(value: number): void
|
|
131
|
+
setFlexShrink(value: number): void
|
|
132
|
+
setFlexBasis(value: number): void
|
|
133
|
+
setFlexBasisPercent(value: number): void
|
|
134
|
+
setFlexWrap(wrap: LayoutWrap): void
|
|
135
|
+
setAlignItems(align: LayoutAlign): void
|
|
136
|
+
setAlignSelf(align: LayoutAlign): void
|
|
137
|
+
setJustifyContent(justify: LayoutJustify): void
|
|
138
|
+
setDisplay(display: LayoutDisplay): void
|
|
139
|
+
getDisplay(): LayoutDisplay
|
|
140
|
+
setPositionType(type: LayoutPositionType): void
|
|
141
|
+
setPosition(edge: LayoutEdge, value: number): void
|
|
142
|
+
setPositionPercent(edge: LayoutEdge, value: number): void
|
|
143
|
+
setOverflow(overflow: LayoutOverflow): void
|
|
144
|
+
setMargin(edge: LayoutEdge, value: number): void
|
|
145
|
+
setPadding(edge: LayoutEdge, value: number): void
|
|
146
|
+
setBorder(edge: LayoutEdge, value: number): void
|
|
147
|
+
setGap(gutter: LayoutGutter, value: number): void
|
|
148
|
+
|
|
149
|
+
// Lifecycle
|
|
150
|
+
free(): void
|
|
151
|
+
freeRecursive(): void
|
|
152
|
+
}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import Yoga, {
|
|
2
|
+
Align,
|
|
3
|
+
Direction,
|
|
4
|
+
Display,
|
|
5
|
+
Edge,
|
|
6
|
+
FlexDirection,
|
|
7
|
+
Gutter,
|
|
8
|
+
Justify,
|
|
9
|
+
MeasureMode,
|
|
10
|
+
Overflow,
|
|
11
|
+
PositionType,
|
|
12
|
+
Wrap,
|
|
13
|
+
type Node as YogaNode,
|
|
14
|
+
} from '../../native-ts/yoga-layout/index.js'
|
|
15
|
+
import {
|
|
16
|
+
type LayoutAlign,
|
|
17
|
+
LayoutDisplay,
|
|
18
|
+
type LayoutEdge,
|
|
19
|
+
type LayoutFlexDirection,
|
|
20
|
+
type LayoutGutter,
|
|
21
|
+
type LayoutJustify,
|
|
22
|
+
type LayoutMeasureFunc,
|
|
23
|
+
LayoutMeasureMode,
|
|
24
|
+
type LayoutNode,
|
|
25
|
+
type LayoutOverflow,
|
|
26
|
+
type LayoutPositionType,
|
|
27
|
+
type LayoutWrap,
|
|
28
|
+
} from './node.js'
|
|
29
|
+
|
|
30
|
+
// --
|
|
31
|
+
// Edge/Gutter mapping
|
|
32
|
+
|
|
33
|
+
const EDGE_MAP: Record<LayoutEdge, Edge> = {
|
|
34
|
+
all: Edge.All,
|
|
35
|
+
horizontal: Edge.Horizontal,
|
|
36
|
+
vertical: Edge.Vertical,
|
|
37
|
+
left: Edge.Left,
|
|
38
|
+
right: Edge.Right,
|
|
39
|
+
top: Edge.Top,
|
|
40
|
+
bottom: Edge.Bottom,
|
|
41
|
+
start: Edge.Start,
|
|
42
|
+
end: Edge.End,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const GUTTER_MAP: Record<LayoutGutter, Gutter> = {
|
|
46
|
+
all: Gutter.All,
|
|
47
|
+
column: Gutter.Column,
|
|
48
|
+
row: Gutter.Row,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// --
|
|
52
|
+
// Yoga adapter
|
|
53
|
+
|
|
54
|
+
export class YogaLayoutNode implements LayoutNode {
|
|
55
|
+
readonly yoga: YogaNode
|
|
56
|
+
|
|
57
|
+
constructor(yoga: YogaNode) {
|
|
58
|
+
this.yoga = yoga
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Tree
|
|
62
|
+
|
|
63
|
+
insertChild(child: LayoutNode, index: number): void {
|
|
64
|
+
this.yoga.insertChild((child as YogaLayoutNode).yoga, index)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
removeChild(child: LayoutNode): void {
|
|
68
|
+
this.yoga.removeChild((child as YogaLayoutNode).yoga)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
getChildCount(): number {
|
|
72
|
+
return this.yoga.getChildCount()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
getParent(): LayoutNode | null {
|
|
76
|
+
const p = this.yoga.getParent()
|
|
77
|
+
return p ? new YogaLayoutNode(p) : null
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Layout
|
|
81
|
+
|
|
82
|
+
calculateLayout(width?: number, _height?: number): void {
|
|
83
|
+
this.yoga.calculateLayout(width, undefined, Direction.LTR)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
setMeasureFunc(fn: LayoutMeasureFunc): void {
|
|
87
|
+
this.yoga.setMeasureFunc((w, wMode) => {
|
|
88
|
+
const mode =
|
|
89
|
+
wMode === MeasureMode.Exactly
|
|
90
|
+
? LayoutMeasureMode.Exactly
|
|
91
|
+
: wMode === MeasureMode.AtMost
|
|
92
|
+
? LayoutMeasureMode.AtMost
|
|
93
|
+
: LayoutMeasureMode.Undefined
|
|
94
|
+
return fn(w, mode)
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
unsetMeasureFunc(): void {
|
|
99
|
+
this.yoga.unsetMeasureFunc()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
markDirty(): void {
|
|
103
|
+
this.yoga.markDirty()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Computed layout
|
|
107
|
+
|
|
108
|
+
getComputedLeft(): number {
|
|
109
|
+
return this.yoga.getComputedLeft()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
getComputedTop(): number {
|
|
113
|
+
return this.yoga.getComputedTop()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
getComputedWidth(): number {
|
|
117
|
+
return this.yoga.getComputedWidth()
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
getComputedHeight(): number {
|
|
121
|
+
return this.yoga.getComputedHeight()
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
getComputedBorder(edge: LayoutEdge): number {
|
|
125
|
+
return this.yoga.getComputedBorder(EDGE_MAP[edge]!)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
getComputedPadding(edge: LayoutEdge): number {
|
|
129
|
+
return this.yoga.getComputedPadding(EDGE_MAP[edge]!)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Style setters
|
|
133
|
+
|
|
134
|
+
setWidth(value: number): void {
|
|
135
|
+
this.yoga.setWidth(value)
|
|
136
|
+
}
|
|
137
|
+
setWidthPercent(value: number): void {
|
|
138
|
+
this.yoga.setWidthPercent(value)
|
|
139
|
+
}
|
|
140
|
+
setWidthAuto(): void {
|
|
141
|
+
this.yoga.setWidthAuto()
|
|
142
|
+
}
|
|
143
|
+
setHeight(value: number): void {
|
|
144
|
+
this.yoga.setHeight(value)
|
|
145
|
+
}
|
|
146
|
+
setHeightPercent(value: number): void {
|
|
147
|
+
this.yoga.setHeightPercent(value)
|
|
148
|
+
}
|
|
149
|
+
setHeightAuto(): void {
|
|
150
|
+
this.yoga.setHeightAuto()
|
|
151
|
+
}
|
|
152
|
+
setMinWidth(value: number): void {
|
|
153
|
+
this.yoga.setMinWidth(value)
|
|
154
|
+
}
|
|
155
|
+
setMinWidthPercent(value: number): void {
|
|
156
|
+
this.yoga.setMinWidthPercent(value)
|
|
157
|
+
}
|
|
158
|
+
setMinHeight(value: number): void {
|
|
159
|
+
this.yoga.setMinHeight(value)
|
|
160
|
+
}
|
|
161
|
+
setMinHeightPercent(value: number): void {
|
|
162
|
+
this.yoga.setMinHeightPercent(value)
|
|
163
|
+
}
|
|
164
|
+
setMaxWidth(value: number): void {
|
|
165
|
+
this.yoga.setMaxWidth(value)
|
|
166
|
+
}
|
|
167
|
+
setMaxWidthPercent(value: number): void {
|
|
168
|
+
this.yoga.setMaxWidthPercent(value)
|
|
169
|
+
}
|
|
170
|
+
setMaxHeight(value: number): void {
|
|
171
|
+
this.yoga.setMaxHeight(value)
|
|
172
|
+
}
|
|
173
|
+
setMaxHeightPercent(value: number): void {
|
|
174
|
+
this.yoga.setMaxHeightPercent(value)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
setFlexDirection(dir: LayoutFlexDirection): void {
|
|
178
|
+
const map: Record<LayoutFlexDirection, FlexDirection> = {
|
|
179
|
+
row: FlexDirection.Row,
|
|
180
|
+
'row-reverse': FlexDirection.RowReverse,
|
|
181
|
+
column: FlexDirection.Column,
|
|
182
|
+
'column-reverse': FlexDirection.ColumnReverse,
|
|
183
|
+
}
|
|
184
|
+
this.yoga.setFlexDirection(map[dir]!)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
setFlexGrow(value: number): void {
|
|
188
|
+
this.yoga.setFlexGrow(value)
|
|
189
|
+
}
|
|
190
|
+
setFlexShrink(value: number): void {
|
|
191
|
+
this.yoga.setFlexShrink(value)
|
|
192
|
+
}
|
|
193
|
+
setFlexBasis(value: number): void {
|
|
194
|
+
this.yoga.setFlexBasis(value)
|
|
195
|
+
}
|
|
196
|
+
setFlexBasisPercent(value: number): void {
|
|
197
|
+
this.yoga.setFlexBasisPercent(value)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
setFlexWrap(wrap: LayoutWrap): void {
|
|
201
|
+
const map: Record<LayoutWrap, Wrap> = {
|
|
202
|
+
nowrap: Wrap.NoWrap,
|
|
203
|
+
wrap: Wrap.Wrap,
|
|
204
|
+
'wrap-reverse': Wrap.WrapReverse,
|
|
205
|
+
}
|
|
206
|
+
this.yoga.setFlexWrap(map[wrap]!)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
setAlignItems(align: LayoutAlign): void {
|
|
210
|
+
const map: Record<LayoutAlign, Align> = {
|
|
211
|
+
auto: Align.Auto,
|
|
212
|
+
stretch: Align.Stretch,
|
|
213
|
+
'flex-start': Align.FlexStart,
|
|
214
|
+
center: Align.Center,
|
|
215
|
+
'flex-end': Align.FlexEnd,
|
|
216
|
+
}
|
|
217
|
+
this.yoga.setAlignItems(map[align]!)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
setAlignSelf(align: LayoutAlign): void {
|
|
221
|
+
const map: Record<LayoutAlign, Align> = {
|
|
222
|
+
auto: Align.Auto,
|
|
223
|
+
stretch: Align.Stretch,
|
|
224
|
+
'flex-start': Align.FlexStart,
|
|
225
|
+
center: Align.Center,
|
|
226
|
+
'flex-end': Align.FlexEnd,
|
|
227
|
+
}
|
|
228
|
+
this.yoga.setAlignSelf(map[align]!)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
setJustifyContent(justify: LayoutJustify): void {
|
|
232
|
+
const map: Record<LayoutJustify, Justify> = {
|
|
233
|
+
'flex-start': Justify.FlexStart,
|
|
234
|
+
center: Justify.Center,
|
|
235
|
+
'flex-end': Justify.FlexEnd,
|
|
236
|
+
'space-between': Justify.SpaceBetween,
|
|
237
|
+
'space-around': Justify.SpaceAround,
|
|
238
|
+
'space-evenly': Justify.SpaceEvenly,
|
|
239
|
+
}
|
|
240
|
+
this.yoga.setJustifyContent(map[justify]!)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
setDisplay(display: LayoutDisplay): void {
|
|
244
|
+
this.yoga.setDisplay(display === 'flex' ? Display.Flex : Display.None)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
getDisplay(): LayoutDisplay {
|
|
248
|
+
return this.yoga.getDisplay() === Display.None
|
|
249
|
+
? LayoutDisplay.None
|
|
250
|
+
: LayoutDisplay.Flex
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
setPositionType(type: LayoutPositionType): void {
|
|
254
|
+
this.yoga.setPositionType(
|
|
255
|
+
type === 'absolute' ? PositionType.Absolute : PositionType.Relative,
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
setPosition(edge: LayoutEdge, value: number): void {
|
|
260
|
+
this.yoga.setPosition(EDGE_MAP[edge]!, value)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
setPositionPercent(edge: LayoutEdge, value: number): void {
|
|
264
|
+
this.yoga.setPositionPercent(EDGE_MAP[edge]!, value)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
setOverflow(overflow: LayoutOverflow): void {
|
|
268
|
+
const map: Record<LayoutOverflow, Overflow> = {
|
|
269
|
+
visible: Overflow.Visible,
|
|
270
|
+
hidden: Overflow.Hidden,
|
|
271
|
+
scroll: Overflow.Scroll,
|
|
272
|
+
}
|
|
273
|
+
this.yoga.setOverflow(map[overflow]!)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
setMargin(edge: LayoutEdge, value: number): void {
|
|
277
|
+
this.yoga.setMargin(EDGE_MAP[edge]!, value)
|
|
278
|
+
}
|
|
279
|
+
setPadding(edge: LayoutEdge, value: number): void {
|
|
280
|
+
this.yoga.setPadding(EDGE_MAP[edge]!, value)
|
|
281
|
+
}
|
|
282
|
+
setBorder(edge: LayoutEdge, value: number): void {
|
|
283
|
+
this.yoga.setBorder(EDGE_MAP[edge]!, value)
|
|
284
|
+
}
|
|
285
|
+
setGap(gutter: LayoutGutter, value: number): void {
|
|
286
|
+
this.yoga.setGap(GUTTER_MAP[gutter]!, value)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Lifecycle
|
|
290
|
+
|
|
291
|
+
free(): void {
|
|
292
|
+
this.yoga.free()
|
|
293
|
+
}
|
|
294
|
+
freeRecursive(): void {
|
|
295
|
+
this.yoga.freeRecursive()
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// --
|
|
300
|
+
// Instance management
|
|
301
|
+
//
|
|
302
|
+
// The TS yoga-layout port is synchronous — no WASM loading, no linear memory
|
|
303
|
+
// growth, so no preload/swap/reset machinery is needed. The Yoga instance is
|
|
304
|
+
// just a plain JS object available at import time.
|
|
305
|
+
|
|
306
|
+
export function createYogaLayoutNode(): LayoutNode {
|
|
307
|
+
return new YogaLayoutNode(Yoga.Node.create())
|
|
308
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { stringWidth } from './stringWidth.js'
|
|
2
|
+
|
|
3
|
+
// During streaming, text grows but completed lines are immutable.
|
|
4
|
+
// Caching stringWidth per-line avoids re-measuring hundreds of
|
|
5
|
+
// unchanged lines on every token (~50x reduction in stringWidth calls).
|
|
6
|
+
const cache = new Map<string, number>()
|
|
7
|
+
|
|
8
|
+
const MAX_CACHE_SIZE = 4096
|
|
9
|
+
|
|
10
|
+
export function lineWidth(line: string): number {
|
|
11
|
+
const cached = cache.get(line)
|
|
12
|
+
if (cached !== undefined) return cached
|
|
13
|
+
|
|
14
|
+
const width = stringWidth(line)
|
|
15
|
+
|
|
16
|
+
// Evict when cache grows too large (e.g. after many different responses).
|
|
17
|
+
// Simple full-clear is fine — the cache repopulates in one frame.
|
|
18
|
+
if (cache.size >= MAX_CACHE_SIZE) {
|
|
19
|
+
cache.clear()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
cache.set(line, width)
|
|
23
|
+
return width
|
|
24
|
+
}
|