claudeup 4.35.0 → 4.36.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 +4 -4
- package/src/__tests__/git-worktree.test.ts +108 -0
- package/src/__tests__/scope-squares.test.tsx +165 -0
- package/src/__tests__/theme-adaptive-colors.test.ts +307 -0
- package/src/__tests__/uppercase-keybindings.test.ts +101 -0
- package/src/__tests__/worktree-registry-resolution.test.ts +107 -0
- package/src/main.tsx +21 -5
- package/src/opentui.d.ts +21 -12
- package/src/services/claude-settings.ts +37 -7
- package/src/services/git-worktree.ts +129 -0
- package/src/services/plugin-manager.ts +10 -1
- package/src/ui/App.tsx +19 -12
- package/src/ui/adapters/pluginsAdapter.ts +174 -168
- package/src/ui/adapters/settingsAdapter.ts +119 -116
- package/src/ui/adapters/skillsAdapter.ts +203 -196
- package/src/ui/components/CategoryHeader.tsx +9 -8
- package/src/ui/components/EmptyFilterState.tsx +10 -5
- package/src/ui/components/FlagDetailEditor.tsx +0 -0
- package/src/ui/components/ScopeIndicator.tsx +10 -6
- package/src/ui/components/ScrollableList.tsx +3 -2
- package/src/ui/components/SearchInput.tsx +2 -1
- package/src/ui/components/StyledText.tsx +5 -4
- package/src/ui/components/TabBar.tsx +4 -3
- package/src/ui/components/layout/FooterHints.tsx +37 -30
- package/src/ui/components/layout/Panel.tsx +6 -5
- package/src/ui/components/layout/ProgressBar.tsx +7 -6
- package/src/ui/components/layout/ScopeTabs.tsx +6 -5
- package/src/ui/components/layout/ScreenLayout.tsx +27 -24
- package/src/ui/components/layout/index.ts +3 -3
- package/src/ui/components/modals/ConfirmModal.tsx +12 -11
- package/src/ui/components/modals/InputModal.tsx +14 -6
- package/src/ui/components/modals/LoadingModal.tsx +6 -5
- package/src/ui/components/modals/MessageModal.tsx +9 -8
- package/src/ui/components/modals/SelectModal.tsx +11 -7
- package/src/ui/components/modals/VersionMismatchModal.tsx +14 -16
- package/src/ui/components/primitives/ActionHints.tsx +26 -26
- package/src/ui/components/primitives/DetailSection.tsx +13 -12
- package/src/ui/components/primitives/KeyValueLine.tsx +9 -8
- package/src/ui/components/primitives/ListCategoryRow.tsx +25 -27
- package/src/ui/components/primitives/MetaText.tsx +3 -3
- package/src/ui/components/primitives/ScopeDetail.tsx +48 -48
- package/src/ui/components/primitives/ScopeSquares.tsx +47 -22
- package/src/ui/components/primitives/SelectableRow.tsx +22 -16
- package/src/ui/hooks/useGitignoreModal.ts +78 -74
- package/src/ui/registry.ts +11 -11
- package/src/ui/renderers/cliToolRenderers.tsx +260 -203
- package/src/ui/renderers/gitignoreRenderers.tsx +43 -42
- package/src/ui/renderers/mcpRenderers.tsx +121 -117
- package/src/ui/renderers/pluginRenderers.tsx +528 -471
- package/src/ui/renderers/profileRenderers.tsx +346 -300
- package/src/ui/renderers/settingsRenderers.tsx +183 -176
- package/src/ui/renderers/skillRenderers.tsx +410 -326
- package/src/ui/screens/AliasScreen.tsx +1336 -1309
- package/src/ui/screens/CliToolsScreen.tsx +92 -40
- package/src/ui/screens/EnvVarsScreen.tsx +19 -13
- package/src/ui/screens/GitignoreScreen.tsx +510 -493
- package/src/ui/screens/McpRegistryScreen.tsx +28 -21
- package/src/ui/screens/McpScreen.tsx +12 -3
- package/src/ui/screens/PluginsScreen.tsx +17 -7
- package/src/ui/screens/ProfilesScreen.tsx +39 -23
- package/src/ui/screens/SkillsScreen.tsx +832 -688
- package/src/ui/theme-mode.ts +73 -0
- package/src/ui/theme.ts +147 -53
|
@@ -1,15 +1,20 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import type React from "react";
|
|
2
|
+
import { theme } from "../../theme.js";
|
|
2
3
|
|
|
3
4
|
/** One footer hint: a key (or key group) and the action it performs. */
|
|
4
5
|
export interface FooterHint {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
/** Key(s) for this action, e.g. ["↑", "↓"] or ["U"]. Joined for display. */
|
|
7
|
+
keys: string[];
|
|
8
|
+
label: string;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
// The shared chip palette, not a local one. This file used to define its own
|
|
12
|
+
// (`theme.colors.dim` block + terminal-foreground ink), which is why the footer
|
|
13
|
+
// stayed a low-contrast grey-on-grey strip after the palette was made vivid —
|
|
14
|
+
// the new colours simply never reached it. Anything chip-shaped uses theme.hints.
|
|
15
|
+
const KEY_BG = theme.hints.defaultBg;
|
|
16
|
+
const KEY_FG = theme.hints.fg;
|
|
17
|
+
const LABEL_FG = theme.colors.muted;
|
|
13
18
|
const GAP = " ";
|
|
14
19
|
|
|
15
20
|
/**
|
|
@@ -20,33 +25,35 @@ const GAP = " ";
|
|
|
20
25
|
* Each key is shown in a chip so it reads as a pressable key, not prose.
|
|
21
26
|
* Shared across screens so every footer looks the same.
|
|
22
27
|
*/
|
|
23
|
-
export function FooterHints({
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
28
|
+
export function FooterHints({
|
|
29
|
+
hints,
|
|
30
|
+
}: { hints: FooterHint[] }): React.ReactNode {
|
|
31
|
+
const nodes: React.ReactNode[] = [];
|
|
32
|
+
hints.forEach((hint, i) => {
|
|
33
|
+
if (i > 0) {
|
|
34
|
+
nodes.push(
|
|
35
|
+
<span key={`gap-${i}`} fg={LABEL_FG}>
|
|
36
|
+
{GAP}
|
|
37
|
+
</span>,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
nodes.push(
|
|
41
|
+
<span key={`k-${i}`} bg={KEY_BG} fg={KEY_FG}>
|
|
42
|
+
{` ${hint.keys.join("")} `}
|
|
43
|
+
</span>,
|
|
44
|
+
);
|
|
45
|
+
nodes.push(
|
|
46
|
+
<span key={`l-${i}`} fg={LABEL_FG}>
|
|
47
|
+
{` ${hint.label}`}
|
|
48
|
+
</span>,
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
return <text fg={theme.colors.text}>{nodes}</text>;
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
/** Convenience for callers that already have a node: same as <FooterHints>. */
|
|
48
55
|
export function renderFooterHints(hints: FooterHint[]): React.ReactNode {
|
|
49
|
-
|
|
56
|
+
return <FooterHints hints={hints} />;
|
|
50
57
|
}
|
|
51
58
|
|
|
52
59
|
export default FooterHints;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { theme, type UiColor } from "../../theme.js";
|
|
2
3
|
|
|
3
4
|
interface PanelProps {
|
|
4
5
|
/** Panel title */
|
|
@@ -6,9 +7,9 @@ interface PanelProps {
|
|
|
6
7
|
/** Panel content */
|
|
7
8
|
children: React.ReactNode;
|
|
8
9
|
/** Border color */
|
|
9
|
-
borderColor?:
|
|
10
|
+
borderColor?: UiColor;
|
|
10
11
|
/** Title color */
|
|
11
|
-
titleColor?:
|
|
12
|
+
titleColor?: UiColor;
|
|
12
13
|
/** Panel width - number or percentage like "50%" */
|
|
13
14
|
width?: number | `${number}%` | "auto";
|
|
14
15
|
/** Panel height - number or percentage like "50%" */
|
|
@@ -22,14 +23,14 @@ interface PanelProps {
|
|
|
22
23
|
export function Panel({
|
|
23
24
|
title,
|
|
24
25
|
children,
|
|
25
|
-
borderColor =
|
|
26
|
-
titleColor =
|
|
26
|
+
borderColor = theme.colors.accent,
|
|
27
|
+
titleColor = theme.colors.accent,
|
|
27
28
|
width,
|
|
28
29
|
height,
|
|
29
30
|
flexGrow = 1,
|
|
30
31
|
focused = false,
|
|
31
32
|
}: PanelProps) {
|
|
32
|
-
const activeColor = focused ?
|
|
33
|
+
const activeColor = focused ? theme.colors.accent : borderColor;
|
|
33
34
|
|
|
34
35
|
return (
|
|
35
36
|
<box
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { theme } from "../../theme.js";
|
|
2
3
|
|
|
3
4
|
interface ProgressBarProps {
|
|
4
5
|
/** Progress message */
|
|
@@ -21,9 +22,9 @@ export function ProgressBar({ message, current, total }: ProgressBarProps) {
|
|
|
21
22
|
|
|
22
23
|
return (
|
|
23
24
|
<box>
|
|
24
|
-
<text fg=
|
|
25
|
-
<text> {message} </text>
|
|
26
|
-
<text fg=
|
|
25
|
+
<text fg={theme.colors.info}>⟳</text>
|
|
26
|
+
<text fg={theme.colors.text}> {message} </text>
|
|
27
|
+
<text fg={theme.colors.info}>
|
|
27
28
|
[{bar}] {current}/{total}
|
|
28
29
|
</text>
|
|
29
30
|
</box>
|
|
@@ -33,9 +34,9 @@ export function ProgressBar({ message, current, total }: ProgressBarProps) {
|
|
|
33
34
|
// Indeterminate progress
|
|
34
35
|
return (
|
|
35
36
|
<box>
|
|
36
|
-
<text fg=
|
|
37
|
-
<text> {message}</text>
|
|
38
|
-
<text fg=
|
|
37
|
+
<text fg={theme.colors.info}>⟳</text>
|
|
38
|
+
<text fg={theme.colors.text}> {message}</text>
|
|
39
|
+
<text fg={theme.colors.muted}> ...</text>
|
|
39
40
|
</box>
|
|
40
41
|
);
|
|
41
42
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { theme } from "../../theme.js";
|
|
2
3
|
|
|
3
4
|
interface ScopeTabsProps {
|
|
4
5
|
/** Current scope */
|
|
@@ -21,29 +22,29 @@ export function ScopeTabs({
|
|
|
21
22
|
{/* Project tab */}
|
|
22
23
|
<box>
|
|
23
24
|
{isProject ? (
|
|
24
|
-
<text bg=
|
|
25
|
+
<text bg={theme.colors.info} fg={theme.hints.fg}>
|
|
25
26
|
<strong> ◆ Project </strong>
|
|
26
27
|
</text>
|
|
27
28
|
) : (
|
|
28
|
-
<text fg=
|
|
29
|
+
<text fg={theme.colors.muted}> ○ Project </text>
|
|
29
30
|
)}
|
|
30
31
|
</box>
|
|
31
32
|
|
|
32
33
|
{/* Global tab */}
|
|
33
34
|
<box>
|
|
34
35
|
{!isProject ? (
|
|
35
|
-
<text bg=
|
|
36
|
+
<text bg={theme.colors.accent} fg={theme.hints.fg}>
|
|
36
37
|
<strong> ◆ Global </strong>
|
|
37
38
|
</text>
|
|
38
39
|
) : (
|
|
39
|
-
<text fg=
|
|
40
|
+
<text fg={theme.colors.muted}> ○ Global </text>
|
|
40
41
|
)}
|
|
41
42
|
</box>
|
|
42
43
|
|
|
43
44
|
{/* Toggle hint */}
|
|
44
45
|
{toggleHint && (
|
|
45
46
|
<box marginLeft={2}>
|
|
46
|
-
<text fg=
|
|
47
|
+
<text fg={theme.colors.muted}>({toggleHint})</text>
|
|
47
48
|
</box>
|
|
48
49
|
)}
|
|
49
50
|
</box>
|
|
@@ -3,6 +3,7 @@ import { useDimensions } from "../../state/DimensionsContext.js";
|
|
|
3
3
|
import { TabBar } from "../TabBar.js";
|
|
4
4
|
import type { Screen } from "../../state/types.js";
|
|
5
5
|
import { FooterHints, type FooterHint } from "./FooterHints.js";
|
|
6
|
+
import { theme } from "../../theme.js";
|
|
6
7
|
|
|
7
8
|
interface ScreenLayoutProps {
|
|
8
9
|
/** Screen title (e.g., "claudeup Plugins") */
|
|
@@ -32,7 +33,7 @@ interface ScreenLayoutProps {
|
|
|
32
33
|
detailPanel: React.ReactNode;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
const HEADER_COLOR =
|
|
36
|
+
const HEADER_COLOR = theme.colors.accent;
|
|
36
37
|
|
|
37
38
|
export function ScreenLayout({
|
|
38
39
|
title,
|
|
@@ -58,7 +59,7 @@ export function ScreenLayout({
|
|
|
58
59
|
<box flexDirection="column" height={dimensions.contentHeight}>
|
|
59
60
|
{/* Line above tabs */}
|
|
60
61
|
<box height={1} paddingLeft={1} paddingRight={1}>
|
|
61
|
-
<text fg=
|
|
62
|
+
<text fg={theme.colors.dim}>{"─".repeat(lineWidth)}</text>
|
|
62
63
|
</box>
|
|
63
64
|
|
|
64
65
|
{/* Tab bar */}
|
|
@@ -68,15 +69,21 @@ export function ScreenLayout({
|
|
|
68
69
|
|
|
69
70
|
{/* Line below tabs */}
|
|
70
71
|
<box height={1} paddingLeft={1} paddingRight={1}>
|
|
71
|
-
<text fg=
|
|
72
|
+
<text fg={theme.colors.dim}>{"─".repeat(lineWidth)}</text>
|
|
72
73
|
</box>
|
|
73
74
|
|
|
74
75
|
{/* Header — title on left, subtitle/status on right */}
|
|
75
|
-
<box
|
|
76
|
+
<box
|
|
77
|
+
height={1}
|
|
78
|
+
paddingLeft={1}
|
|
79
|
+
paddingRight={1}
|
|
80
|
+
flexDirection="row"
|
|
81
|
+
justifyContent="space-between"
|
|
82
|
+
>
|
|
76
83
|
<text fg={HEADER_COLOR}>
|
|
77
84
|
<strong>{title}</strong>
|
|
78
85
|
</text>
|
|
79
|
-
{subtitle && <text fg=
|
|
86
|
+
{subtitle && <text fg={theme.colors.muted}>{subtitle}</text>}
|
|
80
87
|
{!subtitle && statusLine ? statusLine : null}
|
|
81
88
|
</box>
|
|
82
89
|
|
|
@@ -84,16 +91,18 @@ export function ScreenLayout({
|
|
|
84
91
|
{hasSearchBar && (
|
|
85
92
|
<box height={1} paddingLeft={1} paddingRight={1}>
|
|
86
93
|
{search.isActive ? (
|
|
87
|
-
<text>
|
|
88
|
-
<span fg=
|
|
89
|
-
<span fg=
|
|
90
|
-
<span bg=
|
|
94
|
+
<text fg={theme.colors.text}>
|
|
95
|
+
<span fg={theme.colors.success}>{"Filter: "}</span>
|
|
96
|
+
<span fg={theme.colors.text}>{search.query}</span>
|
|
97
|
+
<span bg={theme.colors.text} fg={theme.hints.fg}>
|
|
98
|
+
{" "}
|
|
99
|
+
</span>
|
|
91
100
|
</text>
|
|
92
101
|
) : (
|
|
93
|
-
<text>
|
|
94
|
-
<span fg=
|
|
95
|
-
<span fg=
|
|
96
|
-
<span fg=
|
|
102
|
+
<text fg={theme.colors.text}>
|
|
103
|
+
<span fg={theme.colors.success}>{"Filter: "}</span>
|
|
104
|
+
<span fg={theme.colors.warning}>{search.query}</span>
|
|
105
|
+
<span fg={theme.colors.muted}> (Esc to clear)</span>
|
|
97
106
|
</text>
|
|
98
107
|
)}
|
|
99
108
|
</box>
|
|
@@ -101,7 +110,7 @@ export function ScreenLayout({
|
|
|
101
110
|
|
|
102
111
|
{/* Separator below header */}
|
|
103
112
|
<box height={1} paddingLeft={1} paddingRight={1}>
|
|
104
|
-
<text fg=
|
|
113
|
+
<text fg={theme.colors.border}>{"─".repeat(lineWidth)}</text>
|
|
105
114
|
</box>
|
|
106
115
|
|
|
107
116
|
{/* Main content area */}
|
|
@@ -118,19 +127,13 @@ export function ScreenLayout({
|
|
|
118
127
|
|
|
119
128
|
{/* Vertical separator */}
|
|
120
129
|
<box flexDirection="column" width={1} height={panelHeight}>
|
|
121
|
-
<text fg=
|
|
130
|
+
<text fg={theme.colors.border}>{"│".repeat(panelHeight)}</text>
|
|
122
131
|
</box>
|
|
123
132
|
|
|
124
133
|
{/* Detail panel — scrollable */}
|
|
125
134
|
<box width="50%" height={panelHeight} paddingLeft={1}>
|
|
126
|
-
<scrollbox
|
|
127
|
-
|
|
128
|
-
scrollY={true}
|
|
129
|
-
scrollX={false}
|
|
130
|
-
>
|
|
131
|
-
<box flexDirection="column">
|
|
132
|
-
{detailPanel}
|
|
133
|
-
</box>
|
|
135
|
+
<scrollbox height={panelHeight} scrollY={true} scrollX={false}>
|
|
136
|
+
<box flexDirection="column">{detailPanel}</box>
|
|
134
137
|
</scrollbox>
|
|
135
138
|
</box>
|
|
136
139
|
</box>
|
|
@@ -140,7 +143,7 @@ export function ScreenLayout({
|
|
|
140
143
|
{Array.isArray(footerHints) ? (
|
|
141
144
|
<FooterHints hints={footerHints as FooterHint[]} />
|
|
142
145
|
) : typeof footerHints === "string" ? (
|
|
143
|
-
<text fg=
|
|
146
|
+
<text fg={theme.colors.muted}>{footerHints}</text>
|
|
144
147
|
) : (
|
|
145
148
|
footerHints
|
|
146
149
|
)}
|
|
@@ -3,7 +3,7 @@ export { ScopeTabs } from "./ScopeTabs.js";
|
|
|
3
3
|
export { ProgressBar } from "./ProgressBar.js";
|
|
4
4
|
export { ScreenLayout } from "./ScreenLayout.js";
|
|
5
5
|
export {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
FooterHints,
|
|
7
|
+
renderFooterHints,
|
|
8
|
+
type FooterHint,
|
|
9
9
|
} from "./FooterHints.js";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { useKeyboard } from "../../hooks/useKeyboard.js";
|
|
3
|
+
import { theme } from "../../theme.js";
|
|
3
4
|
|
|
4
5
|
interface ConfirmModalProps {
|
|
5
6
|
/** Modal title */
|
|
@@ -19,9 +20,9 @@ export function ConfirmModal({
|
|
|
19
20
|
onCancel,
|
|
20
21
|
}: ConfirmModalProps) {
|
|
21
22
|
useKeyboard((key) => {
|
|
22
|
-
if (key.name === "y"
|
|
23
|
+
if (key.name === "y") {
|
|
23
24
|
onConfirm();
|
|
24
|
-
} else if (key.name === "n" || key.name === "
|
|
25
|
+
} else if (key.name === "n" || key.name === "escape") {
|
|
25
26
|
onCancel();
|
|
26
27
|
}
|
|
27
28
|
});
|
|
@@ -31,8 +32,8 @@ export function ConfirmModal({
|
|
|
31
32
|
flexDirection="column"
|
|
32
33
|
border
|
|
33
34
|
borderStyle="rounded"
|
|
34
|
-
borderColor=
|
|
35
|
-
backgroundColor=
|
|
35
|
+
borderColor={theme.colors.dim}
|
|
36
|
+
backgroundColor={theme.surface.bg}
|
|
36
37
|
paddingLeft={3}
|
|
37
38
|
paddingRight={3}
|
|
38
39
|
paddingTop={1}
|
|
@@ -40,19 +41,19 @@ export function ConfirmModal({
|
|
|
40
41
|
width={60}
|
|
41
42
|
>
|
|
42
43
|
<box marginBottom={1}>
|
|
43
|
-
<text fg=
|
|
44
|
+
<text fg={theme.colors.text}>
|
|
44
45
|
<strong>{title}</strong>
|
|
45
46
|
</text>
|
|
46
47
|
</box>
|
|
47
48
|
<box marginBottom={1}>
|
|
48
|
-
<text fg=
|
|
49
|
+
<text fg={theme.colors.muted}>{message}</text>
|
|
49
50
|
</box>
|
|
50
51
|
<box marginTop={1} flexDirection="row">
|
|
51
|
-
<text fg=
|
|
52
|
-
<text fg=
|
|
53
|
-
<text fg=
|
|
54
|
-
<text fg=
|
|
55
|
-
<text fg=
|
|
52
|
+
<text fg={theme.colors.muted}>Press </text>
|
|
53
|
+
<text fg={theme.colors.text}>Y</text>
|
|
54
|
+
<text fg={theme.colors.muted}> to confirm • </text>
|
|
55
|
+
<text fg={theme.colors.text}>N</text>
|
|
56
|
+
<text fg={theme.colors.muted}> to cancel</text>
|
|
56
57
|
</box>
|
|
57
58
|
</box>
|
|
58
59
|
);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { theme } from "../../theme.js";
|
|
2
3
|
|
|
3
4
|
interface InputModalProps {
|
|
4
5
|
/** Modal title */
|
|
@@ -24,8 +25,8 @@ export function InputModal({
|
|
|
24
25
|
flexDirection="column"
|
|
25
26
|
border
|
|
26
27
|
borderStyle="rounded"
|
|
27
|
-
borderColor=
|
|
28
|
-
backgroundColor=
|
|
28
|
+
borderColor={theme.colors.dim}
|
|
29
|
+
backgroundColor={theme.surface.bg}
|
|
29
30
|
paddingLeft={3}
|
|
30
31
|
paddingRight={3}
|
|
31
32
|
paddingTop={1}
|
|
@@ -33,16 +34,23 @@ export function InputModal({
|
|
|
33
34
|
width={60}
|
|
34
35
|
>
|
|
35
36
|
<box marginBottom={1}>
|
|
36
|
-
<text fg=
|
|
37
|
+
<text fg={theme.colors.text}>
|
|
37
38
|
<strong>{title}</strong>
|
|
38
39
|
</text>
|
|
39
40
|
</box>
|
|
40
41
|
|
|
41
42
|
<box marginBottom={1}>
|
|
42
|
-
<text fg=
|
|
43
|
+
<text fg={theme.colors.muted}>{label}</text>
|
|
43
44
|
</box>
|
|
44
45
|
|
|
45
|
-
<box
|
|
46
|
+
<box
|
|
47
|
+
border
|
|
48
|
+
borderStyle="rounded"
|
|
49
|
+
borderColor={theme.colors.dim}
|
|
50
|
+
paddingLeft={1}
|
|
51
|
+
paddingRight={1}
|
|
52
|
+
width={54}
|
|
53
|
+
>
|
|
46
54
|
<input
|
|
47
55
|
value={defaultValue}
|
|
48
56
|
onSubmit={onSubmit as any}
|
|
@@ -52,7 +60,7 @@ export function InputModal({
|
|
|
52
60
|
</box>
|
|
53
61
|
|
|
54
62
|
<box marginTop={1}>
|
|
55
|
-
<text fg=
|
|
63
|
+
<text fg={theme.colors.muted}>↵ to confirm • Esc to cancel</text>
|
|
56
64
|
</box>
|
|
57
65
|
</box>
|
|
58
66
|
);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { useState, useEffect } from "react";
|
|
2
|
+
import { theme } from "../../theme.js";
|
|
2
3
|
|
|
3
4
|
interface LoadingModalProps {
|
|
4
5
|
/** Loading message — supports multiline (first line = status, subsequent = detail) */
|
|
@@ -27,20 +28,20 @@ export function LoadingModal({ message }: LoadingModalProps) {
|
|
|
27
28
|
flexDirection="column"
|
|
28
29
|
border
|
|
29
30
|
borderStyle="rounded"
|
|
30
|
-
borderColor=
|
|
31
|
-
backgroundColor=
|
|
31
|
+
borderColor={theme.colors.dim}
|
|
32
|
+
backgroundColor={theme.surface.bg}
|
|
32
33
|
paddingLeft={3}
|
|
33
34
|
paddingRight={3}
|
|
34
35
|
paddingTop={1}
|
|
35
36
|
paddingBottom={1}
|
|
36
37
|
>
|
|
37
38
|
<box flexDirection="row">
|
|
38
|
-
<text fg=
|
|
39
|
-
<text fg=
|
|
39
|
+
<text fg={theme.colors.muted}>{SPINNER_FRAMES[frame]}</text>
|
|
40
|
+
<text fg={theme.colors.text}> {mainMessage}</text>
|
|
40
41
|
</box>
|
|
41
42
|
{detail ? (
|
|
42
43
|
<box marginTop={0} paddingLeft={2}>
|
|
43
|
-
<text fg=
|
|
44
|
+
<text fg={theme.colors.dim}>{detail}</text>
|
|
44
45
|
</box>
|
|
45
46
|
) : null}
|
|
46
47
|
</box>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { useKeyboard } from "../../hooks/useKeyboard.js";
|
|
3
|
+
import { theme } from "../../theme.js";
|
|
3
4
|
|
|
4
5
|
interface MessageModalProps {
|
|
5
6
|
/** Modal title */
|
|
@@ -13,9 +14,9 @@ interface MessageModalProps {
|
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
const variantConfig = {
|
|
16
|
-
info: { icon: "ℹ", color:
|
|
17
|
-
success: { icon: "✓", color:
|
|
18
|
-
error: { icon: "✗", color:
|
|
17
|
+
info: { icon: "ℹ", color: theme.colors.link },
|
|
18
|
+
success: { icon: "✓", color: theme.colors.success },
|
|
19
|
+
error: { icon: "✗", color: theme.colors.danger },
|
|
19
20
|
} as const;
|
|
20
21
|
|
|
21
22
|
export function MessageModal({
|
|
@@ -36,8 +37,8 @@ export function MessageModal({
|
|
|
36
37
|
flexDirection="column"
|
|
37
38
|
border
|
|
38
39
|
borderStyle="rounded"
|
|
39
|
-
borderColor=
|
|
40
|
-
backgroundColor=
|
|
40
|
+
borderColor={theme.colors.dim}
|
|
41
|
+
backgroundColor={theme.surface.bg}
|
|
41
42
|
paddingLeft={3}
|
|
42
43
|
paddingRight={3}
|
|
43
44
|
paddingTop={1}
|
|
@@ -46,17 +47,17 @@ export function MessageModal({
|
|
|
46
47
|
>
|
|
47
48
|
<box marginBottom={1}>
|
|
48
49
|
<text fg={config.color}>{config.icon} </text>
|
|
49
|
-
<text fg=
|
|
50
|
+
<text fg={theme.colors.text}>
|
|
50
51
|
<strong>{title}</strong>
|
|
51
52
|
</text>
|
|
52
53
|
</box>
|
|
53
54
|
|
|
54
55
|
<box marginBottom={1}>
|
|
55
|
-
<text fg=
|
|
56
|
+
<text fg={theme.colors.muted}>{message}</text>
|
|
56
57
|
</box>
|
|
57
58
|
|
|
58
59
|
<box marginTop={1}>
|
|
59
|
-
<text fg=
|
|
60
|
+
<text fg={theme.colors.muted}>Press any key to continue</text>
|
|
60
61
|
</box>
|
|
61
62
|
</box>
|
|
62
63
|
);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { SelectOption } from "../../state/types.js";
|
|
3
3
|
import { useDimensions } from "../../state/DimensionsContext.js";
|
|
4
|
+
import { theme } from "../../theme.js";
|
|
4
5
|
|
|
5
6
|
interface SelectModalProps {
|
|
6
7
|
/** Modal title */
|
|
@@ -44,8 +45,8 @@ export function SelectModal({
|
|
|
44
45
|
flexDirection="column"
|
|
45
46
|
border
|
|
46
47
|
borderStyle="rounded"
|
|
47
|
-
borderColor=
|
|
48
|
-
backgroundColor=
|
|
48
|
+
borderColor={theme.colors.dim}
|
|
49
|
+
backgroundColor={theme.surface.bg}
|
|
49
50
|
paddingLeft={3}
|
|
50
51
|
paddingRight={3}
|
|
51
52
|
paddingTop={1}
|
|
@@ -53,21 +54,24 @@ export function SelectModal({
|
|
|
53
54
|
width={width}
|
|
54
55
|
>
|
|
55
56
|
<box marginBottom={1}>
|
|
56
|
-
<text fg=
|
|
57
|
+
<text fg={theme.colors.text}>
|
|
57
58
|
<strong>{title}</strong>
|
|
58
59
|
</text>
|
|
59
60
|
</box>
|
|
60
61
|
|
|
61
62
|
<box marginBottom={1}>
|
|
62
|
-
<text fg=
|
|
63
|
+
<text fg={theme.colors.muted}>{message}</text>
|
|
63
64
|
</box>
|
|
64
65
|
|
|
65
66
|
<box flexDirection="column" paddingLeft={1}>
|
|
66
67
|
{options.map((option, idx) => {
|
|
67
68
|
const isSelected = idx === selectedIndex;
|
|
68
69
|
return (
|
|
69
|
-
<text
|
|
70
|
-
|
|
70
|
+
<text
|
|
71
|
+
key={option.value}
|
|
72
|
+
fg={isSelected ? theme.colors.text : theme.colors.muted}
|
|
73
|
+
>
|
|
74
|
+
<span fg={isSelected ? theme.colors.text : theme.colors.muted}>
|
|
71
75
|
{isSelected ? "❯ " : " "}
|
|
72
76
|
</span>
|
|
73
77
|
{isSelected ? <strong>{option.label}</strong> : option.label}
|
|
@@ -77,7 +81,7 @@ export function SelectModal({
|
|
|
77
81
|
</box>
|
|
78
82
|
|
|
79
83
|
<box marginTop={1}>
|
|
80
|
-
<text fg=
|
|
84
|
+
<text fg={theme.colors.muted}>↑↓ Select • ↵ Confirm • Esc Cancel</text>
|
|
81
85
|
</box>
|
|
82
86
|
</box>
|
|
83
87
|
);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { VersionMismatchInfo } from "../../../services/plugin-version-check.js";
|
|
3
|
+
import { theme } from "../../theme.js";
|
|
3
4
|
|
|
4
5
|
interface VersionMismatchModalProps {
|
|
5
6
|
/** List of version mismatches detected */
|
|
@@ -8,15 +9,15 @@ interface VersionMismatchModalProps {
|
|
|
8
9
|
defaultIndex?: number;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
const COLOR_BG =
|
|
12
|
-
const COLOR_BORDER =
|
|
13
|
-
const COLOR_TITLE =
|
|
14
|
-
const COLOR_MUTED =
|
|
15
|
-
const COLOR_DIM =
|
|
16
|
-
const COLOR_WARN =
|
|
17
|
-
const COLOR_BAD =
|
|
18
|
-
const COLOR_GOOD =
|
|
19
|
-
const COLOR_ACCENT =
|
|
12
|
+
const COLOR_BG = theme.surface.bg;
|
|
13
|
+
const COLOR_BORDER = theme.colors.dim;
|
|
14
|
+
const COLOR_TITLE = theme.colors.text;
|
|
15
|
+
const COLOR_MUTED = theme.colors.muted;
|
|
16
|
+
const COLOR_DIM = theme.colors.muted;
|
|
17
|
+
const COLOR_WARN = theme.colors.warning;
|
|
18
|
+
const COLOR_BAD = theme.colors.danger;
|
|
19
|
+
const COLOR_GOOD = theme.colors.success;
|
|
20
|
+
const COLOR_ACCENT = theme.colors.text;
|
|
20
21
|
|
|
21
22
|
/** Truncate a plugin name to fit the column width */
|
|
22
23
|
function shortName(pluginId: string, max: number): string {
|
|
@@ -80,7 +81,8 @@ export function VersionMismatchModal({
|
|
|
80
81
|
</strong>
|
|
81
82
|
</text>
|
|
82
83
|
<text fg={COLOR_BORDER}>
|
|
83
|
-
{"─".repeat(NAME_W - 1)} {"─".repeat(VER_W - 1)}
|
|
84
|
+
{"─".repeat(NAME_W - 1)} {"─".repeat(VER_W - 1)}{" "}
|
|
85
|
+
{"─".repeat(VER_W - 1)}
|
|
84
86
|
</text>
|
|
85
87
|
|
|
86
88
|
{/* Table rows */}
|
|
@@ -107,9 +109,7 @@ export function VersionMismatchModal({
|
|
|
107
109
|
|
|
108
110
|
{/* Options */}
|
|
109
111
|
<box flexDirection="column" paddingLeft={1}>
|
|
110
|
-
<text
|
|
111
|
-
fg={selectedIndex === 0 ? COLOR_ACCENT : COLOR_MUTED}
|
|
112
|
-
>
|
|
112
|
+
<text fg={selectedIndex === 0 ? COLOR_ACCENT : COLOR_MUTED}>
|
|
113
113
|
<span fg={selectedIndex === 0 ? COLOR_ACCENT : COLOR_DIM}>
|
|
114
114
|
{selectedIndex === 0 ? "❯ " : " "}
|
|
115
115
|
</span>
|
|
@@ -119,9 +119,7 @@ export function VersionMismatchModal({
|
|
|
119
119
|
"Fix all projects"
|
|
120
120
|
)}
|
|
121
121
|
</text>
|
|
122
|
-
<text
|
|
123
|
-
fg={selectedIndex === 1 ? COLOR_ACCENT : COLOR_MUTED}
|
|
124
|
-
>
|
|
122
|
+
<text fg={selectedIndex === 1 ? COLOR_ACCENT : COLOR_MUTED}>
|
|
125
123
|
<span fg={selectedIndex === 1 ? COLOR_ACCENT : COLOR_DIM}>
|
|
126
124
|
{selectedIndex === 1 ? "❯ " : " "}
|
|
127
125
|
</span>
|