@a9s/cli 1.0.8 → 1.0.10

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/dist/src/App.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useCallback, useEffect, useMemo, useState } from "react";
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
3
  import { Box, Text, useApp } from "ink";
4
4
  import { useAtom } from "jotai";
5
5
  import clipboardy from "clipboardy";
@@ -24,7 +24,9 @@ import { deriveYankHeaderMarkers } from "./hooks/yankHeaderMarkers.js";
24
24
  import { AppMainView } from "./features/AppMainView.js";
25
25
  import { AVAILABLE_COMMANDS } from "./constants/commands.js";
26
26
  import { buildHelpTabs, triggerToString } from "./constants/keybindings.js";
27
- import { currentlySelectedServiceAtom, selectedRegionAtom, selectedProfileAtom, revealSecretsAtom, } from "./state/atoms.js";
27
+ import { useTheme } from "./contexts/ThemeContext.js";
28
+ import { saveConfig } from "./utils/config.js";
29
+ import { currentlySelectedServiceAtom, selectedRegionAtom, selectedProfileAtom, revealSecretsAtom, themeNameAtom, } from "./state/atoms.js";
28
30
  const INITIAL_AWS_PROFILE = process.env.AWS_PROFILE;
29
31
  export function App({ initialService, endpointUrl }) {
30
32
  const { exit } = useApp();
@@ -33,6 +35,13 @@ export function App({ initialService, endpointUrl }) {
33
35
  const [selectedProfile, setSelectedProfile] = useAtom(selectedProfileAtom);
34
36
  const [currentService, setCurrentService] = useAtom(currentlySelectedServiceAtom);
35
37
  const [revealSecrets, setRevealSecrets] = useAtom(revealSecretsAtom);
38
+ const [themeName, setThemeName] = useAtom(themeNameAtom);
39
+ const THEME = useTheme();
40
+ // Live theme preview: refs to restore original when picker is cancelled
41
+ const themeNameRef = useRef(themeName);
42
+ themeNameRef.current = themeName; // always in sync, not a dep
43
+ const originalThemeRef = useRef(themeName);
44
+ const themePickerConfirmedRef = useRef(false);
36
45
  const { accountName, accountId, awsProfile, currentIdentity, region } = useAwsContext(endpointUrl, selectedRegion, selectedProfile);
37
46
  const availableRegions = useAwsRegions(selectedRegion, selectedProfile);
38
47
  const availableProfiles = useAwsProfiles();
@@ -73,6 +82,23 @@ export function App({ initialService, endpointUrl }) {
73
82
  pickers.openPicker("resource");
74
83
  setDidOpenInitialResources(true);
75
84
  }, [didOpenInitialResources, pickers]);
85
+ // Save original theme when theme picker opens; restore it if picker is cancelled
86
+ // eslint-disable-next-line react-hooks/exhaustive-deps
87
+ useEffect(() => {
88
+ if (pickers.theme.open) {
89
+ originalThemeRef.current = themeNameRef.current;
90
+ themePickerConfirmedRef.current = false;
91
+ }
92
+ else if (!themePickerConfirmedRef.current) {
93
+ setThemeName(originalThemeRef.current);
94
+ }
95
+ }, [pickers.theme.open]);
96
+ // Live preview: apply hovered theme immediately as selection changes
97
+ useEffect(() => {
98
+ if (!pickers.theme.open || !pickers.theme.selectedRow)
99
+ return;
100
+ setThemeName(pickers.theme.selectedRow.id);
101
+ }, [pickers.theme.open, pickers.theme.selectedRow, setThemeName]);
76
102
  const switchAdapter = useCallback((serviceId) => {
77
103
  setCurrentService(serviceId);
78
104
  actions.setFilterText("");
@@ -207,6 +233,7 @@ export function App({ initialService, endpointUrl }) {
207
233
  openProfilePicker: () => pickers.openPicker("profile"),
208
234
  openRegionPicker: () => pickers.openPicker("region"),
209
235
  openResourcePicker: () => pickers.openPicker("resource"),
236
+ openThemePicker: () => pickers.openPicker("theme"),
210
237
  exit,
211
238
  });
212
239
  const handleFilterChange = useCallback((value) => {
@@ -373,6 +400,11 @@ export function App({ initialService, endpointUrl }) {
373
400
  onSelectResource: switchAdapter,
374
401
  onSelectRegion: setSelectedRegion,
375
402
  onSelectProfile: setSelectedProfile,
403
+ onSelectTheme: (name) => {
404
+ themePickerConfirmedRef.current = true;
405
+ setThemeName(name);
406
+ saveConfig({ theme: name });
407
+ },
376
408
  }),
377
409
  },
378
410
  mode: {
@@ -472,5 +504,5 @@ export function App({ initialService, endpointUrl }) {
472
504
  });
473
505
  useMainInput(inputDispatch);
474
506
  const activePickerFilter = pickers.activePicker?.filter ?? state.filterText;
475
- return (_jsx(FullscreenBox, { children: _jsxs(Box, { flexDirection: "column", width: termCols, height: termRows, children: [_jsx(HUD, { serviceLabel: adapter.label, hudColor: adapter.hudColor, path: path, accountName: accountName, accountId: accountId, awsProfile: awsProfile, currentIdentity: currentIdentity, region: region, terminalWidth: termCols, loading: isLoading || Boolean(state.describeState?.loading) }), _jsx(Box, { flexDirection: "row", width: "100%", flexGrow: 1, children: _jsx(AppMainView, { helpPanel: helpPanel, helpTabs: helpTabs, pickers: pickers, error: error, describeState: state.describeState, isLoading: isLoading, filteredRows: filteredRows, columns: columns, selectedIndex: navigation.selectedIndex, scrollOffset: navigation.scrollOffset, filterText: state.filterText, adapter: adapter, termCols: termCols, tableHeight: tableHeight, yankHelpOpen: state.yankHelpOpen, yankOptions: yankOptions, yankHelpRow: selectedRow, uploadPending: state.uploadPending, uploadPreview: uploadPreview, panelScrollOffset: panelScrollOffset, ...(yankHeaderMarkers ? { headerMarkers: yankHeaderMarkers } : {}) }) }), !helpPanel.helpOpen && state.yankFeedbackMessage && (_jsx(Box, { paddingX: 1, children: _jsx(Text, { color: "green", children: state.yankFeedbackMessage }) })), state.pendingAction && state.pendingAction.effect.type === "prompt" && (_jsxs(Box, { paddingX: 1, children: [_jsxs(Text, { color: "cyan", children: [state.pendingAction.effect.label, " "] }), _jsx(AdvancedTextInput, { value: state.pendingAction.inputValue, onChange: (value) => actions.setPendingInputValue(value), onSubmit: () => submitPendingAction(state.pendingAction, true), focus: true })] })), state.pendingAction && state.pendingAction.effect.type === "confirm" && (_jsx(Box, { paddingX: 1, children: _jsxs(Text, { color: "yellow", children: [state.pendingAction.effect.message, " (y/n)"] }) })), _jsx(ModeBar, { mode: state.mode, filterText: activePickerFilter, commandText: state.commandText, commandCursorToEndToken: state.commandCursorToEndToken, hintOverride: bottomHint, pickerSearchActive: pickers.activePicker?.pickerMode === "search", onFilterChange: handleFilterChange, onCommandChange: actions.setCommandText, onFilterSubmit: handleFilterSubmit, onCommandSubmit: handleCommandSubmit })] }) }));
507
+ return (_jsx(FullscreenBox, { children: _jsxs(Box, { flexDirection: "column", width: termCols, height: termRows, backgroundColor: THEME.global.mainBg, children: [_jsx(HUD, { serviceLabel: adapter.label, hudColor: THEME.serviceColors[adapter.id] ?? adapter.hudColor, path: path, accountName: accountName, accountId: accountId, awsProfile: awsProfile, currentIdentity: currentIdentity, region: region, terminalWidth: termCols, loading: isLoading || Boolean(state.describeState?.loading) }), _jsx(Box, { flexDirection: "row", width: "100%", flexGrow: 1, children: _jsx(AppMainView, { helpPanel: helpPanel, helpTabs: helpTabs, pickers: pickers, error: error, describeState: state.describeState, isLoading: isLoading, filteredRows: filteredRows, columns: columns, selectedIndex: navigation.selectedIndex, scrollOffset: navigation.scrollOffset, filterText: state.filterText, adapter: adapter, termCols: termCols, tableHeight: tableHeight, yankHelpOpen: state.yankHelpOpen, yankOptions: yankOptions, yankHelpRow: selectedRow, uploadPending: state.uploadPending, uploadPreview: uploadPreview, panelScrollOffset: panelScrollOffset, ...(yankHeaderMarkers ? { headerMarkers: yankHeaderMarkers } : {}) }) }), !helpPanel.helpOpen && state.yankFeedbackMessage && (_jsx(Box, { paddingX: 1, children: _jsx(Text, { color: THEME.feedback.successText, children: state.yankFeedbackMessage }) })), state.pendingAction && state.pendingAction.effect.type === "prompt" && (_jsxs(Box, { paddingX: 1, children: [_jsxs(Text, { color: THEME.feedback.promptText, children: [state.pendingAction.effect.label, " "] }), _jsx(AdvancedTextInput, { value: state.pendingAction.inputValue, onChange: (value) => actions.setPendingInputValue(value), onSubmit: () => submitPendingAction(state.pendingAction, true), focus: true })] })), state.pendingAction && state.pendingAction.effect.type === "confirm" && (_jsx(Box, { paddingX: 1, children: _jsxs(Text, { color: THEME.feedback.confirmText, children: [state.pendingAction.effect.message, " (y/n)"] }) })), _jsx(ModeBar, { mode: state.mode, filterText: activePickerFilter, commandText: state.commandText, commandCursorToEndToken: state.commandCursorToEndToken, hintOverride: bottomHint, pickerSearchActive: pickers.activePicker?.pickerMode === "search", onFilterChange: handleFilterChange, onCommandChange: actions.setCommandText, onFilterSubmit: handleFilterSubmit, onCommandSubmit: handleCommandSubmit })] }) }));
476
508
  }
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useMemo, useState } from "react";
3
3
  import { Text, useInput } from "ink";
