@a9s/cli 1.0.7 → 1.0.9

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 (41) hide show
  1. package/dist/scripts/seed.js +202 -4
  2. package/dist/src/App.js +35 -3
  3. package/dist/src/components/AdvancedTextInput.js +3 -1
  4. package/dist/src/components/AutocompleteInput.js +3 -1
  5. package/dist/src/components/DetailPanel.js +3 -1
  6. package/dist/src/components/DiffViewer.js +3 -1
  7. package/dist/src/components/ErrorStatePanel.js +3 -1
  8. package/dist/src/components/HUD.js +3 -1
  9. package/dist/src/components/HelpPanel.js +6 -4
  10. package/dist/src/components/ModeBar.js +5 -8
  11. package/dist/src/components/Table/index.js +19 -26
  12. package/dist/src/components/TableSkeleton.js +3 -1
  13. package/dist/src/components/YankHelpPanel.js +3 -1
  14. package/dist/src/constants/commands.js +2 -1
  15. package/dist/src/constants/theme.js +608 -0
  16. package/dist/src/contexts/ThemeContext.js +13 -0
  17. package/dist/src/features/AppMainView.integration.test.js +1 -0
  18. package/dist/src/features/AppMainView.js +6 -4
  19. package/dist/src/hooks/useCommandRouter.js +5 -0
  20. package/dist/src/hooks/usePickerManager.js +35 -1
  21. package/dist/src/index.js +2 -1
  22. package/dist/src/services.js +2 -2
  23. package/dist/src/state/atoms.js +3 -0
  24. package/dist/src/utils/config.js +36 -0
  25. package/dist/src/views/dynamodb/adapter.js +313 -9
  26. package/dist/src/views/dynamodb/capabilities/detailCapability.js +94 -0
  27. package/dist/src/views/dynamodb/capabilities/yankCapability.js +6 -0
  28. package/dist/src/views/dynamodb/capabilities/yankOptions.js +69 -0
  29. package/dist/src/views/dynamodb/schema.js +18 -0
  30. package/dist/src/views/dynamodb/types.js +1 -0
  31. package/dist/src/views/dynamodb/utils.js +175 -0
  32. package/dist/src/views/iam/adapter.js +2 -1
  33. package/dist/src/views/route53/adapter.js +166 -9
  34. package/dist/src/views/route53/capabilities/detailCapability.js +63 -0
  35. package/dist/src/views/route53/capabilities/yankCapability.js +6 -0
  36. package/dist/src/views/route53/capabilities/yankOptions.js +58 -0
  37. package/dist/src/views/route53/schema.js +18 -0
  38. package/dist/src/views/route53/types.js +1 -0
  39. package/dist/src/views/s3/adapter.js +2 -1
  40. package/dist/src/views/secretsmanager/adapter.js +2 -1
  41. package/package.json +2 -1
@@ -2,6 +2,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
2
2
  import React from "react";
3
3
  import { Box, Text } from "ink";
4
4
  import { computeColumnWidths } from "./Table/widths.js";
5
+ import { useTheme } from "../contexts/ThemeContext.js";
5
6
  function fill(len, ch = "░") {
6
7
  return ch.repeat(Math.max(1, len));
7
8
  }
@@ -11,6 +12,7 @@ function truncate(str, maxLen) {
11
12
  return str.slice(0, Math.max(1, maxLen - 1)) + "…";
12
13
  }
