@sayknow-cli/tui 0.3.12 → 0.3.15

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.
@@ -1,5 +1,6 @@
1
1
  import type { SymbolTheme } from "../symbols";
2
2
  import type { Component } from "../tui";
3
+ import { type ViewportAnchorSpan } from "../utils";
3
4
  /** Test-only clock seam for streaming throttle tests. */
4
5
  export declare function __setMarkdownNowForTest(now: (() => number) | undefined): void;
5
6
  /** Test/diagnostic seam: number of synchronous highlight invocations since the last reset. */
@@ -70,6 +71,14 @@ export declare class Markdown implements Component {
70
71
  dispose(): void;
71
72
  invalidate(): void;
72
73
  render(width: number): string[];
74
+ renderWithViewportAnchorSource(width: number, source: {
75
+ id: string;
76
+ }): {
77
+ lines: string[];
78
+ anchors: Array<({
79
+ id: string;
80
+ } & ViewportAnchorSpan) | null>;
81
+ };
73
82
  }
74
83
  /**
75
84
  * Render inline markdown (bold, italic, code, links, strikethrough) to a styled string.
@@ -0,0 +1,128 @@
1
+ /**
2
+ * ┌─ SAYKNOW PET SPRITE SPEC ────────────────────────────────────────────────┐
3
+ * The pet is a 16×16 pixel octopus drawn beside the composer. Everything here
4
+ * is data: no PNGs, no assets — each frame is 16 strings of 16 chars, encoded
5
+ * to a sixel or kitty escape at runtime. Author a new frame by drawing a grid.
6
+ *
7
+ * GRID RULES
8
+ * - Exactly 16 rows × 16 columns. Only PALETTE keys below are valid chars.
9
+ * - `.` = transparent. Keep the outer columns transparent so the sprite sits
10
+ * snug beside the input box (the widget reserves +1 column of slack).
11
+ *
12
+ * PALETTE (char → role) — see PALETTE for exact RGB:
13
+ * .=transparent K=dark outline R=mantle body r=body highlight
14
+ * W=eye white V=pupil G=eye sparkle b=underside
15
+ * w=tear H h A=reserved (unused by the octopus art)
16
+ *
17
+ * FRAME CATALOG (SayknowPixelFrameName → PIXEL_GRIDS):
18
+ * base idle rest; also the dance "drop/settle" beat
19
+ * gazeL eyes glance left ┐ idle loop (see sayknow-pet-widget IDLE_LOOP)
20
+ * gazeR eyes glance right │
21
+ * flicker eyes blink ┘
22
+ * flex sparkly "yay" eyes; dance accent + random idle flex burst
23
+ * danceL tentacles sway left ┐ work loop (PARA_PARA_STEPS)
24
+ * danceR tentacles sway right ┘
25
+ * cry1..3 a tear trails from the outer eye corners (BlueOcto sob)
26
+ *
27
+ * RENDERING: buildSayknowPixelFrames({ protocol, cellWidthPx, cellHeightPx,
28
+ * targetRows: 2 }) scales the art to 2 terminal rows and encodes each frame
29
+ * once. Kitty uses a native `Y=` sub-cell drop (set by the widget) to sit on the
30
+ * composer border; sixel uses transparent top padding.
31
+ *
32
+ * BEHAVIOR (timing, positioning, on/off) lives in
33
+ * packages/coding-agent/src/modes/components/sayknow-pet-widget.ts.
34
+ *
35
+ * ADD A FRAME: draw the grid → add its name to SayknowPixelFrameName → register it in
36
+ * PIXEL_GRIDS → reference it from an idle/work loop or a skin burst.
37
+ *
38
+ * ADD A PET (skin): append one entry to PET_SKINS below — { id, label, description,
39
+ * palette, burst }. The id flows into PetSkinId/PetMode automatically, the settings
40
+ * enum, `/pet` command and both selectors derive their options from PET_SKINS, and the
41
+ * widget reads `burst` to animate — no other file needs editing. Recolor with a palette
42
+ * spread (see BLUE_PALETTE); add frames only for poses the catalog lacks.
43
+ * └────────────────────────────────────────────────────────────────────────┘
44
+ */
45
+ type Rgb = readonly [number, number, number];
46
+ export type Palette = Record<string, Rgb | null>;
47
+ export declare const PET_SKIN_IDS: readonly ["red", "blue"];
48
+ export type PetSkinId = (typeof PET_SKIN_IDS)[number];
49
+ /** Every pet mode: "off" plus each skin id, in menu order. */
50
+ export declare const PET_MODE_IDS: readonly ["off", "red", "blue"];
51
+ export type PetMode = (typeof PET_MODE_IDS)[number];
52
+ /** Narrow an arbitrary string to a PetMode. */
53
+ export declare function isPetMode(value: string): value is PetMode;
54
+ /** Logical pixel-pet frame names shared by the overlay state machine. */
55
+ export type SayknowPixelFrameName = "base" | "gazeL" | "gazeR" | "flicker" | "flex" | "danceL" | "danceR" | "cry1" | "cry2" | "cry3";
56
+ /** Para-para work dance beats: the working loop and each skin's burst "work-in" intro. */
57
+ export declare const PARA_PARA_STEPS: ReadonlyArray<readonly [SayknowPixelFrameName, number]>;
58
+ /**
59
+ * A skin's idle burst: a short intro sequence, then an optional looping tail. It drives
60
+ * BOTH the random live show-off AND the selector's preview demo, so give every skin a
61
+ * real animation (reuse PARA_PARA_STEPS for a work-in intro) rather than one held frame.
62
+ */
63
+ export interface PetBurst {
64
+ /** Frames played once, in order, at the start of the burst. */
65
+ intro: ReadonlyArray<readonly [SayknowPixelFrameName, number]>;
66
+ /** Frames cycled every `stepMs` for `ms` after the intro (a held or looping finish). */
67
+ tail?: {
68
+ frames: readonly SayknowPixelFrameName[];
69
+ stepMs: number;
70
+ ms: number;
71
+ };
72
+ }
73
+ /** Everything that defines a pet skin: identity, UI copy, colors and behavior. */
74
+ export interface PetSkin {
75
+ id: PetSkinId;
76
+ /** Selector/settings label, e.g. "RedOctopus". */
77
+ label: string;
78
+ /** One-line selector/settings description. */
79
+ description: string;
80
+ palette: Palette;
81
+ /** Idle burst animation played between quiet idle loops. */
82
+ burst: PetBurst;
83
+ }
84
+ /** Skin registry — the single source for palettes, behavior and selector/command copy. */
85
+ export declare const PET_SKINS: Record<PetSkinId, PetSkin>;
86
+ /** Total burst duration (intro beats plus the looping tail). */
87
+ export declare function petBurstDurationMs(burst: PetBurst): number;
88
+ /** The frame to show `elapsed` ms into a burst (`now` cycles the looping tail). */
89
+ export declare function petBurstFrame(burst: PetBurst, elapsed: number, now: number): SayknowPixelFrameName;
90
+ /** Test-only access to logical art; production rendering still uses encoded frames. */
91
+ export declare const __sayknowPetTestHooks: {
92
+ getPixelGrid(name: SayknowPixelFrameName): string[];
93
+ };
94
+ /** Encode a grid as a transparent SIXEL image, optionally bottom-aligned by top padding. */
95
+ export declare function encodeGridSixel(grid: string[], scale: number, topPaddingPx?: number, palette?: Palette): string;
96
+ /** Encode a bottom-aligned grid as kitty raw RGBA at `scale`. */
97
+ export declare function encodeGridKitty(grid: string[], scale: number, imageId: number, cols: number, rows: number, topPaddingPx?: number, cellYOffsetPx?: number, leftPaddingPx?: number, rightPaddingPx?: number, palette?: Palette): string;
98
+ export interface SayknowPixelFrames {
99
+ /** escape payload per logical frame (drawn at the current cursor cell) */
100
+ frames: Record<SayknowPixelFrameName, string>;
101
+ /** protocol the frames were encoded for */
102
+ protocol: "sixel" | "kitty";
103
+ widthPx: number;
104
+ heightPx: number;
105
+ columns: number;
106
+ rows: number;
107
+ /** terminal rows touched by the encoded raster, including pixel offset */
108
+ rasterRows: number;
109
+ }
110
+ /**
111
+ * Build overlay pixel frames exactly `targetRows` terminal rows tall when the
112
+ * terminal cells permit it. Nearest-neighbor sampling preserves the 16x16 art
113
+ * while allowing fractional scale factors such as 36px / 16px.
114
+ */
115
+ export declare function buildSayknowPixelFrames(options: {
116
+ protocol: "sixel" | "kitty";
117
+ cellWidthPx: number;
118
+ cellHeightPx: number;
119
+ targetRows?: number;
120
+ /** Transparent pixel offset above sixel art for sub-cell vertical placement. */
121
+ sixelTopPaddingPx?: number;
122
+ /** Native sub-cell `Y=` pixel offset that drops the kitty sprite within its first cell. */
123
+ kittyCellYOffsetPx?: number;
124
+ kittyImageId?: number;
125
+ /** Color skin for the sprite palette (default "red"). */
126
+ skin?: PetSkinId;
127
+ }): SayknowPixelFrames;
128
+ export {};
@@ -1,4 +1,5 @@
1
1
  import type { Component } from "../tui";