4
+ import { useTheme } from "../contexts/ThemeContext.js";
4
5
  function clamp(n, min, max) {
5
6
  return Math.max(min, Math.min(max, n));
6
7
  }
@@ -153,6 +154,7 @@ export function applyAdvancedInputEdit(currentValue, currentCursor, input, key)
153
154
  };
154
155
  }
155
156
  export function AdvancedTextInput({ value, onChange, onSubmit, placeholder, focus = true, cursorToEndToken, }) {
157
+ const THEME = useTheme();
156
158
  const [cursor, setCursor] = useState(value.length);
157
159
  useEffect(() => {
158
160
  setCursor((prev) => clamp(prev, 0, value.length));
@@ -194,7 +196,7 @@ export function AdvancedTextInput({ value, onChange, onSubmit, placeholder, focu
194
196
  };
195
197
  }, [cursor, placeholder, value]);
196
198
  if (rendered.isPlaceholder) {
197
- return _jsx(Text, { color: "gray", children: rendered.text });
199
+ return _jsx(Text, { color: THEME.input.placeholderText, children: rendered.text });
198
200
  }
199
201
  return (_jsxs(Text, { children: [rendered.before, _jsx(Text, { inverse: true, children: rendered.at }), rendered.after] }));
200
202
  }
@@ -2,7 +2,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React, { useEffect, useImperativeHandle, useMemo, useState } from "react";
3
3
  import { Box, Text } from "ink";
4
4
  import { AdvancedTextInput } from "./AdvancedTextInput.js";
5
+ import { useTheme } from "../contexts/ThemeContext.js";
5
6
  export const AutocompleteInput = React.forwardRef(({ value, onChange, onSubmit, placeholder, suggestions = [], focus = true, cursorToEndToken }, ref) => {
7
+ const THEME = useTheme();
6
8
  const [inputKey, setInputKey] = useState(0);
7
9
  const matchingSuggestions = useMemo(() => {
8
10
  if (!value || suggestions.length === 0)
@@ -25,5 +27,5 @@ export const AutocompleteInput = React.forwardRef(({ value, onChange, onSubmit,
25
27
  return;
26
28
  setInputKey((k) => k + 1);
27
29
  }, [cursorToEndToken]);
28
- return (_jsxs(Box, { children: [_jsx(AdvancedTextInput, { value: value, onChange: onChange, onSubmit: onSubmit, placeholder: placeholder, focus: focus, ...(cursorToEndToken !== undefined ? { cursorToEndToken } : {}) }, `autocomplete-input-${inputKey}`), suggestion && (_jsx(Text, { color: "gray", dimColor: true, children: suggestion }))] }));
30
+ return (_jsxs(Box, { children: [_jsx(AdvancedTextInput, { value: value, onChange: onChange, onSubmit: onSubmit, placeholder: placeholder, focus: focus, ...(cursorToEndToken !== undefined ? { cursorToEndToken } : {}) }, `autocomplete-input-${inputKey}`), suggestion && (_jsx(Text, { color: THEME.input.suggestionText, dimColor: true, children: suggestion }))] }));
29
31
  });
@@ -1,6 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Box, Text } from "ink";
3
+ import { useTheme } from "../contexts/ThemeContext.js";
3
4
  export function DetailPanel({ title, fields, isLoading, scrollOffset, visibleLines, }) {
5
+ const THEME = useTheme();
4
6
  const labelWidth = Math.max(...fields.map((f) => f.label.length), 12);
5
7
  // Clamp scrollOffset to valid range
6
8
  const clampedOffset = Math.max(0, Math.min(scrollOffset, Math.max(0, fields.length - visibleLines)));
@@ -8,5 +10,5 @@ export function DetailPanel({ title, fields, isLoading, scrollOffset, visibleLin
8
10
  const visibleFields = fields.slice(clampedOffset, clampedOffset + visibleLines);
9
11
  const hasMoreAbove = clampedOffset > 0;
10
12
  const hasMoreBelow = clampedOffset + visibleLines < fields.length;
11
- return (_jsxs(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [_jsx(Text, { bold: true, color: "blue", children: title }), _jsx(Text, { color: "gray", children: "─".repeat(40) }), isLoading ? (_jsx(Text, { color: "gray", children: "Loading..." })) : (_jsxs(_Fragment, { children: [hasMoreAbove && (_jsxs(Text, { color: "gray", dimColor: true, children: ["\u2191 ", clampedOffset, " more above"] })), visibleFields.map((f) => (_jsxs(Box, { children: [_jsx(Text, { color: "gray", children: f.label.padEnd(labelWidth + 2) }), _jsx(Text, { children: f.value })] }, f.label))), hasMoreBelow && (_jsxs(Text, { color: "gray", dimColor: true, children: ["\u2193 ", fields.length - clampedOffset - visibleLines, " more below"] }))] })), _jsx(Text, { color: "gray", children: "─".repeat(40) }), _jsx(Text, { color: "gray", children: "j/k scroll \u2022 Esc close" })] }));
13
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [_jsx(Text, { bold: true, color: THEME.panel.panelTitleText, children: title }), _jsx(Text, { color: THEME.panel.panelDividerText, children: "─".repeat(40) }), isLoading ? (_jsx(Text, { color: THEME.panel.panelHintText, children: "Loading..." })) : (_jsxs(_Fragment, { children: [hasMoreAbove && (_jsxs(Text, { color: THEME.panel.panelHintText, dimColor: true, children: ["\u2191 ", clampedOffset, " more above"] })), visibleFields.map((f) => (_jsxs(Box, { children: [_jsx(Text, { color: THEME.panel.detailFieldLabelText, children: f.label.padEnd(labelWidth + 2) }), _jsx(Text, { children: f.value })] }, f.label))), hasMoreBelow && (_jsxs(Text, { color: THEME.panel.panelHintText, dimColor: true, children: ["\u2193 ", fields.length - clampedOffset - visibleLines, " more below"] }))] })), _jsx(Text, { color: THEME.panel.panelDividerText, children: "─".repeat(40) }), _jsx(Text, { color: THEME.panel.panelHintText, children: "j/k scroll \u2022 Esc close" })] }));
12
14
  }
@@ -1,6 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from "ink";
3
+ import { useTheme } from "../contexts/ThemeContext.js";
3
4
  export function DiffViewer({ oldValue, newValue, scrollOffset, visibleLines }) {
5
+ const THEME = useTheme();
4
6
  const oldLines = oldValue.split("\n");
5
7
  const newLines = newValue.split("\n");
6
8
  const maxLines = Math.max(oldLines.length, newLines.length);
@@ -13,5 +15,5 @@ export function DiffViewer({ oldValue, newValue, scrollOffset, visibleLines }) {
13
15
  const hasMoreBelow = clampedOffset + visibleLines < maxLines;
14
16
  // Calculate column width
15
17
  const colWidth = 35;
16
- return (_jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsxs(Box, { gap: 2, children: [_jsx(Box, { width: colWidth, children: _jsx(Text, { color: "red", bold: true, children: "Original" }) }), _jsx(Box, { children: _jsx(Text, { color: "green", bold: true, children: "Updated" }) })] }), _jsxs(Box, { gap: 2, children: [_jsx(Box, { width: colWidth, children: _jsx(Text, { color: "gray", children: "-".repeat(30) }) }), _jsx(Text, { color: "gray", children: "-".repeat(30) })] }), _jsxs(Box, { gap: 2, flexDirection: "column", children: [hasMoreAbove && (_jsxs(Box, { gap: 2, children: [_jsx(Box, { width: colWidth, children: _jsxs(Text, { color: "gray", dimColor: true, children: ["\u2191 ", clampedOffset, " lines above"] }) }), _jsxs(Text, { color: "gray", dimColor: true, children: ["\u2191 ", clampedOffset, " lines above"] })] })), _jsxs(Box, { gap: 2, children: [_jsx(Box, { width: colWidth, children: _jsx(Text, { children: oldDisplay }) }), _jsx(Box, { children: _jsx(Text, { children: newDisplay }) })] }), hasMoreBelow && (_jsxs(Box, { gap: 2, children: [_jsx(Box, { width: colWidth, children: _jsxs(Text, { color: "gray", dimColor: true, children: ["\u2193 ", maxLines - clampedOffset - visibleLines, " more lines"] }) }), _jsxs(Text, { color: "gray", dimColor: true, children: ["\u2193 ", maxLines - clampedOffset - visibleLines, " more lines"] })] }))] })] }));
18
+ return (_jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsxs(Box, { gap: 2, children: [_jsx(Box, { width: colWidth, children: _jsx(Text, { color: THEME.diff.originalHeaderText, bold: true, children: "Original" }) }), _jsx(Box, { children: _jsx(Text, { color: THEME.diff.updatedHeaderText, bold: true, children: "Updated" }) })] }), _jsxs(Box, { gap: 2, children: [_jsx(Box, { width: colWidth, children: _jsx(Text, { color: THEME.diff.diffDividerText, children: "-".repeat(30) }) }), _jsx(Text, { color: THEME.diff.diffDividerText, children: "-".repeat(30) })] }), _jsxs(Box, { gap: 2, flexDirection: "column", children: [hasMoreAbove && (_jsxs(Box, { gap: 2, children: [_jsx(Box, { width: colWidth, children: _jsxs(Text, { color: THEME.diff.diffDividerText, dimColor: true, children: ["\u2191 ", clampedOffset, " lines above"] }) }), _jsxs(Text, { color: THEME.diff.diffDividerText, dimColor: true, children: ["\u2191 ", clampedOffset, " lines above"] })] })), _jsxs(Box, { gap: 2, children: [_jsx(Box, { width: colWidth, children: _jsx(Text, { children: oldDisplay }) }), _jsx(Box, { children: _jsx(Text, { children: newDisplay }) })] }), hasMoreBelow && (_jsxs(Box, { gap: 2, children: [_jsx(Box, { width: colWidth, children: _jsxs(Text, { color: THEME.diff.diffDividerText, dimColor: true, children: ["\u2193 ", maxLines - clampedOffset - visibleLines, " more lines"] }) }), _jsxs(Text, { color: THEME.diff.diffDividerText, dimColor: true, children: ["\u2193 ", maxLines - clampedOffset - visibleLines, " more lines"] })] }))] })] }));
17
19
  }