13
14
  export function TableSkeleton({ columns, terminalWidth, rows = 8, contextLabel, }) {
15
+ const THEME = useTheme();
14
16
  const FRAMES = ["░", "▒", "▓"];
15
17
  const [frame, setFrame] = React.useState(0);
16
18
  const colWidths = computeColumnWidths(columns, terminalWidth);
@@ -21,5 +23,5 @@ export function TableSkeleton({ columns, terminalWidth, rows = 8, contextLabel,
21
23
  return () => clearInterval(timer);
22
24
  }, []);
23
25
  const shade = FRAMES[frame] ?? "░";
24
- return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [contextLabel ? (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, color: "blue", children: contextLabel }), _jsx(Box, { height: 1 })] })) : null, _jsx(Box, { children: columns.map((col, i) => (_jsxs(React.Fragment, { children: [i > 0 ? _jsx(Text, { color: "gray", children: " \u2502 " }) : null, _jsx(Text, { bold: true, color: "blue", children: truncate(col.label, colWidths[i] ?? 1) })] }, col.key))) }), _jsx(Text, { color: "gray", children: columns.map((_, i) => fill(colWidths[i] ?? 1, "─")).join("─┼─") }), Array.from({ length: rows }).map((_, rowIdx) => (_jsx(Box, { children: columns.map((col, i) => (_jsxs(React.Fragment, { children: [i > 0 ? _jsx(Text, { color: "gray", children: " \u2502 " }) : null, _jsx(Text, { color: "gray", children: fill(Math.max(1, colWidths[i] ?? 1), shade) })] }, `${col.key}-${rowIdx}`))) }, `skeleton-row-${rowIdx}`)))] }));
26
+ return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [contextLabel ? (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, color: THEME.skeleton.skeletonContextLabelText, children: contextLabel }), _jsx(Box, { height: 1 })] })) : null, _jsx(Box, { children: columns.map((col, i) => (_jsxs(React.Fragment, { children: [i > 0 ? _jsx(Text, { color: THEME.skeleton.skeletonSeparatorText, children: " \u2502 " }) : null, _jsx(Text, { bold: true, color: THEME.skeleton.skeletonHeaderText, children: truncate(col.label, colWidths[i] ?? 1) })] }, col.key))) }), _jsx(Text, { color: THEME.skeleton.skeletonDividerText, children: columns.map((_, i) => fill(colWidths[i] ?? 1, "─")).join("─┼─") }), Array.from({ length: rows }).map((_, rowIdx) => (_jsx(Box, { children: columns.map((col, i) => (_jsxs(React.Fragment, { children: [i > 0 ? _jsx(Text, { color: THEME.skeleton.skeletonSeparatorText, children: " \u2502 " }) : null, _jsx(Text, { color: THEME.skeleton.skeletonCellText, children: fill(Math.max(1, colWidths[i] ?? 1), shade) })] }, `${col.key}-${rowIdx}`))) }, `skeleton-row-${rowIdx}`)))] }));
25
27
  }
@@ -3,7 +3,9 @@ import { useEffect, useState } from "react";
3
3
  import { Box, Text } from "ink";
4
4
  import { Alert, Badge, StatusMessage, UnorderedList } from "@inkjs/ui";
5
5
  import { triggerToString } from "../constants/keybindings.js";
6
+ import { useTheme } from "../contexts/ThemeContext.js";
6
7
  export function YankHelpPanel({ options, row }) {
8
+ const THEME = useTheme();
7
9
  const [resolvedValues, setResolvedValues] = useState({});
8
10
  useEffect(() => {
9
11
  let isActive = true;
@@ -37,7 +39,7 @@ export function YankHelpPanel({ options, row }) {
37
39
  isActive = false;
38
40
  };
39
41
  }, [options, row]);
40
- return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Alert, { variant: "info", title: "Yank Options", children: "Press key to copy, Esc or ? to close" }), _jsx(Box, { height: 1 }), !row && _jsx(StatusMessage, { variant: "warning", children: "No row selected" }), _jsx(UnorderedList, { children: options.map((option) => (_jsx(UnorderedList.Item, { children: _jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Badge, { color: "yellow", children: triggerToString(option.trigger) }), _jsxs(Text, { children: [" ", option.label] })] }), _jsx(Text, { color: "gray", children: row
42
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Alert, { variant: "info", title: "Yank Options", children: "Press key to copy, Esc or ? to close" }), _jsx(Box, { height: 1 }), !row && _jsx(StatusMessage, { variant: "warning", children: "No row selected" }), _jsx(UnorderedList, { children: options.map((option) => (_jsx(UnorderedList.Item, { children: _jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Badge, { color: THEME.panel.keyText, children: triggerToString(option.trigger) }), _jsxs(Text, { children: [" ", option.label] })] }), _jsx(Text, { color: THEME.panel.panelHintText, children: row
41
43
  ? ` -> ${resolvedValues[`${option.label}-${triggerToString(option.trigger)}`] ?? "(loading...)"}`
42
44
  : " -> (no value)" })] }) }, `${option.label}-${triggerToString(option.trigger)}`))) })] }));
43
45
  }
