clarity-ai 6.3.0 → 6.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clarity-ai",
3
- "version": "6.3.0",
3
+ "version": "6.3.1",
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": {
@@ -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,
@@ -2,7 +2,6 @@ import React, { useMemo } from 'react';
2
2
  import { Box, Text } from 'ink';
3
3
  import { hex, usym, u } from '../config/theme.js';
4
4
  import { getLayout, wrapText, sliceToViewport, buildLineArray } from '../config/layout.js';
5
- import { MessageBubble } from './MessageBubble.js';
6
5
 
7
6
  function LineRenderer({ type, text, data }) {
8
7
  const { cols } = getLayout();
@@ -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
- }