casabot 1.1.6 → 1.1.8

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/tui/app.js CHANGED
@@ -4,14 +4,14 @@ import { render, Box, Text, Static, useInput, useApp, useStdout } from "ink";
4
4
  import TextInput from "ink-text-input";
5
5
  import Spinner from "ink-spinner";
6
6
  import Gradient from "ink-gradient";
7
- import { marked } from "marked";
7
+ import { Marked } from "marked";
8
8
  import { markedTerminal } from "marked-terminal";
9
9
  import { runAgent } from "../agent/base.js";
10
- marked.use({ gfm: true });
11
10
  function renderMarkdown(content) {
12
11
  const width = Math.max((process.stdout.columns ?? 80) - 8, 40);
13
- marked.use(markedTerminal({ showSectionPrefix: false, tab: 2, width }));
14
- return marked.parse(content, { async: false }).trimEnd();
12
+ const md = new Marked({ gfm: true });
13
+ md.use(markedTerminal({ showSectionPrefix: false, tab: 2, width, reflowText: true }));
14
+ return md.parse(content, { async: false }).trimEnd();
15
15
  }
16
16
  function truncateOutput(content, maxLines = 8) {
17
17
  const lines = content.split("\n");
@@ -20,22 +20,21 @@ function truncateOutput(content, maxLines = 8) {
20
20
  return (lines.slice(0, maxLines).join("\n") +
21
21
  `\n … ${lines.length - maxLines} more lines`);
22
22
  }
23
- function HRule() {
24
- const { stdout } = useStdout();
25
- const width = stdout.columns ?? 80;
26
- return (_jsx(Box, { paddingX: 1, children: _jsx(Text, { dimColor: true, children: "─".repeat(Math.max(width - 4, 10)) }) }));
23
+ function HRule({ columns }) {
24
+ return (_jsx(Box, { paddingX: 1, width: columns, children: _jsx(Text, { dimColor: true, children: "─".repeat(Math.max(columns - 4, 1)) }) }));
27
25
  }
28
- function HeaderBlock() {
29
- return (_jsxs(Box, { flexDirection: "column", paddingTop: 1, children: [_jsx(Box, { paddingX: 2, children: _jsx(Gradient, { name: "vice", children: _jsx(Text, { bold: true, children: "✦ CasAbot" }) }) }), _jsx(Box, { paddingX: 2, children: _jsx(Text, { dimColor: true, children: "Cassiopeia A — Freely creates everything, like a supernova explosion." }) }), _jsx(HRule, {})] }));
26
+ function HeaderBlock({ columns }) {
27
+ return (_jsxs(Box, { flexDirection: "column", paddingTop: 1, width: columns, children: [_jsx(Box, { paddingX: 2, children: _jsx(Gradient, { name: "vice", children: _jsx(Text, { bold: true, children: "✦ CasAbot" }) }) }), _jsx(Box, { paddingX: 2, children: _jsx(Text, { wrap: "wrap", dimColor: true, children: "Cassiopeia A — Freely creates everything, like a supernova explosion." }) }), _jsx(HRule, { columns: columns })] }));
30
28
  }
31
- function UserMessageView({ content }) {
32
- return (_jsxs(Box, { flexDirection: "column", paddingX: 2, marginTop: 1, children: [_jsx(Text, { color: "green", bold: true, children: "▶ You" }), _jsx(Box, { marginLeft: 2, children: _jsx(Text, { wrap: "wrap", children: content }) })] }));
29
+ function UserMessageView({ content, columns }) {
30
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 2, marginTop: 1, width: columns, children: [_jsx(Text, { color: "green", bold: true, children: "▶ You" }), _jsx(Box, { marginLeft: 2, width: Math.max(columns - 6, 10), children: _jsx(Text, { wrap: "wrap", children: content }) })] }));
33
31
  }
34
- function AssistantMessageView({ content, }) {
35
- return (_jsxs(Box, { flexDirection: "column", paddingX: 2, marginTop: 1, children: [_jsx(Text, { color: "cyan", bold: true, children: "✦ CasAbot" }), _jsx(Box, { marginLeft: 2, children: _jsx(Text, { wrap: "wrap", children: renderMarkdown(content) }) })] }));
32
+ function AssistantMessageView({ content, columns, }) {
33
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 2, marginTop: 1, width: columns, children: [_jsx(Text, { color: "cyan", bold: true, children: "✦ CasAbot" }), _jsx(Box, { marginLeft: 2, width: Math.max(columns - 6, 10), children: _jsx(Text, { wrap: "wrap", children: renderMarkdown(content) }) })] }));
36
34
  }
