claudeup 3.7.2 → 3.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.
Files changed (44) hide show
  1. package/package.json +1 -1
  2. package/src/data/settings-catalog.js +612 -0
  3. package/src/data/settings-catalog.ts +689 -0
  4. package/src/services/plugin-manager.js +2 -0
  5. package/src/services/plugin-manager.ts +3 -0
  6. package/src/services/profiles.js +161 -0
  7. package/src/services/profiles.ts +225 -0
  8. package/src/services/settings-manager.js +108 -0
  9. package/src/services/settings-manager.ts +140 -0
  10. package/src/types/index.ts +34 -0
  11. package/src/ui/App.js +17 -18
  12. package/src/ui/App.tsx +21 -23
  13. package/src/ui/components/TabBar.js +8 -8
  14. package/src/ui/components/TabBar.tsx +14 -19
  15. package/src/ui/components/layout/ScreenLayout.js +8 -14
  16. package/src/ui/components/layout/ScreenLayout.tsx +51 -58
  17. package/src/ui/components/modals/ModalContainer.js +43 -11
  18. package/src/ui/components/modals/ModalContainer.tsx +44 -12
  19. package/src/ui/components/modals/SelectModal.js +4 -18
  20. package/src/ui/components/modals/SelectModal.tsx +10 -21
  21. package/src/ui/screens/CliToolsScreen.js +2 -2
  22. package/src/ui/screens/CliToolsScreen.tsx +8 -8
  23. package/src/ui/screens/EnvVarsScreen.js +248 -116
  24. package/src/ui/screens/EnvVarsScreen.tsx +419 -184
  25. package/src/ui/screens/McpRegistryScreen.tsx +18 -6
  26. package/src/ui/screens/McpScreen.js +1 -1
  27. package/src/ui/screens/McpScreen.tsx +15 -5
  28. package/src/ui/screens/ModelSelectorScreen.js +3 -5
  29. package/src/ui/screens/ModelSelectorScreen.tsx +12 -16
  30. package/src/ui/screens/PluginsScreen.js +154 -66
  31. package/src/ui/screens/PluginsScreen.tsx +280 -97
  32. package/src/ui/screens/ProfilesScreen.js +255 -0
  33. package/src/ui/screens/ProfilesScreen.tsx +487 -0
  34. package/src/ui/screens/StatusLineScreen.js +2 -2
  35. package/src/ui/screens/StatusLineScreen.tsx +10 -12
  36. package/src/ui/screens/index.js +2 -2
  37. package/src/ui/screens/index.ts +2 -2
  38. package/src/ui/state/AppContext.js +2 -1
  39. package/src/ui/state/AppContext.tsx +2 -0
  40. package/src/ui/state/reducer.js +63 -19
  41. package/src/ui/state/reducer.ts +68 -19
  42. package/src/ui/state/types.ts +33 -14
  43. package/src/utils/clipboard.js +56 -0
  44. package/src/utils/clipboard.ts +58 -0
@@ -34,9 +34,9 @@ export const initialState = {
34
34
  presets: { status: "idle" },
35
35
  currentPreset: null,
36
36
  },
