wave-code 0.5.0 → 0.6.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/dist/components/App.d.ts.map +1 -1
- package/dist/components/App.js +38 -2
- package/dist/components/BackgroundTaskManager.d.ts +6 -0
- package/dist/components/BackgroundTaskManager.d.ts.map +1 -0
- package/dist/components/{TaskManager.js → BackgroundTaskManager.js} +1 -1
- package/dist/components/ChatInterface.d.ts.map +1 -1
- package/dist/components/ChatInterface.js +39 -5
- package/dist/components/CommandSelector.d.ts.map +1 -1
- package/dist/components/CommandSelector.js +10 -2
- package/dist/components/CompressDisplay.d.ts.map +1 -1
- package/dist/components/CompressDisplay.js +6 -10
- package/dist/components/ConfirmationDetails.d.ts +9 -0
- package/dist/components/ConfirmationDetails.d.ts.map +1 -0
- package/dist/components/ConfirmationDetails.js +53 -0
- package/dist/components/{Confirmation.d.ts → ConfirmationSelector.d.ts} +3 -3
- package/dist/components/ConfirmationSelector.d.ts.map +1 -0
- package/dist/components/{Confirmation.js → ConfirmationSelector.js} +34 -96
- package/dist/components/DiffDisplay.d.ts.map +1 -1
- package/dist/components/DiffDisplay.js +44 -1
- package/dist/components/FileSelector.d.ts.map +1 -1
- package/dist/components/FileSelector.js +2 -2
- package/dist/components/HistorySearch.d.ts.map +1 -1
- package/dist/components/HistorySearch.js +12 -4
- package/dist/components/InputBox.d.ts +1 -3
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +7 -17
- package/dist/components/LoadingIndicator.d.ts +11 -0
- package/dist/components/LoadingIndicator.d.ts.map +1 -0
- package/dist/components/LoadingIndicator.js +6 -0
- package/dist/components/Markdown.d.ts.map +1 -1
- package/dist/components/Markdown.js +114 -121
- package/dist/components/MessageItem.d.ts.map +1 -1
- package/dist/components/MessageItem.js +1 -2
- package/dist/components/MessageList.d.ts +2 -3
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +7 -7
- package/dist/components/PlanDisplay.d.ts.map +1 -1
- package/dist/components/PlanDisplay.js +4 -12
- package/dist/components/RewindCommand.d.ts +4 -0
- package/dist/components/RewindCommand.d.ts.map +1 -1
- package/dist/components/RewindCommand.js +19 -2
- package/dist/components/SubagentBlock.d.ts.map +1 -1
- package/dist/components/SubagentBlock.js +9 -6
- package/dist/components/TaskList.d.ts +3 -0
- package/dist/components/TaskList.d.ts.map +1 -0
- package/dist/components/TaskList.js +49 -0
- package/dist/components/ToolResultDisplay.js +1 -1
- package/dist/contexts/useChat.d.ts +11 -3
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +36 -31
- package/dist/hooks/useInputManager.d.ts +2 -13
- package/dist/hooks/useInputManager.d.ts.map +1 -1
- package/dist/hooks/useInputManager.js +8 -57
- package/dist/hooks/useTasks.d.ts +2 -0
- package/dist/hooks/useTasks.d.ts.map +1 -0
- package/dist/hooks/useTasks.js +5 -0
- package/dist/managers/InputManager.d.ts +4 -28
- package/dist/managers/InputManager.d.ts.map +1 -1
- package/dist/managers/InputManager.js +22 -128
- package/package.json +5 -6
- package/src/components/App.tsx +50 -3
- package/src/components/{TaskManager.tsx → BackgroundTaskManager.tsx} +4 -2
- package/src/components/ChatInterface.tsx +79 -23
- package/src/components/CommandSelector.tsx +35 -17
- package/src/components/CompressDisplay.tsx +5 -22
- package/src/components/ConfirmationDetails.tsx +108 -0
- package/src/components/{Confirmation.tsx → ConfirmationSelector.tsx} +69 -184
- package/src/components/DiffDisplay.tsx +62 -1
- package/src/components/FileSelector.tsx +0 -2
- package/src/components/HistorySearch.tsx +45 -21
- package/src/components/InputBox.tsx +11 -33
- package/src/components/LoadingIndicator.tsx +56 -0
- package/src/components/Markdown.tsx +126 -323
- package/src/components/MessageItem.tsx +1 -3
- package/src/components/MessageList.tsx +10 -67
- package/src/components/PlanDisplay.tsx +4 -27
- package/src/components/RewindCommand.tsx +38 -1
- package/src/components/SubagentBlock.tsx +25 -16
- package/src/components/TaskList.tsx +70 -0
- package/src/components/ToolResultDisplay.tsx +2 -2
- package/src/contexts/useChat.tsx +57 -40
- package/src/hooks/useInputManager.ts +9 -73
- package/src/hooks/useTasks.ts +6 -0
- package/src/managers/InputManager.ts +25 -159
- package/dist/components/Confirmation.d.ts.map +0 -1
- package/dist/components/MemoryDisplay.d.ts +0 -8
- package/dist/components/MemoryDisplay.d.ts.map +0 -1
- package/dist/components/MemoryDisplay.js +0 -25
- package/dist/components/MemoryTypeSelector.d.ts +0 -8
- package/dist/components/MemoryTypeSelector.d.ts.map +0 -1
- package/dist/components/MemoryTypeSelector.js +0 -38
- package/dist/components/TaskManager.d.ts +0 -6
- package/dist/components/TaskManager.d.ts.map +0 -1
- package/src/components/MemoryDisplay.tsx +0 -62
- package/src/components/MemoryTypeSelector.tsx +0 -98
|
@@ -4,8 +4,7 @@ import { useInput } from "ink";
|
|
|
4
4
|
import { FileSelector } from "./FileSelector.js";
|
|
5
5
|
import { CommandSelector } from "./CommandSelector.js";
|
|
6
6
|
import { HistorySearch } from "./HistorySearch.js";
|
|
7
|
-
import {
|
|
8
|
-
import { TaskManager } from "./TaskManager.js";
|
|
7
|
+
import { BackgroundTaskManager } from "./BackgroundTaskManager.js";
|
|
9
8
|
import { McpManager } from "./McpManager.js";
|
|
10
9
|
import { RewindCommand } from "./RewindCommand.js";
|
|
11
10
|
import { useInputManager } from "../hooks/useInputManager.js";
|
|
@@ -14,7 +13,7 @@ import { useChat } from "../contexts/useChat.js";
|
|
|
14
13
|
import type { McpServerStatus, SlashCommand } from "wave-agent-sdk";
|
|
15
14
|
|
|
16
15
|
export const INPUT_PLACEHOLDER_TEXT =
|
|
17
|
-
"Type your message (use @ to reference files, / for commands,
|
|
16
|
+
"Type your message (use @ to reference files, / for commands, Ctrl+R to search history, Ctrl+O to expand messages, Ctrl+T to toggle tasks)...";
|
|
18
17
|
|
|
19
18
|
export const INPUT_PLACEHOLDER_TEXT_PREFIX = INPUT_PLACEHOLDER_TEXT.substring(
|
|
20
19
|
0,
|
|
@@ -25,13 +24,11 @@ export interface InputBoxProps {
|
|
|
25
24
|
isLoading?: boolean;
|
|
26
25
|
isCommandRunning?: boolean;
|
|
27
26
|
workdir?: string;
|
|
28
|
-
userInputHistory?: string[];
|
|
29
27
|
sendMessage?: (
|
|
30
28
|
message: string,
|
|
31
29
|
images?: Array<{ path: string; mimeType: string }>,
|
|
32
30
|
) => void;
|
|
33
31
|
abortMessage?: () => void;
|
|
34
|
-
saveMemory?: (message: string, type: "project" | "user") => Promise<void>;
|
|
35
32
|
// MCP related properties
|
|
36
33
|
mcpServers?: McpServerStatus[];
|
|
37
34
|
connectMcpServer?: (serverName: string) => Promise<boolean>;
|
|
@@ -44,10 +41,8 @@ export interface InputBoxProps {
|
|
|
44
41
|
export const InputBox: React.FC<InputBoxProps> = ({
|
|
45
42
|
isLoading = false,
|
|
46
43
|
isCommandRunning = false,
|
|
47
|
-
userInputHistory = [],
|
|
48
44
|
sendMessage = () => {},
|
|
49
45
|
abortMessage = () => {},
|
|
50
|
-
saveMemory = async () => {},
|
|
51
46
|
mcpServers = [],
|
|
52
47
|
connectMcpServer = async () => false,
|
|
53
48
|
disconnectMcpServer = async () => false,
|
|
@@ -60,6 +55,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
60
55
|
handleRewindSelect,
|
|
61
56
|
backgroundCurrentTask,
|
|
62
57
|
messages,
|
|
58
|
+
getFullMessageThread,
|
|
63
59
|
} = useChat();
|
|
64
60
|
|
|
65
61
|
// Input manager with all input state and functionality (including images)
|
|
@@ -83,26 +79,19 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
83
79
|
handleCancelCommandSelect,
|
|
84
80
|
handleHistorySearchSelect,
|
|
85
81
|
handleCancelHistorySearch,
|
|
86
|
-
// Memory type selector
|
|
87
|
-
showMemoryTypeSelector,
|
|
88
|
-
memoryMessage,
|
|
89
|
-
handleMemoryTypeSelect,
|
|
90
|
-
handleCancelMemoryTypeSelect,
|
|
91
82
|
// History search
|
|
92
83
|
showHistorySearch,
|
|
93
84
|
historySearchQuery,
|
|
94
85
|
// Task/MCP Manager
|
|
95
|
-
|
|
86
|
+
showBackgroundTaskManager,
|
|
96
87
|
showMcpManager,
|
|
97
88
|
showRewindManager,
|
|
98
|
-
|
|
89
|
+
setShowBackgroundTaskManager,
|
|
99
90
|
setShowMcpManager,
|
|
100
91
|
setShowRewindManager,
|
|
101
92
|
// Permission mode
|
|
102
93
|
permissionMode,
|
|
103
94
|
setPermissionMode,
|
|
104
|
-
// Input history
|
|
105
|
-
setUserInputHistory,
|
|
106
95
|
// Main handler
|
|
107
96
|
handleInput,
|
|
108
97
|
// Manager ready state
|
|
@@ -110,7 +99,6 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
110
99
|
} = useInputManager({
|
|
111
100
|
onSendMessage: sendMessage,
|
|
112
101
|
onHasSlashCommand: hasSlashCommand,
|
|
113
|
-
onSaveMemory: saveMemory,
|
|
114
102
|
onAbortMessage: abortMessage,
|
|
115
103
|
onBackgroundCurrentTask: backgroundCurrentTask,
|
|
116
104
|
onPermissionModeChange: setChatPermissionMode,
|
|
@@ -121,11 +109,6 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
121
109
|
setPermissionMode(chatPermissionMode);
|
|
122
110
|
}, [chatPermissionMode, setPermissionMode]);
|
|
123
111
|
|
|
124
|
-
// Set user input history when it changes
|
|
125
|
-
useEffect(() => {
|
|
126
|
-
setUserInputHistory(userInputHistory);
|
|
127
|
-
}, [userInputHistory, setUserInputHistory]);
|
|
128
|
-
|
|
129
112
|
// Use the InputManager's unified input handler
|
|
130
113
|
useInput(async (input, key) => {
|
|
131
114
|
await handleInput(
|
|
@@ -177,6 +160,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
177
160
|
messages={messages}
|
|
178
161
|
onSelect={handleRewindSelectWithClose}
|
|
179
162
|
onCancel={handleRewindCancel}
|
|
163
|
+
getFullMessageThread={getFullMessageThread}
|
|
180
164
|
/>
|
|
181
165
|
);
|
|
182
166
|
}
|
|
@@ -210,18 +194,12 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
210
194
|
/>
|
|
211
195
|
)}
|
|
212
196
|
|
|
213
|
-
{
|
|
214
|
-
<
|
|
215
|
-
|
|
216
|
-
onSelect={handleMemoryTypeSelect}
|
|
217
|
-
onCancel={handleCancelMemoryTypeSelect}
|
|
197
|
+
{showBackgroundTaskManager && (
|
|
198
|
+
<BackgroundTaskManager
|
|
199
|
+
onCancel={() => setShowBackgroundTaskManager(false)}
|
|
218
200
|
/>
|
|
219
201
|
)}
|
|
220
202
|
|
|
221
|
-
{showTaskManager && (
|
|
222
|
-
<TaskManager onCancel={() => setShowTaskManager(false)} />
|
|
223
|
-
)}
|
|
224
|
-
|
|
225
203
|
{showMcpManager && (
|
|
226
204
|
<McpManager
|
|
227
205
|
onCancel={() => setShowMcpManager(false)}
|
|
@@ -231,7 +209,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
231
209
|
/>
|
|
232
210
|
)}
|
|
233
211
|
|
|
234
|
-
{
|
|
212
|
+
{showBackgroundTaskManager || showMcpManager || showRewindManager || (
|
|
235
213
|
<Box flexDirection="column">
|
|
236
214
|
<Box
|
|
237
215
|
borderStyle="single"
|
|
@@ -253,7 +231,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
253
231
|
)}
|
|
254
232
|
</Text>
|
|
255
233
|
</Box>
|
|
256
|
-
<Box paddingRight={1}>
|
|
234
|
+
<Box paddingRight={1} justifyContent="space-between" width="100%">
|
|
257
235
|
<Text color="gray">
|
|
258
236
|
Mode:{" "}
|
|
259
237
|
<Text color={permissionMode === "plan" ? "yellow" : "cyan"}>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
|
|
4
|
+
export interface LoadingIndicatorProps {
|
|
5
|
+
isLoading?: boolean;
|
|
6
|
+
isCommandRunning?: boolean;
|
|
7
|
+
isCompressing?: boolean;
|
|
8
|
+
latestTotalTokens?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const LoadingIndicator = ({
|
|
12
|
+
isLoading = false,
|
|
13
|
+
isCommandRunning = false,
|
|
14
|
+
isCompressing = false,
|
|
15
|
+
latestTotalTokens = 0,
|
|
16
|
+
}: LoadingIndicatorProps) => {
|
|
17
|
+
return (
|
|
18
|
+
<Box flexDirection="column">
|
|
19
|
+
{isLoading && (
|
|
20
|
+
<Box>
|
|
21
|
+
<Text color="yellow">💭 AI is thinking... </Text>
|
|
22
|
+
{latestTotalTokens > 0 && (
|
|
23
|
+
<>
|
|
24
|
+
<Text color="gray" dimColor>
|
|
25
|
+
|{" "}
|
|
26
|
+
</Text>
|
|
27
|
+
<Text color="blue" bold>
|
|
28
|
+
{latestTotalTokens.toLocaleString()}
|
|
29
|
+
</Text>
|
|
30
|
+
<Text color="gray" dimColor>
|
|
31
|
+
{" "}
|
|
32
|
+
tokens{" "}
|
|
33
|
+
</Text>
|
|
34
|
+
</>
|
|
35
|
+
)}
|
|
36
|
+
<Text color="gray" dimColor>
|
|
37
|
+
|{" "}
|
|
38
|
+
</Text>
|
|
39
|
+
<Text color="red" bold>
|
|
40
|
+
Esc
|
|
41
|
+
</Text>
|
|
42
|
+
<Text color="gray" dimColor>
|
|
43
|
+
{" "}
|
|
44
|
+
to abort
|
|
45
|
+
</Text>
|
|
46
|
+
</Box>
|
|
47
|
+
)}
|
|
48
|
+
{isCommandRunning && <Text color="blue">🚀 Command is running...</Text>}
|
|
49
|
+
{isCompressing && (
|
|
50
|
+
<Text color="magenta">🗜️ Compressing message history...</Text>
|
|
51
|
+
)}
|
|
52
|
+
</Box>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
LoadingIndicator.displayName = "LoadingIndicator";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useMemo } from "react";
|
|
2
|
-
import { Box, Text
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { Renderer, marked, type Tokens } from "marked";
|
|
4
|
+
import chalk from "chalk";
|
|
5
5
|
|
|
6
6
|
export interface MarkdownProps {
|
|
7
7
|
children: string;
|
|
@@ -17,339 +17,142 @@ const unescapeHtml = (html: string) => {
|
|
|
17
17
|
.replace(/'/g, "'");
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const t = token as Tokens.Text;
|
|
27
|
-
if (t.tokens) {
|
|
28
|
-
return <InlineRenderer key={index} tokens={t.tokens} />;
|
|
29
|
-
}
|
|
30
|
-
return <Text key={index}>{unescapeHtml(t.text)}</Text>;
|
|
31
|
-
}
|
|
32
|
-
case "strong":
|
|
33
|
-
return (
|
|
34
|
-
<Text key={index} bold>
|
|
35
|
-
{token.tokens ? (
|
|
36
|
-
<InlineRenderer tokens={token.tokens} />
|
|
37
|
-
) : (
|
|
38
|
-
unescapeHtml((token as Tokens.Strong).text)
|
|
39
|
-
)}
|
|
40
|
-
</Text>
|
|
41
|
-
);
|
|
42
|
-
case "em":
|
|
43
|
-
return (
|
|
44
|
-
<Text key={index} italic>
|
|
45
|
-
{token.tokens ? (
|
|
46
|
-
<InlineRenderer tokens={token.tokens} />
|
|
47
|
-
) : (
|
|
48
|
-
unescapeHtml((token as Tokens.Em).text)
|
|
49
|
-
)}
|
|
50
|
-
</Text>
|
|
51
|
-
);
|
|
52
|
-
case "codespan":
|
|
53
|
-
return (
|
|
54
|
-
<Text key={index} color="yellow">
|
|
55
|
-
{unescapeHtml((token as Tokens.Codespan).text)}
|
|
56
|
-
</Text>
|
|
57
|
-
);
|
|
58
|
-
case "link": {
|
|
59
|
-
const t = token as Tokens.Link;
|
|
60
|
-
return (
|
|
61
|
-
<Text key={index}>
|
|
62
|
-
<Text color="blue" underline>
|
|
63
|
-
{t.tokens ? (
|
|
64
|
-
<InlineRenderer tokens={t.tokens} />
|
|
65
|
-
) : (
|
|
66
|
-
unescapeHtml(t.text)
|
|
67
|
-
)}
|
|
68
|
-
</Text>
|
|
69
|
-
<Text color="gray"> ({t.href})</Text>
|
|
70
|
-
</Text>
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
case "br":
|
|
74
|
-
return <Text key={index}>{"\n"}</Text>;
|
|
75
|
-
case "del":
|
|
76
|
-
return (
|
|
77
|
-
<Text key={index} strikethrough>
|
|
78
|
-
{token.tokens ? (
|
|
79
|
-
<InlineRenderer tokens={token.tokens} />
|
|
80
|
-
) : (
|
|
81
|
-
unescapeHtml((token as Tokens.Del).text)
|
|
82
|
-
)}
|
|
83
|
-
</Text>
|
|
84
|
-
);
|
|
85
|
-
default:
|
|
86
|
-
return <Text key={index}>{token.raw}</Text>;
|
|
87
|
-
}
|
|
88
|
-
})}
|
|
89
|
-
</>
|
|
90
|
-
);
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
const TableRenderer = ({ token }: { token: Tokens.Table }) => {
|
|
94
|
-
const { stdout } = useStdout();
|
|
95
|
-
const terminalWidth = (stdout?.columns || 80) - 2;
|
|
20
|
+
class AnsiRenderer extends Renderer<string> {
|
|
21
|
+
override code({ text, lang }: Tokens.Code): string {
|
|
22
|
+
const prefix = lang ? `\`\`\`${lang}` : "```";
|
|
23
|
+
const suffix = "```";
|
|
24
|
+
return `\n${chalk.gray(prefix)}\n${text}\n${chalk.gray(suffix)}\n`;
|
|
25
|
+
}
|
|
96
26
|
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
27
|
+
override blockquote({ tokens }: Tokens.Blockquote): string {
|
|
28
|
+
const body = this.parser.parse(tokens);
|
|
29
|
+
return (
|
|
30
|
+
"\n" +
|
|
31
|
+
body
|
|
32
|
+
.trim()
|
|
33
|
+
.split("\n")
|
|
34
|
+
.map((line) => chalk.gray("> ") + line)
|
|
35
|
+
.join("\n") +
|
|
36
|
+
"\n"
|
|
103
37
|
);
|
|
38
|
+
}
|
|
104
39
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
);
|
|
111
|
-
});
|
|
112
|
-
});
|
|
40
|
+
override heading({ tokens, depth }: Tokens.Heading): string {
|
|
41
|
+
const text = this.parser.parseInline(tokens);
|
|
42
|
+
const hashes = "#".repeat(depth);
|
|
43
|
+
return `\n${chalk.cyan(`${hashes} ${text}`)}\n`;
|
|
44
|
+
}
|
|
113
45
|
|
|
114
|
-
|
|
115
|
-
|
|
46
|
+
override hr(): string {
|
|
47
|
+
return `\n${chalk.gray("─".repeat(20))}\n`;
|
|
48
|
+
}
|
|
116
49
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
50
|
+
override list(token: Tokens.List): string {
|
|
51
|
+
const body = token.items
|
|
52
|
+
.map((item, i) => {
|
|
53
|
+
const text = this.listitem(item);
|
|
54
|
+
const prefix = token.ordered
|
|
55
|
+
? chalk.gray(`${(token.start || 1) + i}. `)
|
|
56
|
+
: chalk.gray("• ");
|
|
57
|
+
const lines = text.split("\n");
|
|
58
|
+
const firstLine = prefix + lines[0];
|
|
59
|
+
const restLines = lines
|
|
60
|
+
.slice(1)
|
|
61
|
+
.filter((line) => line.length > 0)
|
|
62
|
+
.map((line) => " " + line);
|
|
63
|
+
return [firstLine, ...restLines].join("\n") + "\n";
|
|
64
|
+
})
|
|
65
|
+
.join("");
|
|
66
|
+
return `\n${body}`;
|
|
67
|
+
}
|
|
120
68
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return paddedWidths.map((w) =>
|
|
125
|
-
Math.max(minWidth, Math.floor(w * scaleFactor)),
|
|
126
|
-
);
|
|
127
|
-
}, [token, terminalWidth]);
|
|
69
|
+
override listitem(item: Tokens.ListItem): string {
|
|
70
|
+
return `${this.parser.parse(item.tokens).trim()}\n`;
|
|
71
|
+
}
|
|
128
72
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
marginBottom={1}
|
|
133
|
-
borderStyle="single"
|
|
134
|
-
borderColor="gray"
|
|
135
|
-
width={columnWidths.reduce((a, b) => a + b, 0) + token.header.length + 1}
|
|
136
|
-
>
|
|
137
|
-
{/* Header */}
|
|
138
|
-
<Box
|
|
139
|
-
flexDirection="row"
|
|
140
|
-
borderStyle="single"
|
|
141
|
-
borderBottom
|
|
142
|
-
borderTop={false}
|
|
143
|
-
borderLeft={false}
|
|
144
|
-
borderRight={false}
|
|
145
|
-
borderColor="gray"
|
|
146
|
-
>
|
|
147
|
-
{token.header.map((cell, i) => (
|
|
148
|
-
<Box
|
|
149
|
-
key={i}
|
|
150
|
-
width={columnWidths[i]}
|
|
151
|
-
paddingX={1}
|
|
152
|
-
borderStyle="single"
|
|
153
|
-
borderLeft={i > 0}
|
|
154
|
-
borderRight={false}
|
|
155
|
-
borderTop={false}
|
|
156
|
-
borderBottom={false}
|
|
157
|
-
borderColor="gray"
|
|
158
|
-
>
|
|
159
|
-
<Text bold wrap="wrap">
|
|
160
|
-
<InlineRenderer tokens={cell.tokens} />
|
|
161
|
-
</Text>
|
|
162
|
-
</Box>
|
|
163
|
-
))}
|
|
164
|
-
</Box>
|
|
165
|
-
{/* Rows */}
|
|
166
|
-
{token.rows.map((row, rowIndex) => (
|
|
167
|
-
<Box key={rowIndex} flexDirection="row">
|
|
168
|
-
{row.map((cell, i) => (
|
|
169
|
-
<Box
|
|
170
|
-
key={i}
|
|
171
|
-
width={columnWidths[i]}
|
|
172
|
-
paddingX={1}
|
|
173
|
-
borderStyle="single"
|
|
174
|
-
borderLeft={i > 0}
|
|
175
|
-
borderRight={false}
|
|
176
|
-
borderTop={false}
|
|
177
|
-
borderBottom={false}
|
|
178
|
-
borderColor="gray"
|
|
179
|
-
>
|
|
180
|
-
<Text wrap="wrap">
|
|
181
|
-
<InlineRenderer tokens={cell.tokens} />
|
|
182
|
-
</Text>
|
|
183
|
-
</Box>
|
|
184
|
-
))}
|
|
185
|
-
</Box>
|
|
186
|
-
))}
|
|
187
|
-
</Box>
|
|
188
|
-
);
|
|
189
|
-
};
|
|
73
|
+
override checkbox({ checked }: Tokens.Checkbox): string {
|
|
74
|
+
return checked ? chalk.green("[x] ") : chalk.gray("[ ] ");
|
|
75
|
+
}
|
|
190
76
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
case "list": {
|
|
257
|
-
const t = token as Tokens.List;
|
|
258
|
-
return (
|
|
259
|
-
<Box
|
|
260
|
-
key={index}
|
|
261
|
-
flexDirection="column"
|
|
262
|
-
marginBottom={1}
|
|
263
|
-
paddingLeft={2}
|
|
264
|
-
>
|
|
265
|
-
{t.items.map((item, i) => {
|
|
266
|
-
const start = t.start || 1;
|
|
267
|
-
return (
|
|
268
|
-
<Box key={i} flexDirection="row">
|
|
269
|
-
<Text color="gray">
|
|
270
|
-
{t.ordered ? `${start + i}. ` : "• "}
|
|
271
|
-
</Text>
|
|
272
|
-
<Box flexDirection="column" flexGrow={1}>
|
|
273
|
-
{item.tokens.map((itemToken, itemIndex) => {
|
|
274
|
-
if (
|
|
275
|
-
itemToken.type === "text" ||
|
|
276
|
-
itemToken.type === "paragraph"
|
|
277
|
-
) {
|
|
278
|
-
const it = itemToken as
|
|
279
|
-
| Tokens.Text
|
|
280
|
-
| Tokens.Paragraph;
|
|
281
|
-
return (
|
|
282
|
-
<Box key={itemIndex} flexDirection="row">
|
|
283
|
-
<Text>
|
|
284
|
-
<InlineRenderer
|
|
285
|
-
tokens={it.tokens || [itemToken]}
|
|
286
|
-
/>
|
|
287
|
-
</Text>
|
|
288
|
-
</Box>
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
return (
|
|
292
|
-
<BlockRenderer
|
|
293
|
-
key={itemIndex}
|
|
294
|
-
tokens={[itemToken]}
|
|
295
|
-
/>
|
|
296
|
-
);
|
|
297
|
-
})}
|
|
298
|
-
</Box>
|
|
299
|
-
</Box>
|
|
300
|
-
);
|
|
301
|
-
})}
|
|
302
|
-
</Box>
|
|
303
|
-
);
|
|
304
|
-
}
|
|
305
|
-
case "blockquote": {
|
|
306
|
-
const t = token as Tokens.Blockquote;
|
|
307
|
-
return (
|
|
308
|
-
<Box
|
|
309
|
-
key={index}
|
|
310
|
-
flexDirection="column"
|
|
311
|
-
paddingLeft={2}
|
|
312
|
-
borderStyle="single"
|
|
313
|
-
borderLeft
|
|
314
|
-
borderRight={false}
|
|
315
|
-
borderTop={false}
|
|
316
|
-
borderBottom={false}
|
|
317
|
-
borderColor="gray"
|
|
318
|
-
marginBottom={1}
|
|
319
|
-
>
|
|
320
|
-
<BlockRenderer tokens={t.tokens} />
|
|
321
|
-
</Box>
|
|
322
|
-
);
|
|
323
|
-
}
|
|
324
|
-
case "hr":
|
|
325
|
-
return (
|
|
326
|
-
<Box key={index} marginBottom={1}>
|
|
327
|
-
<Text color="gray">{"─".repeat(20)}</Text>
|
|
328
|
-
</Box>
|
|
329
|
-
);
|
|
330
|
-
case "table":
|
|
331
|
-
return <TableRenderer key={index} token={token as Tokens.Table} />;
|
|
332
|
-
case "space":
|
|
333
|
-
return null;
|
|
334
|
-
default:
|
|
335
|
-
return (
|
|
336
|
-
<Box key={index} marginBottom={1}>
|
|
337
|
-
<Text>{token.raw}</Text>
|
|
338
|
-
</Box>
|
|
339
|
-
);
|
|
340
|
-
}
|
|
341
|
-
})}
|
|
342
|
-
</>
|
|
343
|
-
);
|
|
344
|
-
};
|
|
77
|
+
override paragraph({ tokens }: Tokens.Paragraph): string {
|
|
78
|
+
const text = this.parser.parseInline(tokens);
|
|
79
|
+
return `\n${text}\n`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
override table(token: Tokens.Table): string {
|
|
83
|
+
const header = token.header.map((cell) => this.tablecell(cell)).join("");
|
|
84
|
+
const body = token.rows
|
|
85
|
+
.map((row) => row.map((cell) => this.tablecell(cell)).join("") + "\n")
|
|
86
|
+
.join("");
|
|
87
|
+
return `\n${header}\n${body}\n`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
override tablerow({ text }: Tokens.TableRow): string {
|
|
91
|
+
return text + "\n";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
override tablecell(token: Tokens.TableCell): string {
|
|
95
|
+
const text = token.header ? chalk.bold(token.text) : token.text;
|
|
96
|
+
return text + " | ";
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
override strong({ tokens }: Tokens.Strong): string {
|
|
100
|
+
const text = this.parser.parseInline(tokens);
|
|
101
|
+
return chalk.bold(text);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
override em({ tokens }: Tokens.Em): string {
|
|
105
|
+
const text = this.parser.parseInline(tokens);
|
|
106
|
+
return chalk.italic(text);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
override codespan({ text }: Tokens.Codespan): string {
|
|
110
|
+
return chalk.yellow(text);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
override br(): string {
|
|
114
|
+
return "\n";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
override del({ tokens }: Tokens.Del): string {
|
|
118
|
+
const text = this.parser.parseInline(tokens);
|
|
119
|
+
return chalk.strikethrough(text);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
override link({ href, tokens }: Tokens.Link): string {
|
|
123
|
+
const text = this.parser.parseInline(tokens);
|
|
124
|
+
const linkText = chalk.blue.underline(text);
|
|
125
|
+
const hrefText = chalk.gray(`(${href})`);
|
|
126
|
+
return `${linkText} ${hrefText}`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
override image({ href, tokens, text }: Tokens.Image): string {
|
|
130
|
+
const alt = this.parser.parseInline(tokens) || text;
|
|
131
|
+
return chalk.gray(``);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
override text(token: Tokens.Text | Tokens.Escape): string {
|
|
135
|
+
return "tokens" in token && token.tokens
|
|
136
|
+
? this.parser.parseInline(token.tokens)
|
|
137
|
+
: unescapeHtml(token.text);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const renderer = new AnsiRenderer();
|
|
345
142
|
|
|
346
|
-
// Markdown component using custom
|
|
143
|
+
// Markdown component using custom ANSI renderer
|
|
347
144
|
export const Markdown = React.memo(({ children }: MarkdownProps) => {
|
|
348
|
-
const
|
|
145
|
+
const ansiContent = useMemo(() => {
|
|
146
|
+
return marked.parse(children, {
|
|
147
|
+
renderer,
|
|
148
|
+
gfm: true,
|
|
149
|
+
breaks: true,
|
|
150
|
+
}) as string;
|
|
151
|
+
}, [children]);
|
|
349
152
|
|
|
350
153
|
return (
|
|
351
154
|
<Box flexDirection="column">
|
|
352
|
-
<
|
|
155
|
+
<Text>{ansiContent.trim()}</Text>
|
|
353
156
|
</Box>
|
|
354
157
|
);
|
|
355
158
|
});
|