2
+ import { type ViewportAnchorSpan } from "../utils";
2
3
  /**
3
4
  * Text component - displays multi-line text with word wrapping
4
5
  */
@@ -10,4 +11,12 @@ export declare class Text implements Component {
10
11
  setCustomBgFn(customBgFn?: (text: string) => string): void;
11
12
  invalidate(): void;
12
13
  render(width: number): string[];
14
+ renderWithViewportAnchorSource(width: number, source: {
15
+ id: string;
16
+ }): {
17
+ lines: string[];
18
+ anchors: Array<({
19
+ id: string;
20
+ } & ViewportAnchorSpan) | null>;
21
+ };
13
22
  }
@@ -7,6 +7,7 @@ export * from "./components/image";
7
7
  export * from "./components/input";
8
8
  export * from "./components/loader";
9
9
  export * from "./components/markdown";
10
+ export * from "./components/sayknow-pet";
10
11
  export * from "./components/select-list";
11
12
  export * from "./components/settings-list";
12
13
  export * from "./components/spacer";
@@ -22,6 +22,42 @@ export declare class TerminalInfo {
22
22
  sendNotification(message: string): void;
23
23
  }
24
24
  export declare function isNotificationSuppressed(): boolean;
25
+ /**
26
+ * Returns whether the process runs under a terminal multiplexer (tmux, GNU
27
+ * screen, or zellij). Recognizes the same host markers as the renderer's
28
+ * multiplexer predicate in tui.ts so capability selection and viewport-repaint
29
+ * policy agree on what counts as a multiplexed host. Multiplexers intercept
30
+ * graphics escapes and OSC 8 hyperlinks instead of forwarding them to the
31
+ * outer terminal.
32
+ */
33
+ export declare function isUnderTerminalMultiplexer(env?: NodeJS.ProcessEnv): boolean;
34
+ export interface TerminalGraphicsFallbackOptions {
35
+ /**
36
+ * Permit cursor-neutral image escapes (kitty `a=p,C=1` placements) to render
37
+ * inside this fallback scope. Cursor-advancing protocols (iTerm2/SIXEL)
38
+ * remain suppressed. A nested scope without this option revokes the
39
+ * permission for its own subtree.
40
+ */
41
+ allowCursorNeutralImages?: boolean;
42
+ }
43
+ /**
44
+ * Synchronously suppress terminal graphics while rendering a text-only surface.
45
+ * Nested scopes remain active until the outermost scope exits.
46
+ */
47
+ export declare function withTerminalGraphicsFallback<T>(fn: () => T, options?: TerminalGraphicsFallbackOptions): T;
48
+ /** Returns whether terminal graphics are currently suppressed by a render scope. */
49
+ export declare function isTerminalGraphicsFallbackActive(): boolean;
50
+ /**
51
+ * Returns whether cursor-neutral image escapes may render despite an active
52
+ * graphics-fallback scope. True only when every active fallback scope opted in.
53
+ */
54
+ export declare function isCursorNeutralImagePermittedInFallback(): boolean;
55
+ /**
56
+ * Returns whether PI_FORCE_IMAGE_PROTOCOL explicitly configures the image
57
+ * protocol, including an explicit "off". An explicit configuration is
58
+ * authoritative: runtime capability probes must not override it.
59
+ */
60
+ export declare function isImageProtocolForced(): boolean;
25
61
  /**
26
62
  * Returns true when running in Windows Terminal with known SIXEL support.
27
63
  *
@@ -30,6 +66,13 @@ export declare function isNotificationSuppressed(): boolean;
30
66
  export declare function isWindowsTerminalPreviewSixelSupported(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): boolean;
31
67
  export declare const TERMINAL_ID: TerminalId;
32
68
  export declare const TERMINAL: TerminalInfo;
69
+ type ImageProtocolChangeListener = (imageProtocol: ImageProtocol | null) => void;
70
+ /**
71
+ * Subscribe to runtime image-protocol changes (e.g. the asynchronous sixel
72
+ * capability probe enabling graphics after startup). Returns an unsubscribe
73
+ * function. Listeners fire only on actual changes.
74
+ */
75
+ export declare function onImageProtocolChanged(listener: ImageProtocolChangeListener): () => void;
33
76
  /**
34
77
  * Override terminal image protocol at runtime after capability probes complete.
35
78
  */