37
- function ToolCallsView({ message, }) {
38
- return (_jsxs(Box, { flexDirection: "column", paddingX: 2, marginTop: 1, children: [_jsx(Text, { color: "cyan", bold: true, children: "✦ CasAbot" }), message.content ? (_jsx(Box, { marginLeft: 2, children: _jsx(Text, { wrap: "wrap", children: renderMarkdown(message.content) }) })) : null, _jsxs(Box, { flexDirection: "column", marginLeft: 2, marginTop: 1, borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [_jsx(Text, { color: "yellow", bold: true, children: "⚡ Tool Calls" }), message.toolCalls?.map((tc, i) => {
35
+ function ToolCallsView({ message, columns, }) {
36
+ const boxWidth = Math.max(columns - 6, 10);
37
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 2, marginTop: 1, width: columns, children: [_jsx(Text, { color: "cyan", bold: true, children: "✦ CasAbot" }), message.content ? (_jsx(Box, { marginLeft: 2, width: boxWidth, children: _jsx(Text, { wrap: "wrap", children: renderMarkdown(message.content) }) })) : null, _jsxs(Box, { flexDirection: "column", marginLeft: 2, marginTop: 1, borderStyle: "round", borderColor: "yellow", paddingX: 1, width: boxWidth, overflow: "hidden", children: [_jsx(Text, { color: "yellow", bold: true, children: "⚡ Tool Calls" }), message.toolCalls?.map((tc, i) => {
39
38
  let display = tc.arguments;
40
39
  try {
41
40
  const args = JSON.parse(tc.arguments);
@@ -45,38 +44,45 @@ function ToolCallsView({ message, }) {
45
44
  catch {
46
45
  /* keep raw */
47
46
  }
47
+ const maxArgLen = Math.max(boxWidth - tc.name.length - 8, 20);
48
+ if (display.length > maxArgLen) {
49
+ display = display.slice(0, maxArgLen - 1) + "…";
50
+ }
48
51
  return (_jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: tc.name }), _jsx(Text, { children: " → " }), _jsx(Text, { color: "white", wrap: "wrap", children: display })] }, i));
49
52
  })] })] }));
50
53
  }
51
- function ToolResultView({ content, }) {
52
- return (_jsxs(Box, { flexDirection: "column", marginLeft: 4, marginRight: 2, borderStyle: "round", borderColor: "gray", paddingX: 1, children: [_jsx(Text, { dimColor: true, bold: true, children: "📋 Result" }), _jsx(Text, { dimColor: true, wrap: "wrap", children: truncateOutput(content) })] }));
54
+ function ToolResultView({ content, columns, }) {
55
+ const boxWidth = Math.max(columns - 6, 10);
56
+ return (_jsxs(Box, { flexDirection: "column", marginLeft: 4, marginRight: 2, borderStyle: "round", borderColor: "gray", paddingX: 1, width: boxWidth, overflow: "hidden", children: [_jsx(Text, { dimColor: true, bold: true, children: "📋 Result" }), _jsx(Text, { dimColor: true, wrap: "wrap", children: truncateOutput(content) })] }));
53
57
  }
54
- function MessageView({ message, }) {
58
+ function MessageView({ message, columns, }) {
55
59
  if (message.role === "user") {
56
- return _jsx(UserMessageView, { content: message.content });
60
+ return _jsx(UserMessageView, { content: message.content, columns: columns });
57
61
  }
58
62
  if (message.role === "tool") {
59
- return _jsx(ToolResultView, { content: message.content });
63
+ return _jsx(ToolResultView, { content: message.content, columns: columns });
60
64
  }
61
65
  if (message.role === "assistant" && message.toolCalls?.length) {
62
- return _jsx(ToolCallsView, { message: message });
66
+ return _jsx(ToolCallsView, { message: message, columns: columns });
63
67
  }
64
68
  if (message.role === "assistant") {
65
- return _jsx(AssistantMessageView, { content: message.content });
69
+ return _jsx(AssistantMessageView, { content: message.content, columns: columns });
66
70
  }
67
- return _jsx(Text, { children: message.content });
71
+ return _jsx(Text, { wrap: "wrap", children: message.content });
68
72
  }
69
- function WelcomeHint() {
70
- return (_jsxs(Box, { flexDirection: "column", paddingX: 2, marginTop: 1, marginBottom: 1, children: [_jsx(Text, { dimColor: true, children: "Type a message below to get started." }), _jsx(Text, { dimColor: true, children: "CasAbot will orchestrate agents to help you." })] }));
73
+ function WelcomeHint({ columns }) {
74
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 2, marginTop: 1, marginBottom: 1, width: columns, children: [_jsx(Text, { dimColor: true, children: "Type a message below to get started." }), _jsx(Text, { dimColor: true, children: "CasAbot will orchestrate agents to help you." })] }));
71
75
  }
72
- function ProcessingIndicator() {
73
- return (_jsxs(Box, { paddingX: 2, marginTop: 1, gap: 1, children: [_jsx(Text, { color: "yellow", children: _jsx(Spinner, { type: "dots" }) }), _jsx(Text, { color: "yellow", children: "Thinking…" })] }));
76
+ function ProcessingIndicator({ columns }) {
77
+ return (_jsxs(Box, { paddingX: 2, marginTop: 1, gap: 1, width: columns, children: [_jsx(Text, { color: "yellow", children: _jsx(Spinner, { type: "dots" }) }), _jsx(Text, { color: "yellow", children: "Thinking…" })] }));
74
78
  }
75
79
  function App({ provider, conversation, skills, }) {
76
80
  const [messages, setMessages] = useState([]);
77
81
  const [input, setInput] = useState("");
78
82
  const [isProcessing, setIsProcessing] = useState(false);
79
83
  const { exit } = useApp();
84
+ const { stdout } = useStdout();
85
+ const columns = stdout.columns ?? 80;
80
86
  const handleSubmit = useCallback(async (text) => {
81
87
  const trimmed = text.trim();
82
88
  if (!trimmed || isProcessing)
@@ -114,14 +120,14 @@ function App({ provider, conversation, skills, }) {
114
120
  message: msg,
115
121
  })),
116
122
  ], [messages]);
117
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Static, { items: items, children: (item) => {
123
+ return (_jsxs(Box, { flexDirection: "column", width: columns, children: [_jsx(Static, { items: items, children: (item) => {
118
124
  if (item.type === "header") {
119
- return (_jsx(Box, { flexDirection: "column", children: _jsx(HeaderBlock, {}) }, item.key));
125
+ return (_jsx(Box, { flexDirection: "column", width: columns, children: _jsx(HeaderBlock, { columns: columns }) }, item.key));
120
126
  }
121
- return (_jsx(Box, { flexDirection: "column", children: _jsx(MessageView, { message: item.message }) }, item.key));
122
- } }), messages.length === 0 && !isProcessing && _jsx(WelcomeHint, {}), isProcessing && _jsx(ProcessingIndicator, {}), _jsx(HRule, {}), _jsx(Box, { paddingX: 1, children: _jsxs(Box, { borderStyle: "round", borderColor: isProcessing ? "gray" : "cyan", paddingX: 1, width: "100%", children: [_jsx(Text, { color: "cyan", bold: true, children: "❯ " }), _jsx(TextInput, { value: input, onChange: setInput, onSubmit: (val) => {
127
+ return (_jsx(Box, { flexDirection: "column", width: columns, children: _jsx(MessageView, { message: item.message, columns: columns }) }, item.key));
128
+ } }), messages.length === 0 && !isProcessing && _jsx(WelcomeHint, { columns: columns }), isProcessing && _jsx(ProcessingIndicator, { columns: columns }), _jsx(HRule, { columns: columns }), _jsx(Box, { paddingX: 1, width: columns, children: _jsxs(Box, { borderStyle: "round", borderColor: isProcessing ? "gray" : "cyan", paddingX: 1, width: Math.max(columns - 2, 10), overflow: "hidden", children: [_jsx(Text, { color: "cyan", bold: true, children: "❯ " }), _jsx(TextInput, { value: input, onChange: setInput, onSubmit: (val) => {
123
129
  handleSubmit(val).catch(() => { });
124
- }, placeholder: "Type your message\u2026", focus: !isProcessing, showCursor: true })] }) }), _jsxs(Box, { paddingX: 2, justifyContent: "space-between", children: [_jsx(Text, { dimColor: true, children: "Ctrl+C exit" }), _jsxs(Text, { dimColor: true, children: [userCount, " ", userCount === 1 ? "message" : "messages"] })] })] }));
130
+ }, placeholder: "Type your message\u2026", focus: !isProcessing, showCursor: true })] }) }), _jsxs(Box, { paddingX: 2, width: columns, justifyContent: "space-between", children: [_jsx(Text, { dimColor: true, children: "Ctrl+C exit" }), _jsxs(Text, { dimColor: true, children: [userCount, " ", userCount === 1 ? "message" : "messages"] })] })] }));
125
131
  }