37
- envVars: {
37
+ settings: {
38
38
  selectedIndex: 0,
39
- variables: { status: "idle" },
39
+ values: { status: "idle" },
40
40
  },
41
41
  cliTools: {
42
42
  selectedIndex: 0,
@@ -47,6 +47,10 @@ export const initialState = {
47
47
  searchQuery: "",
48
48
  taskSize: "large",
49
49
  },
50
+ profiles: {
51
+ selectedIndex: 0,
52
+ profiles: { status: "idle" },
53
+ },
50
54
  };
51
55
  export function appReducer(state, action) {
52
56
  switch (action.type) {
@@ -54,7 +58,12 @@ export function appReducer(state, action) {
54
58
  // Navigation
55
59
  // =========================================================================
56
60
  case "NAVIGATE":
57
- return { ...state, currentRoute: action.route };
61
+ return {
62
+ ...state,
63
+ currentRoute: action.route,
64
+ // Clear search state when navigating away
65
+ isSearching: false,
66
+ };
58
67
  // =========================================================================
59
68
  // Plugins Screen
60
69
  // =========================================================================
@@ -268,38 +277,38 @@ export function appReducer(state, action) {
268
277
  },
269
278
  };
270
279
  // =========================================================================
271
- // Env Vars Screen
280
+ // Settings Screen
272
281
  // =========================================================================
273
- case "ENVVARS_SELECT":
282
+ case "SETTINGS_SELECT":
274
283
  return {
275
284
  ...state,
276
- envVars: {
277
- ...state.envVars,
285
+ settings: {
286
+ ...state.settings,
278
287
  selectedIndex: action.index,
279
288
  },
280
289
  };
281
- case "ENVVARS_DATA_LOADING":
290
+ case "SETTINGS_DATA_LOADING":
282
291
  return {
283
292
  ...state,
284
- envVars: {
285
- ...state.envVars,
286
- variables: { status: "loading" },
293
+ settings: {
294
+ ...state.settings,
295
+ values: { status: "loading" },
287
296
  },
288
297
  };
289
- case "ENVVARS_DATA_SUCCESS":
298
+ case "SETTINGS_DATA_SUCCESS":
290
299
  return {
291
300
  ...state,
292
- envVars: {
293
- ...state.envVars,
294
- variables: { status: "success", data: action.variables },
301
+ settings: {
302
+ ...state.settings,
303
+ values: { status: "success", data: action.values },
295
304
  },
296
305
  };
297
- case "ENVVARS_DATA_ERROR":
306
+ case "SETTINGS_DATA_ERROR":
298
307
  return {
299
308
  ...state,
300
- envVars: {
301
- ...state.envVars,
302
- variables: { status: "error", error: action.error },
309
+ settings: {
310
+ ...state.settings,
311
+ values: { status: "error", error: action.error },
303
312
  },
304
313
  };
305
314
  // =========================================================================
@@ -380,6 +389,41 @@ export function appReducer(state, action) {
380
389
  },
381
390
  };
382
391
  // =========================================================================
392
+ // Profiles Screen
393
+ // =========================================================================
394
+ case "PROFILES_SELECT":
395
+ return {
396
+ ...state,
397
+ profiles: {
398
+ ...state.profiles,
399
+ selectedIndex: action.index,
400
+ },
401
+ };
402
+ case "PROFILES_DATA_LOADING":
403
+ return {
404
+ ...state,
405
+ profiles: {
406
+ ...state.profiles,
407
+ profiles: { status: "loading" },
408
+ },
409
+ };
410
+ case "PROFILES_DATA_SUCCESS":
411
+ return {
412
+ ...state,
413
+ profiles: {
414
+ ...state.profiles,
415
+ profiles: { status: "success", data: action.profiles },
416
+ },
417
+ };
418
+ case "PROFILES_DATA_ERROR":
419
+ return {
420
+ ...state,
421
+ profiles: {
422
+ ...state.profiles,
423
+ profiles: { status: "error", error: action.error },
424
+ },
425
+ };
426
+ // =========================================================================
383
427
  // Modals
384
428
  // =========================================================================
385
429
  case "SHOW_MODAL":
@@ -44,9 +44,9 @@ export const initialState: AppState = {
44
44
  currentPreset: null,
45
45
  },
46
46
 
47
- envVars: {
47
+ settings: {
48
48
  selectedIndex: 0,
49
- variables: { status: "idle" },
49
+ values: { status: "idle" },
50
50
  },
51
51
 
52
52
  cliTools: {
@@ -59,6 +59,11 @@ export const initialState: AppState = {
59
59
  searchQuery: "",
60
60
  taskSize: "large",
61
61
  },
62
+
63
+ profiles: {
64
+ selectedIndex: 0,
65
+ profiles: { status: "idle" },
66
+ },
62
67
  };
63
68
 
64
69
  export function appReducer(state: AppState, action: AppAction): AppState {
@@ -67,7 +72,12 @@ export function appReducer(state: AppState, action: AppAction): AppState {
67
72
  // Navigation
68
73
  // =========================================================================
69
74
  case "NAVIGATE":
70
- return { ...state, currentRoute: action.route };
75
+ return {
76
+ ...state,
77
+ currentRoute: action.route,
78
+ // Clear search state when navigating away
79
+ isSearching: false,
80
+ };
71
81
 
72
82
  // =========================================================================
73
83
  // Plugins Screen
@@ -304,41 +314,41 @@ export function appReducer(state: AppState, action: AppAction): AppState {
304
314
  };
305
315
 
306
316
  // =========================================================================
307
- // Env Vars Screen
317
+ // Settings Screen
308
318
  // =========================================================================
309
- case "ENVVARS_SELECT":
319
+ case "SETTINGS_SELECT":
310
320
  return {
311
321
  ...state,
312
- envVars: {
313
- ...state.envVars,
322
+ settings: {
323
+ ...state.settings,
314
324
  selectedIndex: action.index,
315
325
  },
316
326
  };
317
327
 
318
- case "ENVVARS_DATA_LOADING":
328
+ case "SETTINGS_DATA_LOADING":
319
329
  return {
320
330
  ...state,
321
- envVars: {
322
- ...state.envVars,
323
- variables: { status: "loading" },
331
+ settings: {
332
+ ...state.settings,
333
+ values: { status: "loading" },
324
334
  },
325
335
  };
326
336
 
327
- case "ENVVARS_DATA_SUCCESS":
337
+ case "SETTINGS_DATA_SUCCESS":
328
338
  return {
329
339
  ...state,
330
- envVars: {
331
- ...state.envVars,
332
- variables: { status: "success", data: action.variables },
340
+ settings: {
341
+ ...state.settings,
342
+ values: { status: "success", data: action.values },
333
343
  },
334
344
  };
335
345
 
336
- case "ENVVARS_DATA_ERROR":
346
+ case "SETTINGS_DATA_ERROR":
337
347
  return {
338
348
  ...state,
339
- envVars: {
340
- ...state.envVars,
341
- variables: { status: "error", error: action.error },
349
+ settings: {
350
+ ...state.settings,
351
+ values: { status: "error", error: action.error },
342
352
  },
343
353
  };
344
354
 
@@ -428,6 +438,45 @@ export function appReducer(state: AppState, action: AppAction): AppState {
428
438
  },
429
439
  };
430
440
 
441
+ // =========================================================================
442
+ // Profiles Screen
443
+ // =========================================================================
444
+ case "PROFILES_SELECT":
445
+ return {
446
+ ...state,
447
+ profiles: {
448
+ ...state.profiles,
449
+ selectedIndex: action.index,
450
+ },
451
+ };
452
+
453
+ case "PROFILES_DATA_LOADING":
454
+ return {
455
+ ...state,
456
+ profiles: {
457
+ ...state.profiles,
458
+ profiles: { status: "loading" },
459
+ },
460
+ };
461
+
462
+ case "PROFILES_DATA_SUCCESS":
463
+ return {
464
+ ...state,
465
+ profiles: {
466
+ ...state.profiles,
467
+ profiles: { status: "success", data: action.profiles },
468
+ },
469
+ };
470
+
471
+ case "PROFILES_DATA_ERROR":
472
+ return {
473
+ ...state,
474
+ profiles: {
475
+ ...state.profiles,
476
+ profiles: { status: "error", error: action.error },
477
+ },
478
+ };
479
+
431
480
  // =========================================================================
432
481
  // Modals
433
482
  // =========================================================================
@@ -2,6 +2,7 @@ import type {
2
2
  Marketplace,
3
3
  McpServer,
4
4
  StatusLineConfig,
5
+ ProfileEntry,
5
6
  } from "../../types/index.js";
6
7
  import type { PluginInfo } from "../../services/plugin-manager.js";
7
8
 
@@ -13,19 +14,19 @@ export type Screen =
13
14
  | "plugins"
14
15
  | "mcp"
15
16
  | "mcp-registry"
16
- | "statusline"
17
- | "env-vars"
17
+ | "settings"
18
18
  | "cli-tools"
19
- | "model-selector";
19
+ | "model-selector"
20
+ | "profiles";
20
21
 
21
22
  export type Route =
22
23
  | { screen: "plugins" }
23
24
  | { screen: "mcp" }
24
25
  | { screen: "mcp-registry"; query?: string }
25
- | { screen: "statusline" }
26
- | { screen: "env-vars" }
26
+ | { screen: "settings" }
27
27
  | { screen: "cli-tools" }
28
- | { screen: "model-selector" };
28
+ | { screen: "model-selector" }
29
+ | { screen: "profiles" };
29
30
 
30
31
  // ============================================================================
31
32
  // Async Data Types
@@ -68,6 +69,7 @@ export type ModalState =
68
69
  title: string;
69
70
  message: string;
70
71
  options: SelectOption[];
72
+ defaultIndex?: number;
71
73
  onSelect: (value: string) => void;
72
74
  onCancel: () => void;
73
75
  }
@@ -125,9 +127,14 @@ export interface StatusLineScreenState {
125
127
  currentPreset: string | null;
126
128
  }
127
129
 
128
- export interface EnvVarsScreenState {
130
+ export interface ScopedSettingValues {
131
+ user: string | undefined;
132
+ project: string | undefined;
133
+ }
134
+
135
+ export interface SettingsScreenState {
129
136
  selectedIndex: number;
130
- variables: AsyncData<Record<string, string>>;
137
+ values: AsyncData<Map<string, ScopedSettingValues>>;
131
138
  }
132
139
 
133
140
  export interface CliTool {
@@ -156,6 +163,11 @@ export interface ModelSelectorScreenState {
156
163
  taskSize: "large" | "small";
157
164
  }
158
165
 
166
+ export interface ProfilesScreenState {
167
+ selectedIndex: number;
168
+ profiles: AsyncData<ProfileEntry[]>;
169
+ }
170
+
159
171
  // ============================================================================
160
172
  // App State
161
173
  // ============================================================================
@@ -181,9 +193,10 @@ export interface AppState {
181
193
  mcp: McpScreenState;
182
194
  mcpRegistry: McpRegistryScreenState;
183
195
  statusline: StatusLineScreenState;
184
- envVars: EnvVarsScreenState;
196
+ settings: SettingsScreenState;
185
197
  cliTools: CliToolsScreenState;
186
198
  modelSelector: ModelSelectorScreenState;
199
+ profiles: ProfilesScreenState;
187
200
  }
188
201
 
189
202
  // ============================================================================
@@ -239,11 +252,11 @@ export type AppAction =
239
252
  }
240
253
  | { type: "STATUSLINE_DATA_ERROR"; error: Error }
241
254
 
242
- // Env vars screen
243
- | { type: "ENVVARS_SELECT"; index: number }
244
- | { type: "ENVVARS_DATA_LOADING" }
245
- | { type: "ENVVARS_DATA_SUCCESS"; variables: Record<string, string> }
246
- | { type: "ENVVARS_DATA_ERROR"; error: Error }
255
+ // Settings screen
256
+ | { type: "SETTINGS_SELECT"; index: number }
257
+ | { type: "SETTINGS_DATA_LOADING" }
258
+ | { type: "SETTINGS_DATA_SUCCESS"; values: Map<string, ScopedSettingValues> }
259
+ | { type: "SETTINGS_DATA_ERROR"; error: Error }
247
260
 
248
261
  // CLI tools screen
249
262
  | { type: "CLITOOLS_SELECT"; index: number }
@@ -269,5 +282,11 @@ export type AppAction =
269
282
  // Search state (blocks global key handlers)
270
283
  | { type: "SET_SEARCHING"; isSearching: boolean }
271
284
 
285
+ // Profiles screen
286
+ | { type: "PROFILES_SELECT"; index: number }
287
+ | { type: "PROFILES_DATA_LOADING" }
288
+ | { type: "PROFILES_DATA_SUCCESS"; profiles: ProfileEntry[] }
289
+ | { type: "PROFILES_DATA_ERROR"; error: Error }
290
+
272
291
  // Data refresh - triggers screens to refetch
273
292
  | { type: "DATA_REFRESH_COMPLETE" };
@@ -0,0 +1,56 @@
1
+ import { execSync } from "node:child_process";
2
+ /**
3
+ * Write text to the system clipboard.
4
+ * macOS: uses pbcopy
5
+ * Linux: uses xclip
6
+ * Throws ClipboardUnavailableError if no clipboard tool is available.
7
+ */
8
+ export async function writeClipboard(text) {
9
+ if (process.platform === "darwin") {
10
+ execSync("pbcopy", { input: text });
11
+ }
12
+ else if (process.platform === "linux") {
13
+ execSync("xclip -selection clipboard", { input: text });
14
+ }
15
+ else {
16
+ throw new ClipboardUnavailableError(`Clipboard not supported on platform: ${process.platform}`);
17
+ }
18
+ }
19
+ /**
20
+ * Read text from the system clipboard.
21
+ * macOS: uses pbpaste
22
+ * Linux: uses xclip
23
+ * Throws ClipboardUnavailableError if no clipboard tool is available.
24
+ */
25
+ export async function readClipboard() {
26
+ if (process.platform === "darwin") {
27
+ return execSync("pbpaste").toString();
28
+ }
29
+ else if (process.platform === "linux") {
30
+ return execSync("xclip -selection clipboard -o").toString();
31
+ }
32
+ else {
33
+ throw new ClipboardUnavailableError(`Clipboard not supported on platform: ${process.platform}`);
34
+ }
35
+ }
36
+ export class ClipboardUnavailableError extends Error {
37
+ constructor(message) {
38
+ super(message);
39
+ this.name = "ClipboardUnavailableError";
40
+ }
41
+ }
42
+ /** Check if clipboard operations are available on this platform/system */
43
+ export function isClipboardAvailable() {
44
+ if (process.platform === "darwin")
45
+ return true;
46
+ if (process.platform === "linux") {
47
+ try {
48
+ execSync("which xclip", { stdio: "ignore" });
49
+ return true;
50
+ }
51
+ catch {
52
+ return false;
53
+ }
54
+ }
55
+ return false;
56
+ }
@@ -0,0 +1,58 @@
1
+ import { execSync } from "node:child_process";
2
+
3
+ /**
4
+ * Write text to the system clipboard.
5
+ * macOS: uses pbcopy
6
+ * Linux: uses xclip
7
+ * Throws ClipboardUnavailableError if no clipboard tool is available.
8
+ */
9
+ export async function writeClipboard(text: string): Promise<void> {
10
+ if (process.platform === "darwin") {
11
+ execSync("pbcopy", { input: text });
12
+ } else if (process.platform === "linux") {
13
+ execSync("xclip -selection clipboard", { input: text });
14
+ } else {
15
+ throw new ClipboardUnavailableError(
16
+ `Clipboard not supported on platform: ${process.platform}`,
17
+ );
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Read text from the system clipboard.
23
+ * macOS: uses pbpaste
24
+ * Linux: uses xclip
25
+ * Throws ClipboardUnavailableError if no clipboard tool is available.
26
+ */
27
+ export async function readClipboard(): Promise<string> {
28
+ if (process.platform === "darwin") {
29
+ return execSync("pbpaste").toString();
30
+ } else if (process.platform === "linux") {
31
+ return execSync("xclip -selection clipboard -o").toString();
32
+ } else {
33
+ throw new ClipboardUnavailableError(
34
+ `Clipboard not supported on platform: ${process.platform}`,
35
+ );
36
+ }
37
+ }
38
+
39
+ export class ClipboardUnavailableError extends Error {
40
+ constructor(message: string) {
41
+ super(message);
42
+ this.name = "ClipboardUnavailableError";
43
+ }
44
+ }
45
+
46
+ /** Check if clipboard operations are available on this platform/system */
47
+ export function isClipboardAvailable(): boolean {
48
+ if (process.platform === "darwin") return true;
49
+ if (process.platform === "linux") {
50
+ try {
51
+ execSync("which xclip", { stdio: "ignore" });
52
+ return true;
53
+ } catch {
54
+ return false;
55
+ }
56
+ }
57
+ return false;
58
+ }