@@ -1,5 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from "ink";
3
+ import { useTheme } from "../contexts/ThemeContext.js";
3
4
  export function ErrorStatePanel({ title, message, hint }) {
4
- return (_jsx(Box, { width: "100%", borderStyle: "round", borderColor: "red", children: _jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Text, { bold: true, color: "red", children: title }), _jsx(Text, { children: message }), hint ? _jsx(Text, { color: "gray", children: hint }) : null] }) }));
5
+ const THEME = useTheme();
6
+ return (_jsx(Box, { width: "100%", borderStyle: "round", borderColor: THEME.error.errorBorderText, children: _jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Text, { bold: true, color: THEME.error.errorTitleText, children: title }), _jsx(Text, { children: message }), hint ? _jsx(Text, { color: THEME.error.errorHintText, children: hint }) : null] }) }));
5
7
  }
@@ -1,7 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from "react";
3
3
  import { Box, Text } from "ink";
4
+ import { useTheme } from "../contexts/ThemeContext.js";
4
5
  export function HUD({ serviceLabel, hudColor, path, accountName, accountId, awsProfile, currentIdentity, region, terminalWidth, loading = false, }) {
6
+ const THEME = useTheme();
5
7
  const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
6
8
  const [spinnerIndex, setSpinnerIndex] = React.useState(0);
7
9
  React.useEffect(() => {
@@ -27,5 +29,5 @@ export function HUD({ serviceLabel, hudColor, path, accountName, accountId, awsP
27
29
  const label = ` ${serviceLabel.toUpperCase()} `;
28
30
  const pathDisplay = ` ${path} `;
29
31
  const padLen = Math.max(0, terminalWidth - label.length - pathDisplay.length);
30
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: "blue", bold: true, children: compactName }), _jsx(Text, { color: "yellow", bold: true, children: idPart }), _jsx(Text, { color: "gray", bold: true, children: "\u00B7" }), _jsx(Text, { color: "green", bold: true, children: region }), _jsx(Text, { color: "gray", bold: true, children: "\u00B7" }), _jsx(Text, { color: "magenta", bold: true, children: profilePart }), _jsx(Text, { children: " ".repeat(topPadLen) }), loading ? (_jsx(Text, { color: "cyan", bold: true, children: SPINNER_FRAMES[spinnerIndex] })) : null] }), _jsxs(Text, { color: "cyan", wrap: "truncate-end", children: [identityLine, " ".repeat(identityPadLen)] }), _jsxs(Box, { children: [_jsx(Text, { backgroundColor: hudColor.bg, color: hudColor.fg, bold: true, children: label }), _jsxs(Text, { backgroundColor: "gray", color: "white", children: [pathDisplay, " ".repeat(padLen)] })] })] }));
32
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: THEME.hud.accountNameText, bold: true, children: compactName }), _jsx(Text, { color: THEME.hud.accountIdText, bold: true, children: idPart }), _jsx(Text, { color: THEME.hud.separatorText, bold: true, children: "\u00B7" }), _jsx(Text, { color: THEME.hud.regionText, bold: true, children: region }), _jsx(Text, { color: THEME.hud.separatorText, bold: true, children: "\u00B7" }), _jsx(Text, { color: THEME.hud.profileText, bold: true, children: profilePart }), _jsx(Text, { children: " ".repeat(topPadLen) }), loading ? (_jsx(Text, { color: THEME.hud.loadingSpinnerText, bold: true, children: SPINNER_FRAMES[spinnerIndex] })) : null] }), _jsxs(Text, { color: THEME.hud.currentIdentityText, wrap: "truncate-end", children: [identityLine, " ".repeat(identityPadLen)] }), _jsxs(Box, { children: [_jsx(Text, { backgroundColor: hudColor.bg, color: hudColor.fg, bold: true, children: label }), _jsxs(Text, { backgroundColor: THEME.hud.pathBarBg, color: THEME.hud.pathBarText, children: [pathDisplay, " ".repeat(padLen)] })] })] }));
31
33
  }
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from "ink";
3
+ import { useTheme } from "../contexts/ThemeContext.js";
3
4
  function truncate(text, maxLen) {
4
5
  if (text.length <= maxLen)
5
6
  return text;
@@ -8,6 +9,7 @@ function truncate(text, maxLen) {
8
9
  return `${text.slice(0, maxLen - 1)}…`;
9
10
  }
10
11
  export function HelpPanel({ title, scopeLabel, tabs, activeTab, terminalWidth, maxRows, scrollOffset, }) {
12
+ const THEME = useTheme();
11
13
  const currentTab = tabs[activeTab] ?? tabs[0];
12
14
  const keyColWidth = 12;
13
15
  const descColWidth = Math.max(16, terminalWidth - keyColWidth - 8);
@@ -24,10 +26,10 @@ export function HelpPanel({ title, scopeLabel, tabs, activeTab, terminalWidth, m
24
26
  }
25
27
  const listRowsBudget = Math.max(1, maxRows);
26
28
  const visibleItems = (currentTab?.items ?? []).slice(scrollOffset, scrollOffset + listRowsBudget);
27
- return (_jsxs(Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [_jsx(Text, { bold: true, color: "blue", children: title }), _jsx(Text, { color: "gray", children: scopeLabel }), _jsx(Box, { children: tabRow.map((chip) => {
29
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [_jsx(Text, { bold: true, color: THEME.panel.panelTitleText, children: title }), _jsx(Text, { color: THEME.panel.panelHintText, children: scopeLabel }), _jsx(Box, { children: tabRow.map((chip) => {
28
30
  const isActive = chip.idx === activeTab;
29
31
  return (_jsx(Text, { ...(isActive
30
- ? { backgroundColor: "blue", color: "white" }
31
- : { color: "cyan" }), bold: isActive, children: chip.label }, `chip-${chip.idx}`));
32
- }) }), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: visibleItems.map((item, idx) => (_jsxs(Box, { children: [_jsx(Text, { color: "yellow", bold: true, children: truncate(item.key, keyColWidth).padEnd(keyColWidth) }), _jsx(Text, { children: truncate(item.description, descColWidth) })] }, `${item.key}-${scrollOffset + idx}`))) })] }));
32
+ ? { backgroundColor: THEME.panel.activeTabBg, color: THEME.panel.activeTabText }
33
+ : { color: THEME.panel.inactiveTabText }), bold: isActive, children: chip.label }, `chip-${chip.idx}`));
34
+ }) }), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: visibleItems.map((item, idx) => (_jsxs(Box, { children: [_jsx(Text, { color: THEME.panel.keyText, bold: true, children: truncate(item.key, keyColWidth).padEnd(keyColWidth) }), _jsx(Text, { children: truncate(item.description, descColWidth) })] }, `${item.key}-${scrollOffset + idx}`))) })] }));
33
35
  }
