chat-layout 0.1.4 → 1.0.0-1

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 CHANGED
@@ -1,27 +1,221 @@
1
1
  # chat-layout
2
2
 
3
- To install dependencies:
3
+ Canvas-based chat and timeline layout primitives with a v2 flex-style layout model.
4
+
5
+ The current recommended APIs are:
6
+
7
+ - `Flex` for row/column layout
8
+ - `FlexItem` for explicit `grow`
9
+ - `Place` for single-child horizontal placement
10
+ - `MultilineText` `align` / `physicalAlign` for text content alignment
11
+ - `ChatRenderer` plus `ListState` for virtualized chat rendering
12
+ - `memoRenderItem` for object items, or `memoRenderItemBy` when your stable key is primitive / explicit
13
+
14
+ **Layout Model**
15
+ `Flex` and `Place` split layout concerns more clearly than the older API:
16
+
17
+ - Use `new Flex(children, { direction: "row" | "column" })` for main-axis layout.
18
+ - Use `new FlexItem(child, { grow: 1 })` when a child should consume remaining space.
19
+ - `Flex` shrink-wraps on the cross axis by default; `maxWidth` / `maxHeight` act as measurement caps rather than implicit fill signals.
20
+ - Use `alignItems` / `alignSelf: "stretch"` when a specific child should fill the container's computed cross axis.
21
+ - Use `new Place(child, { align: "start" | "center" | "end" })` when a single child should fill available width and then be placed left/center/right.
22
+ - Use `justifyContent`, `alignItems`, and `alignSelf` for container/item placement.
23
+ - Use `align: "start" | "center" | "end"` on `MultilineText` for logical alignment that matches `Place.align`.
24
+ - Use `physicalAlign: "left" | "center" | "right"` on `MultilineText` only when you explicitly want physical left/right semantics.
25
+ - `Text` / `MultilineText` preserve blank lines and edge whitespace by default; opt into cleanup with `whitespace: "trim-and-collapse"`.
26
+
27
+ **Example**
28
+ This is the recommended chat bubble shape used by [example/chat.ts](./example/chat.ts):
29
+
30
+ ```ts
31
+ type ChatItem = {
32
+ sender: string;
33
+ content: string;
34
+ reply?: {
35
+ sender: string;
36
+ content: string;
37
+ };
38
+ };
39
+
40
+ const renderItem = memoRenderItem((item: ChatItem): Node<C> => {
41
+ const senderLine = new Flex<C>(
42
+ [avatarDot, senderLabel],
43
+ {
44
+ direction: "row",
45
+ gap: 4,
46
+ mainAxisSize: "fit-content",
47
+ reverse: item.sender === "A",
48
+ },
49
+ );
50
+
51
+ const messageText = new FlexItem(
52
+ new MultilineText(item.content, {
53
+ lineHeight: 20,
54
+ font: "16px system-ui",
55
+ style: "black",
56
+ align: "start",
57
+ }),
58
+ { alignSelf: "start" },
59
+ );
60
+
61
+ const bubbleChildren: Node<C>[] = [];
62
+ if (item.reply != null) {
63
+ bubbleChildren.push(
64
+ new FlexItem(
65
+ new RoundedBox(
66
+ new Flex<C>(
67
+ [
68
+ new Text(item.reply.sender, {
69
+ lineHeight: 14,
70
+ font: "11px system-ui",
71
+ style: "#666",
72
+ }),
73
+ new MultilineText(item.reply.content, {
74
+ lineHeight: 16,
75
+ font: "13px system-ui",
76
+ style: "#444",
77
+ align: "start",
78
+ }),
79
+ ],
80
+ {
81
+ direction: "column",
82
+ gap: 2,
83
+ alignItems: "start",
84
+ },
85
+ ),
86
+ {
87
+ top: 5,
88
+ bottom: 5,
89
+ left: 8,
90
+ right: 8,
91
+ radii: 6,
92
+ fill: "#e2e2e2",
93
+ },
94
+ ),
95
+ { alignSelf: "stretch" },
96
+ ),
97
+ );
98
+ }
99
+ bubbleChildren.push(messageText);
100
+
101
+ const bubbleColumn = new Flex<C>(bubbleChildren, {
102
+ direction: "column",
103
+ gap: 6,
104
+ // The bubble itself stays intrinsic on the cross axis.
105
+ // Only the reply preview stretches to the bubble width.
106
+ alignItems: "start",
107
+ });
108
+
109
+ const content = new RoundedBox(
110
+ bubbleColumn,
111
+ {
112
+ top: 6,
113
+ bottom: 6,
114
+ left: 10,
115
+ right: 10,
116
+ radii: 8,
117
+ fill: "#ccc",
118
+ },
119
+ );
120
+
121
+ const body = new Flex<C>([senderLine, content], {
122
+ direction: "column",
123
+ alignItems: item.sender === "A" ? "end" : "start",
124
+ });
125
+
126
+ const alignedBody = new Place<C>(body, {
127
+ align: item.sender === "A" ? "end" : "start",
128
+ });
129
+
130
+ return new Place(
131
+ new PaddingBox(
132
+ new Flex<C>(
133
+ [
134
+ avatar,
135
+ new FlexItem(alignedBody, { grow: 1 }),
136
+ new Fixed(32, 0),
137
+ ],
138
+ {
139
+ direction: "row",
140
+ gap: 4,
141
+ reverse: item.sender === "A",
142
+ },
143
+ ),
144
+ {
145
+ top: 4,
146
+ bottom: 4,
147
+ left: 4,
148
+ right: 4,
149
+ },
150
+ ),
151
+ {
152
+ align: item.sender === "A" ? "end" : "start",
153
+ },
154
+ );
155
+ });
156
+ ```
157
+
158
+ That combination gives you:
159
+
160
+ - explicit row/column structure
161
+ - explicit grow behavior through `FlexItem`
162
+ - left/right chat placement through `Place`
163
+ - wrapped message bubbles that respect available width without becoming full-width by default
164
+ - nested reply previews that use item-level cross-axis `stretch` to fill the bubble width
165
+
166
+ In other words: a finite `maxWidth` / `maxHeight` limits measurement, but does not force the `Flex` container to fill the cross axis. If you want a child to fill the computed bubble width, mark that child with `alignSelf: "stretch"` (or inherit `alignItems: "stretch"` from the parent).
167
+
168
+ ## API notes
169
+
170
+ - `memoRenderItem()` now only accepts object items. If your list item is a primitive or you want to memoize by an explicit id, use `memoRenderItemBy(keyOf, renderItem)`.
171
+ - `FlexItemOptions` intentionally exposes only the implemented item-level controls: `grow` and `alignSelf`. The previously documented `shrink` / `basis` fields were removed because they were never implemented.
172
+ - `ListState.position` now uses `undefined` as the explicit “use renderer default anchor” state. Use `list.setAnchor(position, offset)` to opt into a concrete anchor.
173
+ - `ListState` can be seeded with `new ListState(items)` and reset with `list.reset(nextItems)`.
174
+ - `MultilineText` now uses only `align` / `physicalAlign`; the old `alignment` field has been removed.
175
+
176
+ ### Migration notes
177
+
178
+ - Before:
179
+ - `memoRenderItem((item: number) => ...)`
180
+ - After:
181
+ - `memoRenderItemBy((item: number) => item, (item) => ...)`
182
+ - Before:
183
+ - `new FlexItem(node, { grow: 1, shrink: 1, basis: 100 })`
184
+ - After:
185
+ - `new FlexItem(node, { grow: 1 })`
186
+ - unsupported sizing semantics should be modeled explicitly in node measurement/layout instead of `shrink` / `basis`
187
+ - Before:
188
+ - `new MultilineText(text, { alignment: "left" })`
189
+ - After:
190
+ - `new MultilineText(text, { align: "start" })`
191
+ - or `new MultilineText(text, { physicalAlign: "left" })` when physical left/right semantics are required
192
+ - Before:
193
+ - `list.position = Number.NaN`
194
+ - After:
195
+ - `list.resetScroll()`
196
+ - or `list.setAnchor(index, offset)` for an explicit anchor
197
+
198
+ **Development**
199
+ Install dependencies:
4
200
 
