wave-code 1.0.0 → 1.0.2
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/dist/cli.js +20 -1
- package/dist/components/App.js +7 -0
- package/dist/components/BtwDisplay.js +13 -3
- package/dist/components/ChatInterface.js +25 -8
- package/dist/components/InputBox.d.ts +1 -3
- package/dist/components/InputBox.js +12 -9
- package/dist/components/LoadingIndicator.d.ts +1 -2
- package/dist/components/LoadingIndicator.js +2 -2
- package/dist/components/LoginCommand.js +4 -2
- package/dist/components/Markdown.js +13 -16
- package/dist/components/Notifications.d.ts +7 -0
- package/dist/components/Notifications.js +9 -0
- package/dist/components/StatusLine.d.ts +0 -4
- package/dist/components/StatusLine.js +6 -10
- package/dist/components/TaskList.js +2 -1
- package/dist/components/ToolDisplay.d.ts +1 -0
- package/dist/components/ToolDisplay.js +17 -9
- package/dist/constants/commands.js +0 -6
- package/dist/contexts/useChat.d.ts +4 -6
- package/dist/contexts/useChat.js +253 -110
- package/dist/daemon-cli.d.ts +10 -0
- package/dist/daemon-cli.js +15 -0
- package/dist/hooks/useInputManager.js +99 -22
- package/dist/index.js +10 -0
- package/dist/managers/inputHandlers.js +50 -22
- package/dist/managers/inputReducer.d.ts +12 -2
- package/dist/managers/inputReducer.js +57 -9
- package/dist/stdio/agentBridge.d.ts +23 -0
- package/dist/stdio/agentBridge.js +134 -16
- package/dist/stdio/daemonServer.d.ts +67 -0
- package/dist/stdio/daemonServer.js +191 -0
- package/dist/stdio/index.d.ts +2 -0
- package/dist/stdio/index.js +2 -0
- package/dist/stdio/jsonRpcConnection.d.ts +30 -0
- package/dist/stdio/jsonRpcConnection.js +127 -0
- package/dist/stdio/protocol.d.ts +2 -2
- package/dist/stdio/stdioServer.d.ts +2 -7
- package/dist/stdio/stdioServer.js +9 -100
- package/dist/utils/bracketedPaste.d.ts +39 -0
- package/dist/utils/bracketedPaste.js +122 -0
- package/dist/utils/markdownTable.d.ts +34 -0
- package/dist/utils/markdownTable.js +302 -0
- package/dist/utils/throttle.d.ts +3 -3
- package/package.json +4 -2
- package/src/cli.tsx +20 -1
- package/src/components/App.tsx +5 -0
- package/src/components/BtwDisplay.tsx +36 -12
- package/src/components/ChatInterface.tsx +30 -15
- package/src/components/InputBox.tsx +25 -24
- package/src/components/LoadingIndicator.tsx +1 -4
- package/src/components/LoginCommand.tsx +4 -2
- package/src/components/Markdown.tsx +15 -18
- package/src/components/Notifications.tsx +31 -0
- package/src/components/StatusLine.tsx +17 -44
- package/src/components/TaskList.tsx +2 -1
- package/src/components/ToolDisplay.tsx +17 -6
- package/src/constants/commands.ts +0 -6
- package/src/contexts/useChat.tsx +326 -140
- package/src/daemon-cli.ts +17 -0
- package/src/hooks/useInputManager.ts +108 -22
- package/src/index.ts +12 -0
- package/src/managers/inputHandlers.ts +49 -22
- package/src/managers/inputReducer.ts +66 -11
- package/src/stdio/agentBridge.ts +196 -17
- package/src/stdio/daemonServer.ts +212 -0
- package/src/stdio/index.ts +2 -0
- package/src/stdio/jsonRpcConnection.ts +160 -0
- package/src/stdio/protocol.ts +5 -2
- package/src/stdio/stdioServer.ts +14 -120
- package/src/utils/bracketedPaste.ts +170 -0
- package/src/utils/markdownTable.ts +359 -0
- package/src/utils/throttle.ts +8 -8
package/dist/stdio/protocol.d.ts
CHANGED
|
@@ -34,8 +34,8 @@ export declare const INVALID_REQUEST = -32600;
|
|
|
34
34
|
export declare const METHOD_NOT_FOUND = -32601;
|
|
35
35
|
export declare const INVALID_PARAMS = -32602;
|
|
36
36
|
export declare const INTERNAL_ERROR = -32603;
|
|
37
|
-
export type RequestMethod = "initialize" | "destroy" | "restoreSession" | "listSessions" | "getSessionInfo" | "sendMessage" | "bang" | "abortMessage" | "clearMessages" | "rewindToMessage" | "listRewindCheckpoints" | "deleteQueuedMessage" | "updateQueuedMessage" | "deleteQueuedMessageById" | "getMessages" | "getFullMessageThread" | "setPermissionMode" | "getPermissionMode" | "getMcpServers" | "connectMcpServer" | "disconnectMcpServer" | "getSlashCommands" | "searchFiles" | "getPromptHistory" | "searchPromptHistory" | "updateConfig" | "getAuthStatus" | "login" | "logout" | "listPlugins" | "installPlugin" | "uninstallPlugin" | "enablePlugin" | "disablePlugin" | "updatePlugin" | "listMarketplaces" | "addMarketplace" | "removeMarketplace" | "updateMarketplace" | "compact" | "getBackgroundTaskOutput" | "stopBackgroundTask" | "getWorkflowRuns" | "stopWorkflowRun" | "listGitBranches" | "createWorktree" | "removeWorktree";
|
|
37
|
+
export type RequestMethod = "initialize" | "destroy" | "restoreSession" | "listSessions" | "getSessionInfo" | "sendMessage" | "bang" | "askBtw" | "abortMessage" | "clearMessages" | "rewindToMessage" | "listRewindCheckpoints" | "deleteQueuedMessage" | "updateQueuedMessage" | "deleteQueuedMessageById" | "getMessages" | "getFullMessageThread" | "setPermissionMode" | "getPermissionMode" | "getMcpServers" | "connectMcpServer" | "disconnectMcpServer" | "getSlashCommands" | "searchFiles" | "getPromptHistory" | "searchPromptHistory" | "updateConfig" | "listPendingPermissions" | "getAuthStatus" | "login" | "logout" | "listPlugins" | "installPlugin" | "uninstallPlugin" | "enablePlugin" | "disablePlugin" | "updatePlugin" | "listMarketplaces" | "addMarketplace" | "removeMarketplace" | "updateMarketplace" | "compact" | "getBackgroundTaskOutput" | "stopBackgroundTask" | "getWorkflowRuns" | "stopWorkflowRun" | "listGitBranches" | "createWorktree" | "removeWorktree";
|
|
38
38
|
export type ClientNotificationMethod = "permissionResponse";
|
|
39
|
-
export type ServerNotificationMethod = "
|
|
39
|
+
export type ServerNotificationMethod = "userMessageAdded" | "assistantMessageAdded" | "assistantContentUpdated" | "assistantReasoningUpdated" | "toolBlockUpdated" | "errorBlockAdded" | "loadingChange" | "commandRunningChange" | "queuedMessagesChange" | "tasksChange" | "sessionIdChange" | "permissionModeChange" | "mcpServersChange" | "workdirChange" | "bangMessageAdded" | "bangMessageUpdated" | "bangMessageCompleted" | "notificationMessageAdded" | "permissionRequest" | "authUrl" | "compactBlockAdded" | "compactionStateChange" | "backgroundTasksChange" | "btwContent";
|
|
40
40
|
export declare function isRequest(msg: unknown): msg is JsonRpcRequest;
|
|
41
41
|
export declare function isNotification(msg: unknown): msg is JsonRpcNotification;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* and writes responses / notifications to stdout.
|
|
4
4
|
*
|
|
5
5
|
* One JSON object per line. stderr is reserved for logger output.
|
|
6
|
+
* Line handling lives in JsonRpcConnection (shared with DaemonServer).
|
|
6
7
|
*/
|
|
7
8
|
import type { Readable, Writable } from "stream";
|
|
8
9
|
import { AgentBridge, type AgentBridgeOptions } from "./agentBridge.js";
|
|
@@ -12,22 +13,16 @@ export interface StdioServerOptions {
|
|
|
12
13
|
bridgeOptions?: AgentBridgeOptions;
|
|
13
14
|
}
|
|
14
15
|
export declare class StdioServer {
|
|
15
|
-
private rl;
|
|
16
16
|
private bridge;
|
|
17
|
-
private
|
|
18
|
-
private output;
|
|
19
|
-
private started;
|
|
17
|
+
private conn;
|
|
20
18
|
constructor(options?: StdioServerOptions);
|
|
21
19
|
get agentBridge(): AgentBridge;
|
|
22
20
|
start(): void;
|
|
23
21
|
stop(): void;
|
|
24
22
|
handleLine(line: string): Promise<void>;
|
|
25
|
-
private handleRequest;
|
|
26
|
-
private handleNotification;
|
|
27
23
|
sendResponse(id: number | string | null, result?: unknown, error?: {
|
|
28
24
|
code: number;
|
|
29
25
|
message: string;
|
|
30
26
|
}): void;
|
|
31
27
|
sendNotification(method: string, params?: unknown, sessionId?: string): void;
|
|
32
|
-
private write;
|
|
33
28
|
}
|
|
@@ -3,125 +3,34 @@
|
|
|
3
3
|
* and writes responses / notifications to stdout.
|
|
4
4
|
*
|
|
5
5
|
* One JSON object per line. stderr is reserved for logger output.
|
|
6
|
+
* Line handling lives in JsonRpcConnection (shared with DaemonServer).
|
|
6
7
|
*/
|
|
7
|
-
import readline from "readline";
|
|
8
8
|
import { AgentBridge } from "./agentBridge.js";
|
|
9
|
-
import {
|
|
9
|
+
import { JsonRpcConnection } from "./jsonRpcConnection.js";
|
|
10
10
|
export class StdioServer {
|
|
11
11
|
constructor(options = {}) {
|
|
12
|
-
this.started = false;
|
|
13
|
-
this.input = options.input ?? process.stdin;
|
|
14
|
-
this.output = options.output ?? process.stdout;
|
|
15
12
|
this.bridge = new AgentBridge({
|
|
16
13
|
...options.bridgeOptions,
|
|
17
14
|
emit: (method, params, sessionId) => this.sendNotification(method, params, sessionId),
|
|
18
15
|
});
|
|
16
|
+
this.conn = new JsonRpcConnection(options.input ?? process.stdin, options.output ?? process.stdout, this.bridge);
|
|
19
17
|
}
|
|
20
18
|
get agentBridge() {
|
|
21
19
|
return this.bridge;
|
|
22
20
|
}
|
|
23
21
|
start() {
|
|
24
|
-
|
|
25
|
-
return;
|
|
26
|
-
this.started = true;
|
|
27
|
-
this.rl = readline.createInterface({
|
|
28
|
-
input: this.input,
|
|
29
|
-
crlfDelay: Infinity,
|
|
30
|
-
});
|
|
31
|
-
this.rl.on("line", (line) => {
|
|
32
|
-
this.handleLine(line).catch((err) => {
|
|
33
|
-
// Should never reach here — handleLine catches internally
|
|
34
|
-
this.sendResponse(null, undefined, {
|
|
35
|
-
code: INTERNAL_ERROR,
|
|
36
|
-
message: `Unhandled error: ${err.message}`,
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
this.rl.on("close", () => {
|
|
41
|
-
this.started = false;
|
|
42
|
-
});
|
|
22
|
+
this.conn.start();
|
|
43
23
|
}
|
|
44
24
|
stop() {
|
|
45
|
-
this.
|
|
46
|
-
this.rl = undefined;
|
|
47
|
-
this.started = false;
|
|
25
|
+
this.conn.stop();
|
|
48
26
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (!trimmed)
|
|
52
|
-
return;
|
|
53
|
-
let msg;
|
|
54
|
-
try {
|
|
55
|
-
msg = JSON.parse(trimmed);
|
|
56
|
-
}
|
|
57
|
-
catch {
|
|
58
|
-
this.sendResponse(null, undefined, {
|
|
59
|
-
code: PARSE_ERROR,
|
|
60
|
-
message: "Parse error: invalid JSON",
|
|
61
|
-
});
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
if (isRequest(msg)) {
|
|
65
|
-
await this.handleRequest(msg);
|
|
66
|
-
}
|
|
67
|
-
else if (isNotification(msg)) {
|
|
68
|
-
this.handleNotification(msg);
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
// Echo back the id if the message has one, otherwise null
|
|
72
|
-
const id = typeof msg === "object" &&
|
|
73
|
-
msg !== null &&
|
|
74
|
-
"id" in msg &&
|
|
75
|
-
(typeof msg.id === "number" ||
|
|
76
|
-
typeof msg.id === "string")
|
|
77
|
-
? msg.id
|
|
78
|
-
: null;
|
|
79
|
-
this.sendResponse(id, undefined, {
|
|
80
|
-
code: INVALID_REQUEST,
|
|
81
|
-
message: "Invalid request: must have 'method' field",
|
|
82
|
-
});
|
|
83
|
-
}
|
|
27
|
+
handleLine(line) {
|
|
28
|
+
return this.conn.handleLine(line);
|
|
84
29
|
}
|
|
85
|
-
async handleRequest(msg) {
|
|
86
|
-
try {
|
|
87
|
-
const result = await this.bridge.handleRequest(msg.method, msg.params, msg.sessionId);
|
|
88
|
-
this.sendResponse(msg.id, result);
|
|
89
|
-
}
|
|
90
|
-
catch (err) {
|
|
91
|
-
const code = err && typeof err === "object" && "code" in err
|
|
92
|
-
? err.code
|
|
93
|
-
: INTERNAL_ERROR;
|
|
94
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
95
|
-
this.sendResponse(msg.id, undefined, { code, message });
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
handleNotification(msg) {
|
|
99
|
-
try {
|
|
100
|
-
this.bridge.handleNotification(msg.method, msg.params);
|
|
101
|
-
}
|
|
102
|
-
catch (err) {
|
|
103
|
-
// Notifications don't get responses, but we log to stderr
|
|
104
|
-
process.stderr.write(`Error handling notification ${msg.method}: ${err.message}\n`);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
// ── Output helpers ────────────────────────────────────────────
|
|
108
30
|
sendResponse(id, result, error) {
|
|
109
|
-
|
|
110
|
-
if (error) {
|
|
111
|
-
response.error = error;
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
response.result = result ?? null;
|
|
115
|
-
}
|
|
116
|
-
this.write(response);
|
|
31
|
+
this.conn.sendResponse(id, result, error);
|
|
117
32
|
}
|
|
118
33
|
sendNotification(method, params, sessionId) {
|
|
119
|
-
|
|
120
|
-
if (sessionId)
|
|
121
|
-
notification.sessionId = sessionId;
|
|
122
|
-
this.write(notification);
|
|
123
|
-
}
|
|
124
|
-
write(obj) {
|
|
125
|
-
this.output.write(JSON.stringify(obj) + "\n");
|
|
34
|
+
this.conn.sendNotification(method, params, sessionId);
|
|
126
35
|
}
|
|
127
36
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bracketed paste (DECSET 2004) detector.
|
|
3
|
+
*
|
|
4
|
+
* When bracketed paste is enabled, terminals wrap pasted text in
|
|
5
|
+
* `\x1b[200~` (start) and `\x1b[201~` (end) markers, which lets the input
|
|
6
|
+
* pipeline distinguish "text was pasted" from "user typed keys" — most
|
|
7
|
+
* importantly, a pasted trailing `\r` must NOT be treated as an Enter key.
|
|
8
|
+
*
|
|
9
|
+
* ink delivers each stdin chunk through useInput after stripping ONE leading
|
|
10
|
+
* ESC from the parsed sequence (see use-input.js), so markers may arrive in
|
|
11
|
+
* either the raw form (`\x1b[200~`) or the stripped form (`[200~`), and may
|
|
12
|
+
* be split across chunks. The detector normalizes both forms and buffers
|
|
13
|
+
* partial markers until they complete (deferral is safe: a deferred partial
|
|
14
|
+
* is flushed unchanged if the next chunk does not complete a marker).
|
|
15
|
+
*/
|
|
16
|
+
export type PasteProcessResult = {
|
|
17
|
+
kind: "input";
|
|
18
|
+
input: string;
|
|
19
|
+
} | {
|
|
20
|
+
kind: "paste";
|
|
21
|
+
text: string;
|
|
22
|
+
leadingInput?: string;
|
|
23
|
+
} | {
|
|
24
|
+
kind: "consume";
|
|
25
|
+
};
|
|
26
|
+
export interface BracketedPasteDetector {
|
|
27
|
+
/**
|
|
28
|
+
* Feed one input chunk (as delivered by ink's useInput callback).
|
|
29
|
+
* - `input`: regular keystrokes, pass through to normal handling.
|
|
30
|
+
* - `paste`: completed bracketed paste; insert `text` without submitting.
|
|
31
|
+
* `leadingInput` (rare) is content that preceded the start marker in the
|
|
32
|
+
* same chunk and should be handled as regular input first.
|
|
33
|
+
* - `consume`: content of an in-flight paste (or an empty paste); do
|
|
34
|
+
* nothing with this chunk.
|
|
35
|
+
*/
|
|
36
|
+
process(chunk: string): PasteProcessResult;
|
|
37
|
+
reset(): void;
|
|
38
|
+
}
|
|
39
|
+
export declare function createBracketedPasteDetector(): BracketedPasteDetector;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
const ESC = "\u001b";
|
|
2
|
+
const START_STRIPPED = "[200~";
|
|
3
|
+
const START_RAW = `${ESC}[200~`;
|
|
4
|
+
const END_STRIPPED = "[201~";
|
|
5
|
+
const END_RAW = `${ESC}[201~`;
|
|
6
|
+
const START_FORMS = [START_RAW, START_STRIPPED];
|
|
7
|
+
const END_FORMS = [END_RAW, END_STRIPPED];
|
|
8
|
+
/**
|
|
9
|
+
* Suffixes that may be the beginning of a marker split across chunks.
|
|
10
|
+
* Longest-first so the longest matching suffix is deferred (e.g. `[200`
|
|
11
|
+
* rather than `[20` for `...x[200`). Deferral is flush-equivalent: if the
|
|
12
|
+
* next chunk does not complete a marker, the deferred suffix is delivered
|
|
13
|
+
* as regular input unchanged.
|
|
14
|
+
*/
|
|
15
|
+
const PARTIAL_SUFFIXES = [
|
|
16
|
+
`${ESC}[201`,
|
|
17
|
+
`${ESC}[200`,
|
|
18
|
+
`${ESC}[20`,
|
|
19
|
+
`${ESC}[2`,
|
|
20
|
+
"[201",
|
|
21
|
+
"[200",
|
|
22
|
+
"[20",
|
|
23
|
+
"[2",
|
|
24
|
+
].sort((a, b) => b.length - a.length);
|
|
25
|
+
export function createBracketedPasteDetector() {
|
|
26
|
+
let inPaste = false;
|
|
27
|
+
let buffer = ""; // paste content collected since the start marker
|
|
28
|
+
let pending = ""; // deferred partial marker suffix from a previous chunk
|
|
29
|
+
const findFirst = (text, forms) => {
|
|
30
|
+
let best = null;
|
|
31
|
+
for (const form of forms) {
|
|
32
|
+
const index = text.indexOf(form);
|
|
33
|
+
if (index !== -1 && (best === null || index < best.index)) {
|
|
34
|
+
best = { index, length: form.length };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return best;
|
|
38
|
+
};
|
|
39
|
+
const endsWithPartial = (text) => {
|
|
40
|
+
for (const prefix of PARTIAL_SUFFIXES) {
|
|
41
|
+
if (text.endsWith(prefix)) {
|
|
42
|
+
return prefix;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
};
|
|
47
|
+
const process = (chunk) => {
|
|
48
|
+
let remaining = pending + chunk;
|
|
49
|
+
pending = "";
|
|
50
|
+
let leadingInput = "";
|
|
51
|
+
let pasteText = null;
|
|
52
|
+
while (remaining.length > 0) {
|
|
53
|
+
if (!inPaste) {
|
|
54
|
+
const start = findFirst(remaining, START_FORMS);
|
|
55
|
+
if (start) {
|
|
56
|
+
leadingInput += remaining.slice(0, start.index);
|
|
57
|
+
remaining = remaining.slice(start.index + start.length);
|
|
58
|
+
inPaste = true;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
// Orphan end marker (start was missed, e.g. consumed by another
|
|
62
|
+
// input handler): treat the preceding text as paste so a trailing
|
|
63
|
+
// `\r` cannot trigger the coalesced-Enter submit heuristic.
|
|
64
|
+
const end = findFirst(remaining, END_FORMS);
|
|
65
|
+
if (end) {
|
|
66
|
+
pasteText =
|
|
67
|
+
(pasteText ?? "") + leadingInput + remaining.slice(0, end.index);
|
|
68
|
+
leadingInput = "";
|
|
69
|
+
remaining = remaining.slice(end.index + end.length);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const partial = endsWithPartial(remaining);
|
|
73
|
+
if (partial) {
|
|
74
|
+
pending = remaining.slice(remaining.length - partial.length);
|
|
75
|
+
remaining = remaining.slice(0, remaining.length - partial.length);
|
|
76
|
+
}
|
|
77
|
+
if (remaining.length > 0) {
|
|
78
|
+
leadingInput += remaining;
|
|
79
|
+
remaining = "";
|
|
80
|
+
}
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
const end = findFirst(remaining, END_FORMS);
|
|
84
|
+
if (end) {
|
|
85
|
+
pasteText = (pasteText ?? "") + buffer + remaining.slice(0, end.index);
|
|
86
|
+
buffer = "";
|
|
87
|
+
inPaste = false;
|
|
88
|
+
remaining = remaining.slice(end.index + end.length);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const partial = endsWithPartial(remaining);
|
|
92
|
+
if (partial) {
|
|
93
|
+
pending = remaining.slice(remaining.length - partial.length);
|
|
94
|
+
remaining = remaining.slice(0, remaining.length - partial.length);
|
|
95
|
+
}
|
|
96
|
+
buffer += remaining;
|
|
97
|
+
remaining = "";
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
if (inPaste) {
|
|
101
|
+
// Paste still in flight: hold the content, deliver nothing yet.
|
|
102
|
+
return { kind: "consume" };
|
|
103
|
+
}
|
|
104
|
+
if (pasteText !== null) {
|
|
105
|
+
const result = {
|
|
106
|
+
kind: "paste",
|
|
107
|
+
text: pasteText,
|
|
108
|
+
};
|
|
109
|
+
if (leadingInput !== "") {
|
|
110
|
+
result.leadingInput = leadingInput;
|
|
111
|
+
}
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
return { kind: "input", input: leadingInput };
|
|
115
|
+
};
|
|
116
|
+
const reset = () => {
|
|
117
|
+
inPaste = false;
|
|
118
|
+
buffer = "";
|
|
119
|
+
pending = "";
|
|
120
|
+
};
|
|
121
|
+
return { process, reset };
|
|
122
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Token, Tokens } from "marked";
|
|
2
|
+
export type CellAlign = "left" | "center" | "right" | null;
|
|
3
|
+
/**
|
|
4
|
+
* Wrap text to fit within a given width, returning array of lines.
|
|
5
|
+
* ANSI-aware: preserves styling across line breaks.
|
|
6
|
+
*
|
|
7
|
+
* @param hard - If true, break words that exceed width (needed when columns
|
|
8
|
+
* are narrower than the longest word). Default false.
|
|
9
|
+
*/
|
|
10
|
+
export declare function wrapText(text: string, width: number, options?: {
|
|
11
|
+
hard?: boolean;
|
|
12
|
+
}): string[];
|
|
13
|
+
/**
|
|
14
|
+
* Pad `content` to `targetWidth` according to alignment. `displayWidth` is the
|
|
15
|
+
* visible width of `content` (caller computes this, e.g. via stringWidth on
|
|
16
|
+
* ANSI-stripped text, so ANSI codes in `content` don't affect padding).
|
|
17
|
+
*/
|
|
18
|
+
export declare function padAligned(content: string, displayWidth: number, targetWidth: number, align: CellAlign | undefined): string;
|
|
19
|
+
/**
|
|
20
|
+
* Render a markdown table to a box-drawing ANSI string for the terminal.
|
|
21
|
+
* Handles terminal width by:
|
|
22
|
+
* 1. Calculating minimum column widths based on longest word
|
|
23
|
+
* 2. Distributing available space proportionally
|
|
24
|
+
* 3. Wrapping text within cells (no truncation)
|
|
25
|
+
* 4. Falling back to a vertical key-value format when rows would be too
|
|
26
|
+
* tall or the table still overflows the terminal width
|
|
27
|
+
*
|
|
28
|
+
* @param token - marked table token
|
|
29
|
+
* @param columns - terminal width
|
|
30
|
+
* @param formatInline - formats inline tokens to an ANSI string (the caller
|
|
31
|
+
* binds its marked parser so this module stays
|
|
32
|
+
* parser-agnostic and directly testable)
|
|
33
|
+
*/
|
|
34
|
+
export declare function renderMarkdownTable(token: Tokens.Table, columns: number, formatInline: (tokens: Token[]) => string): string;
|