clarity-ai 6.3.0 → 6.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clarity-ai",
3
- "version": "6.3.0",
3
+ "version": "6.3.2",
4
4
  "description": "Premium terminal AI agent — fixed-height viewport, box-drawing UI, TrueColor theme, streaming with abort",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,8 +1,9 @@
1
- import React, { useState, useCallback, useRef, useEffect } from 'react';
1
+ import React, { useState, useCallback, useRef } from 'react';
2
2
  import { Box } from 'ink';
3
3
  import { createChatState, handleSend, handleCommand } from '../chat.js';
4
4
  import { hex } from '../config/theme.js';
5
5
  import { Layout } from './Layout.js';
6
+ const { createElement: h } = React;
6
7
 
7
8
  let abortController = null;
8
9
 
@@ -73,8 +74,8 @@ export function App({ config }) {
73
74
  }));
74
75
  }
75
76
 
76
- return Box({ flexDirection: 'column', backgroundColor: hex.bg },
77
- Layout({
77
+ return h(Box, { flexDirection: 'column', backgroundColor: hex.bg },
78
+ h(Layout, {
78
79
  state,
79
80
  streamContent,
80
81
  model,
@@ -1,7 +1,8 @@
1
- import React, { useState, useCallback, useRef } from 'react';
1
+ import React, { useState, useRef } from 'react';
2
2
  import { Box, Text, useInput } from 'ink';
3
3
  import { hex, usym, u } from '../config/theme.js';
4
4
  import { getLayout } from '../config/layout.js';
5
+ const { createElement: h } = React;
5
6
 
6
7
  const MAX_VISIBLE_ROWS = 3;
7
8
 
@@ -57,19 +58,18 @@ export function Composer({ provider, model, agentMode, thinking, onSlash, onSubm
57
58
 
58
59
  const rows = [];
59
60
  rows.push(
60
- Box({ key: 'dock_header', height: 1, backgroundColor: hex.surfaceAlt },
61
- Text({ color: hex.textMuted, backgroundColor: hex.surfaceAlt }, ' ' + u.h.repeat(Math.max(0, cols - 2)))
61
+ h(Box, { key: 'dock_header', height: 1, backgroundColor: hex.surfaceAlt },
62
+ h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt }, ' ' + u.h.repeat(Math.max(0, cols - 2)))
62
63
  )
63
64
  );
64
65
 
65
- const inputRows = [];
66
- for (let i = 0; i < visibleLines; i++) {
66
+ for (let i = 0; i < MAX_VISIBLE_ROWS; i++) {
67
67
  const start = i * w;
68
68
  const end = start + w;
69
69
  const seg = displayText.slice(start, end);
70
- inputRows.push(
71
- Box({ key: 'in' + i, height: 1, backgroundColor: hex.bg },
72
- Text({
70
+ rows.push(
71
+ h(Box, { key: 'in' + i, height: 1, backgroundColor: hex.bg },
72
+ h(Text, {
73
73
  color: isPlaceholder ? hex.textMuted : hex.text,
74
74
  backgroundColor: hex.bg,
75
75
  wrap: 'truncate-end',
@@ -78,27 +78,15 @@ export function Composer({ provider, model, agentMode, thinking, onSlash, onSubm
78
78
  );
79
79
  }
80
80
 
81
- for (let i = visibleLines; i < MAX_VISIBLE_ROWS + 1; i++) {
82
- if (i === MAX_VISIBLE_ROWS + 0) break;
83
- inputRows.push(
84
- Box({ key: 'in_fill' + i, height: 1, backgroundColor: hex.bg },
85
- Text({ color: hex.textMuted, backgroundColor: hex.bg }, ' ' + usym.lightV)
81
+ rows.push(
82
+ h(Box, { key: 'dock_status', height: 1, backgroundColor: hex.surfaceAlt },
83
+ h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt },
84
+ ' ' + provider + ' ' + usym.midDot + ' ' + modelShort +
85
+ (agentMode ? ' ' + usym.midDot + ' Agent' : '') +
86
+ ' ' + usym.midDot + ' Ctrl+P commands'
86
87
  )
87
- );
88
- }
89
-
90
- const statusLine = Box({ key: 'dock_status', height: 1, backgroundColor: hex.surfaceAlt },
91
- Text({ color: hex.textMuted, backgroundColor: hex.surfaceAlt },
92
- ' ' + provider + ' ' + usym.midDot + ' ' + modelShort +
93
- (agentMode ? ' ' + usym.midDot + ' Agent' : '') +
94
- ' ' + usym.midDot + ' Ctrl+P commands'
95
88
  )
96
89
  );
97
90
 
98
- rows.push(...inputRows);
99
- rows.push(statusLine);
100
-
101
- return Box({ flexDirection: 'column', backgroundColor: hex.surfaceAlt },
102
- ...rows
103
- );
91
+ return h(Box, { flexDirection: 'column', backgroundColor: hex.surfaceAlt }, ...rows);
104
92
  }
@@ -6,12 +6,13 @@ import { MessageList } from './MessageList.js';
6
6
  import { Composer } from './Composer.js';
7
7
  import { CommandPicker } from './CommandPicker.js';
8
8
  import { ModelPicker } from './ModelPicker.js';
9
+ const { createElement: h } = React;
9
10
 
10
11
  export function Layout({ state, streamContent, model, provider, showCommands, showModels, onCommandSelect, onModelSelect, onCloseCommands, onCloseModels, onSlash, onSubmit }) {
11
- return Box({ flexDirection: 'column', backgroundColor: hex.bg, minHeight: '100%' },
12
- StatusBar({ model, provider, agentMode: state.agentMode, thinking: state.thinking }),
13
- Box({ flexGrow: 1, flexDirection: 'column' },
14
- MessageList({
12
+ return h(Box, { flexDirection: 'column', backgroundColor: hex.bg, minHeight: '100%' },
13
+ h(StatusBar, { model, provider, agentMode: state.agentMode, thinking: state.thinking }),
14
+ h(Box, { flexGrow: 1, flexDirection: 'column' },
15
+ h(MessageList, {
15
16
  messages: state.messages,
16
17
  thinking: state.thinking,
17
18
  streamContent,
@@ -20,25 +21,13 @@ export function Layout({ state, streamContent, model, provider, showCommands, sh
20
21
  })
21
22
  ),
22
23
  showCommands || showModels
23
- ? Box({ flexDirection: 'column', backgroundColor: hex.surfaceAlt },
24
- showCommands
25
- ? CommandPicker({
26
- query: '',
27
- onSelect: onCommandSelect,
28
- onClose: onCloseCommands,
29
- })
30
- : null,
31
- showModels
32
- ? ModelPicker({
33
- onSelect: onModelSelect,
34
- onClose: onCloseModels,
35
- })
36
- : null
24
+ ? h(Box, { flexDirection: 'column', backgroundColor: hex.surfaceAlt },
25
+ showCommands ? h(CommandPicker, { query: '', onSelect: onCommandSelect, onClose: onCloseCommands }) : null,
26
+ showModels ? h(ModelPicker, { onSelect: onModelSelect, onClose: onCloseModels }) : null
37
27
  )
38
28
  : null,
39
- Composer({
40
- provider,
41
- model,
29
+ h(Composer, {
30
+ provider, model,
42
31
  agentMode: state.agentMode,
43
32
  thinking: state.thinking,
44
33
  onSlash,
@@ -1,70 +1,70 @@
1
1
  import React, { useMemo } from 'react';
2
2
  import { Box, Text } from 'ink';
3
3
  import { hex, usym, u } from '../config/theme.js';
4
- import { getLayout, wrapText, sliceToViewport, buildLineArray } from '../config/layout.js';
5
- import { MessageBubble } from './MessageBubble.js';
4
+ import { getLayout, sliceToViewport, buildLineArray } from '../config/layout.js';
5
+ const { createElement: h } = React;
6
6
 
7
7
  function LineRenderer({ type, text, data }) {
8
8
  const { cols } = getLayout();
9
9
  switch (type) {
10
10
  case 'user_head':
11
- return Box({ height: 1, backgroundColor: hex.userBg },
12
- Text({ color: hex.accent, bold: true, backgroundColor: hex.userBg }, ' ' + usym.circle + ' YOU'),
13
- Text({ color: hex.textMuted, backgroundColor: hex.userBg }, ' ' + u.h.repeat(Math.max(0, cols - 10)))
11
+ return h(Box, { height: 1, backgroundColor: hex.userBg },
12
+ h(Text, { color: hex.accent, bold: true, backgroundColor: hex.userBg }, ' ' + usym.circle + ' YOU'),
13
+ h(Text, { color: hex.textMuted, backgroundColor: hex.userBg }, ' ' + u.h.repeat(Math.max(0, cols - 10)))
14
14
  );
15
15
  case 'user_line':
16
- return Box({ height: 1, backgroundColor: hex.userBg },
17
- Text({ color: hex.text, backgroundColor: hex.userBg, wrap: 'wrap' }, ' ' + (text || ' '))
16
+ return h(Box, { height: 1, backgroundColor: hex.userBg },
17
+ h(Text, { color: hex.text, backgroundColor: hex.userBg, wrap: 'wrap' }, ' ' + (text || ' '))
18
18
  );
19
19
  case 'asst_head':
20
- return Box({ height: 1, backgroundColor: hex.surface },
21
- Text({ color: hex.purple, bold: true, backgroundColor: hex.surface }, ' ' + usym.circle + ' CLARITY'),
22
- Text({ color: hex.textMuted, backgroundColor: hex.surface }, ' ' + u.h.repeat(Math.max(0, cols - 14)))
20
+ return h(Box, { height: 1, backgroundColor: hex.surface },
21
+ h(Text, { color: hex.purple, bold: true, backgroundColor: hex.surface }, ' ' + usym.circle + ' CLARITY'),
22
+ h(Text, { color: hex.textMuted, backgroundColor: hex.surface }, ' ' + u.h.repeat(Math.max(0, cols - 14)))
23
23
  );
24
24
  case 'asst_bar':
25
- return Box({ height: 1, backgroundColor: hex.surface },
26
- Text({ color: hex.textMuted, backgroundColor: hex.surface }, ' ' + usym.lightV)
25
+ return h(Box, { height: 1, backgroundColor: hex.surface },
26
+ h(Text, { color: hex.textMuted, backgroundColor: hex.surface }, ' ' + usym.lightV)
27
27
  );
28
28
  case 'asst_line':
29
- return Box({ height: 1, backgroundColor: hex.surface },
30
- Text({ color: hex.text, backgroundColor: hex.surface, wrap: 'wrap' }, ' ' + (text || ' '))
29
+ return h(Box, { height: 1, backgroundColor: hex.surface },
30
+ h(Text, { color: hex.text, backgroundColor: hex.surface, wrap: 'wrap' }, ' ' + (text || ' '))
31
31
  );
32
32
  case 'asst_foot':
33
- return Box({ height: 1, backgroundColor: hex.surface },
34
- Text({ color: hex.textDim, backgroundColor: hex.surface }, ' ' + usym.triR2 + ' ' + (parseInt(text) < 1000 ? text + 'ms' : (parseInt(text) / 1000).toFixed(1) + 's'))
33
+ return h(Box, { height: 1, backgroundColor: hex.surface },
34
+ h(Text, { color: hex.textDim, backgroundColor: hex.surface }, ' ' + usym.triR2 + ' ' + (parseInt(text) < 1000 ? text + 'ms' : (parseInt(text) / 1000).toFixed(1) + 's'))
35
35
  );
36
36
  case 'tool_head':
37
- return Box({ height: 1, backgroundColor: hex.surfaceAlt },
38
- Text({ color: hex.purple, backgroundColor: hex.surfaceAlt }, ' ' + usym.circle + ' ' + text)
37
+ return h(Box, { height: 1, backgroundColor: hex.surfaceAlt },
38
+ h(Text, { color: hex.purple, backgroundColor: hex.surfaceAlt }, ' ' + usym.circle + ' ' + text)
39
39
  );
40
40
  case 'tool_line':
41
- return Box({ height: 1, backgroundColor: hex.surfaceAlt },
42
- Text({ color: hex.textDim, backgroundColor: hex.surfaceAlt }, ' ' + (text || ' '))
41
+ return h(Box, { height: 1, backgroundColor: hex.surfaceAlt },
42
+ h(Text, { color: hex.textDim, backgroundColor: hex.surfaceAlt }, ' ' + (text || ' '))
43
43
  );
44
44
  case 'sys_line':
45
- return Box({ height: 1, backgroundColor: hex.surfaceAlt },
46
- Text({ color: hex.green, backgroundColor: hex.surfaceAlt }, ' ' + usym.circle + ' ' + (text || ''))
45
+ return h(Box, { height: 1, backgroundColor: hex.surfaceAlt },
46
+ h(Text, { color: hex.green, backgroundColor: hex.surfaceAlt }, ' ' + usym.circle + ' ' + (text || ''))
47
47
  );
48
48
  case 'err_line':
49
- return Box({ height: 1, backgroundColor: hex.surfaceAlt },
50
- Text({ color: hex.red, backgroundColor: hex.surfaceAlt }, ' ' + usym.cross + ' ' + (text || ''))
49
+ return h(Box, { height: 1, backgroundColor: hex.surfaceAlt },
50
+ h(Text, { color: hex.red, backgroundColor: hex.surfaceAlt }, ' ' + usym.cross + ' ' + (text || ''))
51
51
  );
52
52
  case 'stream_head':
53
- return Box({ height: 1, backgroundColor: hex.surface },
54
- Text({ color: hex.purple, bold: true, backgroundColor: hex.surface }, ' ' + usym.circle + ' CLARITY'),
55
- Text({ color: hex.textMuted, backgroundColor: hex.surface }, ' ' + u.h.repeat(Math.max(0, cols - 14)))
53
+ return h(Box, { height: 1, backgroundColor: hex.surface },
54
+ h(Text, { color: hex.purple, bold: true, backgroundColor: hex.surface }, ' ' + usym.circle + ' CLARITY'),
55
+ h(Text, { color: hex.textMuted, backgroundColor: hex.surface }, ' ' + u.h.repeat(Math.max(0, cols - 14)))
56
56
  );
57
57
  case 'stream_bar':
58
- return Box({ height: 1, backgroundColor: hex.surface },
59
- Text({ color: hex.textMuted, backgroundColor: hex.surface }, ' ' + usym.lightV)
58
+ return h(Box, { height: 1, backgroundColor: hex.surface },
59
+ h(Text, { color: hex.textMuted, backgroundColor: hex.surface }, ' ' + usym.lightV)
60
60
  );
61
61
  case 'stream_status':
62
- return Box({ height: 1, backgroundColor: hex.surface },
63
- Text({ color: hex.blue, backgroundColor: hex.surface }, ' ' + usym.dot + ' ' + (text || ''))
62
+ return h(Box, { height: 1, backgroundColor: hex.surface },
63
+ h(Text, { color: hex.blue, backgroundColor: hex.surface }, ' ' + usym.dot + ' ' + (text || ''))
64
64
  );
65
65
  case 'stream_line':
66
- return Box({ height: 1, backgroundColor: hex.surface },
67
- Text({ color: hex.text, backgroundColor: hex.surface, wrap: 'wrap' }, ' ' + (text || ' '))
66
+ return h(Box, { height: 1, backgroundColor: hex.surface },
67
+ h(Text, { color: hex.text, backgroundColor: hex.surface, wrap: 'wrap' }, ' ' + (text || ' '))
68
68
  );
69
69
  default:
70
70
  return null;
@@ -111,13 +111,13 @@ export function MessageList({ messages, thinking, streamContent, agentStatus, to
111
111
  const paddedLines = lines;
112
112
  for (let i = 0; i < padCount; i++) paddedLines.unshift({ type: 'empty' });
113
113
 
114
- return Box({ height: viewport, flexDirection: 'column', overflow: 'hidden' },
114
+ return h(Box, { height: viewport, flexDirection: 'column', overflow: 'hidden' },
115
115
  paddedLines.map((ln, i) =>
116
116
  ln.type === 'empty'
117
- ? Box({ key: 'e' + i, height: 1, backgroundColor: hex.bg },
118
- Text({ color: hex.textMuted, backgroundColor: hex.bg }, ' '))
119
- : Box({ key: (ln.data?.id || 'l') + '-' + i, height: 1 },
120
- LineRenderer({ type: ln.type, text: ln.text, data: ln.data })
117
+ ? h(Box, { key: 'e' + i, height: 1, backgroundColor: hex.bg },
118
+ h(Text, { color: hex.textMuted, backgroundColor: hex.bg }, ' '))
119
+ : h(Box, { key: (ln.data?.id || 'l') + '-' + i, height: 1 },
120
+ h(LineRenderer, { type: ln.type, text: ln.text, data: ln.data })
121
121
  )
122
122
  )
123
123
  );
@@ -2,15 +2,16 @@ import React from 'react';
2
2
  import { Box, Text } from 'ink';
3
3
  import { getLayout } from '../config/layout.js';
4
4
  import { hex, usym } from '../config/theme.js';
5
+ const { createElement: h } = React;
5
6
 
6
7
  export function StatusBar({ model, provider, agentMode, thinking }) {
7
- const { cols, contentWidth } = getLayout();
8
+ const { cols } = getLayout();
8
9
  const modelShort = model.replace(/^[^/]+\//, '').slice(0, 20);
9
10
  const left = '\u25C9 CLARITY ' + usym.midDot + ' ' + modelShort + ' ' + usym.midDot + ' ' + provider;
10
11
  const right = (agentMode ? 'Agent:ON' : 'Agent:OFF') + (thinking ? ' \u25CF' : '');
11
12
  const gap = Math.max(1, cols - left.length - right.length - 2);
12
13
  const line = left + ' '.repeat(gap) + right;
13
- return Box({ height: 1, backgroundColor: hex.surfaceAlt },
14
- Text({ color: hex.textDim, bold: false, backgroundColor: hex.surfaceAlt }, ' ' + line)
14
+ return h(Box, { height: 1, backgroundColor: hex.surfaceAlt },
15
+ h(Text, { color: hex.textDim, backgroundColor: hex.surfaceAlt }, ' ' + line)
15
16
  );
16
17
  }
@@ -2,6 +2,7 @@ import React, { useState } from 'react';
2
2
  import { Box, Text } from 'ink';
3
3
  import { hex, usym, u } from '../config/theme.js';
4
4
  import { getLayout } from '../config/layout.js';
5
+ const { createElement: h } = React;
5
6
 
6
7
  export function ThinkingBlock({ toolResults, duration }) {
7
8
  const [collapsed, setCollapsed] = useState(true);
@@ -16,13 +17,13 @@ export function ThinkingBlock({ toolResults, duration }) {
16
17
  const rows = 1;
17
18
  const totalRows = collapsed ? 1 : 1 + items.length;
18
19
 
19
- return Box({ flexDirection: 'column', backgroundColor: hex.surfaceAlt },
20
- Box({ height: 1 },
21
- Text({ color: hex.purple, backgroundColor: hex.surfaceAlt }, ' ' + headerText)
20
+ return h(Box, { flexDirection: 'column', backgroundColor: hex.surfaceAlt },
21
+ h(Box, { height: 1 },
22
+ h(Text, { color: hex.purple, backgroundColor: hex.surfaceAlt }, ' ' + headerText)
22
23
  ),
23
24
  collapsed
24
25
  ? null
25
- : Box({ flexDirection: 'column', backgroundColor: hex.surfaceAlt },
26
+ : h(Box, { flexDirection: 'column', backgroundColor: hex.surfaceAlt },
26
27
  items.map((tr, i) => {
27
28
  const isLast = i === items.length - 1;
28
29
  const prefix = isLast ? usym.treeTip + u.h : usym.treeFork + u.h;
@@ -34,13 +35,13 @@ export function ThinkingBlock({ toolResults, duration }) {
34
35
  const contentLine = tr.content && tr.content.length < 200
35
36
  ? ' ' + conn + ' ' + String(tr.content).slice(0, cols - 10)
36
37
  : null;
37
- return Box({ key: tr.execId || i, flexDirection: 'column' },
38
- Box({ height: 1 },
39
- Text({ color: hex.textMuted, backgroundColor: hex.surfaceAlt }, line)
38
+ return h(Box, { key: tr.execId || i, flexDirection: 'column' },
39
+ h(Box, { height: 1 },
40
+ h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt }, line)
40
41
  ),
41
42
  contentLine
42
- ? Box({ height: 1 },
43
- Text({ color: hex.textDim, backgroundColor: hex.surfaceAlt }, contentLine)
43
+ ? h(Box, { height: 1 },
44
+ h(Text, { color: hex.textDim, backgroundColor: hex.surfaceAlt }, contentLine)
44
45
  )
45
46
  : null
46
47
  );
@@ -1,7 +1,8 @@
1
1
  import React from 'react';
2
2
  import { Box, Text } from 'ink';
3
3
  import { hex, usym } from '../config/theme.js';
4
- import { getLayout, wrapText } from '../config/layout.js';
4
+ import { getLayout } from '../config/layout.js';
5
+ const { createElement: h } = React;
5
6
 
6
7
  export function ToolCard({ exec, isActive }) {
7
8
  const { cols } = getLayout();
@@ -19,8 +20,8 @@ export function ToolCard({ exec, isActive }) {
19
20
  const col = status === 'failed' ? hex.red : hex.green;
20
21
  const icon = status === 'failed' ? usym.cross : usym.circle;
21
22
  const label = icon + ' Tool: ' + name + ' ' + (status === 'failed' ? 'Failed' : 'Successful') + (duration ? ' (' + duration + ')' : '');
22
- return Box({ height: minRender },
23
- Text({ color: col, backgroundColor: hex.surfaceAlt }, ' ' + label)
23
+ return h(Box, { height: minRender },
24
+ h(Text, { color: col, backgroundColor: hex.surfaceAlt }, ' ' + label)
24
25
  );
25
26
  }
26
27
 
@@ -37,10 +38,10 @@ export function ToolCard({ exec, isActive }) {
37
38
  if (status === 'running') lines.push(' running...');
38
39
 
39
40
  const totalLines = Math.min(lines.length, 4);
40
- return Box({ height: totalLines, flexDirection: 'column', backgroundColor: hex.surfaceAlt },
41
+ return h(Box, { height: totalLines, flexDirection: 'column', backgroundColor: hex.surfaceAlt },
41
42
  lines.slice(0, 4).map((line, i) =>
42
- Box({ key: i, height: 1 },
43
- Text({ color: i === 0 ? hex.purple : hex.textDim, backgroundColor: hex.surfaceAlt }, ' ' + line)
43
+ h(Box, { key: i, height: 1 },
44
+ h(Text, { color: i === 0 ? hex.purple : hex.textDim, backgroundColor: hex.surfaceAlt }, ' ' + line)
44
45
  )
45
46
  )
46
47
  );
@@ -1,98 +0,0 @@
1
- import React from 'react';
2
- import { Box, Text } from 'ink';
3
- import { hex, usym, u, boxTitle } from '../config/theme.js';
4
- import { getLayout, wrapText } from '../config/layout.js';
5
- import { ToolCard } from './ToolCard.js';
6
- import { CodeBlock } from './CodeBlock.js';
7
-
8
- function splitCodeBlocks(text) {
9
- if (!text) return [{ type: 'text', content: '' }];
10
- const parts = [];
11
- const cb = /```(\w*)\n?([\s\S]*?)```/g;
12
- let last = 0, m;
13
- while ((m = cb.exec(text)) !== null) {
14
- if (m.index > last) parts.push({ type: 'text', content: text.slice(last, m.index) });
15
- parts.push({ type: 'code', lang: m[1] || 'text', code: m[2] });
16
- last = m.index + m[0].length;
17
- }
18
- if (last < text.length) parts.push({ type: 'text', content: text.slice(last) });
19
- return parts.length ? parts : [{ type: 'text', content: text }];
20
- }
21
-
22
- export function MessageBubble({ msg }) {
23
- const { cols, contentWidth } = getLayout();
24
- const w = contentWidth;
25
-
26
- if (msg.role === 'user') {
27
- const lines = wrapText(msg.content, w).split('\n');
28
- return Box({ flexDirection: 'column', marginBottom: 0, backgroundColor: hex.userBg },
29
- Box({ height: 1, backgroundColor: hex.userBg },
30
- Text({ color: hex.accent, bold: true, backgroundColor: hex.userBg }, ' ' + usym.circle + ' YOU'),
31
- Text({ color: hex.textMuted, backgroundColor: hex.userBg }, ' ' + u.h.repeat(Math.max(0, cols - 10)))
32
- ),
33
- Box({ flexDirection: 'column', backgroundColor: hex.userBg },
34
- lines.map((line, i) =>
35
- Box({ key: i, height: 1, backgroundColor: hex.userBg },
36
- Text({ color: hex.text, backgroundColor: hex.userBg, wrap: 'wrap' }, ' ' + (line || ' '))
37
- )
38
- )
39
- )
40
- );
41
- }
42
-
43
- if (msg.role === 'assistant') {
44
- const parts = splitCodeBlocks(msg.content);
45
- const hasToolResults = msg.toolResults && msg.toolResults.length > 0;
46
-
47
- return Box({ flexDirection: 'column', backgroundColor: hex.surface },
48
- Box({ height: 1, backgroundColor: hex.surface },
49
- Text({ color: hex.purple, bold: true, backgroundColor: hex.surface }, ' ' + usym.circle + ' CLARITY'),
50
- Text({ color: hex.textMuted, backgroundColor: hex.surface }, ' ' + u.h.repeat(Math.max(0, cols - 14)))
51
- ),
52
- hasToolResults
53
- ? ToolCard({ exec: msg.toolResults[0], isActive: false })
54
- : null,
55
- Box({ flexDirection: 'column', backgroundColor: hex.surface },
56
- parts.map((part, i) =>
57
- part.type === 'code'
58
- ? CodeBlock({ key: i, code: part.code, language: part.lang, width: w })
59
- : Box({ key: i, flexDirection: 'column', backgroundColor: hex.surface },
60
- wrapText(part.content, w).split('\n').map((line, j) =>
61
- Box({ key: j, height: 1 },
62
- Text({ color: hex.text, backgroundColor: hex.surface, wrap: 'wrap' }, ' ' + (line || ' '))
63
- )
64
- )
65
- )
66
- )
67
- ),
68
- msg.duration && !hasToolResults
69
- ? Box({ height: 1, backgroundColor: hex.surface },
70
- Text({ color: hex.textDim, backgroundColor: hex.surface }, ' ' + usym.triR2 + ' ' + (msg.duration < 1000 ? msg.duration + 'ms' : (msg.duration / 1000).toFixed(1) + 's'))
71
- )
72
- : null
73
- );
74
- }
75
-
76
- if (msg.role === 'tool') {
77
- const icon = msg.error ? usym.cross : usym.circle;
78
- const col = msg.error ? hex.red : hex.green;
79
- const name = msg.toolName || 'tool';
80
- return Box({ height: 1, backgroundColor: hex.surfaceAlt },
81
- Text({ color: col, backgroundColor: hex.surfaceAlt }, ' ' + icon + ' ' + name + (msg.duration ? ' ' + msg.duration + 'ms' : ''))
82
- );
83
- }
84
-
85
- if (msg.role === 'error') {
86
- return Box({ height: 1, backgroundColor: hex.surfaceAlt },
87
- Text({ color: hex.red, backgroundColor: hex.surfaceAlt }, ' ' + usym.cross + ' ' + String(msg.content).slice(0, cols - 6))
88
- );
89
- }
90
-
91
- if (msg.role === 'system') {
92
- return Box({ height: 1, backgroundColor: hex.surfaceAlt },
93
- Text({ color: hex.green, backgroundColor: hex.surfaceAlt }, ' ' + usym.circle + ' ' + String(msg.content).slice(0, cols - 6))
94
- );
95
- }
96
-
97
- return null;
98
- }
@@ -1,53 +0,0 @@
1
- import React from 'react';
2
- import { Box, Text } from 'ink';
3
- import { hex, usym, u } from '../config/theme.js';
4
- import { getLayout, wrapText } from '../config/layout.js';
5
-
6
- export function ResponseCard({ content, status, isStreaming }) {
7
- const { cols, contentWidth } = getLayout();
8
- const w = contentWidth;
9
- const header = Box({ height: 1 },
10
- Text({ color: hex.purple, backgroundColor: hex.surface }, ' ' + usym.circle + ' CLARITY'),
11
- Text({ color: hex.textMuted, backgroundColor: hex.surface }, ' ' + u.h.repeat(Math.max(0, cols - 14)))
12
- );
13
-
14
- const bar = Box({ height: 1 },
15
- Text({ color: hex.textMuted, backgroundColor: hex.surface }, ' ' + usym.lightV)
16
- );
17
-
18
- const statusLine = status
19
- ? Box({ height: 1 },
20
- Text({ color: hex.blue, backgroundColor: hex.surface }, ' ' + usym.dot + ' ' + status)
21
- )
22
- : null;
23
-
24
- const wrapped = wrapText(content || '', w);
25
- const contentLines = wrapped ? wrapped.split('\n') : [];
26
-
27
- const maxLines = 40;
28
- const visibleContentLines = contentLines.slice(0, maxLines);
29
- const hasMore = contentLines.length > maxLines;
30
-
31
- const contentBlocks = visibleContentLines.map((line, i) =>
32
- Box({ key: i, height: 1 },
33
- Text({ color: hex.text, backgroundColor: hex.surface, wrap: 'wrap' }, ' ' + line)
34
- )
35
- );
36
-
37
- const moreLine = hasMore
38
- ? Box({ height: 1 },
39
- Text({ color: hex.textMuted, backgroundColor: hex.surface }, ' ' + usym.ellipsis + ' ' + (contentLines.length - maxLines) + ' more lines')
40
- )
41
- : null;
42
-
43
- const footer = isStreaming
44
- ? Box({ height: 1 },
45
- Text({ color: hex.blue, backgroundColor: hex.surface }, ' ' + usym.dot + ' streaming...')
46
- )
47
- : null;
48
-
49
- const rows = [header, bar, statusLine, ...contentBlocks, moreLine, footer].filter(Boolean);
50
- return Box({ flexDirection: 'column', backgroundColor: hex.surface },
51
- ...rows
52
- );
53
- }