@@ -5,6 +5,7 @@ export const AVAILABLE_COMMANDS = [
5
5
  "regions",
6
6
  "profiles",
7
7
  "resources",
8
+ "theme",
8
9
  "region",
9
10
  "profile",
10
11
  "use-region",
@@ -12,4 +13,4 @@ export const AVAILABLE_COMMANDS = [
12
13
  "$default",
13
14
  "quit",
14
15
  ];
15
- export const COMMAND_MODE_HINT = " Commands: s3 route53 dynamodb iam quit regions profiles resources • Enter run • Esc cancel";
16
+ export const COMMAND_MODE_HINT = " Commands: s3 route53 dynamodb iam quit regions profiles resources theme • Enter run • Esc cancel";
@@ -0,0 +1,608 @@
1
+ /**
2
+ * Theme system: multiple named themes, switchable at runtime via :theme command.
3
+ *
4
+ * Monokai color strategy:
5
+ * - Magenta: Primary brand/structure — panel titles, account name
6
+ * - Blue: Active state — selected row bg, path bar bg, active tab bg
7
+ * - Cyan: Table & panel structure — column headers, mode icon, detail labels, profile
8
+ * - Yellow: Data & keys — account ID, identity line, keybinding keys
9
+ * - Green: Location & success — region, success feedback, confirm keys
10
+ * - Red: Errors & destructive
11
+ * - White: Body text
12
+ * - Gray: Subtle chrome — separators, dividers, inactive elements
13
+ *
14
+ * Catppuccin Mocha strategy (256-color hex palette):
15
+ * - #cba6f7 Mauve: panel titles, account name
16
+ * - #89b4fa Blue: selected row bg, path bar bg, active tab bg
17
+ * - #89dceb Sky: column headers, mode icon, detail labels, profile
18
+ * - #f9e2af Yellow: account ID, identity line, keybinding keys
19
+ * - #a6e3a1 Green: region, success feedback
20
+ * - #f38ba8 Red: errors
21
+ * - #cdd6f4 Text: body text
22
+ * - #45475a Surface1: separators, dividers
23
+ */
24
+ export const THEME_LABELS = {
25
+ monokai: "Monokai",
26
+ "catppuccin-mocha": "Catppuccin Mocha",
27
+ nord: "Nord",
28
+ "tokyo-night": "Tokyo Night",
29
+ "gruvbox-dark": "Gruvbox Dark",
30
+ dracula: "Dracula",
31
+ };
32
+ // ─── Monokai (named terminal colors) ──────────────────────────────────────────
33
+ const MONOKAI_THEME = {
34
+ global: {
35
+ mainBg: "black",
36
+ },
37
+ table: {
38
+ tableContainerBg: "black",
39
+ selectedRowBg: "blue",
40
+ selectedRowText: "white",
41
+ filterMatchText: "yellow",
42
+ filterMatchSelectedText: "white",
43
+ columnHeaderText: "cyan",
44
+ columnHeaderMarker: "yellow",
45
+ rowSeparatorText: "gray",
46
+ emptyStateText: "white",
47
+ scrollPositionText: "green",
48
+ },
49
+ hud: {
50
+ accountNameText: "magenta",
51
+ accountIdText: "yellow",
52
+ regionText: "green",
53
+ profileText: "cyan",
54
+ separatorText: "gray",
55
+ currentIdentityText: "yellow",
56
+ pathBarBg: "blue",
57
+ pathBarText: "white",
58
+ loadingSpinnerText: "cyan",
59
+ },
60
+ modebar: {
61
+ modeIconText: "cyan",
62
+ keybindingKeyText: "yellow",
63
+ keybindingDescText: "white",
64
+ keybindingSeparatorText: "gray",
65
+ },
66
+ panel: {
67
+ panelTitleText: "magenta",
68
+ panelDividerText: "gray",
69
+ panelHintText: "white",
70
+ panelScrollIndicatorText: "cyan",
71
+ detailFieldLabelText: "cyan",
72
+ defaultBorderText: "gray",
73
+ helpPanelBorderText: "cyan",
74
+ yankPanelBorderText: "cyan",
75
+ detailPanelBorderText: "gray",
76
+ activeTabBg: "blue",
77
+ activeTabText: "white",
78
+ inactiveTabText: "gray",
79
+ keyText: "yellow",
80
+ },
81
+ diff: {
82
+ originalHeaderText: "red",
83
+ updatedHeaderText: "green",
84
+ diffDividerText: "gray",
85
+ },
86
+ error: {
87
+ errorBorderText: "red",
88
+ errorTitleText: "red",
89
+ errorHintText: "white",
90
+ },
91
+ upload: {
92
+ uploadBorderText: "yellow",
93
+ uploadTitleText: "yellow",
94
+ uploadSubtitleText: "white",
95
+ uploadDiffDividerText: "gray",
96
+ uploadConfirmPromptText: "white",
97
+ uploadLoadingText: "cyan",
98
+ uploadConfirmKeyText: "green",
99
+ uploadCancelKeyText: "red",
100
+ },
101
+ feedback: {
102
+ successText: "green",
103
+ promptText: "cyan",
104
+ confirmText: "green",
105
+ },
106
+ input: {
107
+ placeholderText: "gray",
108
+ suggestionText: "cyan",
109
+ },
110
+ skeleton: {
111
+ skeletonContextLabelText: "cyan",
112
+ skeletonHeaderText: "yellow",
113
+ skeletonDividerText: "gray",
114
+ skeletonCellText: "gray",
115
+ skeletonSeparatorText: "gray",
116
+ },
117
+ serviceColors: {
118
+ s3: { bg: "red", fg: "white" },
119
+ iam: { bg: "blue", fg: "white" },
120
+ secretsmanager: { bg: "magenta", fg: "white" },
121
+ route53: { bg: "cyan", fg: "black" },
122
+ dynamodb: { bg: "green", fg: "black" },
123
+ },
124
+ };
125
+ // ─── Catppuccin Mocha (256-color hex palette) ─────────────────────────────────
126
+ // https://github.com/catppuccin/catppuccin#-palette
127
+ const CATPPUCCIN_MOCHA_THEME = {
128
+ global: {
129
+ mainBg: "#1e1e2e", // Base
130
+ },
131
+ table: {
132
+ tableContainerBg: "#1e1e2e", // Base
133
+ selectedRowBg: "#313244", // Surface0
134
+ selectedRowText: "#cdd6f4", // Text
135
+ filterMatchText: "#f9e2af", // Yellow
136
+ filterMatchSelectedText: "#cdd6f4",
137
+ columnHeaderText: "#89dceb", // Sky
138
+ columnHeaderMarker: "#f9e2af",
139
+ rowSeparatorText: "#313244", // Surface0
140
+ emptyStateText: "#cdd6f4",
141
+ scrollPositionText: "#a6e3a1", // Green
142
+ },
143
+ hud: {
144
+ accountNameText: "#cba6f7", // Mauve
145
+ accountIdText: "#f9e2af", // Yellow
146
+ regionText: "#a6e3a1", // Green
147
+ profileText: "#89dceb", // Sky
148
+ separatorText: "#45475a", // Surface1
149
+ currentIdentityText: "#f9e2af",
150
+ pathBarBg: "#89b4fa", // Blue
151
+ pathBarText: "#1e1e2e", // Base
152
+ loadingSpinnerText: "#89dceb",
153
+ },
154
+ modebar: {
155
+ modeIconText: "#89dceb", // Sky
156
+ keybindingKeyText: "#f9e2af", // Yellow
157
+ keybindingDescText: "#cdd6f4",
158
+ keybindingSeparatorText: "#45475a",
159
+ },
160
+ panel: {
161
+ panelTitleText: "#cba6f7", // Mauve
162
+ panelDividerText: "#45475a", // Surface1
163
+ panelHintText: "#cdd6f4",
164
+ panelScrollIndicatorText: "#89dceb",
165
+ detailFieldLabelText: "#89dceb",
166
+ defaultBorderText: "#45475a",
167
+ helpPanelBorderText: "#89dceb",
168
+ yankPanelBorderText: "#89dceb",
169
+ detailPanelBorderText: "#45475a",
170
+ activeTabBg: "#89b4fa", // Blue
171
+ activeTabText: "#1e1e2e", // Base
172
+ inactiveTabText: "#6c7086", // Overlay0
173
+ keyText: "#f9e2af",
174
+ },
175
+ diff: {
176
+ originalHeaderText: "#f38ba8", // Red
177
+ updatedHeaderText: "#a6e3a1", // Green
178
+ diffDividerText: "#45475a",
179
+ },
180
+ error: {
181
+ errorBorderText: "#f38ba8",
182
+ errorTitleText: "#f38ba8",
183
+ errorHintText: "#cdd6f4",
184
+ },
185
+ upload: {
186
+ uploadBorderText: "#f9e2af",
187
+ uploadTitleText: "#f9e2af",
188
+ uploadSubtitleText: "#cdd6f4",
189
+ uploadDiffDividerText: "#45475a",
190
+ uploadConfirmPromptText: "#cdd6f4",
191
+ uploadLoadingText: "#89dceb",
192
+ uploadConfirmKeyText: "#a6e3a1",
193
+ uploadCancelKeyText: "#f38ba8",
194
+ },
195
+ feedback: {
196
+ successText: "#a6e3a1",
197
+ promptText: "#89dceb",
198
+ confirmText: "#a6e3a1",
199
+ },
200
+ input: {
201
+ placeholderText: "#6c7086", // Overlay0
202
+ suggestionText: "#89dceb",
203
+ },
204
+ skeleton: {
205
+ skeletonContextLabelText: "#89dceb",
206
+ skeletonHeaderText: "#f9e2af",
207
+ skeletonDividerText: "#45475a",
208
+ skeletonCellText: "#45475a",
209
+ skeletonSeparatorText: "#45475a",
210
+ },
211
+ serviceColors: {
212
+ s3: { bg: "#f38ba8", fg: "#1e1e2e" }, // Red
213
+ iam: { bg: "#89b4fa", fg: "#1e1e2e" }, // Blue
214
+ secretsmanager: { bg: "#cba6f7", fg: "#1e1e2e" }, // Mauve
215
+ route53: { bg: "#89dceb", fg: "#1e1e2e" }, // Sky
216
+ dynamodb: { bg: "#a6e3a1", fg: "#1e1e2e" }, // Green
217
+ },
218
+ };
219
+ // ─── Nord ─────────────────────────────────────────────────────────────────────
220
+ // https://www.nordtheme.com/docs/colors-and-palettes
221
+ const NORD_THEME = {
222
+ global: {
223
+ mainBg: "#2e3440", // Polar Night 0
224
+ },
225
+ table: {
226
+ tableContainerBg: "#2e3440",
227
+ selectedRowBg: "#3b4252", // Polar Night 1
228
+ selectedRowText: "#eceff4", // Snow Storm 2
229
+ filterMatchText: "#ebcb8b", // Aurora Yellow
230
+ filterMatchSelectedText: "#eceff4",
231
+ columnHeaderText: "#88c0d0", // Frost Blue
232
+ columnHeaderMarker: "#ebcb8b",
233
+ rowSeparatorText: "#3b4252",
234
+ emptyStateText: "#d8dee9",
235
+ scrollPositionText: "#a3be8c", // Aurora Green
236
+ },
237
+ hud: {
238
+ accountNameText: "#b48ead", // Aurora Purple
239
+ accountIdText: "#ebcb8b",
240
+ regionText: "#a3be8c",
241
+ profileText: "#88c0d0",
242
+ separatorText: "#4c566a", // Polar Night 3
243
+ currentIdentityText: "#ebcb8b",
244
+ pathBarBg: "#5e81ac", // Frost Dark Blue
245
+ pathBarText: "#eceff4",
246
+ loadingSpinnerText: "#88c0d0",
247
+ },
248
+ modebar: {
249
+ modeIconText: "#88c0d0",
250
+ keybindingKeyText: "#ebcb8b",
251
+ keybindingDescText: "#d8dee9",
252
+ keybindingSeparatorText: "#4c566a",
253
+ },
254
+ panel: {
255
+ panelTitleText: "#b48ead",
256
+ panelDividerText: "#4c566a",
257
+ panelHintText: "#d8dee9",
258
+ panelScrollIndicatorText: "#88c0d0",
259
+ detailFieldLabelText: "#88c0d0",
260
+ defaultBorderText: "#4c566a",
261
+ helpPanelBorderText: "#88c0d0",
262
+ yankPanelBorderText: "#88c0d0",
263
+ detailPanelBorderText: "#4c566a",
264
+ activeTabBg: "#5e81ac",
265
+ activeTabText: "#eceff4",
266
+ inactiveTabText: "#4c566a",
267
+ keyText: "#ebcb8b",
268
+ },
269
+ diff: {
270
+ originalHeaderText: "#bf616a", // Aurora Red
271
+ updatedHeaderText: "#a3be8c",
272
+ diffDividerText: "#4c566a",
273
+ },
274
+ error: {
275
+ errorBorderText: "#bf616a",
276
+ errorTitleText: "#bf616a",
277
+ errorHintText: "#d8dee9",
278
+ },
279
+ upload: {
280
+ uploadBorderText: "#ebcb8b",
281
+ uploadTitleText: "#ebcb8b",
282
+ uploadSubtitleText: "#d8dee9",
283
+ uploadDiffDividerText: "#4c566a",
284
+ uploadConfirmPromptText: "#d8dee9",
285
+ uploadLoadingText: "#88c0d0",
286
+ uploadConfirmKeyText: "#a3be8c",
287
+ uploadCancelKeyText: "#bf616a",
288
+ },
289
+ feedback: {
290
+ successText: "#a3be8c",
291
+ promptText: "#88c0d0",
292
+ confirmText: "#a3be8c",
293
+ },
294
+ input: {
295
+ placeholderText: "#4c566a",
296
+ suggestionText: "#88c0d0",
297
+ },
298
+ skeleton: {
299
+ skeletonContextLabelText: "#88c0d0",
300
+ skeletonHeaderText: "#ebcb8b",
301
+ skeletonDividerText: "#4c566a",
302
+ skeletonCellText: "#4c566a",
303
+ skeletonSeparatorText: "#4c566a",
304
+ },
305
+ serviceColors: {
306
+ s3: { bg: "#bf616a", fg: "#eceff4" }, // Aurora Red
307
+ iam: { bg: "#5e81ac", fg: "#eceff4" }, // Frost Dark Blue
308
+ secretsmanager: { bg: "#b48ead", fg: "#eceff4" }, // Aurora Purple
309
+ route53: { bg: "#88c0d0", fg: "#2e3440" }, // Frost Blue
310
+ dynamodb: { bg: "#a3be8c", fg: "#2e3440" }, // Aurora Green
311
+ },
312
+ };
313
+ // ─── Tokyo Night ──────────────────────────────────────────────────────────────
314
+ // https://github.com/folke/tokyonight.nvim
315
+ const TOKYO_NIGHT_THEME = {
316
+ global: {
317
+ mainBg: "#1a1b26",
318
+ },
319
+ table: {
320
+ tableContainerBg: "#1a1b26",
321
+ selectedRowBg: "#292e42", // bg_highlight
322
+ selectedRowText: "#c0caf5", // fg
323
+ filterMatchText: "#e0af68", // yellow
324
+ filterMatchSelectedText: "#c0caf5",
325
+ columnHeaderText: "#7dcfff", // cyan
326
+ columnHeaderMarker: "#e0af68",
327
+ rowSeparatorText: "#292e42",
328
+ emptyStateText: "#c0caf5",
329
+ scrollPositionText: "#9ece6a", // green
330
+ },
331
+ hud: {
332
+ accountNameText: "#bb9af7", // magenta/purple
333
+ accountIdText: "#e0af68",
334
+ regionText: "#9ece6a",
335
+ profileText: "#7dcfff",
336
+ separatorText: "#414868", // terminal_black
337
+ currentIdentityText: "#e0af68",
338
+ pathBarBg: "#7aa2f7", // blue
339
+ pathBarText: "#1a1b26",
340
+ loadingSpinnerText: "#7dcfff",
341
+ },
342
+ modebar: {
343
+ modeIconText: "#7dcfff",
344
+ keybindingKeyText: "#e0af68",
345
+ keybindingDescText: "#c0caf5",
346
+ keybindingSeparatorText: "#414868",
347
+ },
348
+ panel: {
349
+ panelTitleText: "#bb9af7",
350
+ panelDividerText: "#414868",
351
+ panelHintText: "#c0caf5",
352
+ panelScrollIndicatorText: "#7dcfff",
353
+ detailFieldLabelText: "#7dcfff",
354
+ defaultBorderText: "#414868",
355
+ helpPanelBorderText: "#7dcfff",
356
+ yankPanelBorderText: "#7dcfff",
357
+ detailPanelBorderText: "#414868",
358
+ activeTabBg: "#7aa2f7",
359
+ activeTabText: "#1a1b26",
360
+ inactiveTabText: "#414868",
361
+ keyText: "#e0af68",
362
+ },
363
+ diff: {
364
+ originalHeaderText: "#f7768e", // red
365
+ updatedHeaderText: "#9ece6a",
366
+ diffDividerText: "#414868",
367
+ },
368
+ error: {
369
+ errorBorderText: "#f7768e",
370
+ errorTitleText: "#f7768e",
371
+ errorHintText: "#c0caf5",
372
+ },
373
+ upload: {
374
+ uploadBorderText: "#e0af68",
375
+ uploadTitleText: "#e0af68",
376
+ uploadSubtitleText: "#c0caf5",
377
+ uploadDiffDividerText: "#414868",
378
+ uploadConfirmPromptText: "#c0caf5",
379
+ uploadLoadingText: "#7dcfff",
380
+ uploadConfirmKeyText: "#9ece6a",
381
+ uploadCancelKeyText: "#f7768e",
382
+ },
383
+ feedback: {
384
+ successText: "#9ece6a",
385
+ promptText: "#7dcfff",
386
+ confirmText: "#9ece6a",
387
+ },
388
+ input: {
389
+ placeholderText: "#414868",
390
+ suggestionText: "#7dcfff",
391
+ },
392
+ skeleton: {
393
+ skeletonContextLabelText: "#7dcfff",
394
+ skeletonHeaderText: "#e0af68",
395
+ skeletonDividerText: "#414868",
396
+ skeletonCellText: "#414868",
397
+ skeletonSeparatorText: "#414868",
398
+ },
399
+ serviceColors: {
400
+ s3: { bg: "#f7768e", fg: "#1a1b26" }, // red
401
+ iam: { bg: "#7aa2f7", fg: "#1a1b26" }, // blue
402
+ secretsmanager: { bg: "#bb9af7", fg: "#1a1b26" }, // magenta
403
+ route53: { bg: "#7dcfff", fg: "#1a1b26" }, // cyan
404
+ dynamodb: { bg: "#9ece6a", fg: "#1a1b26" }, // green
405
+ },
406
+ };
407
+ // ─── Gruvbox Dark ─────────────────────────────────────────────────────────────
408
+ // https://github.com/morhetz/gruvbox
409
+ const GRUVBOX_DARK_THEME = {
410
+ global: {
411
+ mainBg: "#282828",
412
+ },
413
+ table: {
414
+ tableContainerBg: "#282828",
415
+ selectedRowBg: "#3c3836", // bg1
416
+ selectedRowText: "#ebdbb2", // fg
417
+ filterMatchText: "#fabd2f", // bright yellow
418
+ filterMatchSelectedText: "#ebdbb2",
419
+ columnHeaderText: "#83a598", // bright blue
420
+ columnHeaderMarker: "#fabd2f",
421
+ rowSeparatorText: "#3c3836",
422
+ emptyStateText: "#ebdbb2",
423
+ scrollPositionText: "#b8bb26", // bright green
424
+ },
425
+ hud: {
426
+ accountNameText: "#d3869b", // bright purple
427
+ accountIdText: "#fabd2f",
428
+ regionText: "#b8bb26",
429
+ profileText: "#83a598",
430
+ separatorText: "#665c54", // bg3
431
+ currentIdentityText: "#fabd2f",
432
+ pathBarBg: "#458588", // blue
433
+ pathBarText: "#ebdbb2",
434
+ loadingSpinnerText: "#83a598",
435
+ },
436
+ modebar: {
437
+ modeIconText: "#83a598",
438
+ keybindingKeyText: "#fabd2f",
439
+ keybindingDescText: "#ebdbb2",
440
+ keybindingSeparatorText: "#665c54",
441
+ },
442
+ panel: {
443
+ panelTitleText: "#d3869b",
444
+ panelDividerText: "#665c54",
445
+ panelHintText: "#ebdbb2",
446
+ panelScrollIndicatorText: "#83a598",
447
+ detailFieldLabelText: "#83a598",
448
+ defaultBorderText: "#665c54",
449
+ helpPanelBorderText: "#83a598",
450
+ yankPanelBorderText: "#83a598",
451
+ detailPanelBorderText: "#665c54",
452
+ activeTabBg: "#458588",
453
+ activeTabText: "#ebdbb2",
454
+ inactiveTabText: "#665c54",
455
+ keyText: "#fabd2f",
456
+ },
457
+ diff: {
458
+ originalHeaderText: "#fb4934", // bright red
459
+ updatedHeaderText: "#b8bb26",
460
+ diffDividerText: "#665c54",
461
+ },
462
+ error: {
463
+ errorBorderText: "#fb4934",
464
+ errorTitleText: "#fb4934",
465
+ errorHintText: "#ebdbb2",
466
+ },
467
+ upload: {
468
+ uploadBorderText: "#fabd2f",
469
+ uploadTitleText: "#fabd2f",
470
+ uploadSubtitleText: "#ebdbb2",
471
+ uploadDiffDividerText: "#665c54",
472
+ uploadConfirmPromptText: "#ebdbb2",
473
+ uploadLoadingText: "#83a598",
474
+ uploadConfirmKeyText: "#b8bb26",
475
+ uploadCancelKeyText: "#fb4934",
476
+ },
477
+ feedback: {
478
+ successText: "#b8bb26",
479
+ promptText: "#83a598",
480
+ confirmText: "#b8bb26",
481
+ },
482
+ input: {
483
+ placeholderText: "#665c54",
484
+ suggestionText: "#83a598",
485
+ },
486
+ skeleton: {
487
+ skeletonContextLabelText: "#83a598",
488
+ skeletonHeaderText: "#fabd2f",
489
+ skeletonDividerText: "#665c54",
490
+ skeletonCellText: "#665c54",
491
+ skeletonSeparatorText: "#665c54",
492
+ },
493
+ serviceColors: {
494
+ s3: { bg: "#fb4934", fg: "#282828" }, // bright red
495
+ iam: { bg: "#83a598", fg: "#282828" }, // bright blue
496
+ secretsmanager: { bg: "#d3869b", fg: "#282828" }, // bright purple
497
+ route53: { bg: "#8ec07c", fg: "#282828" }, // bright aqua
498
+ dynamodb: { bg: "#b8bb26", fg: "#282828" }, // bright green
499
+ },
500
+ };
501
+ // ─── Dracula ──────────────────────────────────────────────────────────────────
502
+ // https://draculatheme.com/contribute
503
+ const DRACULA_THEME = {
504
+ global: {
505
+ mainBg: "#282a36",
506
+ },
507
+ table: {
508
+ tableContainerBg: "#282a36",
509
+ selectedRowBg: "#44475a", // Current Line
510
+ selectedRowText: "#f8f8f2", // Foreground
511
+ filterMatchText: "#f1fa8c", // Yellow
512
+ filterMatchSelectedText: "#f8f8f2",
513
+ columnHeaderText: "#8be9fd", // Cyan
514
+ columnHeaderMarker: "#f1fa8c",
515
+ rowSeparatorText: "#44475a",
516
+ emptyStateText: "#f8f8f2",
517
+ scrollPositionText: "#50fa7b", // Green
518
+ },
519
+ hud: {
520
+ accountNameText: "#ff79c6", // Pink
521
+ accountIdText: "#f1fa8c",
522
+ regionText: "#50fa7b",
523
+ profileText: "#8be9fd",
524
+ separatorText: "#6272a4", // Comment
525
+ currentIdentityText: "#f1fa8c",
526
+ pathBarBg: "#bd93f9", // Purple
527
+ pathBarText: "#282a36",
528
+ loadingSpinnerText: "#8be9fd",
529
+ },
530
+ modebar: {
531
+ modeIconText: "#8be9fd",
532
+ keybindingKeyText: "#f1fa8c",
533
+ keybindingDescText: "#f8f8f2",
534
+ keybindingSeparatorText: "#6272a4",
535
+ },
536
+ panel: {
537
+ panelTitleText: "#ff79c6",
538
+ panelDividerText: "#6272a4",
539
+ panelHintText: "#f8f8f2",
540
+ panelScrollIndicatorText: "#8be9fd",
541
+ detailFieldLabelText: "#8be9fd",
542
+ defaultBorderText: "#6272a4",
543
+ helpPanelBorderText: "#8be9fd",
544
+ yankPanelBorderText: "#8be9fd",
545
+ detailPanelBorderText: "#6272a4",
546
+ activeTabBg: "#bd93f9",
547
+ activeTabText: "#282a36",
548
+ inactiveTabText: "#6272a4",
549
+ keyText: "#f1fa8c",
550
+ },
551
+ diff: {
552
+ originalHeaderText: "#ff5555", // Red
553
+ updatedHeaderText: "#50fa7b",
554
+ diffDividerText: "#6272a4",
555
+ },
556
+ error: {
557
+ errorBorderText: "#ff5555",
558
+ errorTitleText: "#ff5555",
559
+ errorHintText: "#f8f8f2",
560
+ },
561
+ upload: {
562
+ uploadBorderText: "#ffb86c", // Orange
563
+ uploadTitleText: "#ffb86c",
564
+ uploadSubtitleText: "#f8f8f2",
565
+ uploadDiffDividerText: "#6272a4",
566
+ uploadConfirmPromptText: "#f8f8f2",
567
+ uploadLoadingText: "#8be9fd",
568
+ uploadConfirmKeyText: "#50fa7b",
569
+ uploadCancelKeyText: "#ff5555",
570
+ },
571
+ feedback: {
572
+ successText: "#50fa7b",
573
+ promptText: "#8be9fd",
574
+ confirmText: "#50fa7b",
575
+ },
576
+ input: {
577
+ placeholderText: "#6272a4",
578
+ suggestionText: "#8be9fd",
579
+ },
580
+ skeleton: {
581
+ skeletonContextLabelText: "#8be9fd",
582
+ skeletonHeaderText: "#f1fa8c",
583
+ skeletonDividerText: "#6272a4",
584
+ skeletonCellText: "#6272a4",
585
+ skeletonSeparatorText: "#6272a4",
586
+ },
587
+ serviceColors: {
588
+ s3: { bg: "#ff5555", fg: "#282a36" }, // Red
589
+ iam: { bg: "#bd93f9", fg: "#282a36" }, // Purple
590
+ secretsmanager: { bg: "#ff79c6", fg: "#282a36" }, // Pink
591
+ route53: { bg: "#8be9fd", fg: "#282a36" }, // Cyan
592
+ dynamodb: { bg: "#50fa7b", fg: "#282a36" }, // Green
593
+ },
594
+ };
595
+ // ─── Registry ─────────────────────────────────────────────────────────────────
596
+ export const THEMES = {
597
+ monokai: MONOKAI_THEME,
598
+ "catppuccin-mocha": CATPPUCCIN_MOCHA_THEME,
599
+ nord: NORD_THEME,
600
+ "tokyo-night": TOKYO_NIGHT_THEME,
601
+ "gruvbox-dark": GRUVBOX_DARK_THEME,
602
+ dracula: DRACULA_THEME,
603
+ };
604
+ /**
605
+ * Backward-compat export for service adapters that statically import hudColor.
606
+ * Components should derive hudColor from useTheme().serviceColors instead.
607
+ */
608
+ export const SERVICE_COLORS = MONOKAI_THEME.serviceColors;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext } from "react";
3
+ import { useAtomValue } from "jotai";
4
+ import { THEMES } from "../constants/theme.js";
5
+ import { themeNameAtom } from "../state/atoms.js";
6
+ const ThemeContext = createContext(THEMES.monokai);
7
+ export function useTheme() {
8
+ return useContext(ThemeContext);
9
+ }
10
+ export function ThemeProvider({ children }) {
11
+ const themeName = useAtomValue(themeNameAtom);
12
+ return _jsx(ThemeContext.Provider, { value: THEMES[themeName], children: children });
13
+ }