@teammates/consolonia 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -0
- package/dist/__tests__/ansi.test.d.ts +1 -0
- package/dist/__tests__/ansi.test.js +520 -0
- package/dist/__tests__/chat-view.test.d.ts +4 -0
- package/dist/__tests__/chat-view.test.js +480 -0
- package/dist/__tests__/drawing.test.d.ts +4 -0
- package/dist/__tests__/drawing.test.js +426 -0
- package/dist/__tests__/input.test.d.ts +5 -0
- package/dist/__tests__/input.test.js +911 -0
- package/dist/__tests__/layout.test.d.ts +4 -0
- package/dist/__tests__/layout.test.js +689 -0
- package/dist/__tests__/pixel.test.d.ts +1 -0
- package/dist/__tests__/pixel.test.js +674 -0
- package/dist/__tests__/render.test.d.ts +1 -0
- package/dist/__tests__/render.test.js +400 -0
- package/dist/__tests__/styled.test.d.ts +4 -0
- package/dist/__tests__/styled.test.js +149 -0
- package/dist/__tests__/widgets.test.d.ts +5 -0
- package/dist/__tests__/widgets.test.js +924 -0
- package/dist/ansi/esc.d.ts +61 -0
- package/dist/ansi/esc.js +85 -0
- package/dist/ansi/output.d.ts +66 -0
- package/dist/ansi/output.js +192 -0
- package/dist/ansi/strip.d.ts +16 -0
- package/dist/ansi/strip.js +74 -0
- package/dist/app.d.ts +68 -0
- package/dist/app.js +297 -0
- package/dist/drawing/clip.d.ts +23 -0
- package/dist/drawing/clip.js +67 -0
- package/dist/drawing/context.d.ts +77 -0
- package/dist/drawing/context.js +275 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +63 -0
- package/dist/input/escape-matcher.d.ts +27 -0
- package/dist/input/escape-matcher.js +253 -0
- package/dist/input/events.d.ts +49 -0
- package/dist/input/events.js +17 -0
- package/dist/input/index.d.ts +15 -0
- package/dist/input/index.js +14 -0
- package/dist/input/matcher.d.ts +23 -0
- package/dist/input/matcher.js +14 -0
- package/dist/input/mouse-matcher.d.ts +27 -0
- package/dist/input/mouse-matcher.js +142 -0
- package/dist/input/paste-matcher.d.ts +23 -0
- package/dist/input/paste-matcher.js +104 -0
- package/dist/input/processor.d.ts +51 -0
- package/dist/input/processor.js +145 -0
- package/dist/input/raw-mode.d.ts +13 -0
- package/dist/input/raw-mode.js +24 -0
- package/dist/input/text-matcher.d.ts +14 -0
- package/dist/input/text-matcher.js +32 -0
- package/dist/layout/box.d.ts +33 -0
- package/dist/layout/box.js +92 -0
- package/dist/layout/column.d.ts +21 -0
- package/dist/layout/column.js +90 -0
- package/dist/layout/control.d.ts +73 -0
- package/dist/layout/control.js +215 -0
- package/dist/layout/row.d.ts +21 -0
- package/dist/layout/row.js +95 -0
- package/dist/layout/stack.d.ts +18 -0
- package/dist/layout/stack.js +64 -0
- package/dist/layout/types.d.ts +27 -0
- package/dist/layout/types.js +4 -0
- package/dist/pixel/background.d.ts +16 -0
- package/dist/pixel/background.js +16 -0
- package/dist/pixel/box-pattern.d.ts +38 -0
- package/dist/pixel/box-pattern.js +57 -0
- package/dist/pixel/buffer.d.ts +25 -0
- package/dist/pixel/buffer.js +51 -0
- package/dist/pixel/color.d.ts +48 -0
- package/dist/pixel/color.js +92 -0
- package/dist/pixel/foreground.d.ts +31 -0
- package/dist/pixel/foreground.js +64 -0
- package/dist/pixel/pixel.d.ts +21 -0
- package/dist/pixel/pixel.js +38 -0
- package/dist/pixel/symbol.d.ts +38 -0
- package/dist/pixel/symbol.js +192 -0
- package/dist/render/regions.d.ts +54 -0
- package/dist/render/regions.js +102 -0
- package/dist/render/render-target.d.ts +42 -0
- package/dist/render/render-target.js +118 -0
- package/dist/styled.d.ts +113 -0
- package/dist/styled.js +176 -0
- package/dist/widgets/border.d.ts +34 -0
- package/dist/widgets/border.js +121 -0
- package/dist/widgets/chat-view.d.ts +239 -0
- package/dist/widgets/chat-view.js +993 -0
- package/dist/widgets/interview.d.ts +87 -0
- package/dist/widgets/interview.js +187 -0
- package/dist/widgets/markdown.d.ts +87 -0
- package/dist/widgets/markdown.js +611 -0
- package/dist/widgets/panel.d.ts +19 -0
- package/dist/widgets/panel.js +35 -0
- package/dist/widgets/scroll-view.d.ts +43 -0
- package/dist/widgets/scroll-view.js +182 -0
- package/dist/widgets/styled-text.d.ts +38 -0
- package/dist/widgets/styled-text.js +183 -0
- package/dist/widgets/syntax.d.ts +37 -0
- package/dist/widgets/syntax.js +670 -0
- package/dist/widgets/text-input.d.ts +121 -0
- package/dist/widgets/text-input.js +618 -0
- package/dist/widgets/text.d.ts +34 -0
- package/dist/widgets/text.js +168 -0
- package/package.json +45 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interview — a modal widget that presents a sequence of questions
|
|
3
|
+
* and collects answers. Designed to run inside a ChatView by replacing
|
|
4
|
+
* the normal input area (via ChatView.setInputOverride).
|
|
5
|
+
*
|
|
6
|
+
* While active the ChatView's own input prompt is hidden; when the
|
|
7
|
+
* interview completes it emits "complete" with all collected answers
|
|
8
|
+
* and the caller can remove the override to restore normal input.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
*
|
|
12
|
+
* const interview = new Interview({
|
|
13
|
+
* title: "Quick intro",
|
|
14
|
+
* subtitle: "press Enter to skip any question",
|
|
15
|
+
* questions: [
|
|
16
|
+
* { key: "name", prompt: "Your name" },
|
|
17
|
+
* { key: "role", prompt: "Your role", placeholder: "e.g., senior backend engineer" },
|
|
18
|
+
* ],
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* chatView.setInputOverride(interview);
|
|
22
|
+
*
|
|
23
|
+
* interview.on("complete", (answers: Record<string, string>) => {
|
|
24
|
+
* chatView.setInputOverride(null); // restore normal input
|
|
25
|
+
* // use answers...
|
|
26
|
+
* });
|
|
27
|
+
*/
|
|
28
|
+
import type { DrawingContext, TextStyle } from "../drawing/context.js";
|
|
29
|
+
import type { InputEvent } from "../input/events.js";
|
|
30
|
+
import { Control } from "../layout/control.js";
|
|
31
|
+
import type { Constraint, Size } from "../layout/types.js";
|
|
32
|
+
export interface InterviewQuestion {
|
|
33
|
+
/** Key used in the answers record. */
|
|
34
|
+
key: string;
|
|
35
|
+
/** Prompt label shown to the left of the input. */
|
|
36
|
+
prompt: string;
|
|
37
|
+
/** Placeholder hint shown when the input is empty. */
|
|
38
|
+
placeholder?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface InterviewOptions {
|
|
41
|
+
/** Title shown above the questions. */
|
|
42
|
+
title?: string;
|
|
43
|
+
/** Subtitle / hint shown below the title. */
|
|
44
|
+
subtitle?: string;
|
|
45
|
+
/** The questions to ask, in order. */
|
|
46
|
+
questions: InterviewQuestion[];
|
|
47
|
+
/** Style for the question prompt label. */
|
|
48
|
+
promptStyle?: TextStyle;
|
|
49
|
+
/** Style for answered values. */
|
|
50
|
+
answeredStyle?: TextStyle;
|
|
51
|
+
/** Style for input text. */
|
|
52
|
+
inputStyle?: TextStyle;
|
|
53
|
+
/** Style for the cursor. */
|
|
54
|
+
cursorStyle?: TextStyle;
|
|
55
|
+
/** Style for the title. */
|
|
56
|
+
titleStyle?: TextStyle;
|
|
57
|
+
/** Style for the subtitle. */
|
|
58
|
+
subtitleStyle?: TextStyle;
|
|
59
|
+
/** Style for placeholder text. */
|
|
60
|
+
placeholderStyle?: TextStyle;
|
|
61
|
+
}
|
|
62
|
+
export declare class Interview extends Control {
|
|
63
|
+
private _title;
|
|
64
|
+
private _subtitle;
|
|
65
|
+
private _questions;
|
|
66
|
+
private _answers;
|
|
67
|
+
private _currentIndex;
|
|
68
|
+
private _input;
|
|
69
|
+
private _done;
|
|
70
|
+
private _promptStyle;
|
|
71
|
+
private _answeredStyle;
|
|
72
|
+
private _titleStyle;
|
|
73
|
+
private _subtitleStyle;
|
|
74
|
+
constructor(options: InterviewOptions);
|
|
75
|
+
/** Get all answers collected so far. */
|
|
76
|
+
get answers(): Record<string, string>;
|
|
77
|
+
/** Whether the interview has completed. */
|
|
78
|
+
get completed(): boolean;
|
|
79
|
+
/** Total number of questions. */
|
|
80
|
+
get questionCount(): number;
|
|
81
|
+
/** Current question index (0-based). */
|
|
82
|
+
get currentQuestionIndex(): number;
|
|
83
|
+
private _recordAnswer;
|
|
84
|
+
handleInput(event: InputEvent): boolean;
|
|
85
|
+
measure(constraint: Constraint): Size;
|
|
86
|
+
render(ctx: DrawingContext): void;
|
|
87
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interview — a modal widget that presents a sequence of questions
|
|
3
|
+
* and collects answers. Designed to run inside a ChatView by replacing
|
|
4
|
+
* the normal input area (via ChatView.setInputOverride).
|
|
5
|
+
*
|
|
6
|
+
* While active the ChatView's own input prompt is hidden; when the
|
|
7
|
+
* interview completes it emits "complete" with all collected answers
|
|
8
|
+
* and the caller can remove the override to restore normal input.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
*
|
|
12
|
+
* const interview = new Interview({
|
|
13
|
+
* title: "Quick intro",
|
|
14
|
+
* subtitle: "press Enter to skip any question",
|
|
15
|
+
* questions: [
|
|
16
|
+
* { key: "name", prompt: "Your name" },
|
|
17
|
+
* { key: "role", prompt: "Your role", placeholder: "e.g., senior backend engineer" },
|
|
18
|
+
* ],
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* chatView.setInputOverride(interview);
|
|
22
|
+
*
|
|
23
|
+
* interview.on("complete", (answers: Record<string, string>) => {
|
|
24
|
+
* chatView.setInputOverride(null); // restore normal input
|
|
25
|
+
* // use answers...
|
|
26
|
+
* });
|
|
27
|
+
*/
|
|
28
|
+
import { Control } from "../layout/control.js";
|
|
29
|
+
import { TextInput } from "./text-input.js";
|
|
30
|
+
// ── Interview ───────────────────────────────────────────────────────
|
|
31
|
+
export class Interview extends Control {
|
|
32
|
+
_title;
|
|
33
|
+
_subtitle;
|
|
34
|
+
_questions;
|
|
35
|
+
_answers = new Map();
|
|
36
|
+
_currentIndex = 0;
|
|
37
|
+
_input;
|
|
38
|
+
_done = false;
|
|
39
|
+
// Styles
|
|
40
|
+
_promptStyle;
|
|
41
|
+
_answeredStyle;
|
|
42
|
+
_titleStyle;
|
|
43
|
+
_subtitleStyle;
|
|
44
|
+
constructor(options) {
|
|
45
|
+
super();
|
|
46
|
+
this.focusable = true;
|
|
47
|
+
this._title = options.title ?? "";
|
|
48
|
+
this._subtitle = options.subtitle ?? "";
|
|
49
|
+
this._questions = options.questions;
|
|
50
|
+
this._promptStyle = options.promptStyle ?? {};
|
|
51
|
+
this._answeredStyle = options.answeredStyle ?? { italic: true };
|
|
52
|
+
this._titleStyle = options.titleStyle ?? {};
|
|
53
|
+
this._subtitleStyle = options.subtitleStyle ?? { italic: true };
|
|
54
|
+
// Create the shared TextInput for answering
|
|
55
|
+
const q = this._questions[0];
|
|
56
|
+
this._input = new TextInput({
|
|
57
|
+
prompt: q ? ` ${q.prompt}: ` : "",
|
|
58
|
+
promptStyle: options.promptStyle ?? {},
|
|
59
|
+
style: options.inputStyle ?? {},
|
|
60
|
+
cursorStyle: options.cursorStyle ?? {},
|
|
61
|
+
placeholder: q?.placeholder ? ` ${q.placeholder}` : "",
|
|
62
|
+
placeholderStyle: options.placeholderStyle ?? { italic: true },
|
|
63
|
+
});
|
|
64
|
+
this._input.focusable = true;
|
|
65
|
+
this._input.onFocus();
|
|
66
|
+
this.addChild(this._input);
|
|
67
|
+
// On submit, record answer and advance
|
|
68
|
+
this._input.on("submit", (text) => {
|
|
69
|
+
this._recordAnswer(text);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// ── Public API ──────────────────────────────────────────────────
|
|
73
|
+
/** Get all answers collected so far. */
|
|
74
|
+
get answers() {
|
|
75
|
+
const result = {};
|
|
76
|
+
for (const [k, v] of this._answers) {
|
|
77
|
+
result[k] = v;
|
|
78
|
+
}
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
/** Whether the interview has completed. */
|
|
82
|
+
get completed() {
|
|
83
|
+
return this._done;
|
|
84
|
+
}
|
|
85
|
+
/** Total number of questions. */
|
|
86
|
+
get questionCount() {
|
|
87
|
+
return this._questions.length;
|
|
88
|
+
}
|
|
89
|
+
/** Current question index (0-based). */
|
|
90
|
+
get currentQuestionIndex() {
|
|
91
|
+
return this._currentIndex;
|
|
92
|
+
}
|
|
93
|
+
// ── Internal ────────────────────────────────────────────────────
|
|
94
|
+
_recordAnswer(text) {
|
|
95
|
+
if (this._done)
|
|
96
|
+
return;
|
|
97
|
+
const q = this._questions[this._currentIndex];
|
|
98
|
+
this._answers.set(q.key, text.trim());
|
|
99
|
+
this._currentIndex++;
|
|
100
|
+
if (this._currentIndex >= this._questions.length) {
|
|
101
|
+
// All questions answered
|
|
102
|
+
this._done = true;
|
|
103
|
+
this._input.visible = false;
|
|
104
|
+
this.invalidate();
|
|
105
|
+
this.emit("complete", this.answers);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
// Advance to next question
|
|
109
|
+
const next = this._questions[this._currentIndex];
|
|
110
|
+
this._input.clear();
|
|
111
|
+
this._input.prompt = ` ${next.prompt}: `;
|
|
112
|
+
this._input.placeholder = next.placeholder ? ` ${next.placeholder}` : "";
|
|
113
|
+
this.invalidate();
|
|
114
|
+
}
|
|
115
|
+
// ── Input handling ──────────────────────────────────────────────
|
|
116
|
+
handleInput(event) {
|
|
117
|
+
if (this._done)
|
|
118
|
+
return false;
|
|
119
|
+
return this._input.handleInput(event);
|
|
120
|
+
}
|
|
121
|
+
// ── Layout ──────────────────────────────────────────────────────
|
|
122
|
+
measure(constraint) {
|
|
123
|
+
// Height: title(1) + subtitle(1) + answered questions + current input(1)
|
|
124
|
+
let h = 0;
|
|
125
|
+
if (this._title)
|
|
126
|
+
h++; // title row
|
|
127
|
+
if (this._subtitle)
|
|
128
|
+
h++; // subtitle row
|
|
129
|
+
if (this._title || this._subtitle)
|
|
130
|
+
h++; // blank line after header
|
|
131
|
+
// Answered questions
|
|
132
|
+
const answeredCount = Math.min(this._currentIndex, this._questions.length);
|
|
133
|
+
h += answeredCount;
|
|
134
|
+
// Current input row (if not done)
|
|
135
|
+
if (!this._done) {
|
|
136
|
+
h++; // input row
|
|
137
|
+
}
|
|
138
|
+
const size = {
|
|
139
|
+
width: constraint.maxWidth,
|
|
140
|
+
height: Math.max(1, Math.min(h, constraint.maxHeight)),
|
|
141
|
+
};
|
|
142
|
+
this.desiredSize = size;
|
|
143
|
+
return size;
|
|
144
|
+
}
|
|
145
|
+
render(ctx) {
|
|
146
|
+
const b = this.bounds;
|
|
147
|
+
if (!b || b.width < 1 || b.height < 1)
|
|
148
|
+
return;
|
|
149
|
+
let y = b.y;
|
|
150
|
+
// Title
|
|
151
|
+
if (this._title && y < b.y + b.height) {
|
|
152
|
+
ctx.drawText(b.x + 2, y, this._title, this._titleStyle);
|
|
153
|
+
y++;
|
|
154
|
+
}
|
|
155
|
+
// Subtitle
|
|
156
|
+
if (this._subtitle && y < b.y + b.height) {
|
|
157
|
+
ctx.drawText(b.x + 2, y, this._subtitle, this._subtitleStyle);
|
|
158
|
+
y++;
|
|
159
|
+
}
|
|
160
|
+
// Blank line after header
|
|
161
|
+
if ((this._title || this._subtitle) && y < b.y + b.height) {
|
|
162
|
+
y++;
|
|
163
|
+
}
|
|
164
|
+
// Answered questions (dimmed)
|
|
165
|
+
const answeredCount = Math.min(this._currentIndex, this._questions.length);
|
|
166
|
+
for (let i = 0; i < answeredCount && y < b.y + b.height; i++) {
|
|
167
|
+
const q = this._questions[i];
|
|
168
|
+
const a = this._answers.get(q.key) || "";
|
|
169
|
+
const display = a || "(skipped)";
|
|
170
|
+
const label = ` ${q.prompt}: `;
|
|
171
|
+
ctx.drawText(b.x, y, label, this._promptStyle);
|
|
172
|
+
ctx.drawText(b.x + label.length, y, display, this._answeredStyle);
|
|
173
|
+
y++;
|
|
174
|
+
}
|
|
175
|
+
// Current input
|
|
176
|
+
if (!this._done && y < b.y + b.height) {
|
|
177
|
+
this._input.measure({
|
|
178
|
+
minWidth: 0,
|
|
179
|
+
maxWidth: b.width,
|
|
180
|
+
minHeight: 0,
|
|
181
|
+
maxHeight: 1,
|
|
182
|
+
});
|
|
183
|
+
this._input.arrange({ x: b.x, y, width: b.width, height: 1 });
|
|
184
|
+
this._input.render(ctx);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown — terminal-rendered markdown widget.
|
|
3
|
+
*
|
|
4
|
+
* Parses markdown with marked.js and renders it to styled lines
|
|
5
|
+
* that can be displayed in a consolonia terminal UI.
|
|
6
|
+
*
|
|
7
|
+
* Supported elements:
|
|
8
|
+
* - Headings (h1–h6)
|
|
9
|
+
* - Paragraphs with inline bold, italic, code, strikethrough
|
|
10
|
+
* - Links (shown as text + URL)
|
|
11
|
+
* - Unordered and ordered lists (nested)
|
|
12
|
+
* - Task lists (checkboxes)
|
|
13
|
+
* - Code blocks (fenced and indented)
|
|
14
|
+
* - Blockquotes (nested)
|
|
15
|
+
* - Tables (aligned columns with box-drawing borders)
|
|
16
|
+
* - Horizontal rules
|
|
17
|
+
* - Images (alt text shown)
|
|
18
|
+
*/
|
|
19
|
+
import type { TextStyle } from "../drawing/context.js";
|
|
20
|
+
import { type SyntaxTheme } from "./syntax.js";
|
|
21
|
+
export interface MarkdownTheme {
|
|
22
|
+
/** Body text. */
|
|
23
|
+
text: TextStyle;
|
|
24
|
+
/** Bold text. */
|
|
25
|
+
bold: TextStyle;
|
|
26
|
+
/** Italic text. */
|
|
27
|
+
italic: TextStyle;
|
|
28
|
+
/** Bold+italic text. */
|
|
29
|
+
boldItalic: TextStyle;
|
|
30
|
+
/** Inline code. */
|
|
31
|
+
code: TextStyle;
|
|
32
|
+
/** Strikethrough text. */
|
|
33
|
+
strikethrough: TextStyle;
|
|
34
|
+
/** Link text. */
|
|
35
|
+
link: TextStyle;
|
|
36
|
+
/** Link URL (shown in parens). */
|
|
37
|
+
linkUrl: TextStyle;
|
|
38
|
+
/** Heading level 1. */
|
|
39
|
+
h1: TextStyle;
|
|
40
|
+
/** Heading level 2. */
|
|
41
|
+
h2: TextStyle;
|
|
42
|
+
/** Heading levels 3–6. */
|
|
43
|
+
h3: TextStyle;
|
|
44
|
+
/** Code block text. */
|
|
45
|
+
codeBlock: TextStyle;
|
|
46
|
+
/** Code block border/language label. */
|
|
47
|
+
codeBlockChrome: TextStyle;
|
|
48
|
+
/** Blockquote bar and text. */
|
|
49
|
+
blockquote: TextStyle;
|
|
50
|
+
/** List bullet/number. */
|
|
51
|
+
listMarker: TextStyle;
|
|
52
|
+
/** Table borders. */
|
|
53
|
+
tableBorder: TextStyle;
|
|
54
|
+
/** Table header text. */
|
|
55
|
+
tableHeader: TextStyle;
|
|
56
|
+
/** Horizontal rule. */
|
|
57
|
+
hr: TextStyle;
|
|
58
|
+
/** Task checkbox. */
|
|
59
|
+
checkbox: TextStyle;
|
|
60
|
+
}
|
|
61
|
+
/** Segment of styled text. */
|
|
62
|
+
interface Seg {
|
|
63
|
+
text: string;
|
|
64
|
+
style: TextStyle;
|
|
65
|
+
}
|
|
66
|
+
/** A line is an array of segments. */
|
|
67
|
+
type Line = Seg[];
|
|
68
|
+
export interface MarkdownOptions {
|
|
69
|
+
/** Maximum width for wrapping (default 80). */
|
|
70
|
+
width?: number;
|
|
71
|
+
/** Visual theme overrides. */
|
|
72
|
+
theme?: Partial<MarkdownTheme>;
|
|
73
|
+
/** Syntax highlighting theme overrides for code blocks. */
|
|
74
|
+
syntaxTheme?: Partial<SyntaxTheme>;
|
|
75
|
+
/** Indent string prepended to every line (default ""). */
|
|
76
|
+
indent?: string;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Render a markdown string to an array of styled lines.
|
|
80
|
+
*
|
|
81
|
+
* Each line is an array of { text, style } segments suitable for
|
|
82
|
+
* DrawingContext.drawStyledText() or StyledText widget.
|
|
83
|
+
*
|
|
84
|
+
* This is a pure function — no widget state, no side effects.
|
|
85
|
+
*/
|
|
86
|
+
export declare function renderMarkdown(source: string, options?: MarkdownOptions): Line[];
|
|
87
|
+
export {};
|