@@ -3,17 +3,14 @@ import React, { useRef } from "react";
3
3
  import { Box, Text } from "ink";
4
4
  import { AutocompleteInput } from "./AutocompleteInput.js";
5
5
  import { AVAILABLE_COMMANDS } from "../constants/commands.js";
6
+ import { useTheme } from "../contexts/ThemeContext.js";
6
7
  const MODE_ICONS = {
7
8
  navigate: "◉",
8
9
  search: "/",
9
10
  command: ":",
10
11
  };
11
- const MODE_COLORS = {
12
- navigate: "blue",
13
- search: "blue",
14
- command: "blue",
15
- };
16
12
  export const ModeBar = React.forwardRef(({ mode, filterText, commandText, commandCursorToEndToken, hintOverride, pickerSearchActive, onFilterChange, onCommandChange, onFilterSubmit, onCommandSubmit, }, ref) => {
13
+ const THEME = useTheme();
17
14
  const commandInputRef = useRef(null);
18
15
  const filterInputRef = useRef(null);
19
16
  const renderHint = (hint) => {
@@ -22,11 +19,11 @@ export const ModeBar = React.forwardRef(({ mode, filterText, commandText, comman
22
19
  .split("•")
23
20
  .map((x) => x.trim())
24
21
  .filter(Boolean);
25
- return (_jsx(Text, { color: "gray", wrap: "truncate-end", children: entries.map((entry, idx) => {
22
+ return (_jsx(Text, { color: THEME.modebar.keybindingDescText, wrap: "truncate-end", children: entries.map((entry, idx) => {
26
23
  const [rawKey, rawDesc] = entry.split("·").map((x) => x.trim());
27
24
  const keyPart = rawKey ?? entry;
28
25
  const descPart = rawDesc ?? "";
29
- return (_jsxs(React.Fragment, { children: [_jsx(Text, { color: "yellow", children: keyPart }), descPart ? _jsxs(Text, { color: "gray", children: [" ", descPart] }) : null, idx < entries.length - 1 ? _jsx(Text, { color: "gray", children: " \u2022 " }) : null] }, `hint-${idx}`));
26
+ return (_jsxs(React.Fragment, { children: [_jsx(Text, { color: THEME.modebar.keybindingKeyText, children: keyPart }), descPart ? _jsxs(Text, { color: THEME.modebar.keybindingDescText, children: [" ", descPart] }) : null, idx < entries.length - 1 ? _jsx(Text, { color: THEME.modebar.keybindingSeparatorText, children: " \u2022 " }) : null] }, `hint-${idx}`));
30
27
  }) }));
31
28
  };
32
29
  React.useImperativeHandle(ref, () => ({
@@ -37,7 +34,7 @@ export const ModeBar = React.forwardRef(({ mode, filterText, commandText, comman
37
34
  const icon = isPickerSearch ? "/" : MODE_ICONS[mode];
38
35
  const showNavigateHint = mode === "navigate" && !isPickerSearch;
39
36
  const showFilterInput = mode === "search" || isPickerSearch;
40
- return (_jsx(Box, { flexDirection: "column", width: "100%", children: _jsxs(Box, { paddingX: 1, children: [_jsx(Text, { color: MODE_COLORS[mode], bold: true, children: icon }), _jsx(Text, { children: " " }), showNavigateHint && renderHint(hintOverride ?? ""), showFilterInput && (_jsx(AutocompleteInput, { ref: filterInputRef, value: filterText, onChange: onFilterChange, onSubmit: onFilterSubmit, placeholder: "Type to filter", focus: showFilterInput })), mode === "command" && (_jsx(AutocompleteInput, { ref: commandInputRef, value: commandText, onChange: onCommandChange, onSubmit: onCommandSubmit, placeholder: "Type a command", suggestions: [...AVAILABLE_COMMANDS], focus: mode === "command", ...(commandCursorToEndToken !== undefined
37
+ return (_jsx(Box, { flexDirection: "column", width: "100%", children: _jsxs(Box, { paddingX: 1, children: [_jsx(Text, { color: THEME.modebar.modeIconText, bold: true, children: icon }), _jsx(Text, { children: " " }), showNavigateHint && renderHint(hintOverride ?? ""), showFilterInput && (_jsx(AutocompleteInput, { ref: filterInputRef, value: filterText, onChange: onFilterChange, onSubmit: onFilterSubmit, placeholder: "Type to filter", focus: showFilterInput })), mode === "command" && (_jsx(AutocompleteInput, { ref: commandInputRef, value: commandText, onChange: onCommandChange, onSubmit: onCommandSubmit, placeholder: "Type a command", suggestions: [...AVAILABLE_COMMANDS], focus: mode === "command", ...(commandCursorToEndToken !== undefined
41
38
  ? { cursorToEndToken: commandCursorToEndToken }
42
39
  : {}) }))] }) }));
43
40
  });
@@ -2,16 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import React, { useMemo } from "react";
3
3
  import { Box, Text } from "ink";
4
4
  import { computeColumnWidths } from "./widths.js";
5
- // Color constants for table styling
6
- const COLORS = {
7
- separator: "gray", // │ and ─ dividers
8
- headerText: "blue", // Column header text
9
- selectedBg: "cyan", // Selected row background
10
- selectedText: "black", // Selected row text
11
- highlightText: "yellow", // Filtered match highlight
12
- emptyText: "gray", // Empty state text
13
- highlightSelectedText: "black", // Highlight text on selected row
14
- };
5
+ import { useTheme } from "../../contexts/ThemeContext.js";
15
6
  function truncate(str, maxLen) {
16
7
  if (str.length <= maxLen)
17
8
  return str.padEnd(maxLen);
@@ -26,14 +17,14 @@ function truncateNoPad(str, maxLen) {
26
17
  return "…";
27
18
  return str.slice(0, maxLen - 1) + "…";
28
19
  }
29
- function highlightMatch(text, filter, isSelected = false) {
20
+ function highlightMatch(text, filter, isSelected, theme) {
30
21
  if (!filter || !text)
31
22
  return [text];
32
23
  const parts = [];
33
24
  const lowerText = text.toLowerCase();
34
25
  const lowerFilter = filter.toLowerCase();
35
26
  let lastIdx = 0;
36
- const highlightColor = isSelected ? COLORS.highlightSelectedText : COLORS.highlightText;
27
+ const highlightColor = isSelected ? theme.table.filterMatchSelectedText : theme.table.filterMatchText;
37
28
  let idx = lowerText.indexOf(lowerFilter);
38
29
  while (idx !== -1) {
39
30
  if (idx > lastIdx) {
@@ -49,24 +40,26 @@ function highlightMatch(text, filter, isSelected = false) {
49
40
  return parts.length > 0 ? parts : [text];
50
41
  }
51
42
  const Row = React.memo(function Row({ row, isSelected, columns, colWidths, filterText }) {
43
+ const THEME = useTheme();
52
44
  const parts = [];
53
45
  columns.forEach((col, i) => {
54
46
  if (i > 0)
55
- parts.push(_jsxs(Text, { color: COLORS.separator, children: [" ", "\u2502", " "] }, `sep-${i}`));
47
+ parts.push(_jsxs(Text, { color: THEME.table.rowSeparatorText, children: [" ", "\u2502", " "] }, `sep-${i}`));
56
48
  const cellData = row.cells[col.key] ?? "";
57
49
  const cellValue = typeof cellData === "string" ? cellData : cellData.displayName;
58
50
  const truncated = truncate(cellValue, colWidths[i]);
59
- const highlighted = filterText && truncated ? highlightMatch(truncated, filterText, isSelected) : [truncated];
51
+ const highlighted = filterText && truncated ? highlightMatch(truncated, filterText, isSelected, THEME) : [truncated];
60
52
  if (isSelected) {
61
- parts.push(_jsx(Text, { color: COLORS.selectedText, bold: true, children: highlighted }, `cell-${i}`));
53
+ parts.push(_jsx(Text, { color: THEME.table.selectedRowText, bold: true, children: highlighted }, `cell-${i}`));
62
54
  }
63
55
  else {
64
56
  parts.push(_jsx(Text, { children: highlighted }, `cell-${i}`));
65
57
  }
66
58
  });
67
- return isSelected ? _jsx(Box, { backgroundColor: COLORS.selectedBg, children: parts }) : _jsx(Box, { children: parts });
59
+ return isSelected ? _jsx(Box, { backgroundColor: THEME.table.selectedRowBg, children: parts }) : _jsx(Box, { children: parts });
68
60
  });
69
61
  export const Table = React.memo(function Table({ columns, rows, selectedIndex, filterText, terminalWidth, maxHeight, scrollOffset, contextLabel, headerMarkers, }) {
62
+ const THEME = useTheme();
70
63
  // Memoize column widths computation
71
64
  const colWidths = useMemo(() => computeColumnWidths(columns, terminalWidth), [columns, terminalWidth]);
72
65
  // Rows are pre-filtered by parent, no need to filter again
@@ -76,34 +69,34 @@ export const Table = React.memo(function Table({ columns, rows, selectedIndex, f
76
69
  const parts = [];
77
70
  columns.forEach((col, i) => {
78
71
  if (i > 0)
79
- parts.push(_jsxs(Text, { color: COLORS.separator, children: [" ", "\u2502", " "] }, `sep-${i}`));
72
+ parts.push(_jsxs(Text, { color: THEME.table.rowSeparatorText, children: [" ", "\u2502", " "] }, `sep-${i}`));
80
73
  const width = colWidths[i];
81
74
  const markers = headerMarkers?.[col.key] ?? [];
82
75
  const markerText = markers.length > 0 ? ` [${markers.join(",")}]` : "";
83
76
  if (!markerText) {
84
- parts.push(_jsx(Text, { bold: true, color: COLORS.headerText, children: truncate(col.label, width) }, col.key));
77
+ parts.push(_jsx(Text, { bold: true, color: THEME.table.columnHeaderText, children: truncate(col.label, width) }, col.key));
85
78
  return;
86
79
  }
87
80
  if (markerText.length >= width) {
88
81
  const markerDisplay = truncate(markerText, width);
89
- parts.push(_jsx(Text, { color: "cyan", children: markerDisplay }, `${col.key}-markers-only`));
82
+ parts.push(_jsx(Text, { color: THEME.table.columnHeaderMarker, children: markerDisplay }, `${col.key}-markers-only`));
90
83
  return;
91
84
  }
92
85
  const labelMax = width - markerText.length;
93
86
  const labelDisplay = truncateNoPad(col.label, labelMax);
94
87
  const trailingPadLen = Math.max(0, width - (labelDisplay.length + markerText.length));
95
- parts.push(_jsx(Text, { bold: true, color: COLORS.headerText, children: labelDisplay }, `${col.key}-label`));
96
- parts.push(_jsx(Text, { color: "cyan", children: markerText }, `${col.key}-markers`));
88
+ parts.push(_jsx(Text, { bold: true, color: THEME.table.columnHeaderText, children: labelDisplay }, `${col.key}-label`));
89
+ parts.push(_jsx(Text, { color: THEME.table.columnHeaderMarker, children: markerText }, `${col.key}-markers`));
97
90
  if (trailingPadLen > 0) {
98
- parts.push(_jsx(Text, { color: COLORS.headerText, children: " ".repeat(trailingPadLen) }, `${col.key}-pad`));
91
+ parts.push(_jsx(Text, { color: THEME.table.columnHeaderText, children: " ".repeat(trailingPadLen) }, `${col.key}-pad`));
99
92
  }
100
93
  });
101
94
  return _jsx(Box, { children: parts });
102
95
  };
103
- const renderDivider = () => (_jsx(Text, { color: COLORS.separator, children: columns.map((col, i) => "─".repeat(colWidths[i])).join("─┼─") }));
104
- const renderEmpty = () => (_jsx(Text, { color: COLORS.emptyText, children: filterText ? `No results for "${filterText}"` : "No items" }));
96
+ const renderDivider = () => (_jsx(Text, { color: THEME.table.rowSeparatorText, children: columns.map((col, i) => "─".repeat(colWidths[i])).join("─┼─") }));
97
+ const renderEmpty = () => (_jsx(Text, { color: THEME.table.emptyStateText, children: filterText ? `No results for "${filterText}"` : "No items" }));
105
98
  if (rows.length === 0) {
106
- return (_jsxs(Box, { flexDirection: "column", children: [contextLabel && (_jsx(Text, { bold: true, color: COLORS.headerText, children: contextLabel })), contextLabel && _jsx(Box, { height: 1 }), renderHeader(), renderDivider(), renderEmpty()] }));
99
+ return (_jsxs(Box, { flexDirection: "column", children: [contextLabel && (_jsx(Text, { bold: true, color: THEME.table.columnHeaderText, children: contextLabel })), contextLabel && _jsx(Box, { height: 1 }), renderHeader(), renderDivider(), renderEmpty()] }));
107
100
  }
108
- return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [contextLabel && (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, color: COLORS.headerText, children: contextLabel }), _jsx(Box, { height: 1 })] })), renderHeader(), renderDivider(), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: visibleRows.map((row, i) => (_jsx(Row, { row: row, isSelected: i === adjustedSelected, columns: columns, colWidths: colWidths, filterText: filterText }, row.id))) }), rows.length > maxHeight && (_jsx(Box, { paddingTop: 1, children: _jsxs(Text, { color: "gray", children: [scrollOffset + visibleRows.length, " / ", rows.length, " items"] }) }))] }));
101
+ return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [contextLabel && (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, color: THEME.table.columnHeaderText, children: contextLabel }), _jsx(Box, { height: 1 })] })), renderHeader(), renderDivider(), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: visibleRows.map((row, i) => (_jsx(Row, { row: row, isSelected: i === adjustedSelected, columns: columns, colWidths: colWidths, filterText: filterText }, row.id))) }), rows.length > maxHeight && (_jsx(Box, { paddingTop: 1, children: _jsxs(Text, { color: THEME.table.scrollPositionText, children: [scrollOffset + visibleRows.length, " / ", rows.length, " items"] }) }))] }));
109
102
  });
@@ -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";