5
201
  ```bash
6
202
  bun install
7
203
  ```
8
204
 
9
- To type-check:
205
+ Type-check:
10
206
 
11
207
  ```bash
12
208
  bun run typecheck
13
209
  ```
14
210
 
15
- To build distributable files:
211
+ Build distributable files:
16
212
 
17
213
  ```bash
18
214
  bun run dist
19
215
  ```
20
216
 
21
- To build example:
217
+ Build the chat example:
22
218
 
23
219
  ```bash
24
220
  bun run example
25
221
  ```
26
-
27
- This project was created using `bun init` in bun v1.1.37. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
package/index.d.mts CHANGED
@@ -11,13 +11,50 @@ interface RenderFeedback {
11
11
  /** Largest visible continuous item position, expressed in item coordinates rather than pixels. */
12
12
  max: number;
13
13
  }
14
- type Alignment = "left" | "center" | "right";
14
+ type Axis = "row" | "column";
15
+ type MainAxisAlignment = "start" | "center" | "end" | "space-between" | "space-around" | "space-evenly";
16
+ type CrossAxisAlignment = "start" | "center" | "end" | "stretch";
17
+ type MainAxisSize = "fill" | "fit-content";
18
+ type TextAlign = "start" | "center" | "end";
19
+ type PhysicalTextAlign = "left" | "center" | "right";
20
+ type TextWhitespaceMode = "preserve" | "trim-and-collapse";
21
+ interface TextStyleOptions<C extends CanvasRenderingContext2D> {
22
+ lineHeight: number;
23
+ font: string;
24
+ style: DynValue<C, string>;
25
+ /** Default: preserve input whitespace, including blank lines and edge spaces. */
26
+ whitespace?: TextWhitespaceMode;
27
+ }
28
+ interface MultilineTextOptions<C extends CanvasRenderingContext2D> extends TextStyleOptions<C> {
29
+ /** Logical alignment that matches `Place.align`. */
30
+ align?: TextAlign;
31
+ /** Explicit physical alignment when left/right semantics are required. */
32
+ physicalAlign?: PhysicalTextAlign;
33
+ }
34
+ interface TextOptions<C extends CanvasRenderingContext2D> extends TextStyleOptions<C> {}
35
+ interface LayoutConstraints {
36
+ minWidth?: number;
37
+ maxWidth?: number;
38
+ minHeight?: number;
39
+ maxHeight?: number;
40
+ }
41
+ interface FlexItemOptions {
42
+ grow?: number;
43
+ alignSelf?: CrossAxisAlignment | "auto";
44
+ }
45
+ interface FlexContainerOptions {
46
+ direction?: Axis;
47
+ gap?: number;
48
+ justifyContent?: MainAxisAlignment;
49
+ alignItems?: CrossAxisAlignment;
50
+ reverse?: boolean;
51
+ mainAxisSize?: MainAxisSize;
52
+ }
15
53
  interface Context<C extends CanvasRenderingContext2D> {
16
54
  graphics: C;
17
- remainingWidth: number;
18
- alignment: Alignment;
19
- reverse: boolean;
20
- measureNode(node: Node<C>): Box;
55
+ /** v2: 显式布局约束 */
56
+ constraints?: LayoutConstraints;
57
+ measureNode(node: Node<C>, constraints?: LayoutConstraints): Box;
21
58
  invalidateNode(node: Node<C>): void;
22
59
  resolveDynValue<T>(value: DynValue<C, T>): T;
23
60
  with<T>(this: Context<C>, cb: (g: C) => T): T;
@@ -35,55 +72,47 @@ interface Node<C extends CanvasRenderingContext2D> {
35
72
  measure(ctx: Context<C>): Box;
36
73
  draw(ctx: Context<C>, x: number, y: number): boolean;
37
74
  hittest(ctx: Context<C>, test: HitTest): boolean;
38
- readonly flex: boolean;
75
+ }
76
+ interface LayoutRect {
77
+ x: number;
78
+ y: number;
79
+ width: number;
80
+ height: number;
81
+ }
82
+ interface ChildLayoutResult<C extends CanvasRenderingContext2D> {
83
+ node: Node<C>;
84
+ rect: LayoutRect;
85
+ contentBox: LayoutRect;
86
+ constraints?: LayoutConstraints;
87
+ }
88
+ interface FlexLayoutResult<C extends CanvasRenderingContext2D> {
89
+ containerBox: LayoutRect;
90
+ contentBox: LayoutRect;
91
+ children: ChildLayoutResult<C>[];
92
+ constraints?: LayoutConstraints;
39
93
  }
40
94
  //#endregion
41
- //#region src/nodes.d.ts
95
+ //#region src/nodes/base.d.ts
42
96
  declare abstract class Group<C extends CanvasRenderingContext2D> implements Node<C> {
43
- readonly children: Node<C>[];
97
+ #private;
44
98
  constructor(children: Node<C>[]);
99
+ get children(): readonly Node<C>[];
100
+ replaceChildren(nextChildren: Node<C>[]): void;
45
101
  abstract measure(ctx: Context<C>): Box;
46
102
  abstract draw(ctx: Context<C>, x: number, y: number): boolean;
47
103
  abstract hittest(ctx: Context<C>, test: HitTest): boolean;
48
- get flex(): boolean;
49
- }
50
- declare class VStack<C extends CanvasRenderingContext2D> extends Group<C> {
51
- readonly options: {
52
- gap?: number;
53
- alignment?: "left" | "center" | "right";
54
- };
55
- constructor(children: Node<C>[], options?: {
56
- gap?: number;
57
- alignment?: "left" | "center" | "right";
58
- });
59
- measure(ctx: Context<C>): Box;
60
- draw(ctx: Context<C>, x: number, y: number): boolean;
61
- hittest(ctx: Context<C>, test: HitTest): boolean;
62
- }
63
- declare class HStack<C extends CanvasRenderingContext2D> extends Group<C> {
64
- readonly children: Node<C>[];
65
- readonly options: {
66
- reverse?: boolean;
67
- gap?: number;
68
- };
69
- constructor(children: Node<C>[], options?: {
70
- reverse?: boolean;
71
- gap?: number;
72
- });
73
- measure(ctx: Context<C>): Box;
74
- draw(ctx: Context<C>, x: number, y: number): boolean;
75
- hittest(ctx: Context<C>, test: HitTest): boolean;
76
104
  }
77
105
  declare class Wrapper<C extends CanvasRenderingContext2D> implements Node<C> {
78
106
  #private;
79
107
  constructor(inner: Node<C>);
80
108
  get inner(): Node<C>;
81
109
  set inner(newNode: Node<C>);
82
- get flex(): boolean;
83
110
  measure(ctx: Context<C>): Box;
84
111
  draw(ctx: Context<C>, x: number, y: number): boolean;
85
112
  hittest(ctx: Context<C>, test: HitTest): boolean;
86
113
  }
114
+ //#endregion
115
+ //#region src/nodes/box.d.ts
87
116
  declare class PaddingBox<C extends CanvasRenderingContext2D> extends Wrapper<C> {
88
117
  #private;
89
118
  readonly padding: {
@@ -102,103 +131,142 @@ declare class PaddingBox<C extends CanvasRenderingContext2D> extends Wrapper<C>
102
131
  draw(ctx: Context<C>, x: number, y: number): boolean;
103
132
  hittest(ctx: Context<C>, test: HitTest): boolean;
104
133
  }
105
- declare class AlignBox<C extends CanvasRenderingContext2D> extends Wrapper<C> {
106
- #private;
134
+ declare class Fixed<C extends CanvasRenderingContext2D> implements Node<C> {
135
+ readonly width: number;
136
+ readonly height: number;
137
+ constructor(width: number, height: number);
138
+ measure(_ctx: Context<C>): Box;
139
+ draw(_ctx: Context<C>, _x: number, _y: number): boolean;
140
+ hittest(_ctx: Context<C>, _test: HitTest): boolean;
141
+ }
142
+ //#endregion
143
+ //#region src/nodes/flex.d.ts
144
+ declare class FlexItem<C extends CanvasRenderingContext2D> extends Wrapper<C> {
145
+ readonly item: FlexItemOptions;
146
+ constructor(inner: Node<C>, item?: FlexItemOptions);
147
+ }
148
+ declare class Flex<C extends CanvasRenderingContext2D> extends Group<C> {
149
+ readonly options: FlexContainerOptions;
150
+ constructor(children: Node<C>[], options?: FlexContainerOptions);
151
+ measure(ctx: Context<C>): Box;
152
+ draw(ctx: Context<C>, x: number, y: number): boolean;
153
+ hittest(ctx: Context<C>, test: HitTest): boolean;
154
+ }
155
+ //#endregion
156
+ //#region src/nodes/place.d.ts
157
+ declare class Place<C extends CanvasRenderingContext2D> extends Wrapper<C> {
107
158
  readonly options: {
108
- alignment: Alignment;
159
+ align?: TextAlign;
160
+ expand?: boolean;
109
161
  };
110
- constructor(inner: Node<C>, options: {
111
- alignment: Alignment;
162
+ constructor(inner: Node<C>, options?: {
163
+ align?: TextAlign;
164
+ expand?: boolean;
112
165
  });
113
166
  measure(ctx: Context<C>): Box;
114
167
  draw(ctx: Context<C>, x: number, y: number): boolean;
115
168
  hittest(ctx: Context<C>, test: HitTest): boolean;
116
169
  }
170
+ //#endregion
171
+ //#region src/nodes/text.d.ts
117
172
  declare class MultilineText<C extends CanvasRenderingContext2D> implements Node<C> {
118
- #private;
119
173
  readonly text: string;
120
- readonly options: {
121
- lineHeight: number;
122
- font: string;
123
- alignment: "left" | "center" | "right";
124
- style: DynValue<C, string>;
125
- };
126
- constructor(text: string, options: {
127
- lineHeight: number;
128
- font: string;
129
- alignment: "left" | "center" | "right";
130
- style: DynValue<C, string>;
131
- });
132
- get flex(): boolean;
174
+ readonly options: MultilineTextOptions<C>;
175
+ constructor(text: string, options: MultilineTextOptions<C>);
133
176
  measure(ctx: Context<C>): Box;
134
177
  draw(ctx: Context<C>, x: number, y: number): boolean;
135
- hittest(_ctx: Context<C>, _test: HitTest): boolean;
178
+ hittest(_ctx: Context<C>, _test: {
179
+ x: number;
180
+ y: number;
181
+ type: "click" | "auxclick" | "hover";
182
+ }): boolean;
136
183
  }
137
184
  declare class Text<C extends CanvasRenderingContext2D> implements Node<C> {
138
- #private;
139
185
  readonly text: string;
140
- readonly options: {
141
- lineHeight: number;
142
- font: string;
143
- style: DynValue<C, string>;
144
- };
145
- constructor(text: string, options: {
146
- lineHeight: number;
147
- font: string;
148
- style: DynValue<C, string>;
149
- });
150
- get flex(): boolean;
186
+ readonly options: TextOptions<C>;
187
+ constructor(text: string, options: TextOptions<C>);
151
188
  measure(ctx: Context<C>): Box;
152
189
  draw(ctx: Context<C>, x: number, y: number): boolean;
153
- hittest(_ctx: Context<C>, _test: HitTest): boolean;
154
- }
155
- declare class Fixed<C extends CanvasRenderingContext2D> implements Node<C> {
156
- readonly width: number;
157
- readonly height: number;
158
- constructor(width: number, height: number);
159
- get flex(): boolean;
160
- measure(_ctx: Context<C>): Box;
161
- draw(_ctx: Context<C>, _x: number, _y: number): boolean;
162
- hittest(_ctx: Context<C>, _test: HitTest): boolean;
190
+ hittest(_ctx: Context<C>, _test: {
191
+ x: number;
192
+ y: number;
193
+ type: "click" | "auxclick" | "hover";
194
+ }): boolean;
163
195
  }
164
196
  //#endregion
165
- //#region src/renderer.d.ts
197
+ //#region src/renderer/base.d.ts
166
198
  declare class BaseRenderer<C extends CanvasRenderingContext2D, O extends {} = {}> {
167
199
  #private;
168
200
  readonly options: RendererOptions & O;
169
201
  graphics: C;
170
202
  protected get context(): Context<C>;
171
203
  constructor(graphics: C, options: RendererOptions & O);
204
+ protected getRootConstraints(): LayoutConstraints;
205
+ protected getRootContext(): Context<C>;
206
+ protected measureRootNode(node: Node<C>): Box;
207
+ protected drawRootNode(node: Node<C>, x?: number, y?: number): boolean;
208
+ protected hittestRootNode(node: Node<C>, test: HitTest): boolean;
172
209
  invalidateNode(node: Node<C>): void;
173
- measureNode(node: Node<C>, ctx?: Context<C>): Box;
210
+ getLayoutResult(node: Node<C>, constraints?: LayoutConstraints): FlexLayoutResult<C> | undefined;
211
+ setLayoutResult(node: Node<C>, result: FlexLayoutResult<C>, constraints?: LayoutConstraints): void;
212
+ protected getTextLayout<T>(node: Node<C>, key: string): T | undefined;
213
+ protected setTextLayout<T>(node: Node<C>, key: string, layout: T): void;
214
+ measureNode(node: Node<C>, constraints?: LayoutConstraints): Box;
174
215
  }
175
216
  declare class DebugRenderer<C extends CanvasRenderingContext2D> extends BaseRenderer<C> {
176
217
  draw(node: Node<C>): boolean;
177
218
  hittest(node: Node<C>, test: HitTest): boolean;
178
219
  }
179
- declare function memoRenderItem<C extends CanvasRenderingContext2D, T extends {}>(renderItem: (item: T) => Node<C>): ((item: T) => Node<C>) & {
180
- reset: (key: T) => boolean;
181
- };
220
+ //#endregion
221
+ //#region src/renderer/list-state.d.ts
182
222
  declare class ListState<T extends {}> {
183
223
  offset: number;
184
- position: number;
224
+ position: number | undefined;
185
225
  items: T[];
226
+ constructor(items?: T[]);
186
227
  unshift(...items: T[]): void;
187
228
  unshiftAll(items: T[]): void;
188
229
  push(...items: T[]): void;
189
230
  pushAll(items: T[]): void;
190
- reset(): void;
231
+ setAnchor(position: number, offset?: number): void;
232
+ reset(items?: T[]): void;
191
233
  resetScroll(): void;
192
234
  applyScroll(delta: number): void;
193
235
  }
194
- type DrawItem<C extends CanvasRenderingContext2D> = {
236
+ //#endregion
237
+ //#region src/renderer/memo.d.ts
238
+ declare function memoRenderItem<C extends CanvasRenderingContext2D, T extends object>(renderItem: (item: T) => Node<C>): ((item: T) => Node<C>) & {
239
+ reset: (key: T) => boolean;
240
+ };
241
+ declare function memoRenderItemBy<C extends CanvasRenderingContext2D, T, K>(keyOf: (item: T) => K, renderItem: (item: T) => Node<C>): ((item: T) => Node<C>) & {
242
+ reset: (item: T) => boolean;
243
+ resetKey: (key: K) => boolean;
244
+ };
245
+ //#endregion
246
+ //#region src/renderer/virtualized/solver.d.ts
247
+ interface VisibleListState {
248
+ position?: number;
249
+ offset: number;
250
+ }
251
+ interface NormalizedListState {
252
+ position: number;
253
+ offset: number;
254
+ }
255
+ interface VisibleWindowEntry<T> {
195
256
  idx: number;
196
- node: Node<C>;
257
+ value: T;
197
258
  offset: number;
198
259
  height: number;
199
- };
260
+ }
261
+ interface VisibleWindow<T> {
262
+ drawList: VisibleWindowEntry<T>[];
263
+ shift: number;
264
+ }
265
+ //#endregion
266
+ //#region src/renderer/virtualized/base.d.ts
200
267
  interface JumpToOptions {
201
268
  animated?: boolean;
269
+ block?: "start" | "center" | "end";
202
270
  duration?: number;
203
271
  onComplete?: () => void;
204
272
  }
@@ -210,48 +278,65 @@ declare abstract class VirtualizedRenderer<C extends CanvasRenderingContext2D, T
210
278
  static readonly MIN_JUMP_DURATION = 160;
211
279
  static readonly MAX_JUMP_DURATION = 420;
212
280
  static readonly JUMP_DURATION_PER_ITEM = 28;
213
- get position(): number;
214
- set position(value: number);
281
+ get position(): number | undefined;
282
+ set position(value: number | undefined);
215
283
  get offset(): number;
216
284
  set offset(value: number);
217
285
  get items(): T[];
218
286
  set items(value: T[]);
219
287
  abstract render(feedback?: RenderFeedback): boolean;
220
- abstract hittest(test: HitTest): boolean;
288
+ abstract hittest(test: {
289
+ x: number;
290
+ y: number;
291
+ type: "click" | "auxclick" | "hover";
292
+ }): boolean;
293
+ protected _readListState(): VisibleListState;
294
+ protected _commitListState(state: NormalizedListState): void;
221
295
  jumpTo(index: number, options?: JumpToOptions): void;
222
296
  protected _resetRenderFeedback(feedback?: RenderFeedback): void;
223
297
  protected _accumulateRenderFeedback(feedback: RenderFeedback, idx: number, top: number, height: number): void;
224
- protected _renderDrawList(list: DrawItem<C>[], shift: number, feedback?: RenderFeedback): boolean;
298
+ protected _renderDrawList(list: VisibleWindow<Node<C>>["drawList"], shift: number, feedback?: RenderFeedback): boolean;
299
+ protected _renderVisibleWindow(window: VisibleWindow<Node<C>>, feedback?: RenderFeedback): boolean;
300
+ protected _hittestVisibleWindow(window: VisibleWindow<Node<C>>, test: {
301
+ x: number;
302
+ y: number;
303
+ type: "click" | "auxclick" | "hover";
304
+ }): boolean;
225
305
  protected _prepareRender(): boolean;
226
306
  protected _finishRender(requestRedraw: boolean): boolean;
227
307
  protected _clampItemIndex(index: number): number;
228
308
  protected _getItemHeight(index: number): number;
229
- protected abstract _prepareAnchorState(): void;
230
- protected abstract _readAnchor(): number;
309
+ protected _getAnchorAtOffset(index: number, offset: number): number;
310
+ protected abstract _normalizeListState(state: VisibleListState): NormalizedListState;
311
+ protected abstract _readAnchor(state: NormalizedListState): number;
231
312
  protected abstract _applyAnchor(anchor: number): void;
232
- protected abstract _getTargetAnchor(index: number): number;
313
+ protected abstract _getDefaultJumpBlock(): NonNullable<JumpToOptions["block"]>;
314
+ protected abstract _getTargetAnchor(index: number, block: NonNullable<JumpToOptions["block"]>): number;
233
315
  }
234
- declare class TimelineRenderer<C extends CanvasRenderingContext2D, T extends {}> extends VirtualizedRenderer<C, T> {
235
- protected _prepareAnchorState(): void;
236
- protected _readAnchor(): number;
316
+ //#endregion
317
+ //#region src/renderer/virtualized/chat.d.ts
318
+ declare class ChatRenderer<C extends CanvasRenderingContext2D, T extends {}> extends VirtualizedRenderer<C, T> {
319
+ #private;
320
+ protected _getDefaultJumpBlock(): NonNullable<JumpToOptions["block"]>;
321
+ protected _normalizeListState(state: VisibleListState): NormalizedListState;
322
+ protected _readAnchor(state: NormalizedListState): number;
237
323
  protected _applyAnchor(anchor: number): void;
238
- protected _getTargetAnchor(index: number): number;
324
+ protected _getTargetAnchor(index: number, block: NonNullable<JumpToOptions["block"]>): number;
239
325
  render(feedback?: RenderFeedback): boolean;
240
326
  hittest(test: HitTest): boolean;
241
327
  }
242
- declare class ChatRenderer<C extends CanvasRenderingContext2D, T extends {}> extends VirtualizedRenderer<C, T> {
243
- protected _prepareAnchorState(): void;
244
- protected _readAnchor(): number;
328
+ //#endregion
329
+ //#region src/renderer/virtualized/timeline.d.ts
330
+ declare class TimelineRenderer<C extends CanvasRenderingContext2D, T extends {}> extends VirtualizedRenderer<C, T> {
331
+ #private;
332
+ protected _getDefaultJumpBlock(): NonNullable<JumpToOptions["block"]>;
333
+ protected _normalizeListState(state: VisibleListState): NormalizedListState;
334
+ protected _readAnchor(state: NormalizedListState): number;
245
335
  protected _applyAnchor(anchor: number): void;
246
- protected _getTargetAnchor(index: number): number;
336
+ protected _getTargetAnchor(index: number, block: NonNullable<JumpToOptions["block"]>): number;
247
337
  render(feedback?: RenderFeedback): boolean;
248
338
  hittest(test: HitTest): boolean;
249
339
  }
250
340
  //#endregion
251
- //#region src/registry.d.ts
252
- declare function registerNodeParent<C extends CanvasRenderingContext2D>(node: Node<C>, parent: Node<C>): void;
253
- declare function unregisterNodeParent<C extends CanvasRenderingContext2D>(node: Node<C>): void;
254
- declare function getNodeParent<C extends CanvasRenderingContext2D>(node: Node<C>): Node<C> | undefined;
255
- //#endregion
256
- export { AlignBox, Alignment, BaseRenderer, Box, ChatRenderer, Context, DebugRenderer, DynValue, Fixed, Group, HStack, HitTest, JumpToOptions, ListState, MultilineText, Node, PaddingBox, RenderFeedback, RendererOptions, Text, TimelineRenderer, VStack, VirtualizedRenderer, Wrapper, getNodeParent, memoRenderItem, registerNodeParent, unregisterNodeParent };
341
+ export { Axis, BaseRenderer, Box, ChatRenderer, ChildLayoutResult, Context, CrossAxisAlignment, DebugRenderer, DynValue, Fixed, Flex, FlexContainerOptions, FlexItem, FlexItemOptions, FlexLayoutResult, Group, HitTest, JumpToOptions, LayoutConstraints, LayoutRect, ListState, MainAxisAlignment, MainAxisSize, MultilineText, MultilineTextOptions, Node, PaddingBox, PhysicalTextAlign, Place, RenderFeedback, RendererOptions, Text, TextAlign, TextOptions, TextStyleOptions, TextWhitespaceMode, TimelineRenderer, VirtualizedRenderer, Wrapper, memoRenderItem, memoRenderItemBy };
257
342
  //# sourceMappingURL=index.d.mts.map