rl-rockcli 0.0.9 → 0.0.11

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 (90) hide show
  1. package/commands/attach/basic-repl.js +212 -0
  2. package/commands/attach/cleanup-history.js +189 -0
  3. package/commands/attach/cleanup-manager.js +163 -0
  4. package/commands/attach/copy-ui/copyRepl.js +195 -0
  5. package/commands/attach/copy-ui/index.js +7 -0
  6. package/commands/attach/copy-ui/render/outputBlock.js +25 -0
  7. package/commands/attach/copy-ui/viewport/viewport.js +23 -0
  8. package/commands/attach/copy-ui/viewport/wheel.js +14 -0
  9. package/commands/attach/history-manager.js +507 -0
  10. package/commands/attach/history-session.js +48 -0
  11. package/commands/attach/ink-repl/InkREPL.js +1507 -0
  12. package/commands/attach/ink-repl/builtinCommands.js +1253 -0
  13. package/commands/attach/ink-repl/components/ConnectingScreen.js +76 -0
  14. package/commands/attach/ink-repl/components/Console.js +191 -0
  15. package/commands/attach/ink-repl/components/DetailView.js +148 -0
  16. package/commands/attach/ink-repl/components/DropdownMenu.js +86 -0
  17. package/commands/attach/ink-repl/components/InputArea.js +125 -0
  18. package/commands/attach/ink-repl/components/InputLine.js +18 -0
  19. package/commands/attach/ink-repl/components/OutputArea.js +22 -0
  20. package/commands/attach/ink-repl/components/OutputItem.js +96 -0
  21. package/commands/attach/ink-repl/components/ShellLayout.js +61 -0
  22. package/commands/attach/ink-repl/components/Spinner.js +79 -0
  23. package/commands/attach/ink-repl/components/StatusBar.js +106 -0
  24. package/commands/attach/ink-repl/components/WelcomeBanner.js +48 -0
  25. package/commands/attach/ink-repl/contexts/LayoutContext.js +12 -0
  26. package/commands/attach/ink-repl/contexts/ThemeContext.js +43 -0
  27. package/commands/attach/ink-repl/hooks/useFunctionKeys.js +70 -0
  28. package/commands/attach/ink-repl/hooks/useMouse.js +162 -0
  29. package/commands/attach/ink-repl/hooks/useResources.js +132 -0
  30. package/commands/attach/ink-repl/hooks/useSpinner.js +49 -0
  31. package/commands/attach/ink-repl/index.js +112 -0
  32. package/commands/attach/ink-repl/package.json +3 -0
  33. package/commands/attach/ink-repl/replState.js +947 -0
  34. package/commands/attach/ink-repl/shortcuts/defaultKeybindings.js +138 -0
  35. package/commands/attach/ink-repl/shortcuts/index.js +332 -0
  36. package/commands/attach/ink-repl/themes/defaultDark.js +18 -0
  37. package/commands/attach/ink-repl/themes/defaultLight.js +18 -0
  38. package/commands/attach/ink-repl/themes/index.js +4 -0
  39. package/commands/attach/ink-repl/themes/themeManager.js +45 -0
  40. package/commands/attach/ink-repl/themes/themeTokens.js +15 -0
  41. package/commands/attach/ink-repl/utils/atCompletion.js +346 -0
  42. package/commands/attach/ink-repl/utils/clipboard.js +50 -0
  43. package/commands/attach/ink-repl/utils/consoleLogger.js +81 -0
  44. package/commands/attach/ink-repl/utils/exitCodeHandler.js +49 -0
  45. package/commands/attach/ink-repl/utils/exitCodeTips.js +56 -0
  46. package/commands/attach/ink-repl/utils/formatTime.js +12 -0
  47. package/commands/attach/ink-repl/utils/outputSelection.js +120 -0
  48. package/commands/attach/ink-repl/utils/outputViewport.js +77 -0
  49. package/commands/attach/ink-repl/utils/paginatedFileLoading.js +76 -0
  50. package/commands/attach/ink-repl/utils/paramHint.js +60 -0
  51. package/commands/attach/ink-repl/utils/parseError.js +174 -0
  52. package/commands/attach/ink-repl/utils/pathCompletion.js +167 -0
  53. package/commands/attach/ink-repl/utils/remotePathSafety.js +56 -0
  54. package/commands/attach/ink-repl/utils/replSelection.js +205 -0
  55. package/commands/attach/ink-repl/utils/responseFormatter.js +127 -0
  56. package/commands/attach/ink-repl/utils/textWrap.js +117 -0
  57. package/commands/attach/ink-repl/utils/truncate.js +115 -0
  58. package/commands/attach/opentui-repl/App.tsx +891 -0
  59. package/commands/attach/opentui-repl/builtinCommands.ts +80 -0
  60. package/commands/attach/opentui-repl/components/ConfirmDialog.tsx +116 -0
  61. package/commands/attach/opentui-repl/components/ConnectingScreen.tsx +131 -0
  62. package/commands/attach/opentui-repl/components/Console.tsx +73 -0
  63. package/commands/attach/opentui-repl/components/DetailView.tsx +45 -0
  64. package/commands/attach/opentui-repl/components/DropdownMenu.tsx +130 -0
  65. package/commands/attach/opentui-repl/components/ExecutionStatus.tsx +66 -0
  66. package/commands/attach/opentui-repl/components/Header.tsx +24 -0
  67. package/commands/attach/opentui-repl/components/OutputArea.tsx +25 -0
  68. package/commands/attach/opentui-repl/components/OutputBlock.tsx +108 -0
  69. package/commands/attach/opentui-repl/components/PromptInput.tsx +109 -0
  70. package/commands/attach/opentui-repl/components/StatusBar.tsx +63 -0
  71. package/commands/attach/opentui-repl/components/Toast.tsx +65 -0
  72. package/commands/attach/opentui-repl/components/WelcomeBanner.tsx +41 -0
  73. package/commands/attach/opentui-repl/contexts/ReplContext.tsx +137 -0
  74. package/commands/attach/opentui-repl/contexts/SessionContext.tsx +32 -0
  75. package/commands/attach/opentui-repl/contexts/ThemeContext.tsx +70 -0
  76. package/commands/attach/opentui-repl/contexts/ToastContext.tsx +69 -0
  77. package/commands/attach/opentui-repl/contexts/toast-logic.js +71 -0
  78. package/commands/attach/opentui-repl/hooks/useResources.ts +102 -0
  79. package/commands/attach/opentui-repl/hooks/useSpinner.ts +46 -0
  80. package/commands/attach/opentui-repl/index.js +99 -0
  81. package/commands/attach/opentui-repl/keybindings.ts +39 -0
  82. package/commands/attach/opentui-repl/package.json +3 -0
  83. package/commands/attach/opentui-repl/render.tsx +72 -0
  84. package/commands/attach/opentui-repl/tsconfig.json +12 -0
  85. package/commands/attach/repl.js +791 -0
  86. package/commands/attach/sandbox-id-resolver.js +56 -0
  87. package/commands/attach/session-manager.js +307 -0
  88. package/commands/attach/ui-mode.js +146 -0
  89. package/commands/attach.js +186 -0
  90. package/package.json +1 -1
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Default keybindings for attach REPL
3
+ *
4
+ * This module defines the default key bindings and their display labels.
5
+ * Users can override these in ~/.rock/settings.json under attach.keybindings
6
+ */
7
+
8
+ /**
9
+ * Default keybindings configuration
10
+ * Each action maps to an array of key combinations
11
+ */
12
+ export const DEFAULT_KEYBINDINGS = {
13
+ // Global actions
14
+ exit: ['ctrl+c', 'ctrl+d'],
15
+ cancelExecution: ['escape'],
16
+ toggleConsole: ['f12'],
17
+ // toggleMouseCapture: ['f11'], // REMOVED - mouse always enabled
18
+ viewOutput: ['ctrl+v'],
19
+ copyLastOutput: ['ctrl+y'],
20
+
21
+ // History navigation
22
+ historyUp: ['up'],
23
+ historyDown: ['down'],
24
+
25
+ // Editing
26
+ deleteWord: ['alt+backspace', 'meta+backspace'],
27
+
28
+ // Completion
29
+ autoComplete: ['tab'],
30
+
31
+ // Command submission
32
+ submit: ['enter'],
33
+
34
+ // Menu actions
35
+ closeMenu: ['escape'],
36
+ menuConfirm: ['enter', 'tab'],
37
+ menuUp: ['up'],
38
+ menuDown: ['down'],
39
+
40
+ // Detail view actions
41
+ detailScrollUp: ['up', 'k'],
42
+ detailScrollDown: ['down', 'j'],
43
+ detailExit: ['escape'],
44
+
45
+ // Console actions
46
+ consoleScrollUp: ['pageup'],
47
+ consoleScrollDown: ['pagedown'],
48
+ };
49
+
50
+ /**
51
+ * Display labels for key names
52
+ * Used to generate human-readable hints in StatusBar
53
+ */
54
+ export const KEY_LABELS = {
55
+ 'ctrl+c': 'ctrl+c',
56
+ 'ctrl+d': 'ctrl+d',
57
+ 'ctrl+v': 'ctrl+v',
58
+ 'ctrl+y': 'ctrl+y',
59
+ 'alt+backspace': 'alt+⌫',
60
+ 'meta+backspace': 'opt+⌫',
61
+ 'pageup': 'PgUp',
62
+ 'pagedown': 'PgDn',
63
+ 'f1': 'F1',
64
+ 'f2': 'F2',
65
+ 'f3': 'F3',
66
+ 'f4': 'F4',
67
+ 'f5': 'F5',
68
+ 'f6': 'F6',
69
+ 'f7': 'F7',
70
+ 'f8': 'F8',
71
+ 'f9': 'F9',
72
+ 'f10': 'F10',
73
+ 'f11': 'F11',
74
+ 'f12': 'F12',
75
+ 'enter': '⏎',
76
+ 'tab': '⇥',
77
+ 'escape': 'esc',
78
+ 'up': '↑',
79
+ 'down': '↓',
80
+ 'left': '←',
81
+ 'right': '→',
82
+ 'space': 'space',
83
+ 'backspace': '⌫',
84
+ 'delete': '⌦',
85
+ };
86
+
87
+ /**
88
+ * Function key escape sequences (xterm/VT style)
89
+ * Maps raw terminal escape sequences to key names
90
+ */
91
+ export const FN_KEY_SEQUENCES = {
92
+ '\x1b[11~': 'f1',
93
+ '\x1b[12~': 'f2',
94
+ '\x1b[13~': 'f3',
95
+ '\x1b[14~': 'f4',
96
+ '\x1b[15~': 'f5',
97
+ '\x1b[17~': 'f6',
98
+ '\x1b[18~': 'f7',
99
+ '\x1b[19~': 'f8',
100
+ '\x1b[20~': 'f9',
101
+ '\x1b[21~': 'f10',
102
+ '\x1b[23~': 'f11',
103
+ '\x1b[24~': 'f12',
104
+ // Alternative sequences (some terminals)
105
+ '\x1bOP': 'f1',
106
+ '\x1bOQ': 'f2',
107
+ '\x1bOR': 'f3',
108
+ '\x1bOS': 'f4',
109
+ // Page Up/Down
110
+ '\x1b[5~': 'pageup',
111
+ '\x1b[6~': 'pagedown',
112
+ };
113
+
114
+ /**
115
+ * Action descriptions for help text
116
+ */
117
+ export const ACTION_DESCRIPTIONS = {
118
+ exit: 'Exit',
119
+ cancelExecution: 'Cancel execution',
120
+ toggleConsole: 'Toggle console',
121
+ // toggleMouseCapture: 'Toggle mouse capture', // REMOVED - always enabled
122
+ viewOutput: 'View full output',
123
+ copyLastOutput: 'Copy last output',
124
+ historyUp: 'Previous command',
125
+ historyDown: 'Next command',
126
+ deleteWord: 'Delete word',
127
+ autoComplete: 'Auto-complete',
128
+ submit: 'Submit command',
129
+ closeMenu: 'Close menu',
130
+ menuConfirm: 'Confirm selection',
131
+ menuUp: 'Move up',
132
+ menuDown: 'Move down',
133
+ detailScrollUp: 'Scroll up',
134
+ detailScrollDown: 'Scroll down',
135
+ detailExit: 'Exit detail view',
136
+ consoleScrollUp: 'Scroll console up',
137
+ consoleScrollDown: 'Scroll console down',
138
+ };
@@ -0,0 +1,332 @@
1
+ /**
2
+ * Keybinding Manager for attach REPL
3
+ *
4
+ * Provides configurable keyboard shortcuts with user customization support.
5
+ * Settings are stored in ~/.rock/settings.json under attach.keybindings
6
+ */
7
+
8
+ import fs from 'fs-extra';
9
+ import path from 'path';
10
+ import os from 'os';
11
+
12
+ import {
13
+ DEFAULT_KEYBINDINGS,
14
+ KEY_LABELS,
15
+ FN_KEY_SEQUENCES,
16
+ } from './defaultKeybindings.js';
17
+
18
+ const CONFIG_PATH = path.join(os.homedir(), '.rock', 'settings.json');
19
+
20
+ /**
21
+ * KeybindingManager class
22
+ * Manages keybindings with support for user customization
23
+ */
24
+ export class KeybindingManager {
25
+ constructor() {
26
+ this.keybindings = null;
27
+ }
28
+
29
+ /**
30
+ * Load keybindings from config file, merged with defaults
31
+ * @returns {Object} Merged keybindings
32
+ */
33
+ loadKeybindings() {
34
+ if (this.keybindings) {
35
+ return this.keybindings;
36
+ }
37
+
38
+ let userBindings = {};
39
+
40
+ try {
41
+ if (fs.existsSync(CONFIG_PATH)) {
42
+ const config = fs.readJsonSync(CONFIG_PATH);
43
+ if (config.attach && config.attach.keybindings) {
44
+ userBindings = config.attach.keybindings;
45
+ }
46
+ }
47
+ } catch (error) {
48
+ // Ignore config read errors, use defaults
49
+ }
50
+
51
+ // Merge: user bindings override defaults
52
+ this.keybindings = {
53
+ ...DEFAULT_KEYBINDINGS,
54
+ ...userBindings,
55
+ };
56
+
57
+ return this.keybindings;
58
+ }
59
+
60
+ /**
61
+ * Get keybindings (load if not already loaded)
62
+ * @returns {Object} Current keybindings
63
+ */
64
+ getKeybindings() {
65
+ if (!this.keybindings) {
66
+ this.loadKeybindings();
67
+ }
68
+ return this.keybindings;
69
+ }
70
+
71
+ /**
72
+ * Normalize a key string to lowercase
73
+ * @param {string} key Key string
74
+ * @returns {string} Normalized key
75
+ */
76
+ normalizeKey(key) {
77
+ return key.toLowerCase();
78
+ }
79
+
80
+ /**
81
+ * Parse ink useInput (input, key) into a normalized key string
82
+ * @param {string} input The input character
83
+ * @param {Object} key The key object from useInput
84
+ * @returns {string} Normalized key string like 'ctrl+c', 'up', 'enter'
85
+ */
86
+ parseInkKey(input, key) {
87
+ // Handle special keys
88
+ if (key.return) return 'enter';
89
+ if (key.escape) return 'escape';
90
+ if (key.tab) return 'tab';
91
+ if (key.upArrow) return 'up';
92
+ if (key.downArrow) return 'down';
93
+ if (key.leftArrow) return 'left';
94
+ if (key.rightArrow) return 'right';
95
+
96
+ // Handle meta/alt + backspace (Option+Delete on macOS)
97
+ if ((key.meta || key.alt) && (key.backspace || key.delete)) {
98
+ return 'meta+backspace';
99
+ }
100
+
101
+ // Handle regular backspace
102
+ if (key.backspace || key.delete) return 'backspace';
103
+
104
+ // Handle Ctrl+D (EOT character)
105
+ if (input === '\u0004') return 'ctrl+d';
106
+
107
+ // Handle ctrl/meta combinations
108
+ if (key.ctrl && input) {
109
+ return `ctrl+${input.toLowerCase()}`;
110
+ }
111
+ if ((key.meta || key.alt) && input) {
112
+ return `meta+${input.toLowerCase()}`;
113
+ }
114
+
115
+ // Regular character
116
+ if (input) {
117
+ return input.toLowerCase();
118
+ }
119
+
120
+ return '';
121
+ }
122
+
123
+ /**
124
+ * Check if useInput matches an action
125
+ * @param {string} action Action name (e.g., 'exit', 'submit')
126
+ * @param {string} input The input from useInput
127
+ * @param {Object} key The key object from useInput
128
+ * @returns {boolean} True if matches
129
+ */
130
+ matchesAction(action, input, key) {
131
+ const bindings = this.getKeybindings();
132
+ const actionKeys = bindings[action];
133
+
134
+ if (!actionKeys || !Array.isArray(actionKeys)) {
135
+ return false;
136
+ }
137
+
138
+ const parsedKey = this.parseInkKey(input, key);
139
+ return actionKeys.some(k => this.normalizeKey(k) === parsedKey);
140
+ }
141
+
142
+ /**
143
+ * Parse raw terminal input for function keys
144
+ * @param {string} rawInput Raw terminal input
145
+ * @returns {string|null} Function key name or null
146
+ */
147
+ parseFunctionKey(rawInput) {
148
+ return FN_KEY_SEQUENCES[rawInput] || null;
149
+ }
150
+
151
+ /**
152
+ * Check if a function key matches an action
153
+ * @param {string} action Action name
154
+ * @param {string} rawInput Raw terminal input
155
+ * @returns {boolean} True if matches
156
+ */
157
+ matchesFunctionKeyAction(action, rawInput) {
158
+ const bindings = this.getKeybindings();
159
+ const actionKeys = bindings[action];
160
+
161
+ if (!actionKeys || !Array.isArray(actionKeys)) {
162
+ return false;
163
+ }
164
+
165
+ const fnKey = this.parseFunctionKey(rawInput);
166
+ if (!fnKey) {
167
+ return false;
168
+ }
169
+
170
+ return actionKeys.some(k => this.normalizeKey(k) === fnKey);
171
+ }
172
+
173
+ /**
174
+ * Get the display label for a key
175
+ * @param {string} key Key string
176
+ * @returns {string} Display label
177
+ */
178
+ getKeyLabel(key) {
179
+ const normalized = this.normalizeKey(key);
180
+ return KEY_LABELS[normalized] || key.toUpperCase();
181
+ }
182
+
183
+ /**
184
+ * Get display labels for an action's first key
185
+ * @param {string} action Action name
186
+ * @returns {string} Display label for the first bound key
187
+ */
188
+ getActionKeyLabel(action) {
189
+ const bindings = this.getKeybindings();
190
+ const actionKeys = bindings[action];
191
+
192
+ if (!actionKeys || actionKeys.length === 0) {
193
+ return '';
194
+ }
195
+
196
+ return this.getKeyLabel(actionKeys[0]);
197
+ }
198
+
199
+ /**
200
+ * Get keyboard hints for StatusBar based on current mode and state
201
+ * @param {string} mode Current mode ('repl' or 'detail')
202
+ * @param {Object} state Current state object
203
+ * @returns {string} Formatted hint string
204
+ */
205
+ getKeyHints(mode, state = {}) {
206
+ const { menuVisible, isExecuting, consoleVisible } = state;
207
+
208
+ if (mode === 'detail') {
209
+ const upKey = this.getActionKeyLabel('detailScrollUp');
210
+ const downKey = this.getActionKeyLabel('detailScrollDown');
211
+ const exitKey = this.getActionKeyLabel('detailExit');
212
+ const consoleKey = this.getActionKeyLabel('toggleConsole');
213
+ return `${upKey}${downKey}/wheel scroll ${exitKey} back ${consoleKey} console`;
214
+ }
215
+
216
+ if (isExecuting) {
217
+ const exitKey = this.getActionKeyLabel('exit');
218
+ const consoleKey = this.getActionKeyLabel('toggleConsole');
219
+ return `${exitKey} cancel ${consoleKey} console`;
220
+ }
221
+
222
+ if (menuVisible) {
223
+ const upKey = this.getActionKeyLabel('menuUp');
224
+ const downKey = this.getActionKeyLabel('menuDown');
225
+ const confirmKey = this.getActionKeyLabel('menuConfirm');
226
+ const closeKey = this.getActionKeyLabel('closeMenu');
227
+ return `${upKey}${downKey} select ${confirmKey} confirm ${closeKey} close`;
228
+ }
229
+
230
+ if (consoleVisible) {
231
+ const consoleKey = this.getActionKeyLabel('toggleConsole');
232
+ const exitKey = this.getActionKeyLabel('exit');
233
+ return `${consoleKey} hide console ${exitKey} exit`;
234
+ }
235
+
236
+ // Default REPL mode
237
+ const historyUpKey = this.getActionKeyLabel('historyUp');
238
+ const historyDownKey = this.getActionKeyLabel('historyDown');
239
+ const copyKey = this.getActionKeyLabel('copyLastOutput');
240
+ const consoleKey = this.getActionKeyLabel('toggleConsole');
241
+ const scrollUpKey = this.getActionKeyLabel('consoleScrollUp');
242
+ const scrollDownKey = this.getActionKeyLabel('consoleScrollDown');
243
+ const exitKey = this.getActionKeyLabel('exit');
244
+ return `/ cmds ${historyUpKey}${historyDownKey} history ${copyKey} copy ${consoleKey} console ${scrollUpKey}${scrollDownKey} scroll/wheel ${exitKey} exit`;
245
+ }
246
+
247
+ /**
248
+ * Save custom keybindings to config file
249
+ * @param {Object} keybindings Keybindings to save
250
+ * @returns {boolean} Success status
251
+ */
252
+ saveKeybindings(keybindings) {
253
+ try {
254
+ let config = {};
255
+
256
+ if (fs.existsSync(CONFIG_PATH)) {
257
+ config = fs.readJsonSync(CONFIG_PATH);
258
+ }
259
+
260
+ if (!config.attach) {
261
+ config.attach = {};
262
+ }
263
+
264
+ config.attach.keybindings = keybindings;
265
+
266
+ // Ensure directory exists
267
+ const configDir = path.dirname(CONFIG_PATH);
268
+ if (!fs.existsSync(configDir)) {
269
+ fs.mkdirpSync(configDir);
270
+ }
271
+
272
+ fs.writeJsonSync(CONFIG_PATH, config, { spaces: 2 });
273
+
274
+ // Update cached keybindings
275
+ this.keybindings = {
276
+ ...DEFAULT_KEYBINDINGS,
277
+ ...keybindings,
278
+ };
279
+
280
+ return true;
281
+ } catch (error) {
282
+ return false;
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Reset keybindings to defaults (removes user customizations)
288
+ * @returns {boolean} Success status
289
+ */
290
+ resetToDefaults() {
291
+ try {
292
+ if (fs.existsSync(CONFIG_PATH)) {
293
+ const config = fs.readJsonSync(CONFIG_PATH);
294
+
295
+ if (config.attach && config.attach.keybindings) {
296
+ delete config.attach.keybindings;
297
+
298
+ // Clean up empty attach object
299
+ if (Object.keys(config.attach).length === 0) {
300
+ delete config.attach;
301
+ }
302
+
303
+ fs.writeJsonSync(CONFIG_PATH, config, { spaces: 2 });
304
+ }
305
+ }
306
+
307
+ // Reset cached keybindings to defaults
308
+ this.keybindings = { ...DEFAULT_KEYBINDINGS };
309
+
310
+ return true;
311
+ } catch (error) {
312
+ return false;
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Reload keybindings from config (useful after external changes)
318
+ * @returns {Object} Reloaded keybindings
319
+ */
320
+ reloadKeybindings() {
321
+ this.keybindings = null;
322
+ return this.loadKeybindings();
323
+ }
324
+ }
325
+
326
+ // Singleton instance
327
+ export const keybindingManager = new KeybindingManager();
328
+
329
+ export default keybindingManager;
330
+
331
+ // Re-export constants for convenience
332
+ export { DEFAULT_KEYBINDINGS, KEY_LABELS, FN_KEY_SEQUENCES } from './defaultKeybindings.js';
@@ -0,0 +1,18 @@
1
+ import { createTheme, defaultGradient } from './themeTokens.js';
2
+
3
+ export const DefaultDark = createTheme('rock-dark', {
4
+ background: '#1e1e2e',
5
+ surface: '#181825',
6
+ surfaceHighlight: '#11111b',
7
+ border: '#313244',
8
+ textPrimary: '#cdd6f4',
9
+ textSecondary: '#a6adc8',
10
+ accent: '#89b4fa',
11
+ accentMuted: '#b4befe',
12
+ warning: '#f9e2af',
13
+ success: '#a6e3a1',
14
+ danger: '#f38ba8',
15
+ prompt: '#89dceb',
16
+ cursor: '#cdd6f4',
17
+ gradient: defaultGradient,
18
+ });
@@ -0,0 +1,18 @@
1
+ import { createTheme, defaultGradient } from './themeTokens.js';
2
+
3
+ export const DefaultLight = createTheme('rock-light', {
4
+ background: '#fafafa',
5
+ surface: '#ffffff',
6
+ surfaceHighlight: '#f3f4f6',
7
+ border: '#d0d7de',
8
+ textPrimary: '#1f2328',
9
+ textSecondary: '#4c566a',
10
+ accent: '#4796e4',
11
+ accentMuted: '#c3677f',
12
+ warning: '#bf6500',
13
+ success: '#1a7f37',
14
+ danger: '#b62324',
15
+ prompt: '#0a3069',
16
+ cursor: '#1f2328',
17
+ gradient: defaultGradient,
18
+ });
@@ -0,0 +1,4 @@
1
+ export { themeManager } from './themeManager.js';
2
+ export { DefaultDark } from './defaultDark.js';
3
+ export { DefaultLight } from './defaultLight.js';
4
+ export { DEFAULT_THEME_NAME } from './themeTokens.js';
@@ -0,0 +1,45 @@
1
+ import { DEFAULT_THEME_NAME } from './themeTokens.js';
2
+ import { DefaultDark } from './defaultDark.js';
3
+ import { DefaultLight } from './defaultLight.js';
4
+
5
+ const BUILTIN_THEMES = [DefaultDark, DefaultLight];
6
+
7
+ class ThemeManager {
8
+ constructor() {
9
+ this.themes = new Map();
10
+ BUILTIN_THEMES.forEach(theme => {
11
+ this.themes.set(theme.name, theme);
12
+ });
13
+ this.activeThemeName = DEFAULT_THEME_NAME;
14
+ }
15
+
16
+ getActiveTheme() {
17
+ return this.themes.get(this.activeThemeName) || DefaultDark;
18
+ }
19
+
20
+ getTheme(name) {
21
+ return this.themes.get(name);
22
+ }
23
+
24
+ listThemes() {
25
+ return Array.from(this.themes.values()).map(theme => ({
26
+ name: theme.name,
27
+ type: theme.type,
28
+ }));
29
+ }
30
+
31
+ registerTheme(theme) {
32
+ if (!theme || !theme.name) return;
33
+ this.themes.set(theme.name, theme);
34
+ }
35
+
36
+ setActiveTheme(name) {
37
+ if (this.themes.has(name)) {
38
+ this.activeThemeName = name;
39
+ return true;
40
+ }
41
+ return false;
42
+ }
43
+ }
44
+
45
+ export const themeManager = new ThemeManager();
@@ -0,0 +1,15 @@
1
+ export const DEFAULT_THEME_NAME = 'rock-dark';
2
+
3
+ export function createTheme(name, colors, type = 'default') {
4
+ return {
5
+ name,
6
+ type,
7
+ colors,
8
+ semantic: {
9
+ banner: colors.gradient || defaultGradient,
10
+ },
11
+ };
12
+ }
13
+
14
+ // Logo/banner gradient aligned with web (blue → indigo → purple).
15
+ export const defaultGradient = ['#2563EB', '#4F46E5', '#7C3AED'];