claudeup 4.6.1 → 4.7.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/data/settings-catalog.js +2 -7
- package/src/data/settings-catalog.ts +2 -7
- package/src/opentui.d.ts +7 -2
- 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
package/package.json
CHANGED
|
@@ -186,13 +186,8 @@ export const SETTINGS_CATALOG = [
|
|
|
186
186
|
description: "Adjust Claude's response style",
|
|
187
187
|
category: "workflow",
|
|
188
188
|
type: "select",
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
{ label: "Concise", value: "concise" },
|
|
192
|
-
{ label: "Explanatory", value: "explanatory" },
|
|
193
|
-
{ label: "Formal", value: "formal" },
|
|
194
|
-
{ label: "Minimal", value: "minimal" },
|
|
195
|
-
],
|
|
189
|
+
// Options populated dynamically from installed output-style plugins
|
|
190
|
+
options: [],
|
|
196
191
|
storage: { type: "setting", key: "outputStyle" },
|
|
197
192
|
},
|
|
198
193
|
{
|
|
@@ -233,13 +233,8 @@ export const SETTINGS_CATALOG: SettingDefinition[] = [
|
|
|
233
233
|
description: "Adjust Claude's response style",
|
|
234
234
|
category: "workflow",
|
|
235
235
|
type: "select",
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
{ label: "Concise", value: "concise" },
|
|
239
|
-
{ label: "Explanatory", value: "explanatory" },
|
|
240
|
-
{ label: "Formal", value: "formal" },
|
|
241
|
-
{ label: "Minimal", value: "minimal" },
|
|
242
|
-
],
|
|
236
|
+
// Options populated dynamically from installed output-style plugins
|
|
237
|
+
options: [],
|
|
243
238
|
storage: { type: "setting", key: "outputStyle" },
|
|
244
239
|
},
|
|
245
240
|
{
|
package/src/opentui.d.ts
CHANGED
|
@@ -101,8 +101,10 @@ interface TextProps {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
interface InputProps {
|
|
104
|
-
value
|
|
105
|
-
onChange
|
|
104
|
+
value?: string;
|
|
105
|
+
onChange?: (value: string) => void;
|
|
106
|
+
onInput?: (value: string) => void;
|
|
107
|
+
onSubmit?: ((value: string) => void) | undefined;
|
|
106
108
|
placeholder?: string;
|
|
107
109
|
focused?: boolean;
|
|
108
110
|
width?: number;
|
|
@@ -137,6 +139,9 @@ interface TabSelectProps {
|
|
|
137
139
|
|
|
138
140
|
interface ScrollboxProps {
|
|
139
141
|
focused?: boolean;
|
|
142
|
+
height?: number | `${number}%` | "auto";
|
|
143
|
+
scrollY?: boolean;
|
|
144
|
+
scrollX?: boolean;
|
|
140
145
|
style?: {
|
|
141
146
|
rootOptions?: Record<string, unknown>;
|
|
142
147
|
wrapperOptions?: Record<string, unknown>;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
1
3
|
import { readSettings, writeSettings, readGlobalSettings, writeGlobalSettings, } from "./claude-settings.js";
|
|
2
4
|
/** Read the current value of a setting from the given scope */
|
|
3
5
|
export async function readSettingValue(setting, scope, projectPath) {
|
|
@@ -62,11 +64,6 @@ export async function writeSettingValue(setting, value, scope, projectPath) {
|
|
|
62
64
|
}
|
|
63
65
|
}
|
|
64
66
|
else if (setting.storage.type === "attribution-text") {
|
|
65
|
-
const attr = settings.attribution;
|
|
66
|
-
// If attribution is explicitly disabled ({ commit: "", pr: "" }), do not overwrite it
|
|
67
|
-
if (attr && attr.commit === "" && attr.pr === "") {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
67
|
if (value && value.trim().length > 0) {
|
|
71
68
|
// Write custom text: commit gets a Co-Authored-By trailer + the text; pr gets the text
|
|
72
69
|
settings.attribution = {
|
|
@@ -162,3 +159,85 @@ export async function readAllSettingsBothScopes(catalog, projectPath) {
|
|
|
162
159
|
}
|
|
163
160
|
return result;
|
|
164
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* Discover available output styles from installed plugins.
|
|
164
|
+
* Scans enabled plugins for outputStyles entries and *-output-style plugin names.
|
|
165
|
+
* Returns options suitable for a select setting.
|
|
166
|
+
*/
|
|
167
|
+
export async function discoverOutputStyles(projectPath) {
|
|
168
|
+
const styles = [
|
|
169
|
+
{ label: "Default", value: "" },
|
|
170
|
+
];
|
|
171
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
172
|
+
const cacheDir = path.join(homeDir, ".claude", "plugins", "cache");
|
|
173
|
+
// Collect enabled plugins from both scopes
|
|
174
|
+
const enabledPlugins = new Set();
|
|
175
|
+
try {
|
|
176
|
+
const userSettings = await readGlobalSettings();
|
|
177
|
+
for (const [k, v] of Object.entries(userSettings.enabledPlugins || {})) {
|
|
178
|
+
if (v)
|
|
179
|
+
enabledPlugins.add(k);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
catch { }
|
|
183
|
+
try {
|
|
184
|
+
const projSettings = await readSettings(projectPath);
|
|
185
|
+
for (const [k, v] of Object.entries(projSettings.enabledPlugins || {})) {
|
|
186
|
+
if (v)
|
|
187
|
+
enabledPlugins.add(k);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
catch { }
|
|
191
|
+
const seen = new Set();
|
|
192
|
+
for (const pluginId of enabledPlugins) {
|
|
193
|
+
const [pluginName, marketplace] = pluginId.split("@");
|
|
194
|
+
// Check if this is a dedicated output-style plugin (e.g. "explanatory-output-style")
|
|
195
|
+
if (pluginName.endsWith("-output-style")) {
|
|
196
|
+
const styleName = pluginName
|
|
197
|
+
.replace(/-output-style$/, "")
|
|
198
|
+
.split("-")
|
|
199
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
200
|
+
.join(" ");
|
|
201
|
+
if (!seen.has(styleName)) {
|
|
202
|
+
seen.add(styleName);
|
|
203
|
+
styles.push({ label: styleName, value: styleName });
|
|
204
|
+
}
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
// Check plugin.json for outputStyles entries
|
|
208
|
+
if (!marketplace)
|
|
209
|
+
continue;
|
|
210
|
+
const pluginDir = path.join(cacheDir, marketplace, pluginName);
|
|
211
|
+
try {
|
|
212
|
+
const versions = fs.readdirSync(pluginDir).sort();
|
|
213
|
+
const latest = versions[versions.length - 1];
|
|
214
|
+
if (!latest)
|
|
215
|
+
continue;
|
|
216
|
+
const manifestPath = path.join(pluginDir, latest, "plugin.json");
|
|
217
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
218
|
+
const outputStyles = manifest.outputStyles;
|
|
219
|
+
if (!outputStyles?.length)
|
|
220
|
+
continue;
|
|
221
|
+
for (const stylePath of outputStyles) {
|
|
222
|
+
const fullPath = path.join(pluginDir, latest, stylePath);
|
|
223
|
+
try {
|
|
224
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
225
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
226
|
+
if (fmMatch) {
|
|
227
|
+
const nameMatch = fmMatch[1].match(/^name:\s*(.+)$/m);
|
|
228
|
+
if (nameMatch) {
|
|
229
|
+
const name = nameMatch[1].trim();
|
|
230
|
+
if (!seen.has(name)) {
|
|
231
|
+
seen.add(name);
|
|
232
|
+
styles.push({ label: name, value: name });
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
catch { }
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch { }
|
|
241
|
+
}
|
|
242
|
+
return styles;
|
|
243
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
1
3
|
import {
|
|
2
4
|
readSettings,
|
|
3
5
|
writeSettings,
|
|
@@ -80,11 +82,6 @@ export async function writeSettingValue(
|
|
|
80
82
|
delete (settings as any).attribution;
|
|
81
83
|
}
|
|
82
84
|
} else if (setting.storage.type === "attribution-text") {
|
|
83
|
-
const attr = (settings as any).attribution;
|
|
84
|
-
// If attribution is explicitly disabled ({ commit: "", pr: "" }), do not overwrite it
|
|
85
|
-
if (attr && attr.commit === "" && attr.pr === "") {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
85
|
if (value && value.trim().length > 0) {
|
|
89
86
|
// Write custom text: commit gets a Co-Authored-By trailer + the text; pr gets the text
|
|
90
87
|
(settings as any).attribution = {
|
|
@@ -188,3 +185,87 @@ export async function readAllSettingsBothScopes(
|
|
|
188
185
|
}
|
|
189
186
|
return result;
|
|
190
187
|
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Discover available output styles from installed plugins.
|
|
191
|
+
* Scans enabled plugins for outputStyles entries and *-output-style plugin names.
|
|
192
|
+
* Returns options suitable for a select setting.
|
|
193
|
+
*/
|
|
194
|
+
export async function discoverOutputStyles(
|
|
195
|
+
projectPath?: string,
|
|
196
|
+
): Promise<Array<{ label: string; value: string }>> {
|
|
197
|
+
const styles: Array<{ label: string; value: string }> = [
|
|
198
|
+
{ label: "Default", value: "" },
|
|
199
|
+
];
|
|
200
|
+
|
|
201
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
202
|
+
const cacheDir = path.join(homeDir, ".claude", "plugins", "cache");
|
|
203
|
+
|
|
204
|
+
// Collect enabled plugins from both scopes
|
|
205
|
+
const enabledPlugins = new Set<string>();
|
|
206
|
+
try {
|
|
207
|
+
const userSettings = await readGlobalSettings();
|
|
208
|
+
for (const [k, v] of Object.entries((userSettings as any).enabledPlugins || {})) {
|
|
209
|
+
if (v) enabledPlugins.add(k);
|
|
210
|
+
}
|
|
211
|
+
} catch {}
|
|
212
|
+
try {
|
|
213
|
+
const projSettings = await readSettings(projectPath);
|
|
214
|
+
for (const [k, v] of Object.entries((projSettings as any).enabledPlugins || {})) {
|
|
215
|
+
if (v) enabledPlugins.add(k);
|
|
216
|
+
}
|
|
217
|
+
} catch {}
|
|
218
|
+
|
|
219
|
+
const seen = new Set<string>();
|
|
220
|
+
|
|
221
|
+
for (const pluginId of enabledPlugins) {
|
|
222
|
+
const [pluginName, marketplace] = pluginId.split("@");
|
|
223
|
+
|
|
224
|
+
// Check if this is a dedicated output-style plugin (e.g. "explanatory-output-style")
|
|
225
|
+
if (pluginName.endsWith("-output-style")) {
|
|
226
|
+
const styleName = pluginName
|
|
227
|
+
.replace(/-output-style$/, "")
|
|
228
|
+
.split("-")
|
|
229
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
230
|
+
.join(" ");
|
|
231
|
+
if (!seen.has(styleName)) {
|
|
232
|
+
seen.add(styleName);
|
|
233
|
+
styles.push({ label: styleName, value: styleName });
|
|
234
|
+
}
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Check plugin.json for outputStyles entries
|
|
239
|
+
if (!marketplace) continue;
|
|
240
|
+
const pluginDir = path.join(cacheDir, marketplace, pluginName);
|
|
241
|
+
try {
|
|
242
|
+
const versions = fs.readdirSync(pluginDir).sort();
|
|
243
|
+
const latest = versions[versions.length - 1];
|
|
244
|
+
if (!latest) continue;
|
|
245
|
+
const manifestPath = path.join(pluginDir, latest, "plugin.json");
|
|
246
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
247
|
+
const outputStyles = manifest.outputStyles as string[] | undefined;
|
|
248
|
+
if (!outputStyles?.length) continue;
|
|
249
|
+
|
|
250
|
+
for (const stylePath of outputStyles) {
|
|
251
|
+
const fullPath = path.join(pluginDir, latest, stylePath);
|
|
252
|
+
try {
|
|
253
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
254
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
255
|
+
if (fmMatch) {
|
|
256
|
+
const nameMatch = fmMatch[1].match(/^name:\s*(.+)$/m);
|
|
257
|
+
if (nameMatch) {
|
|
258
|
+
const name = nameMatch[1].trim();
|
|
259
|
+
if (!seen.has(name)) {
|
|
260
|
+
seen.add(name);
|
|
261
|
+
styles.push({ label: name, value: name });
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
} catch {}
|
|
266
|
+
}
|
|
267
|
+
} catch {}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return styles;
|
|
271
|
+
}
|
|
@@ -52,12 +52,12 @@ export function getEffectiveValue(scoped) {
|
|
|
52
52
|
}
|
|
53
53
|
export function formatValue(setting, value) {
|
|
54
54
|
if (value === undefined || value === "") {
|
|
55
|
-
if (setting.defaultValue !== undefined) {
|
|
56
|
-
return setting.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
if (setting.type === "boolean" && setting.defaultValue !== undefined) {
|
|
56
|
+
return setting.defaultValue === "true" ? "on" : "off";
|
|
57
|
+
}
|
|
58
|
+
if (setting.type === "select" && setting.defaultValue !== undefined) {
|
|
59
|
+
const opt = setting.options?.find((o) => o.value === setting.defaultValue);
|
|
60
|
+
return opt ? opt.label : setting.defaultValue;
|
|
61
61
|
}
|
|
62
62
|
return "—";
|
|
63
63
|
}
|
|
@@ -68,8 +68,8 @@ export function formatValue(setting, value) {
|
|
|
68
68
|
const opt = setting.options.find((o) => o.value === value);
|
|
69
69
|
return opt ? opt.label : value;
|
|
70
70
|
}
|
|
71
|
-
if (value.length >
|
|
72
|
-
return value.slice(0,
|
|
71
|
+
if (value.length > 12) {
|
|
72
|
+
return value.slice(0, 12) + "…";
|
|
73
73
|
}
|
|
74
74
|
return value;
|
|
75
75
|
}
|
|
@@ -94,12 +94,12 @@ export function formatValue(
|
|
|
94
94
|
value: string | undefined,
|
|
95
95
|
): string {
|
|
96
96
|
if (value === undefined || value === "") {
|
|
97
|
-
if (setting.defaultValue !== undefined) {
|
|
98
|
-
return setting.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
97
|
+
if (setting.type === "boolean" && setting.defaultValue !== undefined) {
|
|
98
|
+
return setting.defaultValue === "true" ? "on" : "off";
|
|
99
|
+
}
|
|
100
|
+
if (setting.type === "select" && setting.defaultValue !== undefined) {
|
|
101
|
+
const opt = setting.options?.find((o) => o.value === setting.defaultValue);
|
|
102
|
+
return opt ? opt.label : setting.defaultValue;
|
|
103
103
|
}
|
|
104
104
|
return "—";
|
|
105
105
|
}
|
|
@@ -113,8 +113,8 @@ export function formatValue(
|
|
|
113
113
|
return opt ? opt.label : value;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
if (value.length >
|
|
117
|
-
return value.slice(0,
|
|
116
|
+
if (value.length > 12) {
|
|
117
|
+
return value.slice(0, 12) + "…";
|
|
118
118
|
}
|
|
119
119
|
return value;
|
|
120
120
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "@opentui/react/jsx-runtime";
|
|
2
|
-
import { useKeyboardHandler } from "../hooks/useKeyboardHandler";
|
|
3
2
|
const TABS = [
|
|
4
3
|
{ key: "1", label: "Plugins", screen: "plugins" },
|
|
5
4
|
{ key: "2", label: "Skills", screen: "skills" },
|
|
@@ -8,28 +7,7 @@ const TABS = [
|
|
|
8
7
|
{ key: "5", label: "Profiles", screen: "profiles" },
|
|
9
8
|
{ key: "6", label: "CLI", screen: "cli-tools" },
|
|
10
9
|
];
|
|
11
|
-
export function TabBar({ currentScreen
|
|
12
|
-
// Handle number key shortcuts (1-5)
|
|
13
|
-
useKeyboardHandler((input, key) => {
|
|
14
|
-
if (!onTabChange)
|
|
15
|
-
return;
|
|
16
|
-
// Number keys 1-5
|
|
17
|
-
const tabIndex = Number.parseInt(input, 10);
|
|
18
|
-
if (tabIndex >= 1 && tabIndex <= TABS.length) {
|
|
19
|
-
const tab = TABS[tabIndex - 1];
|
|
20
|
-
if (tab && tab.screen !== currentScreen) {
|
|
21
|
-
onTabChange(tab.screen);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
// Tab key to cycle through tabs
|
|
25
|
-
if (key.tab) {
|
|
26
|
-
const currentIndex = TABS.findIndex((t) => t.screen === currentScreen);
|
|
27
|
-
const nextIndex = key.shift
|
|
28
|
-
? (currentIndex - 1 + TABS.length) % TABS.length
|
|
29
|
-
: (currentIndex + 1) % TABS.length;
|
|
30
|
-
onTabChange(TABS[nextIndex].screen);
|
|
31
|
-
}
|
|
32
|
-
});
|
|
10
|
+
export function TabBar({ currentScreen }) {
|
|
33
11
|
return (_jsx("box", { flexDirection: "row", gap: 0, children: TABS.map((tab, index) => {
|
|
34
12
|
const isSelected = tab.screen === currentScreen;
|
|
35
13
|
const isLast = index === TABS.length - 1;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { useKeyboardHandler } from "../hooks/useKeyboardHandler";
|
|
3
2
|
import type { Screen } from "../state/types.js";
|
|
4
3
|
|
|
5
4
|
interface Tab {
|
|
@@ -19,33 +18,9 @@ const TABS: Tab[] = [
|
|
|
19
18
|
|
|
20
19
|
interface TabBarProps {
|
|
21
20
|
currentScreen: Screen;
|
|
22
|
-
onTabChange?: (screen: Screen) => void;
|
|
23
21
|
}
|
|
24
22
|
|
|
25
|
-
export function TabBar({ currentScreen
|
|
26
|
-
// Handle number key shortcuts (1-5)
|
|
27
|
-
useKeyboardHandler((input, key) => {
|
|
28
|
-
if (!onTabChange) return;
|
|
29
|
-
|
|
30
|
-
// Number keys 1-5
|
|
31
|
-
const tabIndex = Number.parseInt(input, 10);
|
|
32
|
-
if (tabIndex >= 1 && tabIndex <= TABS.length) {
|
|
33
|
-
const tab = TABS[tabIndex - 1];
|
|
34
|
-
if (tab && tab.screen !== currentScreen) {
|
|
35
|
-
onTabChange(tab.screen);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Tab key to cycle through tabs
|
|
40
|
-
if (key.tab) {
|
|
41
|
-
const currentIndex = TABS.findIndex((t) => t.screen === currentScreen);
|
|
42
|
-
const nextIndex = key.shift
|
|
43
|
-
? (currentIndex - 1 + TABS.length) % TABS.length
|
|
44
|
-
: (currentIndex + 1) % TABS.length;
|
|
45
|
-
onTabChange(TABS[nextIndex].screen);
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
|
|
23
|
+
export function TabBar({ currentScreen }: TabBarProps) {
|
|
49
24
|
return (
|
|
50
25
|
<box flexDirection="row" gap={0}>
|
|
51
26
|
{TABS.map((tab, index) => {
|
|
@@ -9,6 +9,6 @@ export function ConfirmModal({ title, message, onConfirm, onCancel, }) {
|
|
|
9
9
|
onCancel();
|
|
10
10
|
}
|
|
11
11
|
});
|
|
12
|
-
return (_jsxs("box", { flexDirection: "column", border: true, borderStyle: "rounded", borderColor: "
|
|
12
|
+
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: message }) }), _jsxs("box", { marginTop: 1, children: [_jsx("text", { fg: "#71717A", children: "Press " }), _jsx("text", { fg: "#EDEDED", children: "Y" }), _jsx("text", { fg: "#71717A", children: " to confirm \u2022 " }), _jsx("text", { fg: "#EDEDED", children: "N" }), _jsx("text", { fg: "#71717A", children: " to cancel" })] })] }));
|
|
13
13
|
}
|
|
14
14
|
export default ConfirmModal;
|
|
@@ -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;
|