@iaforged/context-code 1.1.9 → 1.2.1

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 (78) hide show
  1. package/README.md +24 -0
  2. package/dist/src/commands/model/model.js +9 -5
  3. package/dist/src/commands/timeline/index.js +8 -0
  4. package/dist/src/commands/timeline/timeline.js +194 -0
  5. package/dist/src/commands.js +2 -0
  6. package/dist/src/components/AgentActivitySidebar.js +50 -0
  7. package/dist/src/components/AgentProgressLine.js +5 -5
  8. package/dist/src/components/ModelPicker.js +252 -441
  9. package/dist/src/components/PromptInput/PromptInputFooter.js +10 -29
  10. package/dist/src/components/Spinner/TeammateSpinnerLine.js +20 -62
  11. package/dist/src/components/Spinner/TeammateSpinnerTree.js +16 -258
  12. package/dist/src/components/Spinner/teammateSelectHint.js +1 -1
  13. package/dist/src/components/Spinner/utils.js +3 -6
  14. package/dist/src/components/ThemeBrowser.js +120 -0
  15. package/dist/src/components/ThemePicker.js +113 -321
  16. package/dist/src/components/design-system/ThemeProvider.js +3 -0
  17. package/dist/src/components/mcp/MCPListPanel.js +138 -444
  18. package/dist/src/components/permissions/SandboxPermissionRequest.js +5 -5
  19. package/dist/src/components/teams/TeamStatus.js +7 -71
  20. package/dist/src/constants/spinnerVerbs.js +80 -180
  21. package/dist/src/context/modalStackContext.js +12 -0
  22. package/dist/src/hooks/useTextInput.js +28 -18
  23. package/dist/src/main.js +12 -0
  24. package/dist/src/screens/REPL.js +386 -320
  25. package/dist/src/skills/loadSkillsDir.js +1 -0
  26. package/dist/src/tools/AgentTool/UI.js +8 -8
  27. package/dist/src/tools/BashTool/bashSecurity.js +1 -1
  28. package/dist/src/utils/handlePromptSubmit.js +12 -2
  29. package/dist/src/utils/processUserInput/processSlashCommand.js +9 -5
  30. package/dist/src/utils/sembleMcp/common.js +5 -0
  31. package/dist/src/utils/sembleMcp/setup.js +119 -0
  32. package/dist/src/utils/theme.js +24 -3
  33. package/dist/src/utils/themes/bootstrap.js +109 -0
  34. package/dist/src/utils/themes/builtin/opencode/_index.json +41 -0
  35. package/dist/src/utils/themes/builtin/opencode/amoled.json +49 -0
  36. package/dist/src/utils/themes/builtin/opencode/aura.json +51 -0
  37. package/dist/src/utils/themes/builtin/opencode/ayu.json +51 -0
  38. package/dist/src/utils/themes/builtin/opencode/carbonfox.json +53 -0
  39. package/dist/src/utils/themes/builtin/opencode/catppuccin-frappe.json +85 -0
  40. package/dist/src/utils/themes/builtin/opencode/catppuccin-macchiato.json +85 -0
  41. package/dist/src/utils/themes/builtin/opencode/catppuccin.json +45 -0
  42. package/dist/src/utils/themes/builtin/opencode/cobalt2.json +87 -0
  43. package/dist/src/utils/themes/builtin/opencode/cursor.json +91 -0
  44. package/dist/src/utils/themes/builtin/opencode/dracula.json +49 -0
  45. package/dist/src/utils/themes/builtin/opencode/everforest.json +89 -0
  46. package/dist/src/utils/themes/builtin/opencode/flexoki.json +86 -0
  47. package/dist/src/utils/themes/builtin/opencode/github.json +85 -0
  48. package/dist/src/utils/themes/builtin/opencode/gruvbox.json +45 -0
  49. package/dist/src/utils/themes/builtin/opencode/kanagawa.json +89 -0
  50. package/dist/src/utils/themes/builtin/opencode/lucent-orng.json +87 -0
  51. package/dist/src/utils/themes/builtin/opencode/material.json +87 -0
  52. package/dist/src/utils/themes/builtin/opencode/matrix.json +91 -0
  53. package/dist/src/utils/themes/builtin/opencode/mercury.json +86 -0
  54. package/dist/src/utils/themes/builtin/opencode/monokai.json +49 -0
  55. package/dist/src/utils/themes/builtin/opencode/nightowl.json +46 -0
  56. package/dist/src/utils/themes/builtin/opencode/nord.json +46 -0
  57. package/dist/src/utils/themes/builtin/opencode/oc-2.json +88 -0
  58. package/dist/src/utils/themes/builtin/opencode/one-dark.json +89 -0
  59. package/dist/src/utils/themes/builtin/opencode/onedarkpro.json +45 -0
  60. package/dist/src/utils/themes/builtin/opencode/opencode.json +89 -0
  61. package/dist/src/utils/themes/builtin/opencode/orng.json +87 -0
  62. package/dist/src/utils/themes/builtin/opencode/osaka-jade.json +88 -0
  63. package/dist/src/utils/themes/builtin/opencode/palenight.json +85 -0
  64. package/dist/src/utils/themes/builtin/opencode/rosepine.json +85 -0
  65. package/dist/src/utils/themes/builtin/opencode/shadesofpurple.json +51 -0
  66. package/dist/src/utils/themes/builtin/opencode/solarized.json +49 -0
  67. package/dist/src/utils/themes/builtin/opencode/synthwave84.json +87 -0
  68. package/dist/src/utils/themes/builtin/opencode/tokyonight.json +47 -0
  69. package/dist/src/utils/themes/builtin/opencode/vercel.json +90 -0
  70. package/dist/src/utils/themes/builtin/opencode/vesper.json +51 -0
  71. package/dist/src/utils/themes/builtin/opencode/zenburn.json +87 -0
  72. package/dist/src/utils/themes/index.js +4 -0
  73. package/dist/src/utils/themes/loader.js +147 -0
  74. package/dist/src/utils/themes/opencodeMapper.js +124 -0
  75. package/dist/src/utils/themes/resolver.js +66 -0
  76. package/dist/src/utils/themes/types.js +1 -0
  77. package/docs/MCP_SERVERS.md +27 -1
  78. package/package.json +1 -1
