claudeup 4.6.1 → 4.8.0
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 +1 -1
- package/src/__tests__/gap-fill-versions.test.ts +382 -0
- package/src/data/settings-catalog.js +2 -7
- package/src/data/settings-catalog.ts +2 -7
- package/src/opentui.d.ts +7 -2
- package/src/prerunner/index.js +31 -17
- package/src/prerunner/index.ts +35 -18
- package/src/services/claude-settings.js +74 -0
- package/src/services/claude-settings.ts +92 -0
- package/src/services/plugin-manager.js +13 -16
- package/src/services/plugin-manager.ts +17 -16
- package/src/services/settings-manager.js +84 -5
- package/src/services/settings-manager.ts +86 -5
- package/src/ui/adapters/settingsAdapter.js +8 -8
- package/src/ui/adapters/settingsAdapter.ts +8 -8
- package/src/ui/components/TabBar.js +1 -23
- package/src/ui/components/TabBar.tsx +1 -26
- package/src/ui/components/modals/ConfirmModal.js +1 -1
- package/src/ui/components/modals/ConfirmModal.tsx +17 -16
- package/src/ui/components/modals/InputModal.js +2 -13
- package/src/ui/components/modals/InputModal.tsx +21 -24
- package/src/ui/components/modals/LoadingModal.js +1 -1
- package/src/ui/components/modals/LoadingModal.tsx +6 -6
- package/src/ui/components/modals/MessageModal.js +4 -4
- package/src/ui/components/modals/MessageModal.tsx +13 -13
- package/src/ui/components/modals/ModalContainer.js +25 -2
- package/src/ui/components/modals/ModalContainer.tsx +25 -2
- package/src/ui/components/modals/SelectModal.js +3 -4
- package/src/ui/components/modals/SelectModal.tsx +18 -15
- package/src/ui/renderers/settingsRenderers.js +1 -1
- package/src/ui/renderers/settingsRenderers.tsx +5 -3
- package/src/ui/screens/CliToolsScreen.js +2 -2
- package/src/ui/screens/CliToolsScreen.tsx +3 -1
- package/src/ui/screens/EnvVarsScreen.js +27 -10
- package/src/ui/screens/EnvVarsScreen.tsx +33 -16
- package/src/ui/screens/McpRegistryScreen.js +2 -2
- package/src/ui/screens/McpRegistryScreen.tsx +3 -1
- package/src/ui/screens/McpScreen.js +1 -1
- package/src/ui/screens/McpScreen.tsx +2 -1
- package/src/ui/screens/ModelSelectorScreen.js +2 -2
- package/src/ui/screens/ModelSelectorScreen.tsx +3 -2
- package/src/ui/screens/ProfilesScreen.js +1 -1
- package/src/ui/screens/ProfilesScreen.tsx +2 -1
- package/src/ui/screens/StatusLineScreen.js +1 -1
- package/src/ui/screens/StatusLineScreen.tsx +2 -1
- package/src/ui/state/DimensionsContext.js +2 -2
- package/src/ui/state/DimensionsContext.tsx +3 -3
- package/src/ui/components/ScrollableDetail.js +0 -23
- package/src/ui/components/ScrollableDetail.tsx +0 -55
|
@@ -31,28 +31,29 @@ export function ConfirmModal({
|
|
|
31
31
|
flexDirection="column"
|
|
32
32
|
border
|
|
33
33
|
borderStyle="rounded"
|
|
34
|
-
borderColor="
|
|
35
|
-
backgroundColor="#
|
|
36
|
-
paddingLeft={
|
|
37
|
-
paddingRight={
|
|
34
|
+
borderColor="#525252"
|
|
35
|
+
backgroundColor="#1C1C1E"
|
|
36
|
+
paddingLeft={3}
|
|
37
|
+
paddingRight={3}
|
|
38
38
|
paddingTop={1}
|
|
39
39
|
paddingBottom={1}
|
|
40
40
|
width={60}
|
|
41
41
|
>
|
|
42
|
-
<
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
<box marginTop={1} marginBottom={1}>
|
|
46
|
-
<text>{message}</text>
|
|
47
|
-
</box>
|
|
48
|
-
<box>
|
|
49
|
-
<text>
|
|
50
|
-
<span fg="green">[Y]</span>
|
|
51
|
-
<span>es </span>
|
|
52
|
-
<span fg="red">[N]</span>
|
|
53
|
-
<span>o</span>
|
|
42
|
+
<box marginBottom={1}>
|
|
43
|
+
<text fg="#EDEDED">
|
|
44
|
+
<strong>{title}</strong>
|
|
54
45
|
</text>
|
|
55
46
|
</box>
|
|
47
|
+
<box marginBottom={1}>
|
|
48
|
+
<text fg="#A1A1AA">{message}</text>
|
|
49
|
+
</box>
|
|
50
|
+
<box marginTop={1}>
|
|
51
|
+
<text fg="#71717A">Press </text>
|
|
52
|
+
<text fg="#EDEDED">Y</text>
|
|
53
|
+
<text fg="#71717A"> to confirm • </text>
|
|
54
|
+
<text fg="#EDEDED">N</text>
|
|
55
|
+
<text fg="#71717A"> to cancel</text>
|
|
56
|
+
</box>
|
|
56
57
|
</box>
|
|
57
58
|
);
|
|
58
59
|
}
|
|
@@ -1,16 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export function InputModal({ title, label, defaultValue = "", onSubmit, onCancel, }) {
|
|
5
|
-
const [value, setValue] = useState(defaultValue);
|
|
6
|
-
useKeyboard((key) => {
|
|
7
|
-
if (key.name === "enter") {
|
|
8
|
-
onSubmit(value);
|
|
9
|
-
}
|
|
10
|
-
else if (key.name === "escape") {
|
|
11
|
-
onCancel();
|
|
12
|
-
}
|
|
13
|
-
});
|
|
14
|
-
return (_jsxs("box", { flexDirection: "column", border: true, borderStyle: "rounded", borderColor: "cyan", backgroundColor: "#1a1a2e", paddingLeft: 2, paddingRight: 2, paddingTop: 1, paddingBottom: 1, width: 60, children: [_jsx("text", { children: _jsx("strong", { children: title }) }), _jsx("box", { marginTop: 1, marginBottom: 1, children: _jsx("text", { children: label }) }), _jsx("input", { value: value, onChange: setValue, focused: true, width: 54 }), _jsx("box", { marginTop: 1, children: _jsx("text", { fg: "#666666", children: "Enter to confirm \u2022 Escape to cancel" }) })] }));
|
|
2
|
+
export function InputModal({ title, label, defaultValue = "", onSubmit, }) {
|
|
3
|
+
return (_jsxs("box", { flexDirection: "column", border: true, borderStyle: "rounded", borderColor: "#525252", backgroundColor: "#1C1C1E", paddingLeft: 3, paddingRight: 3, paddingTop: 1, paddingBottom: 1, width: 60, children: [_jsx("box", { marginBottom: 1, children: _jsx("text", { fg: "#EDEDED", children: _jsx("strong", { children: title }) }) }), _jsx("box", { marginBottom: 1, children: _jsx("text", { fg: "#A1A1AA", children: label }) }), _jsx("box", { border: true, borderStyle: "rounded", borderColor: "#3F3F46", paddingLeft: 1, paddingRight: 1, width: 54, children: _jsx("input", { value: defaultValue, onSubmit: onSubmit, focused: true, width: 50 }) }), _jsx("box", { marginTop: 1, children: _jsx("text", { fg: "#71717A", children: "\u21B5 to confirm \u2022 Esc to cancel" }) })] }));
|
|
15
4
|
}
|
|
16
5
|
export default InputModal;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import { useKeyboard } from "../../hooks/useKeyboard.js";
|
|
1
|
+
import React from "react";
|
|
3
2
|
|
|
4
3
|
interface InputModalProps {
|
|
5
4
|
/** Modal title */
|
|
@@ -19,43 +18,41 @@ export function InputModal({
|
|
|
19
18
|
label,
|
|
20
19
|
defaultValue = "",
|
|
21
20
|
onSubmit,
|
|
22
|
-
onCancel,
|
|
23
21
|
}: InputModalProps) {
|
|
24
|
-
const [value, setValue] = useState(defaultValue);
|
|
25
|
-
|
|
26
|
-
useKeyboard((key) => {
|
|
27
|
-
if (key.name === "enter") {
|
|
28
|
-
onSubmit(value);
|
|
29
|
-
} else if (key.name === "escape") {
|
|
30
|
-
onCancel();
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
|
|
34
22
|
return (
|
|
35
23
|
<box
|
|
36
24
|
flexDirection="column"
|
|
37
25
|
border
|
|
38
26
|
borderStyle="rounded"
|
|
39
|
-
borderColor="
|
|
40
|
-
backgroundColor="#
|
|
41
|
-
paddingLeft={
|
|
42
|
-
paddingRight={
|
|
27
|
+
borderColor="#525252"
|
|
28
|
+
backgroundColor="#1C1C1E"
|
|
29
|
+
paddingLeft={3}
|
|
30
|
+
paddingRight={3}
|
|
43
31
|
paddingTop={1}
|
|
44
32
|
paddingBottom={1}
|
|
45
33
|
width={60}
|
|
46
34
|
>
|
|
47
|
-
<
|
|
48
|
-
<
|
|
49
|
-
|
|
35
|
+
<box marginBottom={1}>
|
|
36
|
+
<text fg="#EDEDED">
|
|
37
|
+
<strong>{title}</strong>
|
|
38
|
+
</text>
|
|
39
|
+
</box>
|
|
50
40
|
|
|
51
|
-
<box
|
|
52
|
-
<text>{label}</text>
|
|
41
|
+
<box marginBottom={1}>
|
|
42
|
+
<text fg="#A1A1AA">{label}</text>
|
|
53
43
|
</box>
|
|
54
44
|
|
|
55
|
-
<
|
|
45
|
+
<box border borderStyle="rounded" borderColor="#3F3F46" paddingLeft={1} paddingRight={1} width={54}>
|
|
46
|
+
<input
|
|
47
|
+
value={defaultValue}
|
|
48
|
+
onSubmit={onSubmit as any}
|
|
49
|
+
focused
|
|
50
|
+
width={50}
|
|
51
|
+
/>
|
|
52
|
+
</box>
|
|
56
53
|
|
|
57
54
|
<box marginTop={1}>
|
|
58
|
-
<text fg="#
|
|
55
|
+
<text fg="#71717A">↵ to confirm • Esc to cancel</text>
|
|
59
56
|
</box>
|
|
60
57
|
</box>
|
|
61
58
|
);
|
|
@@ -9,6 +9,6 @@ export function LoadingModal({ message }) {
|
|
|
9
9
|
}, 80);
|
|
10
10
|
return () => clearInterval(interval);
|
|
11
11
|
}, []);
|
|
12
|
-
return (_jsxs("box", { flexDirection: "row", border: true, borderStyle: "rounded", borderColor: "
|
|
12
|
+
return (_jsxs("box", { flexDirection: "row", border: true, borderStyle: "rounded", borderColor: "#525252", backgroundColor: "#1C1C1E", paddingLeft: 3, paddingRight: 3, paddingTop: 1, paddingBottom: 1, children: [_jsx("text", { fg: "#A1A1AA", children: SPINNER_FRAMES[frame] }), _jsxs("text", { fg: "#EDEDED", children: [" ", message] })] }));
|
|
13
13
|
}
|
|
14
14
|
export default LoadingModal;
|
|
@@ -23,15 +23,15 @@ export function LoadingModal({ message }: LoadingModalProps) {
|
|
|
23
23
|
flexDirection="row"
|
|
24
24
|
border
|
|
25
25
|
borderStyle="rounded"
|
|
26
|
-
borderColor="
|
|
27
|
-
backgroundColor="#
|
|
28
|
-
paddingLeft={
|
|
29
|
-
paddingRight={
|
|
26
|
+
borderColor="#525252"
|
|
27
|
+
backgroundColor="#1C1C1E"
|
|
28
|
+
paddingLeft={3}
|
|
29
|
+
paddingRight={3}
|
|
30
30
|
paddingTop={1}
|
|
31
31
|
paddingBottom={1}
|
|
32
32
|
>
|
|
33
|
-
<text fg="
|
|
34
|
-
<text> {message}</text>
|
|
33
|
+
<text fg="#A1A1AA">{SPINNER_FRAMES[frame]}</text>
|
|
34
|
+
<text fg="#EDEDED"> {message}</text>
|
|
35
35
|
</box>
|
|
36
36
|
);
|
|
37
37
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "@opentui/react/jsx-runtime";
|
|
2
2
|
import { useKeyboard } from "../../hooks/useKeyboard.js";
|
|
3
3
|
const variantConfig = {
|
|
4
|
-
info: { icon: "ℹ", color: "
|
|
5
|
-
success: { icon: "✓", color: "
|
|
6
|
-
error: { icon: "✗", color: "
|
|
4
|
+
info: { icon: "ℹ", color: "#60A5FA" },
|
|
5
|
+
success: { icon: "✓", color: "#4ADE80" },
|
|
6
|
+
error: { icon: "✗", color: "#F87171" },
|
|
7
7
|
};
|
|
8
8
|
export function MessageModal({ title, message, variant, onDismiss, }) {
|
|
9
9
|
const config = variantConfig[variant];
|
|
@@ -11,6 +11,6 @@ export function MessageModal({ title, message, variant, onDismiss, }) {
|
|
|
11
11
|
// Any key dismisses
|
|
12
12
|
onDismiss();
|
|
13
13
|
});
|
|
14
|
-
return (_jsxs("box", { flexDirection: "column", border: true, borderStyle: "rounded", borderColor:
|
|
14
|
+
return (_jsxs("box", { flexDirection: "column", border: true, borderStyle: "rounded", borderColor: "#525252", backgroundColor: "#1C1C1E", paddingLeft: 3, paddingRight: 3, paddingTop: 1, paddingBottom: 1, width: 60, children: [_jsxs("box", { marginBottom: 1, children: [_jsxs("text", { fg: config.color, children: [config.icon, " "] }), _jsx("text", { fg: "#EDEDED", children: _jsx("strong", { children: title }) })] }), _jsx("box", { marginBottom: 1, children: _jsx("text", { fg: "#A1A1AA", children: message }) }), _jsx("box", { marginTop: 1, children: _jsx("text", { fg: "#71717A", children: "Press any key to continue" }) })] }));
|
|
15
15
|
}
|
|
16
16
|
export default MessageModal;
|
|
@@ -13,9 +13,9 @@ interface MessageModalProps {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const variantConfig = {
|
|
16
|
-
info: { icon: "ℹ", color: "
|
|
17
|
-
success: { icon: "✓", color: "
|
|
18
|
-
error: { icon: "✗", color: "
|
|
16
|
+
info: { icon: "ℹ", color: "#60A5FA" },
|
|
17
|
+
success: { icon: "✓", color: "#4ADE80" },
|
|
18
|
+
error: { icon: "✗", color: "#F87171" },
|
|
19
19
|
} as const;
|
|
20
20
|
|
|
21
21
|
export function MessageModal({
|
|
@@ -36,27 +36,27 @@ export function MessageModal({
|
|
|
36
36
|
flexDirection="column"
|
|
37
37
|
border
|
|
38
38
|
borderStyle="rounded"
|
|
39
|
-
borderColor=
|
|
40
|
-
backgroundColor="#
|
|
41
|
-
paddingLeft={
|
|
42
|
-
paddingRight={
|
|
39
|
+
borderColor="#525252"
|
|
40
|
+
backgroundColor="#1C1C1E"
|
|
41
|
+
paddingLeft={3}
|
|
42
|
+
paddingRight={3}
|
|
43
43
|
paddingTop={1}
|
|
44
44
|
paddingBottom={1}
|
|
45
45
|
width={60}
|
|
46
46
|
>
|
|
47
|
-
<box>
|
|
47
|
+
<box marginBottom={1}>
|
|
48
48
|
<text fg={config.color}>{config.icon} </text>
|
|
49
|
-
<text>
|
|
49
|
+
<text fg="#EDEDED">
|
|
50
50
|
<strong>{title}</strong>
|
|
51
51
|
</text>
|
|
52
52
|
</box>
|
|
53
53
|
|
|
54
|
-
<box
|
|
55
|
-
<text>{message}</text>
|
|
54
|
+
<box marginBottom={1}>
|
|
55
|
+
<text fg="#A1A1AA">{message}</text>
|
|
56
56
|
</box>
|
|
57
57
|
|
|
58
|
-
<box>
|
|
59
|
-
<text fg="#
|
|
58
|
+
<box marginTop={1}>
|
|
59
|
+
<text fg="#71717A">Press any key to continue</text>
|
|
60
60
|
</box>
|
|
61
61
|
</box>
|
|
62
62
|
);
|
|
@@ -11,6 +11,9 @@ import { LoadingModal } from "./LoadingModal.js";
|
|
|
11
11
|
* Container that renders the active modal as an overlay
|
|
12
12
|
* Handles ALL keyboard events when a modal is open to avoid
|
|
13
13
|
* conflicts with multiple useKeyboard hooks in child components
|
|
14
|
+
*
|
|
15
|
+
* Input modal: Enter/Escape handled by OpenTUI <input> onSubmit + ModalContainer escape
|
|
16
|
+
* Select modal: keyboard fully handled here (index tracking + Enter/arrows)
|
|
14
17
|
*/
|
|
15
18
|
export function ModalContainer() {
|
|
16
19
|
const { state } = useApp();
|
|
@@ -25,14 +28,14 @@ export function ModalContainer() {
|
|
|
25
28
|
setSelectIndex(modal.defaultIndex ?? 0);
|
|
26
29
|
}
|
|
27
30
|
}
|
|
28
|
-
// Handle
|
|
31
|
+
// Handle keyboard events for modals
|
|
29
32
|
useKeyboard((key) => {
|
|
30
33
|
if (!modal)
|
|
31
34
|
return;
|
|
32
35
|
if (modal.type === "loading")
|
|
33
36
|
return;
|
|
34
37
|
// Escape — close any modal
|
|
35
|
-
if (key.name === "escape"
|
|
38
|
+
if (key.name === "escape") {
|
|
36
39
|
if (modal.type === "confirm")
|
|
37
40
|
modal.onCancel();
|
|
38
41
|
else if (modal.type === "input")
|
|
@@ -43,6 +46,20 @@ export function ModalContainer() {
|
|
|
43
46
|
modal.onDismiss();
|
|
44
47
|
return;
|
|
45
48
|
}
|
|
49
|
+
// 'q' to close — but NOT for input modals (need to type 'q')
|
|
50
|
+
if (key.name === "q" && modal.type !== "input") {
|
|
51
|
+
if (modal.type === "confirm")
|
|
52
|
+
modal.onCancel();
|
|
53
|
+
else if (modal.type === "select")
|
|
54
|
+
modal.onCancel();
|
|
55
|
+
else if (modal.type === "message")
|
|
56
|
+
modal.onDismiss();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
// Input modal — let OpenTUI <input> handle Enter via onSubmit
|
|
60
|
+
if (modal.type === "input") {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
46
63
|
// Select modal — handle navigation and selection
|
|
47
64
|
if (modal.type === "select") {
|
|
48
65
|
if (key.name === "return" || key.name === "enter") {
|
|
@@ -62,6 +79,12 @@ export function ModalContainer() {
|
|
|
62
79
|
modal.onDismiss();
|
|
63
80
|
}
|
|
64
81
|
}
|
|
82
|
+
// Confirm modal — Enter to confirm
|
|
83
|
+
if (modal.type === "confirm") {
|
|
84
|
+
if (key.name === "return" || key.name === "enter") {
|
|
85
|
+
modal.onConfirm();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
65
88
|
});
|
|
66
89
|
if (!modal) {
|
|
67
90
|
return null;
|
|
@@ -11,6 +11,9 @@ import { LoadingModal } from "./LoadingModal.js";
|
|
|
11
11
|
* Container that renders the active modal as an overlay
|
|
12
12
|
* Handles ALL keyboard events when a modal is open to avoid
|
|
13
13
|
* conflicts with multiple useKeyboard hooks in child components
|
|
14
|
+
*
|
|
15
|
+
* Input modal: Enter/Escape handled by OpenTUI <input> onSubmit + ModalContainer escape
|
|
16
|
+
* Select modal: keyboard fully handled here (index tracking + Enter/arrows)
|
|
14
17
|
*/
|
|
15
18
|
export function ModalContainer() {
|
|
16
19
|
const { state } = useApp();
|
|
@@ -28,13 +31,13 @@ export function ModalContainer() {
|
|
|
28
31
|
}
|
|
29
32
|
}
|
|
30
33
|
|
|
31
|
-
// Handle
|
|
34
|
+
// Handle keyboard events for modals
|
|
32
35
|
useKeyboard((key) => {
|
|
33
36
|
if (!modal) return;
|
|
34
37
|
if (modal.type === "loading") return;
|
|
35
38
|
|
|
36
39
|
// Escape — close any modal
|
|
37
|
-
if (key.name === "escape"
|
|
40
|
+
if (key.name === "escape") {
|
|
38
41
|
if (modal.type === "confirm") modal.onCancel();
|
|
39
42
|
else if (modal.type === "input") modal.onCancel();
|
|
40
43
|
else if (modal.type === "select") modal.onCancel();
|
|
@@ -42,6 +45,19 @@ export function ModalContainer() {
|
|
|
42
45
|
return;
|
|
43
46
|
}
|
|
44
47
|
|
|
48
|
+
// 'q' to close — but NOT for input modals (need to type 'q')
|
|
49
|
+
if (key.name === "q" && modal.type !== "input") {
|
|
50
|
+
if (modal.type === "confirm") modal.onCancel();
|
|
51
|
+
else if (modal.type === "select") modal.onCancel();
|
|
52
|
+
else if (modal.type === "message") modal.onDismiss();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Input modal — let OpenTUI <input> handle Enter via onSubmit
|
|
57
|
+
if (modal.type === "input") {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
45
61
|
// Select modal — handle navigation and selection
|
|
46
62
|
if (modal.type === "select") {
|
|
47
63
|
if (key.name === "return" || key.name === "enter") {
|
|
@@ -60,6 +76,13 @@ export function ModalContainer() {
|
|
|
60
76
|
modal.onDismiss();
|
|
61
77
|
}
|
|
62
78
|
}
|
|
79
|
+
|
|
80
|
+
// Confirm modal — Enter to confirm
|
|
81
|
+
if (modal.type === "confirm") {
|
|
82
|
+
if (key.name === "return" || key.name === "enter") {
|
|
83
|
+
modal.onConfirm();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
63
86
|
});
|
|
64
87
|
|
|
65
88
|
if (!modal) {
|
|
@@ -3,10 +3,9 @@ export function SelectModal({ title, message, options, defaultIndex, onSelect: _
|
|
|
3
3
|
// Keyboard handling is done by ModalContainer
|
|
4
4
|
// defaultIndex is the live selectedIndex from ModalContainer state
|
|
5
5
|
const selectedIndex = defaultIndex ?? 0;
|
|
6
|
-
return (_jsxs("box", { flexDirection: "column", border: true, borderStyle: "rounded", borderColor: "
|
|
6
|
+
return (_jsxs("box", { flexDirection: "column", border: true, borderStyle: "rounded", borderColor: "#525252", backgroundColor: "#1C1C1E", paddingLeft: 3, paddingRight: 3, paddingTop: 1, paddingBottom: 1, width: 50, children: [_jsx("box", { marginBottom: 1, children: _jsx("text", { fg: "#EDEDED", children: _jsx("strong", { children: title }) }) }), _jsx("box", { marginBottom: 1, children: _jsx("text", { fg: "#A1A1AA", children: message }) }), _jsx("box", { flexDirection: "column", paddingLeft: 1, children: options.map((option, idx) => {
|
|
7
7
|
const isSelected = idx === selectedIndex;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}) }), _jsx("box", { marginTop: 1, children: _jsx("text", { fg: "#666666", children: "\u2191\u2193 Select \u2022 Enter \u2022 Esc" }) })] }));
|
|
8
|
+
return (_jsxs("text", { fg: isSelected ? "#F4F4F5" : "#A1A1AA", children: [_jsx("span", { fg: isSelected ? "#F4F4F5" : "#71717A", children: isSelected ? "❯ " : " " }), isSelected ? _jsx("strong", { children: option.label }) : option.label] }, option.value));
|
|
9
|
+
}) }), _jsx("box", { marginTop: 1, children: _jsx("text", { fg: "#71717A", children: "\u2191\u2193 Select \u2022 \u21B5 Confirm \u2022 Esc Cancel" }) })] }));
|
|
11
10
|
}
|
|
12
11
|
export default SelectModal;
|
|
@@ -33,37 +33,40 @@ export function SelectModal({
|
|
|
33
33
|
flexDirection="column"
|
|
34
34
|
border
|
|
35
35
|
borderStyle="rounded"
|
|
36
|
-
borderColor="
|
|
37
|
-
backgroundColor="#
|
|
38
|
-
paddingLeft={
|
|
39
|
-
paddingRight={
|
|
36
|
+
borderColor="#525252"
|
|
37
|
+
backgroundColor="#1C1C1E"
|
|
38
|
+
paddingLeft={3}
|
|
39
|
+
paddingRight={3}
|
|
40
40
|
paddingTop={1}
|
|
41
41
|
paddingBottom={1}
|
|
42
42
|
width={50}
|
|
43
43
|
>
|
|
44
|
-
<
|
|
45
|
-
<
|
|
46
|
-
|
|
44
|
+
<box marginBottom={1}>
|
|
45
|
+
<text fg="#EDEDED">
|
|
46
|
+
<strong>{title}</strong>
|
|
47
|
+
</text>
|
|
48
|
+
</box>
|
|
47
49
|
|
|
48
|
-
<box
|
|
49
|
-
<text>{message}</text>
|
|
50
|
+
<box marginBottom={1}>
|
|
51
|
+
<text fg="#A1A1AA">{message}</text>
|
|
50
52
|
</box>
|
|
51
53
|
|
|
52
|
-
<box flexDirection="column">
|
|
54
|
+
<box flexDirection="column" paddingLeft={1}>
|
|
53
55
|
{options.map((option, idx) => {
|
|
54
56
|
const isSelected = idx === selectedIndex;
|
|
55
|
-
const label = isSelected ? `> ${option.label}` : ` ${option.label}`;
|
|
56
57
|
return (
|
|
57
|
-
<text key={option.value} fg={isSelected ? "
|
|
58
|
-
{isSelected
|
|
59
|
-
|
|
58
|
+
<text key={option.value} fg={isSelected ? "#F4F4F5" : "#A1A1AA"}>
|
|
59
|
+
<span fg={isSelected ? "#F4F4F5" : "#71717A"}>
|
|
60
|
+
{isSelected ? "❯ " : " "}
|
|
61
|
+
</span>
|
|
62
|
+
{isSelected ? <strong>{option.label}</strong> : option.label}
|
|
60
63
|
</text>
|
|
61
64
|
);
|
|
62
65
|
})}
|
|
63
66
|
</box>
|
|
64
67
|
|
|
65
68
|
<box marginTop={1}>
|
|
66
|
-
<text fg="#
|
|
69
|
+
<text fg="#71717A">↑↓ Select • ↵ Confirm • Esc Cancel</text>
|
|
67
70
|
</box>
|
|
68
71
|
</box>
|
|
69
72
|
);
|
|
@@ -8,7 +8,7 @@ const categoryRenderer = {
|
|
|
8
8
|
const star = item.category === "recommended" ? "★ " : "";
|
|
9
9
|
const label = `${star}${CATEGORY_LABELS[item.category]}`;
|
|
10
10
|
const bg = isSelected ? theme.selection.bg : CATEGORY_BG[item.category];
|
|
11
|
-
return (_jsx("text", { bg: bg, fg: "white", children: _jsxs("strong", { children: [" ", label, " "] }) }));
|
|
11
|
+
return (_jsx("box", { width: "100%", children: _jsx("text", { bg: bg, fg: "white", children: _jsxs("strong", { children: [" ", label, " "] }) }) }));
|
|
12
12
|
},
|
|
13
13
|
renderDetail: ({ item }) => {
|
|
14
14
|
const cat = item.category;
|
|
@@ -24,9 +24,11 @@ const categoryRenderer: ItemRenderer<SettingsCategoryItem> = {
|
|
|
24
24
|
const bg = isSelected ? theme.selection.bg : CATEGORY_BG[item.category];
|
|
25
25
|
|
|
26
26
|
return (
|
|
27
|
-
<
|
|
28
|
-
<
|
|
29
|
-
|
|
27
|
+
<box width="100%">
|
|
28
|
+
<text bg={bg} fg="white">
|
|
29
|
+
<strong> {label} </strong>
|
|
30
|
+
</text>
|
|
31
|
+
</box>
|
|
30
32
|
);
|
|
31
33
|
},
|
|
32
34
|
|
|
@@ -6,9 +6,9 @@ import { useApp, useModal } from "../state/AppContext.js";
|
|
|
6
6
|
import { useDimensions } from "../state/DimensionsContext.js";
|
|
7
7
|
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
8
8
|
import { ScreenLayout } from "../components/layout/index.js";
|
|
9
|
-
import { ScrollableList } from "../components/ScrollableList.js";
|
|
10
9
|
import { cliTools } from "../../data/cli-tools.js";
|
|
11
10
|
import { renderCliToolRow, renderCliToolDetail, } from "../renderers/cliToolRenderers.js";
|
|
11
|
+
import { ScrollableList } from "../components/ScrollableList.js";
|
|
12
12
|
const execAsync = promisify(exec);
|
|
13
13
|
// ─── Version helpers ───────────────────────────────────────────────────────────
|
|
14
14
|
// Session-level cache
|
|
@@ -328,6 +328,6 @@ export function CliToolsScreen() {
|
|
|
328
328
|
const installedCount = toolStatuses.filter((s) => s.installed).length;
|
|
329
329
|
const updateCount = toolStatuses.filter((s) => s.hasUpdate).length;
|
|
330
330
|
const statusContent = (_jsxs("text", { children: [_jsx("span", { fg: "gray", children: "Installed: " }), _jsxs("span", { fg: "cyan", children: [installedCount, "/", toolStatuses.length] }), updateCount > 0 && (_jsxs(_Fragment, { children: [_jsx("span", { fg: "gray", children: " \u2502 Updates: " }), _jsx("span", { fg: "yellow", children: updateCount })] }))] }));
|
|
331
|
-
return (_jsx(ScreenLayout, { title: "claudeup CLI Tools", currentScreen: "cli-tools", statusLine: statusContent, footerHints: "Enter:install \u2502 a:update all \u2502 c:fix conflict \u2502 r:refresh", listPanel: _jsx(ScrollableList, { items: toolStatuses, selectedIndex: cliToolsState.selectedIndex, renderItem: renderCliToolRow, maxHeight: dimensions.listPanelHeight }), detailPanel: renderCliToolDetail(selectedStatus) }));
|
|
331
|
+
return (_jsx(ScreenLayout, { title: "claudeup CLI Tools", currentScreen: "cli-tools", statusLine: statusContent, footerHints: "Enter:install \u2502 a:update all \u2502 c:fix conflict \u2502 r:refresh", listPanel: _jsx(ScrollableList, { items: toolStatuses, selectedIndex: cliToolsState.selectedIndex, renderItem: renderCliToolRow, maxHeight: dimensions.listPanelHeight, getKey: (status) => status.tool.name }), detailPanel: renderCliToolDetail(selectedStatus) }));
|
|
332
332
|
}
|
|
333
333
|
export default CliToolsScreen;
|
|
@@ -5,7 +5,7 @@ import { useApp, useModal } from "../state/AppContext.js";
|
|
|
5
5
|
import { useDimensions } from "../state/DimensionsContext.js";
|
|
6
6
|
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
7
7
|
import { ScreenLayout } from "../components/layout/index.js";
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
import { cliTools } from "../../data/cli-tools.js";
|
|
10
10
|
import {
|
|
11
11
|
renderCliToolRow,
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
type CliToolStatus,
|
|
14
14
|
type InstallMethod,
|
|
15
15
|
} from "../renderers/cliToolRenderers.js";
|
|
16
|
+
import { ScrollableList } from "../components/ScrollableList.js";
|
|
16
17
|
|
|
17
18
|
const execAsync = promisify(exec);
|
|
18
19
|
|
|
@@ -423,6 +424,7 @@ export function CliToolsScreen() {
|
|
|
423
424
|
selectedIndex={cliToolsState.selectedIndex}
|
|
424
425
|
renderItem={renderCliToolRow}
|
|
425
426
|
maxHeight={dimensions.listPanelHeight}
|
|
427
|
+
getKey={(status) => status.tool.name}
|
|
426
428
|
/>
|
|
427
429
|
}
|
|
428
430
|
detailPanel={renderCliToolDetail(selectedStatus)}
|
|
@@ -4,11 +4,11 @@ import { useApp, useModal } from "../state/AppContext.js";
|
|
|
4
4
|
import { useDimensions } from "../state/DimensionsContext.js";
|
|
5
5
|
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
6
6
|
import { ScreenLayout } from "../components/layout/index.js";
|
|
7
|
-
import { ScrollableList } from "../components/ScrollableList.js";
|
|
8
7
|
import { SETTINGS_CATALOG, } from "../../data/settings-catalog.js";
|
|
9
|
-
import { readAllSettingsBothScopes, writeSettingValue, } from "../../services/settings-manager.js";
|
|
8
|
+
import { readAllSettingsBothScopes, writeSettingValue, discoverOutputStyles, } from "../../services/settings-manager.js";
|
|
10
9
|
import { buildSettingsBrowserItems, } from "../adapters/settingsAdapter.js";
|
|
11
10
|
import { renderSettingRow, renderSettingDetail } from "../renderers/settingsRenderers.js";
|
|
11
|
+
import { ScrollableList } from "../components/ScrollableList.js";
|
|
12
12
|
export function SettingsScreen() {
|
|
13
13
|
const { state, dispatch } = useApp();
|
|
14
14
|
const { settings } = state;
|
|
@@ -17,6 +17,11 @@ export function SettingsScreen() {
|
|
|
17
17
|
const fetchData = useCallback(async () => {
|
|
18
18
|
dispatch({ type: "SETTINGS_DATA_LOADING" });
|
|
19
19
|
try {
|
|
20
|
+
// Populate dynamic output style options from installed plugins
|
|
21
|
+
const outputStyleSetting = SETTINGS_CATALOG.find((s) => s.id === "output-style");
|
|
22
|
+
if (outputStyleSetting && outputStyleSetting.type === "select") {
|
|
23
|
+
outputStyleSetting.options = await discoverOutputStyles(state.projectPath);
|
|
24
|
+
}
|
|
20
25
|
const values = await readAllSettingsBothScopes(SETTINGS_CATALOG, state.projectPath);
|
|
21
26
|
dispatch({ type: "SETTINGS_DATA_SUCCESS", values });
|
|
22
27
|
}
|
|
@@ -76,15 +81,27 @@ export function SettingsScreen() {
|
|
|
76
81
|
}
|
|
77
82
|
}
|
|
78
83
|
else {
|
|
79
|
-
|
|
80
|
-
if (
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
// String type: if already set, clear it; if unset, show input modal
|
|
85
|
+
if (currentValue !== undefined && currentValue !== "") {
|
|
86
|
+
try {
|
|
87
|
+
await writeSettingValue(setting, undefined, scope, state.projectPath);
|
|
88
|
+
await fetchData();
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
await modal.message("Error", `Failed to update: ${error}`, "error");
|
|
92
|
+
}
|
|
85
93
|
}
|
|
86
|
-
|
|
87
|
-
await modal.
|
|
94
|
+
else {
|
|
95
|
+
const newValue = await modal.input(`${setting.name} — ${scope}`, setting.description, currentValue || setting.defaultValue || "");
|
|
96
|
+
if (newValue === null)
|
|
97
|
+
return;
|
|
98
|
+
try {
|
|
99
|
+
await writeSettingValue(setting, newValue || undefined, scope, state.projectPath);
|
|
100
|
+
await fetchData();
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
await modal.message("Error", `Failed to update: ${error}`, "error");
|
|
104
|
+
}
|
|
88
105
|
}
|
|
89
106
|
}
|
|
90
107
|
};
|