126
132
  export function startTUI(provider, conversation, skills) {
127
133
  render(_jsx(App, { provider: provider, conversation: conversation, skills: skills }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "casabot",
3
- "version": "1.1.6",
3
+ "version": "1.1.8",
4
4
  "description": "CasAbot — Skill-driven multi-agent orchestrator system",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/tui/app.tsx CHANGED
@@ -3,18 +3,17 @@ import { render, Box, Text, Static, useInput, useApp, useStdout } from "ink";
3
3
  import TextInput from "ink-text-input";
4
4
  import Spinner from "ink-spinner";
5
5
  import Gradient from "ink-gradient";
6
- import { marked } from "marked";
6
+ import { Marked } from "marked";
7
7
  import { markedTerminal } from "marked-terminal";
8
8
  import type { ChatProvider } from "../providers/base.js";
9
9
  import type { ConversationHistory, Message, Skill } from "../config/types.js";
10
10
  import { runAgent } from "../agent/base.js";
11
11
 
12
- marked.use({ gfm: true });
13
-
14
12
  function renderMarkdown(content: string): string {
15
13
  const width = Math.max((process.stdout.columns ?? 80) - 8, 40);
16
- marked.use(markedTerminal({ showSectionPrefix: false, tab: 2, width }));
17
- return (marked.parse(content, { async: false }) as string).trimEnd();
14
+ const md = new Marked({ gfm: true });
15
+ md.use(markedTerminal({ showSectionPrefix: false, tab: 2, width, reflowText: true }));
16
+ return (md.parse(content, { async: false }) as string).trimEnd();
18
17
  }
19
18
 
20
19
  function truncateOutput(content: string, maxLines = 8): string {
@@ -26,41 +25,39 @@ function truncateOutput(content: string, maxLines = 8): string {
26
25
  );
27
26
  }
28
27
 
29
- function HRule(): React.ReactElement {
30
- const { stdout } = useStdout();
31
- const width = stdout.columns ?? 80;
28
+ function HRule({ columns }: { columns: number }): React.ReactElement {
32
29
  return (
33
- <Box paddingX={1}>
34
- <Text dimColor>{"─".repeat(Math.max(width - 4, 10))}</Text>
30
+ <Box paddingX={1} width={columns}>
31
+ <Text dimColor>{"─".repeat(Math.max(columns - 4, 1))}</Text>
35
32
  </Box>
36
33
  );
37
34
  }
38
35
 
39
- function HeaderBlock(): React.ReactElement {
36
+ function HeaderBlock({ columns }: { columns: number }): React.ReactElement {
40
37
  return (
41
- <Box flexDirection="column" paddingTop={1}>
38
+ <Box flexDirection="column" paddingTop={1} width={columns}>
42
39
  <Box paddingX={2}>
43
40
  <Gradient name="vice">
44
41
  <Text bold>{"✦ CasAbot"}</Text>
45
42
  </Gradient>
46
43
  </Box>
47
44
  <Box paddingX={2}>
48
- <Text dimColor>
45
+ <Text wrap="wrap" dimColor>
49
46
  {"Cassiopeia A — Freely creates everything, like a supernova explosion."}
50
47
  </Text>
51
48
  </Box>
52
- <HRule />
49
+ <HRule columns={columns} />
53
50
  </Box>
54
51
  );
55
52
  }
56
53
 
57
- function UserMessageView({ content }: { content: string }): React.ReactElement {
54
+ function UserMessageView({ content, columns }: { content: string; columns: number }): React.ReactElement {
58
55
  return (
59
- <Box flexDirection="column" paddingX={2} marginTop={1}>
56
+ <Box flexDirection="column" paddingX={2} marginTop={1} width={columns}>
60
57
  <Text color="green" bold>
61
58
  {"▶ You"}
62
59
  </Text>
63
- <Box marginLeft={2}>
60
+ <Box marginLeft={2} width={Math.max(columns - 6, 10)}>
64
61
  <Text wrap="wrap">{content}</Text>
65
62
  </Box>
66
63
  </Box>
@@ -69,15 +66,17 @@ function UserMessageView({ content }: { content: string }): React.ReactElement {
69
66
 
70
67
  function AssistantMessageView({
71
68
  content,
69
+ columns,
72
70
  }: {
73
71
  content: string;
72
+ columns: number;
74
73
  }): React.ReactElement {
75
74
  return (
76
- <Box flexDirection="column" paddingX={2} marginTop={1}>
75
+ <Box flexDirection="column" paddingX={2} marginTop={1} width={columns}>
77
76
  <Text color="cyan" bold>
78
77
  {"✦ CasAbot"}
79
78
  </Text>
80
- <Box marginLeft={2}>
79
+ <Box marginLeft={2} width={Math.max(columns - 6, 10)}>
81
80
  <Text wrap="wrap">{renderMarkdown(content)}</Text>
82
81
  </Box>
83
82
  </Box>
@@ -86,16 +85,19 @@ function AssistantMessageView({
86
85
 
87
86
  function ToolCallsView({
88
87
  message,
88
+ columns,
89
89
  }: {
90
90
  message: Message;
91
+ columns: number;
91
92
  }): React.ReactElement {
93
+ const boxWidth = Math.max(columns - 6, 10);
92
94
  return (
93
- <Box flexDirection="column" paddingX={2} marginTop={1}>
95
+ <Box flexDirection="column" paddingX={2} marginTop={1} width={columns}>
94
96
  <Text color="cyan" bold>
95
97
  {"✦ CasAbot"}
96
98
  </Text>
97
99
  {message.content ? (
98
- <Box marginLeft={2}>
100
+ <Box marginLeft={2} width={boxWidth}>
99
101
  <Text wrap="wrap">{renderMarkdown(message.content)}</Text>
100
102
  </Box>
101
103
  ) : null}
@@ -106,6 +108,8 @@ function ToolCallsView({
106
108
  borderStyle="round"
107
109
  borderColor="yellow"
108
110
  paddingX={1}
111
+ width={boxWidth}
112
+ overflow="hidden"
109
113
  >
110
114
  <Text color="yellow" bold>
111
115
  {"⚡ Tool Calls"}
@@ -118,6 +122,10 @@ function ToolCallsView({
118
122
  } catch {
119
123
  /* keep raw */
120
124
  }
125
+ const maxArgLen = Math.max(boxWidth - tc.name.length - 8, 20);
126
+ if (display.length > maxArgLen) {
127
+ display = display.slice(0, maxArgLen - 1) + "…";
128
+ }
121
129
  return (
122
130
  <Box key={i}>
123
131
  <Text dimColor>{tc.name}</Text>
@@ -133,9 +141,12 @@ function ToolCallsView({
133
141
 
134
142
  function ToolResultView({
135
143
  content,
144
+ columns,
136
145
  }: {
137
146
  content: string;
147
+ columns: number;
138
148
  }): React.ReactElement {
149
+ const boxWidth = Math.max(columns - 6, 10);
139
150
  return (
140
151
  <Box
141
152
  flexDirection="column"
@@ -144,6 +155,8 @@ function ToolResultView({
144
155
  borderStyle="round"
145
156
  borderColor="gray"
146
157
  paddingX={1}
158
+ width={boxWidth}
159
+ overflow="hidden"
147
160
  >
148
161
  <Text dimColor bold>
149
162
  {"📋 Result"}
@@ -155,36 +168,38 @@ function ToolResultView({
155
168
 
156
169
  function MessageView({
157
170
  message,
171
+ columns,
158
172
  }: {
159
173
  message: Message;
174
+ columns: number;
160
175
  }): React.ReactElement {
161
176
  if (message.role === "user") {
162
- return <UserMessageView content={message.content} />;
177
+ return <UserMessageView content={message.content} columns={columns} />;
163
178
  }
164
179
  if (message.role === "tool") {
165
- return <ToolResultView content={message.content} />;
180
+ return <ToolResultView content={message.content} columns={columns} />;
166
181
  }
167
182
  if (message.role === "assistant" && message.toolCalls?.length) {
168
- return <ToolCallsView message={message} />;
183
+ return <ToolCallsView message={message} columns={columns} />;
169
184
  }
170
185
  if (message.role === "assistant") {
171
- return <AssistantMessageView content={message.content} />;
186
+ return <AssistantMessageView content={message.content} columns={columns} />;
172
187
  }
173
- return <Text>{message.content}</Text>;
188
+ return <Text wrap="wrap">{message.content}</Text>;
174
189
  }
175
190
 
176
- function WelcomeHint(): React.ReactElement {
191
+ function WelcomeHint({ columns }: { columns: number }): React.ReactElement {
177
192
  return (
178
- <Box flexDirection="column" paddingX={2} marginTop={1} marginBottom={1}>
193
+ <Box flexDirection="column" paddingX={2} marginTop={1} marginBottom={1} width={columns}>
179
194
  <Text dimColor>{"Type a message below to get started."}</Text>
180
195
  <Text dimColor>{"CasAbot will orchestrate agents to help you."}</Text>
181
196
  </Box>
182
197
  );
183
198
  }
184
199
 
185
- function ProcessingIndicator(): React.ReactElement {
200
+ function ProcessingIndicator({ columns }: { columns: number }): React.ReactElement {
186
201
  return (
187
- <Box paddingX={2} marginTop={1} gap={1}>
202
+ <Box paddingX={2} marginTop={1} gap={1} width={columns}>
188
203
  <Text color="yellow">
189
204
  <Spinner type="dots" />
190
205
  </Text>
@@ -212,6 +227,8 @@ function App({
212
227
  const [input, setInput] = useState("");
213
228
  const [isProcessing, setIsProcessing] = useState(false);
214
229
  const { exit } = useApp();
230
+ const { stdout } = useStdout();
231
+ const columns = stdout.columns ?? 80;
215
232
 
216
233
  const handleSubmit = useCallback(
217
234
  async (text: string) => {
@@ -260,35 +277,36 @@ function App({
260
277
  ], [messages]);
261
278
 
262
279
  return (
263
- <Box flexDirection="column">
280
+ <Box flexDirection="column" width={columns}>
264
281
  <Static items={items}>
265
282
  {(item) => {
266
283
  if (item.type === "header") {
267
284
  return (
268
- <Box key={item.key} flexDirection="column">
269
- <HeaderBlock />
285
+ <Box key={item.key} flexDirection="column" width={columns}>
286
+ <HeaderBlock columns={columns} />
270
287
  </Box>
271
288
  );
272
289
  }
273
290
  return (
274
- <Box key={item.key} flexDirection="column">
275
- <MessageView message={item.message} />
291
+ <Box key={item.key} flexDirection="column" width={columns}>
292
+ <MessageView message={item.message} columns={columns} />
276
293
  </Box>
277
294
  );
278
295
  }}
279
296
  </Static>
280
297
 
281
- {messages.length === 0 && !isProcessing && <WelcomeHint />}
282
- {isProcessing && <ProcessingIndicator />}
298
+ {messages.length === 0 && !isProcessing && <WelcomeHint columns={columns} />}
299
+ {isProcessing && <ProcessingIndicator columns={columns} />}
283
300
 
284
- <HRule />
301
+ <HRule columns={columns} />
285
302
 
286
- <Box paddingX={1}>
303
+ <Box paddingX={1} width={columns}>
287
304
  <Box
288
305
  borderStyle="round"
289
306
  borderColor={isProcessing ? "gray" : "cyan"}
290
307
  paddingX={1}
291
- width="100%"
308
+ width={Math.max(columns - 2, 10)}
309
+ overflow="hidden"
292
310
  >
293
311
  <Text color="cyan" bold>
294
312
  {"❯ "}
@@ -306,7 +324,7 @@ function App({
306
324
  </Box>
307
325
  </Box>
308
326
 
309
- <Box paddingX={2} justifyContent="space-between">
327
+ <Box paddingX={2} width={columns} justifyContent="space-between">
310
328
  <Text dimColor>{"Ctrl+C exit"}</Text>
311
329
  <Text dimColor>
312
330
  {userCount} {userCount === 1 ? "message" : "messages"}