@@ -0,0 +1,120 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useState } from 'react';
3
+ import { Box, Text, useInput } from '../ink.js';
4
+ import { useTheme } from './design-system/ThemeProvider.js';
5
+ import { loadAllThemes, resolveTheme } from '../utils/themes/index.js';
6
+ const PREVIEW_TOKENS = [
7
+ 'claude',
8
+ 'success',
9
+ 'error',
10
+ 'warning',
11
+ 'text',
12
+ 'background',
13
+ ];
14
+ const SOURCE_LABEL = {
15
+ cli: 'CLI',
16
+ opencode: 'Opencode',
17
+ user: 'User',
18
+ };
19
+ const SOURCE_ORDER = ['cli', 'opencode', 'user'];
20
+ function buildRows(themes) {
21
+ const grouped = {
22
+ cli: [],
23
+ opencode: [],
24
+ user: [],
25
+ };
26
+ for (const t of themes)
27
+ grouped[t.source].push(t);
28
+ const rows = [];
29
+ const selectables = [];
30
+ for (const src of SOURCE_ORDER) {
31
+ const list = grouped[src];
32
+ if (list.length === 0)
33
+ continue;
34
+ rows.push({ kind: 'header', source: src });
35
+ for (const theme of list) {
36
+ const selectableIndex = selectables.length;
37
+ selectables.push(theme);
38
+ rows.push({ kind: 'item', theme, selectableIndex });
39
+ }
40
+ }
41
+ return { rows, selectables };
42
+ }
43
+ function ThemePreview({ json, mode, }) {
44
+ const colors = useMemo(() => resolveTheme(json, mode), [json, mode]);
45
+ return (_jsx(Box, { children: PREVIEW_TOKENS.map(token => {
46
+ const c = colors[token];
47
+ return (_jsx(Text, { color: c ?? 'rgb(128,128,128)', children: '█' }, token));
48
+ }) }));
49
+ }
50
+ export function ThemeBrowser({ onSelect, onCancel, initialThemeId, mode = 'dark', }) {
51
+ const [themes, setThemes] = useState(null);
52
+ const [error, setError] = useState(null);
53
+ const [cursor, setCursor] = useState(0);
54
+ // Keep useTheme so this component re-renders if the live theme changes.
55
+ useTheme();
56
+ useEffect(() => {
57
+ let cancelled = false;
58
+ loadAllThemes()
59
+ .then(loaded => {
60
+ if (cancelled)
61
+ return;
62
+ setThemes(loaded);
63
+ })
64
+ .catch((e) => {
65
+ if (cancelled)
66
+ return;
67
+ setError(e instanceof Error ? e.message : String(e));
68
+ });
69
+ return () => {
70
+ cancelled = true;
71
+ };
72
+ }, []);
73
+ const { rows, selectables } = useMemo(() => buildRows(themes ?? []), [themes]);
74
+ // Position cursor on initialThemeId once themes are available.
75
+ useEffect(() => {
76
+ if (!initialThemeId || selectables.length === 0)
77
+ return;
78
+ const idx = selectables.findIndex(t => t.id === initialThemeId);
79
+ if (idx >= 0)
80
+ setCursor(idx);
81
+ }, [initialThemeId, selectables]);
82
+ useInput((input, key) => {
83
+ if (key.escape || input === 'q') {
84
+ onCancel();
85
+ return;
86
+ }
87
+ if (selectables.length === 0)
88
+ return;
89
+ if (key.upArrow || input === 'k') {
90
+ setCursor(c => (c - 1 + selectables.length) % selectables.length);
91
+ return;
92
+ }
93
+ if (key.downArrow || input === 'j') {
94
+ setCursor(c => (c + 1) % selectables.length);
95
+ return;
96
+ }
97
+ if (key.return) {
98
+ const picked = selectables[cursor];
99
+ if (picked)
100
+ onSelect(picked.id);
101
+ }
102
+ });
103
+ if (error) {
104
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "error", children: ["No se pudieron cargar los temas: ", error] }), _jsx(Text, { dimColor: true, children: "Esc Cancelar" })] }));
105
+ }
106
+ if (themes === null) {
107
+ return (_jsx(Box, { flexDirection: "column", children: _jsx(Text, { dimColor: true, children: "Cargando temas..." }) }));
108
+ }
109
+ if (selectables.length === 0) {
110
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "No hay temas disponibles." }), _jsx(Text, { dimColor: true, children: "Esc Cancelar" })] }));
111
+ }
112
+ return (_jsxs(Box, { flexDirection: "column", children: [rows.map((row, idx) => {
113
+ if (row.kind === 'header') {
114
+ return (_jsx(Box, { marginTop: idx === 0 ? 0 : 1, children: _jsx(Text, { bold: true, color: "permission", children: SOURCE_LABEL[row.source] }) }, `h-${row.source}-${idx}`));
115
+ }
116
+ const isSelected = row.selectableIndex === cursor;
117
+ return (_jsxs(Box, { children: [_jsx(Box, { width: 2, children: _jsx(Text, { color: isSelected ? 'claude' : 'text', children: isSelected ? '> ' : ' ' }) }), _jsx(Box, { flexGrow: 1, children: _jsx(Text, { bold: isSelected, color: isSelected ? 'claude' : 'text', children: row.theme.json.name ?? row.theme.id }) }), _jsx(Box, { marginLeft: 1, children: _jsx(ThemePreview, { json: row.theme.json, mode: mode }) })] }, `t-${row.theme.source}-${row.theme.id}`));
118
+ }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: '↑↓ Navegar Enter Aplicar Esc Cancelar' }) })] }));
119
+ }
120
+ export default ThemeBrowser;
@@ -1,330 +1,122 @@
1
- import { feature } from '../recovery/bunBundleShim.js';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { c as _c } from "react/compiler-runtime";
4
- import { useExitOnCtrlCDWithKeybindings } from '../hooks/useExitOnCtrlCDWithKeybindings.js';
5
- import { useTerminalSize } from '../hooks/useTerminalSize.js';
6
- import { Box, Text, usePreviewTheme, useTheme, useThemeSetting } from '../ink.js';
7
- import { useRegisterKeybindingContext } from '../keybindings/KeybindingContext.js';
8
- import { useKeybinding } from '../keybindings/useKeybinding.js';
9
- import { useShortcutDisplay } from '../keybindings/useShortcutDisplay.js';
10
- import { useAppState, useSetAppState } from '../state/AppState.js';
11
- import { gracefulShutdown } from '../utils/gracefulShutdown.js';
12
- import { updateSettingsForSource } from '../utils/settings/settings.js';
13
- import { Select } from './CustomSelect/index.js';
14
- import { Byline } from './design-system/Byline.js';
15
- import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js';
16
- import { getColorModuleUnavailableReason, getSyntaxTheme } from './StructuredDiff/colorDiff.js';
17
- import { StructuredDiff } from './StructuredDiff.js';
18
- export function ThemePicker(t0) {
19
- const $ = _c(59);
20
- const { onThemeSelect, showIntroText: t1, helpText: t2, showHelpTextBelow: t3, hideEscToCancel: t4, skipExitHandling: t5, onCancel: onCancelProp } = t0;
21
- const showIntroText = t1 === undefined ? false : t1;
22
- const helpText = t2 === undefined ? "" : t2;
23
- const showHelpTextBelow = t3 === undefined ? false : t3;
24
- const hideEscToCancel = t4 === undefined ? false : t4;
25
- const skipExitHandling = t5 === undefined ? false : t5;
26
- const [theme] = useTheme();
27
- const themeSetting = useThemeSetting();
28
- const { columns } = useTerminalSize();
29
- let t6;
30
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
31
- t6 = getColorModuleUnavailableReason();
32
- $[0] = t6;
33
- }
34
- else {
35
- t6 = $[0];
36
- }
37
- const colorModuleUnavailableReason = t6;
38
- let t7;
39
- if ($[1] !== theme) {
40
- t7 = colorModuleUnavailableReason === null ? getSyntaxTheme(theme) : null;
41
- $[1] = theme;
42
- $[2] = t7;
43
- }
44
- else {
45
- t7 = $[2];
46
- }
47
- const syntaxTheme = t7;
48
- const { setPreviewTheme, savePreview, cancelPreview } = usePreviewTheme();
49
- const syntaxHighlightingDisabled = useAppState(_temp) ?? false;
50
- const setAppState = useSetAppState();
51
- useRegisterKeybindingContext("ThemePicker");
52
- const syntaxToggleShortcut = useShortcutDisplay("theme:toggleSyntaxHighlighting", "ThemePicker", "ctrl+t");
53
- let t8;
54
- if ($[3] !== setAppState || $[4] !== syntaxHighlightingDisabled) {
55
- t8 = () => {
56
- if (colorModuleUnavailableReason === null) {
57
- const newValue = !syntaxHighlightingDisabled;
58
- updateSettingsForSource("userSettings", {
59
- syntaxHighlightingDisabled: newValue
60
- });
61
- setAppState(prev => ({
62
- ...prev,
63
- settings: {
64
- ...prev.settings,
65
- syntaxHighlightingDisabled: newValue
66
- }
67
- }));
68
- }
69
- };
70
- $[3] = setAppState;
71
- $[4] = syntaxHighlightingDisabled;
72
- $[5] = t8;
73
- }
74
- else {
75
- t8 = $[5];
76
- }
77
- let t9;
78
- if ($[6] === Symbol.for("react.memo_cache_sentinel")) {
79
- t9 = {
80
- context: "ThemePicker"
81
- };
82
- $[6] = t9;
83
- }
84
- else {
85
- t9 = $[6];
86
- }
87
- useKeybinding("theme:toggleSyntaxHighlighting", t8, t9);
88
- const exitState = useExitOnCtrlCDWithKeybindings(skipExitHandling ? _temp2 : undefined);
89
- let t10;
90
- if ($[7] === Symbol.for("react.memo_cache_sentinel")) {
91
- t10 = [...(feature("AUTO_THEME") ? [{
92
- label: "Automático (según terminal)",
93
- value: "auto"
94
- }] : []), {
95
- label: "Modo oscuro",
96
- value: "dark"
97
- }, {
98
- label: "Modo claro",
99
- value: "light"
100
- }, {
101
- label: "Modo oscuro (apto para daltonismo)",
102
- value: "dark-daltonized"
103
- }, {
104
- label: "Modo claro (apto para daltonismo)",
105
- value: "light-daltonized"
106
- }, {
107
- label: "Modo oscuro (solo colores ANSI)",
108
- value: "dark-ansi"
109
- }, {
110
- label: "Modo claro (solo colores ANSI)",
111
- value: "light-ansi"
112
- }];
113
- $[7] = t10;
114
- }
115
- else {
116
- t10 = $[7];
117
- }
118
- const themeOptions = t10;
119
- let t11;
120
- if ($[8] !== showIntroText) {
121
- t11 = showIntroText ? _jsx(Text, { children: "Empecemos." }) : _jsx(Text, { bold: true, color: "permission", children: "Tema" });
122
- $[8] = showIntroText;
123
- $[9] = t11;
124
- }
125
- else {
126
- t11 = $[9];
127
- }
128
- let t12;
129
- if ($[10] === Symbol.for("react.memo_cache_sentinel")) {
130
- t12 = _jsx(Text, { bold: true, children: "Elige el estilo de texto que mejor se vea en tu terminal" });
131
- $[10] = t12;
132
- }
133
- else {
134
- t12 = $[10];
135
- }
136
- let t13;
137
- if ($[11] !== helpText || $[12] !== showHelpTextBelow) {
138
- t13 = helpText && !showHelpTextBelow && _jsx(Text, { dimColor: true, children: helpText });
139
- $[11] = helpText;
140
- $[12] = showHelpTextBelow;
141
- $[13] = t13;
142
- }
143
- else {
144
- t13 = $[13];
145
- }
146
- let t14;
147
- if ($[14] !== t13) {
148
- t14 = _jsxs(Box, { flexDirection: "column", children: [t12, t13] });
149
- $[14] = t13;
150
- $[15] = t14;
151
- }
152
- else {
153
- t14 = $[15];
154
- }
155
- let t15;
156
- if ($[16] !== setPreviewTheme) {
157
- t15 = setting => {
158
- setPreviewTheme(setting);
159
- };
160
- $[16] = setPreviewTheme;
161
- $[17] = t15;
162
- }
163
- else {
164
- t15 = $[17];
165
- }
166
- let t16;
167
- if ($[18] !== onThemeSelect || $[19] !== savePreview) {
168
- t16 = setting_0 => {
169
- savePreview();
170
- onThemeSelect(setting_0);
171
- };
172
- $[18] = onThemeSelect;
173
- $[19] = savePreview;
174
- $[20] = t16;
175
- }
176
- else {
177
- t16 = $[20];
178
- }
179
- let t17;
180
- if ($[21] !== cancelPreview || $[22] !== onCancelProp || $[23] !== skipExitHandling) {
181
- t17 = skipExitHandling ? () => {
182
- cancelPreview();
183
- onCancelProp?.();
184
- } : async () => {
185
- cancelPreview();
186
- await gracefulShutdown(0);
187
- };
188
- $[21] = cancelPreview;
189
- $[22] = onCancelProp;
190
- $[23] = skipExitHandling;
191
- $[24] = t17;
192
- }
193
- else {
194
- t17 = $[24];
195
- }
196
- let t18;
197
- if ($[25] !== t15 || $[26] !== t16 || $[27] !== t17 || $[28] !== themeSetting) {
198
- t18 = _jsx(Select, { options: themeOptions, onFocus: t15, onChange: t16, onCancel: t17, visibleOptionCount: themeOptions.length, defaultValue: themeSetting, defaultFocusValue: themeSetting });
199
- $[25] = t15;
200
- $[26] = t16;
201
- $[27] = t17;
202
- $[28] = themeSetting;
203
- $[29] = t18;
204
- }
205
- else {
206
- t18 = $[29];
207
- }
208
- let t19;
209
- if ($[30] !== t11 || $[31] !== t14 || $[32] !== t18) {
210
- t19 = _jsxs(Box, { flexDirection: "column", gap: 1, children: [t11, t14, t18] });
211
- $[30] = t11;
212
- $[31] = t14;
213
- $[32] = t18;
214
- $[33] = t19;
215
- }
216
- else {
217
- t19 = $[33];
218
- }
219
- let t20;
220
- if ($[34] === Symbol.for("react.memo_cache_sentinel")) {
221
- t20 = {
222
- oldStart: 1,
223
- newStart: 1,
224
- oldLines: 3,
225
- newLines: 3,
226
- lines: [" function greet() {", "- console.log(\"Hello, World!\");", "+ console.log(\"Hello, Claude!\");", " }"]
227
- };
228
- $[34] = t20;
229
- }
230
- else {
231
- t20 = $[34];
232
- }
233
- let t21;
234
- if ($[35] !== columns) {
235
- t21 = _jsx(Box, { flexDirection: "column", borderTop: true, borderBottom: true, borderLeft: false, borderRight: false, borderStyle: "dashed", borderColor: "subtle", children: _jsx(StructuredDiff, { patch: t20, dim: false, filePath: "demo.js", firstLine: null, width: columns }) });
236
- $[35] = columns;
237
- $[36] = t21;
238
- }
239
- else {
240
- t21 = $[36];
241
- }
242
- const t22 = colorModuleUnavailableReason === "env" ? `Syntax highlighting disabled (via CLAUDE_CODE_SYNTAX_HIGHLIGHT=${process.env.CLAUDE_CODE_SYNTAX_HIGHLIGHT})` : syntaxHighlightingDisabled ? `Syntax highlighting disabled (${syntaxToggleShortcut} to enable)` : syntaxTheme ? `Syntax theme: ${syntaxTheme.theme}${syntaxTheme.source ? ` (from ${syntaxTheme.source})` : ""} (${syntaxToggleShortcut} to disable)` : `Syntax highlighting enabled (${syntaxToggleShortcut} to disable)`;
243
- let t23;
244
- if ($[37] !== t22) {
245
- t23 = _jsxs(Text, { dimColor: true, children: [" ", t22] });
246
- $[37] = t22;
247
- $[38] = t23;
248
- }
249
- else {
250
- t23 = $[38];
251
- }
252
- let t24;
253
- if ($[39] !== t21 || $[40] !== t23) {
254
- t24 = _jsxs(Box, { flexDirection: "column", width: "100%", children: [t21, t23] });
255
- $[39] = t21;
256
- $[40] = t23;
257
- $[41] = t24;
258
- }
259
- else {
260
- t24 = $[41];
261
- }
262
- let t25;
263
- if ($[42] !== t19 || $[43] !== t24) {
264
- t25 = _jsxs(Box, { flexDirection: "column", gap: 1, children: [t19, t24] });
265
- $[42] = t19;
266
- $[43] = t24;
267
- $[44] = t25;
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useState } from 'react';
3
+ import { Box, Text, useInput } from '../ink.js';
4
+ import { useTheme } from './design-system/ThemeProvider.js';
5
+ import { loadAllThemesSync, resolveTheme } from '../utils/themes/index.js';
6
+ import { BUILT_IN_THEME_NAMES, getTheme } from '../utils/theme.js';
7
+ const PREVIEW_TOKENS = [
8
+ 'claude',
9
+ 'success',
10
+ 'error',
11
+ 'warning',
12
+ 'text',
13
+ 'background',
14
+ ];
15
+ const SOURCE_LABEL = {
16
+ cli: 'Built-in',
17
+ opencode: 'Opencode',
18
+ user: 'Personalizados',
19
+ };
20
+ const SOURCE_ORDER = ['cli', 'opencode', 'user'];
21
+ function buildRows(themes) {
22
+ const grouped = {
23
+ cli: [],
24
+ opencode: [],
25
+ user: [],
26
+ };
27
+ for (const t of themes)
28
+ grouped[t.source].push(t);
29
+ const rows = [];
30
+ const selectables = [];
31
+ for (const src of SOURCE_ORDER) {
32
+ const list = grouped[src];
33
+ if (list.length === 0)
34
+ continue;
35
+ rows.push({ kind: 'header', source: src });
36
+ for (const theme of list) {
37
+ const selectableIndex = selectables.length;
38
+ selectables.push(theme);
39
+ rows.push({ kind: 'item', theme, selectableIndex });
40
+ }
268
41
  }
269
- else {
270
- t25 = $[44];
42
+ return { rows, selectables };
43
+ }
44
+ function themeToJson(theme) {
45
+ const t = {};
46
+ for (const [k, v] of Object.entries(theme)) {
47
+ t[k] = String(v);
271
48
  }
272
- const content = t25;
273
- if (!showIntroText) {
274
- let t26;
275
- if ($[45] !== content) {
276
- t26 = _jsx(Box, { flexDirection: "column", children: content });
277
- $[45] = content;
278
- $[46] = t26;
279
- }
280
- else {
281
- t26 = $[46];
282
- }
283
- let t27;
284
- if ($[47] !== helpText || $[48] !== showHelpTextBelow) {
285
- t27 = showHelpTextBelow && helpText && _jsx(Box, { marginLeft: 3, children: _jsx(Text, { dimColor: true, children: helpText }) });
286
- $[47] = helpText;
287
- $[48] = showHelpTextBelow;
288
- $[49] = t27;
289
- }
290
- else {
291
- t27 = $[49];
292
- }
293
- let t28;
294
- if ($[50] !== exitState || $[51] !== hideEscToCancel) {
295
- t28 = !hideEscToCancel && _jsx(Box, { children: _jsx(Text, { dimColor: true, italic: true, children: exitState.pending ? _jsxs(_Fragment, { children: ["Presiona ", exitState.keyName, " de nuevo para salir"] }) : _jsxs(Byline, { children: [_jsx(KeyboardShortcutHint, { shortcut: "Enter", action: "seleccionar" }), _jsx(KeyboardShortcutHint, { shortcut: "Esc", action: "cancelar" })] }) }) });
296
- $[50] = exitState;
297
- $[51] = hideEscToCancel;
298
- $[52] = t28;
49
+ return { theme: t };
50
+ }
51
+ function builtInThemes() {
52
+ return BUILT_IN_THEME_NAMES.map(id => ({
53
+ id,
54
+ source: 'cli',
55
+ json: themeToJson(getTheme(id)),
56
+ }));
57
+ }
58
+ function ThemePreview({ json, mode, }) {
59
+ const colors = useMemo(() => resolveTheme(json, mode), [json, mode]);
60
+ return (_jsx(Box, { children: PREVIEW_TOKENS.map(token => {
61
+ const c = colors[token];
62
+ return (_jsx(Text, { color: c ?? 'rgb(128,128,128)', children: '█' }, token));
63
+ }) }));
64
+ }
65
+ export function ThemePicker({ onSelect, onCancel, initialThemeId, mode = 'dark', onThemeSelect, showIntroText: _showIntroText, helpText: _helpText, showHelpTextBelow: _showHelpTextBelow, hideEscToCancel: _hideEscToCancel, skipExitHandling: _skipExitHandling, }) {
66
+ const handleSelect = onThemeSelect ?? onSelect ?? (() => { });
67
+ const handleCancel = onCancel ?? (() => { });
68
+ const [themes] = useState(() => {
69
+ let dynamic = [];
70
+ try {
71
+ dynamic = loadAllThemesSync();
299
72
  }
300
- else {
301
- t28 = $[52];
73
+ catch {
74
+ dynamic = [];
302
75
  }
303
- let t29;
304
- if ($[53] !== t27 || $[54] !== t28) {
305
- t29 = _jsxs(Box, { marginTop: 1, children: [t27, t28] });
306
- $[53] = t27;
307
- $[54] = t28;
308
- $[55] = t29;
76
+ return [...builtInThemes(), ...dynamic];
77
+ });
78
+ const [cursor, setCursor] = useState(0);
79
+ // Keep useTheme so this component re-renders if the live theme changes.
80
+ useTheme();
81
+ const { rows, selectables } = useMemo(() => buildRows(themes), [themes]);
82
+ // Position cursor on initialThemeId once themes are available.
83
+ useEffect(() => {
84
+ if (!initialThemeId || selectables.length === 0)
85
+ return;
86
+ const idx = selectables.findIndex(t => t.id === initialThemeId);
87
+ if (idx >= 0)
88
+ setCursor(idx);
89
+ }, [initialThemeId, selectables]);
90
+ useInput((input, key) => {
91
+ if (key.escape || input === 'q') {
92
+ handleCancel();
93
+ return;
309
94
  }
310
- else {
311
- t29 = $[55];
95
+ if (selectables.length === 0)
96
+ return;
97
+ if (key.upArrow || input === 'k') {
98
+ setCursor(c => (c - 1 + selectables.length) % selectables.length);
99
+ return;
312
100
  }
313
- let t30;
314
- if ($[56] !== t26 || $[57] !== t29) {
315
- t30 = _jsxs(_Fragment, { children: [t26, t29] });
316
- $[56] = t26;
317
- $[57] = t29;
318
- $[58] = t30;
101
+ if (key.downArrow || input === 'j') {
102
+ setCursor(c => (c + 1) % selectables.length);
103
+ return;
319
104
  }
320
- else {
321
- t30 = $[58];
105
+ if (key.return) {
106
+ const picked = selectables[cursor];
107
+ if (picked)
108
+ handleSelect(picked.id);
322
109
  }
323
- return t30;
324
- }
325
- return content;
326
- }
327
- function _temp2() { }
328
- function _temp(s) {
329
- return s.settings.syntaxHighlightingDisabled;
110
+ });
111
+ if (selectables.length === 0) {
112
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "No hay temas disponibles." }), _jsx(Text, { dimColor: true, children: "Esc Cancelar" })] }));
113
+ }
114
+ return (_jsxs(Box, { flexDirection: "column", children: [rows.map((row, idx) => {
115
+ if (row.kind === 'header') {
116
+ return (_jsx(Box, { marginTop: idx === 0 ? 0 : 1, children: _jsx(Text, { bold: true, color: "permission", children: SOURCE_LABEL[row.source] }) }, `h-${row.source}-${idx}`));
117
+ }
118
+ const isSelected = row.selectableIndex === cursor;
119
+ return (_jsxs(Box, { children: [_jsx(Box, { width: 2, children: _jsx(Text, { color: isSelected ? 'claude' : 'text', children: isSelected ? '> ' : ' ' }) }), _jsx(Box, { flexGrow: 1, children: _jsx(Text, { bold: isSelected, color: isSelected ? 'claude' : 'text', children: row.theme.json.name ?? row.theme.id }) }), _jsx(Box, { marginLeft: 1, children: _jsx(ThemePreview, { json: row.theme.json, mode: mode }) })] }, `t-${row.theme.source}-${row.theme.id}`));
120
+ }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: '↑↓ Navegar Enter Aplicar Esc Cancelar' }) })] }));
330
121
  }
122
+ export default ThemePicker;
@@ -5,6 +5,9 @@ import { createContext, useContext, useEffect, useMemo, useState } from 'react';
5
5
  import useStdin from '../../ink/hooks/use-stdin.js';
6
6
  import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js';
7
7
  import { getSystemThemeName } from '../../utils/systemTheme.js';
8
+ import { bootstrapDynamicThemes } from '../../utils/themes/bootstrap.js';
9
+ // Eagerly load JSON themes so getTheme() can resolve them on first render.
10
+ bootstrapDynamicThemes();
8
11
  // Non-'auto' default so useTheme() works without a provider (tests, tooling).
9
12
  const DEFAULT_THEME = 'dark';
10
13
  const ThemeContext = createContext({