@@ -141,3 +184,4 @@ export interface RenderedImage {
141
184
  }
142
185
  export declare function renderImage(base64Data: string, imageDimensions: ImageDimensions, options?: ImageRenderOptions): RenderedImage | null;
143
186
  export declare function imageFallback(mimeType: string, dimensions?: ImageDimensions, filename?: string): string;
187
+ export {};
@@ -57,6 +57,32 @@ export declare function isFocusable(component: Component | null): component is C
57
57
  */
58
58
  export declare const CURSOR_MARKER = "\u001B_pi:c\u0007";
59
59
  export { visibleWidth };
60
+ /** Durable source identifier for a semantically anchored viewport row. */
61
+ export type ViewportAnchorId = string;
62
+ export interface ViewportAnchorRow {
63
+ id: ViewportAnchorId;
64
+ graphemeStart: number;
65
+ graphemeEnd: number;
66
+ cellStart: number;
67
+ cellEnd: number;
68
+ }
69
+ export interface ViewportAnchorRender {
70
+ lines: string[];
71
+ anchors: Array<ViewportAnchorRow | null>;
72
+ }
73
+ export interface ViewportAnchorProvider extends Component {
74
+ renderWithViewportAnchors(width: number): ViewportAnchorRender;
75
+ }
76
+ export interface ViewportAnchorSource {
77
+ id: ViewportAnchorId;
78
+ }
79
+ export interface ViewportAnchorSourceRenderer extends Component {
80
+ renderWithViewportAnchorSource(width: number, source: ViewportAnchorSource): ViewportAnchorRender;
81
+ }
82
+ export declare function isViewportAnchorProvider(component: Component): component is ViewportAnchorProvider;
83
+ export declare function isViewportAnchorSourceRenderer(component: Component): component is ViewportAnchorSourceRenderer;
84
+ export declare function renderComponentWithViewportAnchors(component: Component, width: number): ViewportAnchorRender;
85
+ export declare function renderComponentWithViewportAnchorSource(component: Component, width: number, source: ViewportAnchorSource): ViewportAnchorRender;
60
86
  /**
61
87
  * Anchor position for overlays
62
88
  */
