@oh-my-pi/pi-tui 8.0.20 → 8.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/package.json +2 -10
- package/src/components/markdown.ts +97 -1
- package/src/components/settings-list.ts +1 -1
- package/src/index.ts +8 -0
- package/src/mermaid.ts +140 -0
- package/src/terminal.ts +43 -27
- package/tsconfig.json +0 -42
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-tui",
|
|
3
|
-
"version": "8.0
|
|
3
|
+
"version": "8.1.0",
|
|
4
4
|
"description": "Terminal User Interface library with differential rendering for efficient text-based applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
12
|
"src/**/*",
|
|
13
|
-
"README.md"
|
|
14
|
-
"tsconfig.json"
|
|
13
|
+
"README.md"
|
|
15
14
|
],
|
|
16
15
|
"keywords": [
|
|
17
16
|
"tui",
|
|
@@ -43,12 +42,5 @@
|
|
|
43
42
|
"devDependencies": {
|
|
44
43
|
"@xterm/headless": "^5.5.0",
|
|
45
44
|
"@xterm/xterm": "^5.5.0"
|
|
46
|
-
},
|
|
47
|
-
"exports": {
|
|
48
|
-
".": {
|
|
49
|
-
"types": "./src/index.ts",
|
|
50
|
-
"import": "./src/index.ts"
|
|
51
|
-
},
|
|
52
|
-
"./*": "./src/*"
|
|
53
45
|
}
|
|
54
46
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import type { MermaidImage } from "@oh-my-pi/pi-tui/mermaid";
|
|
1
2
|
import type { SymbolTheme } from "@oh-my-pi/pi-tui/symbols";
|
|
3
|
+
import { encodeITerm2, encodeKitty, getCapabilities, getCellDimensions } from "@oh-my-pi/pi-tui/terminal-image";
|
|
2
4
|
import type { Component } from "@oh-my-pi/pi-tui/tui";
|
|
3
5
|
import { applyBackgroundToLine, visibleWidth, wrapTextWithAnsi } from "@oh-my-pi/pi-tui/utils";
|
|
4
6
|
import { marked, type Token } from "marked";
|
|
@@ -42,6 +44,12 @@ export interface MarkdownTheme {
|
|
|
42
44
|
strikethrough: (text: string) => string;
|
|
43
45
|
underline: (text: string) => string;
|
|
44
46
|
highlightCode?: (code: string, lang?: string) => string[];
|
|
47
|
+
/**
|
|
48
|
+
* Lookup a pre-rendered mermaid image by source hash.
|
|
49
|
+
* Hash is computed as `Bun.hash(source.trim()).toString(16)`.
|
|
50
|
+
* Return null to fall back to text rendering.
|
|
51
|
+
*/
|
|
52
|
+
getMermaidImage?: (sourceHash: string) => MermaidImage | null;
|
|
45
53
|
symbols: SymbolTheme;
|
|
46
54
|
}
|
|
47
55
|
|
|
@@ -125,7 +133,12 @@ export class Markdown implements Component {
|
|
|
125
133
|
// Wrap lines (NO padding, NO background yet)
|
|
126
134
|
const wrappedLines: string[] = [];
|
|
127
135
|
for (const line of renderedLines) {
|
|
128
|
-
|
|
136
|
+
// Skip wrapping for image protocol lines (would corrupt escape sequences)
|
|
137
|
+
if (this.containsImage(line)) {
|
|
138
|
+
wrappedLines.push(line);
|
|
139
|
+
} else {
|
|
140
|
+
wrappedLines.push(...wrapTextWithAnsi(line, contentWidth));
|
|
141
|
+
}
|
|
129
142
|
}
|
|
130
143
|
|
|
131
144
|
// Add margins and background to each wrapped line
|
|
@@ -135,6 +148,12 @@ export class Markdown implements Component {
|
|
|
135
148
|
const contentLines: string[] = [];
|
|
136
149
|
|
|
137
150
|
for (const line of wrappedLines) {
|
|
151
|
+
// Image lines must be output raw - no margins or background
|
|
152
|
+
if (this.containsImage(line)) {
|
|
153
|
+
contentLines.push(line);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
|
|
138
157
|
const lineWithMargins = leftMargin + line + rightMargin;
|
|
139
158
|
|
|
140
159
|
if (bgFn) {
|
|
@@ -267,6 +286,24 @@ export class Markdown implements Component {
|
|
|
267
286
|
}
|
|
268
287
|
|
|
269
288
|
case "code": {
|
|
289
|
+
// Handle mermaid diagrams with image rendering when available
|
|
290
|
+
if (token.lang === "mermaid" && this.theme.getMermaidImage) {
|
|
291
|
+
const hash = Bun.hash(token.text.trim()).toString(16);
|
|
292
|
+
const image = this.theme.getMermaidImage(hash);
|
|
293
|
+
const caps = getCapabilities();
|
|
294
|
+
|
|
295
|
+
if (image && caps.images) {
|
|
296
|
+
const imageLines = this.renderMermaidImage(image, width);
|
|
297
|
+
if (imageLines) {
|
|
298
|
+
lines.push(...imageLines);
|
|
299
|
+
if (nextTokenType !== "space") {
|
|
300
|
+
lines.push("");
|
|
301
|
+
}
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
270
307
|
const codeIndent = " ".repeat(this.codeBlockIndent);
|
|
271
308
|
lines.push(this.theme.codeBlockBorder(`\`\`\`${token.lang || ""}`));
|
|
272
309
|
if (this.theme.highlightCode) {
|
|
@@ -658,4 +695,63 @@ export class Markdown implements Component {
|
|
|
658
695
|
lines.push(""); // Add spacing after table
|
|
659
696
|
return lines;
|
|
660
697
|
}
|
|
698
|
+
|
|
699
|
+
private containsImage(line: string): boolean {
|
|
700
|
+
return line.includes("\x1b_G") || line.includes("\x1b]1337;File=");
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Render a mermaid image using terminal graphics protocol.
|
|
705
|
+
* Returns array of lines (image placeholder rows) or null if rendering fails.
|
|
706
|
+
*/
|
|
707
|
+
private renderMermaidImage(image: MermaidImage, availableWidth: number): string[] | null {
|
|
708
|
+
const caps = getCapabilities();
|
|
709
|
+
if (!caps.images) return null;
|
|
710
|
+
|
|
711
|
+
const cellDims = getCellDimensions();
|
|
712
|
+
const scale = 0.5; // Render at 50% of natural size
|
|
713
|
+
|
|
714
|
+
// Calculate natural size in cells (don't scale up, only down if needed)
|
|
715
|
+
const naturalColumns = Math.ceil((image.widthPx * scale) / cellDims.widthPx);
|
|
716
|
+
const naturalRows = Math.ceil((image.heightPx * scale) / cellDims.heightPx);
|
|
717
|
+
|
|
718
|
+
// Use natural size, but cap to available width
|
|
719
|
+
const columns = Math.min(naturalColumns, availableWidth);
|
|
720
|
+
|
|
721
|
+
// If we had to shrink width, calculate proportional height
|
|
722
|
+
let rows: number;
|
|
723
|
+
if (columns < naturalColumns) {
|
|
724
|
+
// Scaled down - recalculate height
|
|
725
|
+
const scale = columns / naturalColumns;
|
|
726
|
+
rows = Math.max(1, Math.ceil(naturalRows * scale));
|
|
727
|
+
} else {
|
|
728
|
+
// Natural size
|
|
729
|
+
rows = naturalRows;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
let sequence: string;
|
|
733
|
+
if (caps.images === "kitty") {
|
|
734
|
+
sequence = encodeKitty(image.base64, { columns, rows });
|
|
735
|
+
} else if (caps.images === "iterm2") {
|
|
736
|
+
sequence = encodeITerm2(image.base64, {
|
|
737
|
+
width: columns,
|
|
738
|
+
height: "auto",
|
|
739
|
+
preserveAspectRatio: true,
|
|
740
|
+
});
|
|
741
|
+
} else {
|
|
742
|
+
return null;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// Reserve space with empty lines, then output image with cursor-up
|
|
746
|
+
// This ensures TUI accounts for image height in layout
|
|
747
|
+
const lines: string[] = [];
|
|
748
|
+
for (let i = 0; i < rows - 1; i++) {
|
|
749
|
+
lines.push("");
|
|
750
|
+
}
|
|
751
|
+
// Move cursor up to first row, then output image
|
|
752
|
+
const moveUp = rows > 1 ? `\x1b[${rows - 1}A` : "";
|
|
753
|
+
lines.push(moveUp + sequence);
|
|
754
|
+
|
|
755
|
+
return lines;
|
|
756
|
+
}
|
|
661
757
|
}
|
|
@@ -131,7 +131,7 @@ export class SettingsList implements Component {
|
|
|
131
131
|
|
|
132
132
|
// Add hint
|
|
133
133
|
lines.push("");
|
|
134
|
-
lines.push(this.theme.hint("
|
|
134
|
+
lines.push(this.theme.hint("Enter/Space to change · Esc to cancel"));
|
|
135
135
|
|
|
136
136
|
return lines;
|
|
137
137
|
}
|
package/src/index.ts
CHANGED
|
@@ -47,6 +47,14 @@ export {
|
|
|
47
47
|
parseKittySequence,
|
|
48
48
|
setKittyProtocolActive,
|
|
49
49
|
} from "./keys";
|
|
50
|
+
// Mermaid diagram support
|
|
51
|
+
export {
|
|
52
|
+
extractMermaidBlocks,
|
|
53
|
+
type MermaidImage,
|
|
54
|
+
type MermaidRenderOptions,
|
|
55
|
+
prerenderMermaidBlocks,
|
|
56
|
+
renderMermaidToPng,
|
|
57
|
+
} from "./mermaid";
|
|
50
58
|
// Input buffering for batch splitting
|
|
51
59
|
export { StdinBuffer, type StdinBufferEventMap, type StdinBufferOptions } from "./stdin-buffer";
|
|
52
60
|
export type { BoxSymbols, SymbolTheme } from "./symbols";
|
package/src/mermaid.ts
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { mkdir, rm } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { $ } from "bun";
|
|
5
|
+
|
|
6
|
+
export interface MermaidImage {
|
|
7
|
+
base64: string;
|
|
8
|
+
widthPx: number;
|
|
9
|
+
heightPx: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface MermaidRenderOptions {
|
|
13
|
+
theme?: "default" | "dark" | "forest" | "neutral";
|
|
14
|
+
backgroundColor?: string;
|
|
15
|
+
width?: number;
|
|
16
|
+
scale?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Render mermaid diagram source to PNG.
|
|
21
|
+
*
|
|
22
|
+
* Uses `mmdc` (mermaid-cli) which must be installed and in PATH.
|
|
23
|
+
* Returns null if rendering fails or mmdc is unavailable.
|
|
24
|
+
*/
|
|
25
|
+
export async function renderMermaidToPng(
|
|
26
|
+
source: string,
|
|
27
|
+
options: MermaidRenderOptions = {},
|
|
28
|
+
): Promise<MermaidImage | null> {
|
|
29
|
+
const mmdc = Bun.which("mmdc");
|
|
30
|
+
if (!mmdc) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const tmpDir = join(tmpdir(), `mermaid-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
35
|
+
const inputPath = join(tmpDir, "input.mmd");
|
|
36
|
+
const outputPath = join(tmpDir, "output.png");
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
await mkdir(tmpDir, { recursive: true });
|
|
40
|
+
await Bun.write(inputPath, source);
|
|
41
|
+
|
|
42
|
+
const args: string[] = ["-i", inputPath, "-o", outputPath, "-q"];
|
|
43
|
+
|
|
44
|
+
if (options.theme) {
|
|
45
|
+
args.push("-t", options.theme);
|
|
46
|
+
}
|
|
47
|
+
if (options.backgroundColor) {
|
|
48
|
+
args.push("-b", options.backgroundColor);
|
|
49
|
+
}
|
|
50
|
+
if (options.width) {
|
|
51
|
+
args.push("-w", String(options.width));
|
|
52
|
+
}
|
|
53
|
+
if (options.scale) {
|
|
54
|
+
args.push("-s", String(options.scale));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const result = await $`${mmdc} ${args}`.quiet().nothrow();
|
|
58
|
+
if (result.exitCode !== 0) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const outputFile = Bun.file(outputPath);
|
|
63
|
+
if (!(await outputFile.exists())) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const buffer = Buffer.from(await outputFile.arrayBuffer());
|
|
68
|
+
const base64 = buffer.toString("base64");
|
|
69
|
+
|
|
70
|
+
const dims = parsePngDimensions(buffer);
|
|
71
|
+
if (!dims) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
base64,
|
|
77
|
+
widthPx: dims.width,
|
|
78
|
+
heightPx: dims.height,
|
|
79
|
+
};
|
|
80
|
+
} catch {
|
|
81
|
+
return null;
|
|
82
|
+
} finally {
|
|
83
|
+
await rm(tmpDir, { recursive: true, force: true }).catch(() => {});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function parsePngDimensions(buffer: Buffer): { width: number; height: number } | null {
|
|
88
|
+
if (buffer.length < 24) return null;
|
|
89
|
+
if (buffer[0] !== 0x89 || buffer[1] !== 0x50 || buffer[2] !== 0x4e || buffer[3] !== 0x47) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
width: buffer.readUInt32BE(16),
|
|
94
|
+
height: buffer.readUInt32BE(20),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Extract mermaid code blocks from markdown text.
|
|
100
|
+
* Returns array of { source, startIndex, endIndex } for each block.
|
|
101
|
+
*/
|
|
102
|
+
export function extractMermaidBlocks(markdown: string): { source: string; hash: string }[] {
|
|
103
|
+
const blocks: { source: string; hash: string }[] = [];
|
|
104
|
+
const regex = /```mermaid\s*\n([\s\S]*?)```/g;
|
|
105
|
+
|
|
106
|
+
for (let match = regex.exec(markdown); match !== null; match = regex.exec(markdown)) {
|
|
107
|
+
const source = match[1].trim();
|
|
108
|
+
const hash = Bun.hash(source).toString(16);
|
|
109
|
+
blocks.push({ source, hash });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return blocks;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Pre-render all mermaid blocks in markdown text.
|
|
117
|
+
* Returns a cache map: hash → MermaidImage.
|
|
118
|
+
*/
|
|
119
|
+
export async function prerenderMermaidBlocks(
|
|
120
|
+
markdown: string,
|
|
121
|
+
options: MermaidRenderOptions = {},
|
|
122
|
+
): Promise<Map<string, MermaidImage>> {
|
|
123
|
+
const blocks = extractMermaidBlocks(markdown);
|
|
124
|
+
const cache = new Map<string, MermaidImage>();
|
|
125
|
+
|
|
126
|
+
const results = await Promise.all(
|
|
127
|
+
blocks.map(async ({ source, hash }) => {
|
|
128
|
+
const image = await renderMermaidToPng(source, options);
|
|
129
|
+
return { hash, image };
|
|
130
|
+
}),
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
for (const { hash, image } of results) {
|
|
134
|
+
if (image) {
|
|
135
|
+
cache.set(hash, image);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return cache;
|
|
140
|
+
}
|
package/src/terminal.ts
CHANGED
|
@@ -13,20 +13,24 @@ let activeTerminal: ProcessTerminal | null = null;
|
|
|
13
13
|
* Resets terminal state without requiring access to the ProcessTerminal instance
|
|
14
14
|
*/
|
|
15
15
|
export function emergencyTerminalRestore(): void {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
terminal
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"\x1b[
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
process.stdin.setRawMode
|
|
16
|
+
try {
|
|
17
|
+
const terminal = activeTerminal;
|
|
18
|
+
if (terminal) {
|
|
19
|
+
terminal.stop();
|
|
20
|
+
terminal.showCursor();
|
|
21
|
+
} else {
|
|
22
|
+
// Blind restore if no instance tracked - covers edge cases
|
|
23
|
+
process.stdout.write(
|
|
24
|
+
"\x1b[?2004l" + // Disable bracketed paste
|
|
25
|
+
"\x1b[<u" + // Pop kitty keyboard protocol
|
|
26
|
+
"\x1b[?25h", // Show cursor
|
|
27
|
+
);
|
|
28
|
+
if (process.stdin.setRawMode) {
|
|
29
|
+
process.stdin.setRawMode(false);
|
|
30
|
+
}
|
|
29
31
|
}
|
|
32
|
+
} catch {
|
|
33
|
+
// Terminal may already be dead during crash cleanup - ignore errors
|
|
30
34
|
}
|
|
31
35
|
}
|
|
32
36
|
export interface Terminal {
|
|
@@ -93,7 +97,7 @@ export class ProcessTerminal implements Terminal {
|
|
|
93
97
|
process.stdin.resume();
|
|
94
98
|
|
|
95
99
|
// Enable bracketed paste mode - terminal will wrap pastes in \x1b[200~ ... \x1b[201~
|
|
96
|
-
|
|
100
|
+
this.safeWrite("\x1b[?2004h");
|
|
97
101
|
|
|
98
102
|
// Set up resize handler immediately
|
|
99
103
|
process.stdout.on("resize", this.resizeHandler);
|
|
@@ -137,7 +141,7 @@ export class ProcessTerminal implements Terminal {
|
|
|
137
141
|
// Flag 1 = disambiguate escape codes
|
|
138
142
|
// Flag 2 = report event types (press/repeat/release)
|
|
139
143
|
// Flag 4 = report alternate keys
|
|
140
|
-
|
|
144
|
+
this.safeWrite("\x1b[>7u");
|
|
141
145
|
return; // Don't forward protocol response to TUI
|
|
142
146
|
}
|
|
143
147
|
}
|
|
@@ -172,7 +176,7 @@ export class ProcessTerminal implements Terminal {
|
|
|
172
176
|
private queryAndEnableKittyProtocol(): void {
|
|
173
177
|
this.setupStdinBuffer();
|
|
174
178
|
process.stdin.on("data", this.stdinDataHandler!);
|
|
175
|
-
|
|
179
|
+
this.safeWrite("\x1b[?u");
|
|
176
180
|
}
|
|
177
181
|
|
|
178
182
|
stop(): void {
|
|
@@ -182,11 +186,11 @@ export class ProcessTerminal implements Terminal {
|
|
|
182
186
|
}
|
|
183
187
|
|
|
184
188
|
// Disable bracketed paste mode
|
|
185
|
-
|
|
189
|
+
this.safeWrite("\x1b[?2004l");
|
|
186
190
|
|
|
187
191
|
// Disable Kitty keyboard protocol (pop the flags we pushed) - only if we enabled it
|
|
188
192
|
if (this._kittyProtocolActive) {
|
|
189
|
-
|
|
193
|
+
this.safeWrite("\x1b[<u");
|
|
190
194
|
this._kittyProtocolActive = false;
|
|
191
195
|
setKittyProtocolActive(false);
|
|
192
196
|
}
|
|
@@ -215,7 +219,19 @@ export class ProcessTerminal implements Terminal {
|
|
|
215
219
|
}
|
|
216
220
|
|
|
217
221
|
write(data: string): void {
|
|
218
|
-
|
|
222
|
+
this.safeWrite(data);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
private safeWrite(data: string): void {
|
|
226
|
+
try {
|
|
227
|
+
process.stdout.write(data);
|
|
228
|
+
} catch (err) {
|
|
229
|
+
// EIO means terminal is dead - exit gracefully instead of crashing
|
|
230
|
+
if (err && typeof err === "object" && (err as { code?: string }).code === "EIO") {
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
233
|
+
throw err;
|
|
234
|
+
}
|
|
219
235
|
}
|
|
220
236
|
|
|
221
237
|
get columns(): number {
|
|
@@ -229,36 +245,36 @@ export class ProcessTerminal implements Terminal {
|
|
|
229
245
|
moveBy(lines: number): void {
|
|
230
246
|
if (lines > 0) {
|
|
231
247
|
// Move down
|
|
232
|
-
|
|
248
|
+
this.safeWrite(`\x1b[${lines}B`);
|
|
233
249
|
} else if (lines < 0) {
|
|
234
250
|
// Move up
|
|
235
|
-
|
|
251
|
+
this.safeWrite(`\x1b[${-lines}A`);
|
|
236
252
|
}
|
|
237
253
|
// lines === 0: no movement
|
|
238
254
|
}
|
|
239
255
|
|
|
240
256
|
hideCursor(): void {
|
|
241
|
-
|
|
257
|
+
this.safeWrite("\x1b[?25l");
|
|
242
258
|
}
|
|
243
259
|
|
|
244
260
|
showCursor(): void {
|
|
245
|
-
|
|
261
|
+
this.safeWrite("\x1b[?25h");
|
|
246
262
|
}
|
|
247
263
|
|
|
248
264
|
clearLine(): void {
|
|
249
|
-
|
|
265
|
+
this.safeWrite("\x1b[K");
|
|
250
266
|
}
|
|
251
267
|
|
|
252
268
|
clearFromCursor(): void {
|
|
253
|
-
|
|
269
|
+
this.safeWrite("\x1b[J");
|
|
254
270
|
}
|
|
255
271
|
|
|
256
272
|
clearScreen(): void {
|
|
257
|
-
|
|
273
|
+
this.safeWrite("\x1b[2J\x1b[H"); // Clear screen and move to home (1,1)
|
|
258
274
|
}
|
|
259
275
|
|
|
260
276
|
setTitle(title: string): void {
|
|
261
277
|
// OSC 0;title BEL - set terminal window title
|
|
262
|
-
|
|
278
|
+
this.safeWrite(`\x1b]0;${title}\x07`);
|
|
263
279
|
}
|
|
264
280
|
}
|
package/tsconfig.json
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2024",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"lib": [
|
|
6
|
-
"ES2024"
|
|
7
|
-
],
|
|
8
|
-
"strict": true,
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"skipLibCheck": true,
|
|
11
|
-
"forceConsistentCasingInFileNames": true,
|
|
12
|
-
"moduleResolution": "Bundler",
|
|
13
|
-
"resolveJsonModule": true,
|
|
14
|
-
"allowImportingTsExtensions": true,
|
|
15
|
-
"experimentalDecorators": true,
|
|
16
|
-
"emitDecoratorMetadata": true,
|
|
17
|
-
"useDefineForClassFields": false,
|
|
18
|
-
"types": [
|
|
19
|
-
"bun",
|
|
20
|
-
"node"
|
|
21
|
-
],
|
|
22
|
-
"noEmit": true,
|
|
23
|
-
"baseUrl": ".",
|
|
24
|
-
"paths": {
|
|
25
|
-
"@oh-my-pi/pi-tui": [
|
|
26
|
-
"./src/index.ts"
|
|
27
|
-
],
|
|
28
|
-
"@oh-my-pi/pi-tui/*": [
|
|
29
|
-
"./src/*"
|
|
30
|
-
]
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
"include": [
|
|
34
|
-
"src/**/*.ts"
|
|
35
|
-
],
|
|
36
|
-
"exclude": [
|
|
37
|
-
"node_modules",
|
|
38
|
-
"dist",
|
|
39
|
-
"**/*.test.ts",
|
|
40
|
-
"test/**"
|
|
41
|
-
]
|
|
42
|
-
}
|