claude-think 0.3.0 → 0.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/CHANGELOG.md +8 -0
- package/package.json +1 -1
- package/src/tui/App.tsx +30 -11
- package/src/tui/components/Navigation.tsx +50 -12
- package/src/tui/components/StatusBar.tsx +11 -7
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.3.1] - 2025-02-05
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Responsive TUI - adapts to terminal width:
|
|
12
|
+
- Narrow terminals (<80 cols): compact nav labels, shorter status
|
|
13
|
+
- Very narrow (<50 cols): minimal header, single nav indicator
|
|
14
|
+
- Footer shortcuts adjust to available space
|
|
15
|
+
|
|
8
16
|
## [0.3.0] - 2025-02-05
|
|
9
17
|
|
|
10
18
|
### Added
|
package/package.json
CHANGED
package/src/tui/App.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState, useEffect } from "react";
|
|
2
|
-
import { Box, Text, useInput, useApp } from "ink";
|
|
2
|
+
import { Box, Text, useInput, useApp, useStdout } from "ink";
|
|
3
3
|
import { Navigation } from "./components/Navigation";
|
|
4
4
|
import { Profile } from "./components/Profile";
|
|
5
5
|
import { Preferences } from "./components/Preferences";
|
|
@@ -30,11 +30,17 @@ type Modal = "none" | "help" | "actions" | "preview" | "search";
|
|
|
30
30
|
|
|
31
31
|
export function App() {
|
|
32
32
|
const { exit } = useApp();
|
|
33
|
+
const { stdout } = useStdout();
|
|
33
34
|
const [section, setSection] = useState<Section>("profile");
|
|
34
35
|
const [modal, setModal] = useState<Modal>("none");
|
|
35
36
|
const [statusMessage, setStatusMessage] = useState<string | undefined>();
|
|
36
37
|
const [initialized, setInitialized] = useState(false);
|
|
37
38
|
|
|
39
|
+
// Get terminal dimensions
|
|
40
|
+
const width = stdout?.columns ?? 80;
|
|
41
|
+
const isNarrow = width < 80;
|
|
42
|
+
const isVeryNarrow = width < 50;
|
|
43
|
+
|
|
38
44
|
useEffect(() => {
|
|
39
45
|
setInitialized(existsSync(CONFIG.thinkDir));
|
|
40
46
|
}, []);
|
|
@@ -97,7 +103,7 @@ export function App() {
|
|
|
97
103
|
if (modal === "actions") {
|
|
98
104
|
return (
|
|
99
105
|
<Box flexDirection="column" padding={1}>
|
|
100
|
-
<Header />
|
|
106
|
+
<Header width={width} />
|
|
101
107
|
<QuickActions
|
|
102
108
|
onMessage={handleMessage}
|
|
103
109
|
onClose={() => setModal("none")}
|
|
@@ -109,7 +115,7 @@ export function App() {
|
|
|
109
115
|
if (modal === "preview") {
|
|
110
116
|
return (
|
|
111
117
|
<Box flexDirection="column" padding={1}>
|
|
112
|
-
<Header />
|
|
118
|
+
<Header width={width} />
|
|
113
119
|
<Preview onClose={() => setModal("none")} />
|
|
114
120
|
</Box>
|
|
115
121
|
);
|
|
@@ -118,7 +124,7 @@ export function App() {
|
|
|
118
124
|
if (modal === "search") {
|
|
119
125
|
return (
|
|
120
126
|
<Box flexDirection="column" padding={1}>
|
|
121
|
-
<Header />
|
|
127
|
+
<Header width={width} />
|
|
122
128
|
<Search onClose={() => setModal("none")} />
|
|
123
129
|
</Box>
|
|
124
130
|
);
|
|
@@ -147,7 +153,7 @@ export function App() {
|
|
|
147
153
|
|
|
148
154
|
return (
|
|
149
155
|
<Box flexDirection="column">
|
|
150
|
-
<Header />
|
|
156
|
+
<Header width={width} />
|
|
151
157
|
|
|
152
158
|
<Navigation currentSection={section} onSectionChange={setSection} />
|
|
153
159
|
|
|
@@ -168,20 +174,33 @@ export function App() {
|
|
|
168
174
|
|
|
169
175
|
<Box marginTop={1}>
|
|
170
176
|
<Text color="gray">
|
|
171
|
-
|
|
177
|
+
{isNarrow
|
|
178
|
+
? "Tab:nav a:act p:prev /:search ?:help q:quit"
|
|
179
|
+
: "Tab: sections | a: actions | p: preview | /: search | ?: help | q: quit"}
|
|
172
180
|
</Text>
|
|
173
181
|
</Box>
|
|
174
182
|
</Box>
|
|
175
183
|
);
|
|
176
184
|
}
|
|
177
185
|
|
|
178
|
-
function Header() {
|
|
186
|
+
function Header({ width }: { width: number }) {
|
|
187
|
+
// Full banner needs ~45 chars, compact needs ~25
|
|
188
|
+
if (width >= 50) {
|
|
189
|
+
return (
|
|
190
|
+
<Box marginBottom={1}>
|
|
191
|
+
<Text color="green" bold>
|
|
192
|
+
▀█▀ █░█ █ █▄░█ █▄▀
|
|
193
|
+
</Text>
|
|
194
|
+
<Text color="gray"> Personal Context for Claude</Text>
|
|
195
|
+
</Box>
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Compact header for narrow terminals
|
|
179
200
|
return (
|
|
180
201
|
<Box marginBottom={1}>
|
|
181
|
-
<Text color="green" bold>
|
|
182
|
-
|
|
183
|
-
</Text>
|
|
184
|
-
<Text color="gray"> Personal Context for Claude</Text>
|
|
202
|
+
<Text color="green" bold>think</Text>
|
|
203
|
+
<Text color="gray"> - Claude Context</Text>
|
|
185
204
|
</Box>
|
|
186
205
|
);
|
|
187
206
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { Box, Text, useInput } from "ink";
|
|
2
|
+
import { Box, Text, useInput, useStdout } from "ink";
|
|
3
3
|
|
|
4
4
|
type Section =
|
|
5
5
|
| "profile"
|
|
@@ -15,38 +15,76 @@ interface NavigationProps {
|
|
|
15
15
|
onSectionChange: (section: Section) => void;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
const sections: { key: Section; label: string }[] = [
|
|
19
|
-
{ key: "profile", label: "Profile" },
|
|
20
|
-
{ key: "preferences", label: "Preferences" },
|
|
21
|
-
{ key: "memory", label: "Memory" },
|
|
22
|
-
{ key: "permissions", label: "Permissions" },
|
|
23
|
-
{ key: "skills", label: "Skills" },
|
|
24
|
-
{ key: "agents", label: "Agents" },
|
|
25
|
-
{ key: "automation", label: "Automation" },
|
|
18
|
+
const sections: { key: Section; label: string; short: string }[] = [
|
|
19
|
+
{ key: "profile", label: "Profile", short: "Prof" },
|
|
20
|
+
{ key: "preferences", label: "Preferences", short: "Pref" },
|
|
21
|
+
{ key: "memory", label: "Memory", short: "Mem" },
|
|
22
|
+
{ key: "permissions", label: "Permissions", short: "Perm" },
|
|
23
|
+
{ key: "skills", label: "Skills", short: "Skill" },
|
|
24
|
+
{ key: "agents", label: "Agents", short: "Agent" },
|
|
25
|
+
{ key: "automation", label: "Automation", short: "Auto" },
|
|
26
26
|
];
|
|
27
27
|
|
|
28
28
|
export function Navigation({
|
|
29
29
|
currentSection,
|
|
30
30
|
onSectionChange,
|
|
31
31
|
}: NavigationProps) {
|
|
32
|
+
const { stdout } = useStdout();
|
|
33
|
+
const width = stdout?.columns ?? 80;
|
|
34
|
+
const isNarrow = width < 80;
|
|
35
|
+
const isVeryNarrow = width < 50;
|
|
36
|
+
|
|
32
37
|
useInput((input, key) => {
|
|
33
38
|
if (key.tab) {
|
|
34
39
|
const currentIndex = sections.findIndex((s) => s.key === currentSection);
|
|
35
40
|
const nextIndex = key.shift
|
|
36
41
|
? (currentIndex - 1 + sections.length) % sections.length
|
|
37
42
|
: (currentIndex + 1) % sections.length;
|
|
38
|
-
onSectionChange(sections[nextIndex]
|
|
43
|
+
onSectionChange(sections[nextIndex]!.key);
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
// Number keys for quick navigation
|
|
42
47
|
const num = parseInt(input);
|
|
43
48
|
if (num >= 1 && num <= sections.length) {
|
|
44
|
-
onSectionChange(sections[num - 1]
|
|
49
|
+
onSectionChange(sections[num - 1]!.key);
|
|
45
50
|
}
|
|
46
51
|
});
|
|
47
52
|
|
|
53
|
+
// Very narrow: show only current + numbers
|
|
54
|
+
if (isVeryNarrow) {
|
|
55
|
+
const currentIdx = sections.findIndex((s) => s.key === currentSection);
|
|
56
|
+
const current = sections[currentIdx]!;
|
|
57
|
+
return (
|
|
58
|
+
<Box>
|
|
59
|
+
<Text color="green" bold>
|
|
60
|
+
[{currentIdx + 1}/{sections.length}] {current.label}
|
|
61
|
+
</Text>
|
|
62
|
+
<Text color="gray"> (Tab/1-7)</Text>
|
|
63
|
+
</Box>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Narrow: use short labels
|
|
68
|
+
if (isNarrow) {
|
|
69
|
+
return (
|
|
70
|
+
<Box flexWrap="wrap">
|
|
71
|
+
{sections.map((section, index) => (
|
|
72
|
+
<Box key={section.key} marginRight={1}>
|
|
73
|
+
<Text
|
|
74
|
+
color={currentSection === section.key ? "green" : "gray"}
|
|
75
|
+
bold={currentSection === section.key}
|
|
76
|
+
>
|
|
77
|
+
{index + 1}.{section.short}
|
|
78
|
+
</Text>
|
|
79
|
+
</Box>
|
|
80
|
+
))}
|
|
81
|
+
</Box>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Full width: show full labels with indicator
|
|
48
86
|
return (
|
|
49
|
-
<Box>
|
|
87
|
+
<Box flexWrap="wrap">
|
|
50
88
|
{sections.map((section, index) => (
|
|
51
89
|
<Box key={section.key} marginRight={1}>
|
|
52
90
|
<Text
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState, useEffect } from "react";
|
|
2
|
-
import { Box, Text } from "ink";
|
|
2
|
+
import { Box, Text, useStdout } from "ink";
|
|
3
3
|
import { existsSync, statSync } from "fs";
|
|
4
4
|
import { readFile } from "fs/promises";
|
|
5
5
|
import { CONFIG, thinkPath } from "../../core/config";
|
|
@@ -43,6 +43,10 @@ export function StatusBar({ message }: StatusBarProps) {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
const { stdout } = useStdout();
|
|
47
|
+
const width = stdout?.columns ?? 80;
|
|
48
|
+
const isNarrow = width < 60;
|
|
49
|
+
|
|
46
50
|
return (
|
|
47
51
|
<Box borderStyle="single" borderColor="gray" paddingX={1}>
|
|
48
52
|
<Box flexGrow={1}>
|
|
@@ -52,18 +56,18 @@ export function StatusBar({ message }: StatusBarProps) {
|
|
|
52
56
|
<Text color="gray">Ready</Text>
|
|
53
57
|
)}
|
|
54
58
|
</Box>
|
|
55
|
-
<Box marginLeft={
|
|
59
|
+
<Box marginLeft={1}>
|
|
56
60
|
<Text color="green">{learningsCount}</Text>
|
|
57
|
-
<Text color="gray"> learnings</Text>
|
|
61
|
+
<Text color="gray">{isNarrow ? "L" : " learnings"}</Text>
|
|
58
62
|
</Box>
|
|
59
63
|
{pendingCount > 0 && (
|
|
60
|
-
<Box marginLeft={
|
|
64
|
+
<Box marginLeft={1}>
|
|
61
65
|
<Text color="yellow">{pendingCount}</Text>
|
|
62
|
-
<Text color="gray"> pending</Text>
|
|
66
|
+
<Text color="gray">{isNarrow ? "P" : " pending"}</Text>
|
|
63
67
|
</Box>
|
|
64
68
|
)}
|
|
65
|
-
{lastSync && (
|
|
66
|
-
<Box marginLeft={
|
|
69
|
+
{lastSync && !isNarrow && (
|
|
70
|
+
<Box marginLeft={1}>
|
|
67
71
|
<Text color="gray">synced {lastSync}</Text>
|
|
68
72
|
</Box>
|
|
69
73
|
)}
|