@@ -72,6 +98,18 @@ export interface OverlayMargin {
72
98
  }
73
99
  /** Value that can be absolute (number) or percentage (string like "50%") */
74
100
  export type SizeValue = number | `${number}%`;
101
+ /**
102
+ * Startup sixel capability probe policy (pure; exported for tests):
103
+ * - Never probe when PI_FORCE_IMAGE_PROTOCOL is set — an explicit
104
+ * configuration (including "off") is authoritative.
105
+ * - Never probe inside a terminal multiplexer: tmux advertises DA1 ";4"
106
+ * whenever it was compiled with sixel support, regardless of whether the
107
+ * attached client terminal can render sixel, so a positive reply is not
108
+ * end-to-end evidence. Graphics under a multiplexer are strictly opt-in
109
+ * via PI_FORCE_IMAGE_PROTOCOL=sixel.
110
+ * - Probe Windows Terminal (>=1.22 renders sixel but exposes no env marker).
111
+ */
112
+ export declare function shouldProbeSixelCapability(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): boolean;
75
113
  /**
76
114
  * True when repainting only the live viewport is safer than clearing/replaying
77
115
  * the full transcript. Native Windows console hosts are included even when
@@ -125,7 +163,7 @@ export interface OverlayHandle {
125
163
  /**
126
164
  * Container - a component that contains other components
127
165
  */
128
- export declare class Container implements Component {
166
+ export declare class Container implements ViewportAnchorProvider {
129
167
  #private;
130
168
  children: Component[];
131
169
  addChild(component: Component): void;
@@ -135,9 +173,12 @@ export declare class Container implements Component {
135
173
  clear(): void;
136
174
  /** Remove all children without disposing them (for detach-then-readd reuse). */
137
175
  detachAll(): void;
176
+ /** Registers a direct child as eligible for semantic viewport anchoring. */
177
+ setViewportAnchorSource(component: Component, source: ViewportAnchorSource | null): void;
138
178
  dispose(): void;
139
179
  invalidate(): void;
140
180
  render(width: number): string[];
181
+ renderWithViewportAnchors(width: number): ViewportAnchorRender;
141
182
  }
142
183
  type TuiRenderCounterSnapshot = {
143
184
  debugRedrawEnvReads: number;
@@ -174,6 +215,12 @@ export declare class TUI extends Container {
174
215
  setClearOnShrink(enabled: boolean): void;
175
216
  setFocus(component: Component | null): void;
176
217
  setBottomPinnedComponent(component: Component | null): void;
218
+ /** Register the direct child whose rows are eligible for semantic viewport anchoring. */
219
+ setViewportAnchorComponent(component: Component | null): void;
220
+ /** Clear manual viewport ownership before replacing the transcript identity namespace. */
221
+ resetViewportAnchorIntent(): void;
222
+ /** Allow one semantic-neighbor reconciliation after a definitive same-transcript rebuild. */
223
+ prepareViewportAnchorForTranscriptRebuild(): void;
177
224
  scrollViewportPages(direction: -1 | 1): boolean;
178
225
  followLiveViewport(): boolean;
179
226
  /**
@@ -212,4 +259,11 @@ export declare class TUI extends Container {
212
259
  normalizationLimit: number;
213
260
  truncationLimit: number;
214
261
  };
262
+ /**
263
+ * Register an emitter whose escape payload is appended to every render
264
+ * write (inside its own synchronized-output block, cursor saved/restored).
265
+ * Used for absolute-positioned overlays such as pixel-image pets that live
266
+ * outside the line-based component model. Return null to emit nothing.
267
+ */
268
+ setPostRenderEmitter(emitter: (() => string | null) | undefined): void;
215
269
  }
@@ -29,6 +29,29 @@ export declare function padding(n: number): string;
29
29
  * Get the shared grapheme segmenter instance.
30
30
  */
31
31
  export declare function getSegmenter(): Intl.Segmenter;
32
+ export interface ViewportAnchorSpan {
33
+ graphemeStart: number;
34
+ graphemeEnd: number;
35
+ cellStart: number;
36
+ cellEnd: number;
37
+ }
38
+ export interface ViewportAnchorAnnotation {
39
+ text: string;
40
+ nextGrapheme: number;
41
+ nextCell: number;
42
+ token: string;
43
+ }
44
+ export declare const VIEWPORT_ANCHOR_PREFIX = "\u001B_ASKC_ANCHOR:";
45
+ /**
46
+ * Tag every visible grapheme with an APC marker that survives ANSI-aware
47
+ * wrapping. The marker contains source grapheme and monotonic cell offsets.
48
+ */
49
+ export declare function annotateViewportAnchorGraphemes(text: string, startGrapheme?: number, startCell?: number, token?: `${string}-${string}-${string}-${string}-${string}`): ViewportAnchorAnnotation;
50
+ /** Remove viewport anchor markers and return the exact marked span for each row. */
51
+ export declare function extractViewportAnchorRows(lines: readonly string[], token: string): {
52
+ lines: string[];
53
+ spans: Array<ViewportAnchorSpan | null>;
54
+ };
32
55
  export declare function visibleWidthRaw(str: string): number;
33
56
  /**
34
57
  * Calculate the visible width of a string in terminal columns.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@sayknow-cli/tui",
4
- "version": "0.3.12",
4
+ "version": "0.3.15",
5
5
  "description": "Terminal User Interface library with differential rendering for efficient text-based applications",
6
6
  "homepage": "https://sayknow-cli.com",
7
7
  "author": "jaybeyond",
@@ -38,8 +38,8 @@
38
38
  "fmt": "biome format --write ."
39
39
  },
40
40
  "dependencies": {
41
- "@sayknow-cli/natives": "0.3.12",
42
- "@sayknow-cli/utils": "0.3.12",
41
+ "@sayknow-cli/natives": "0.3.15",
42
+ "@sayknow-cli/utils": "0.3.15",
43
43
  "lru-cache": "11.3.6",
44
44
  "marked": "^18.0.3"
45
45
  },
@@ -3,6 +3,8 @@ import {
3
3
  type ImageDimensions,
4
4
  ImageProtocol,
5
5
  imageFallback,
6
+ isCursorNeutralImagePermittedInFallback,
7
+ isTerminalGraphicsFallbackActive,
6
8
  kittyImageId,
7
9
  renderImage,
8
10
  TERMINAL,
@@ -40,7 +42,8 @@ export class Image implements Component {
40
42
 
41
43
  #cachedLines?: string[];
42
44
  #cachedWidth?: number;
43
- // Kitty graphics: content-derived image id + per-instance placement id.
45
+ #cachedFallbackActive?: boolean;
46
+ #cachedProtocol?: ImageProtocol | null;
44
47
  // Computed lazily so non-kitty terminals never pay the hash cost.
45
48
  #kittyImageId?: number;
46
49
  readonly #kittyPlacementId = allocatePlacementId();
@@ -62,6 +65,8 @@ export class Image implements Component {
62
65
  invalidate(): void {
63
66
  this.#cachedLines = undefined;
64
67
  this.#cachedWidth = undefined;
68
+ this.#cachedFallbackActive = undefined;
69
+ this.#cachedProtocol = undefined;
65
70
  }
66
71
 
67
72
  get retainedBase64DataForTest(): string | undefined {
@@ -81,7 +86,19 @@ export class Image implements Component {
81
86
  }
82
87
 
83
88
  render(width: number): string[] {
84
- if (this.#cachedLines && this.#cachedWidth === width) {
89
+ // Kitty placements are cursor-neutral, so an opted-in fallback scope
90
+ // (e.g. the IRC split) can still render them safely; iTerm2/SIXEL
91
+ // advance the cursor and stay suppressed.
92
+ const graphicsSuppressed =
93
+ isTerminalGraphicsFallbackActive() &&
94
+ !(TERMINAL.imageProtocol === ImageProtocol.Kitty && isCursorNeutralImagePermittedInFallback());
95
+ const protocol = TERMINAL.imageProtocol;
96
+ if (
97
+ this.#cachedLines &&
98
+ this.#cachedWidth === width &&
99
+ this.#cachedFallbackActive === graphicsSuppressed &&
100
+ this.#cachedProtocol === protocol
101
+ ) {
85
102
  return this.#cachedLines;
86
103
  }
87
104
 
@@ -90,12 +107,12 @@ export class Image implements Component {
90
107
 
91
108
  let lines: string[];
92
109
 
93
- if (TERMINAL.imageProtocol) {
110
+ if (protocol && !graphicsSuppressed) {
94
111
  const base64Data = this.#getBase64Data();
95
112
  if (!base64Data) {
96
113
  lines = this.#fallbackLines();
97
114
  } else {
98
- if (TERMINAL.imageProtocol === ImageProtocol.Kitty) {
115
+ if (protocol === ImageProtocol.Kitty) {
99
116
  this.#kittyImageId ??= kittyImageId(base64Data);
100
117
  }
101
118
  const result = renderImage(base64Data, this.#dimensions, {
@@ -139,6 +156,8 @@ export class Image implements Component {
139
156
 
140
157
  this.#cachedLines = lines;
141
158
  this.#cachedWidth = width;
159
+ this.#cachedFallbackActive = graphicsSuppressed;
160
+ this.#cachedProtocol = protocol;
142
161
 
143
162
  return lines;
144
163
  }