browser-terminal-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1038 @@
1
+ const themes = {
2
+ default: {
3
+ background: '#1e1e1e',
4
+ foreground: '#d4d4d4',
5
+ cursor: '#ffffff',
6
+ cursorAccent: '#000000',
7
+ selection: 'rgba(255, 255, 255, 0.3)',
8
+ black: '#000000',
9
+ red: '#cd3131',
10
+ green: '#0dbc79',
11
+ yellow: '#e5e510',
12
+ blue: '#2472c8',
13
+ magenta: '#bc3fbc',
14
+ cyan: '#11a8cd',
15
+ white: '#e5e5e5',
16
+ brightBlack: '#666666',
17
+ brightRed: '#f14c4c',
18
+ brightGreen: '#23d18b',
19
+ brightYellow: '#f5f543',
20
+ brightBlue: '#3b8eea',
21
+ brightMagenta: '#d670d6',
22
+ brightCyan: '#29b8db',
23
+ brightWhite: '#ffffff',
24
+ },
25
+ dark: {
26
+ background: '#0d1117',
27
+ foreground: '#c9d1d9',
28
+ cursor: '#58a6ff',
29
+ selection: 'rgba(56, 139, 253, 0.4)',
30
+ black: '#484f58',
31
+ red: '#ff7b72',
32
+ green: '#3fb950',
33
+ yellow: '#d29922',
34
+ blue: '#58a6ff',
35
+ magenta: '#bc8cff',
36
+ cyan: '#39c5cf',
37
+ white: '#b1bac4',
38
+ },
39
+ light: {
40
+ background: '#ffffff',
41
+ foreground: '#333333',
42
+ cursor: '#000000',
43
+ selection: 'rgba(0, 0, 0, 0.2)',
44
+ black: '#000000',
45
+ red: '#c91b00',
46
+ green: '#00c200',
47
+ yellow: '#c7c400',
48
+ blue: '#0225c7',
49
+ magenta: '#c930c7',
50
+ cyan: '#00c5c7',
51
+ white: '#c7c7c7',
52
+ },
53
+ matrix: {
54
+ background: '#0d0208',
55
+ foreground: '#00ff41',
56
+ cursor: '#00ff41',
57
+ selection: 'rgba(0, 255, 65, 0.3)',
58
+ black: '#0d0208',
59
+ red: '#00ff41',
60
+ green: '#00ff41',
61
+ yellow: '#00ff41',
62
+ blue: '#003b00',
63
+ magenta: '#00ff41',
64
+ cyan: '#00ff41',
65
+ white: '#00ff41',
66
+ },
67
+ ubuntu: {
68
+ background: '#300a24',
69
+ foreground: '#eeeeec',
70
+ cursor: '#bbbbbb',
71
+ selection: 'rgba(255, 255, 255, 0.3)',
72
+ black: '#2e3436',
73
+ red: '#cc0000',
74
+ green: '#4e9a06',
75
+ yellow: '#c4a000',
76
+ blue: '#3465a4',
77
+ magenta: '#75507b',
78
+ cyan: '#06989a',
79
+ white: '#d3d7cf',
80
+ },
81
+ monokai: {
82
+ background: '#272822',
83
+ foreground: '#f8f8f2',
84
+ cursor: '#f8f8f2',
85
+ selection: 'rgba(73, 72, 62, 0.8)',
86
+ black: '#272822',
87
+ red: '#f92672',
88
+ green: '#a6e22e',
89
+ yellow: '#f4bf75',
90
+ blue: '#66d9ef',
91
+ magenta: '#ae81ff',
92
+ cyan: '#a1efe4',
93
+ white: '#f8f8f2',
94
+ },
95
+ dracula: {
96
+ background: '#282a36',
97
+ foreground: '#f8f8f2',
98
+ cursor: '#f8f8f2',
99
+ selection: 'rgba(68, 71, 90, 0.8)',
100
+ black: '#21222c',
101
+ red: '#ff5555',
102
+ green: '#50fa7b',
103
+ yellow: '#f1fa8c',
104
+ blue: '#bd93f9',
105
+ magenta: '#ff79c6',
106
+ cyan: '#8be9fd',
107
+ white: '#f8f8f2',
108
+ },
109
+ 'solarized-dark': {
110
+ background: '#002b36',
111
+ foreground: '#839496',
112
+ cursor: '#839496',
113
+ selection: 'rgba(7, 54, 66, 0.8)',
114
+ black: '#073642',
115
+ red: '#dc322f',
116
+ green: '#859900',
117
+ yellow: '#b58900',
118
+ blue: '#268bd2',
119
+ magenta: '#d33682',
120
+ cyan: '#2aa198',
121
+ white: '#eee8d5',
122
+ },
123
+ 'solarized-light': {
124
+ background: '#fdf6e3',
125
+ foreground: '#657b83',
126
+ cursor: '#657b83',
127
+ selection: 'rgba(238, 232, 213, 0.8)',
128
+ black: '#073642',
129
+ red: '#dc322f',
130
+ green: '#859900',
131
+ yellow: '#b58900',
132
+ blue: '#268bd2',
133
+ magenta: '#d33682',
134
+ cyan: '#2aa198',
135
+ white: '#fdf6e3',
136
+ },
137
+ };
138
+ function getTheme(theme) {
139
+ if (typeof theme === 'string') {
140
+ return themes[theme] || themes.default;
141
+ }
142
+ return { ...themes.default, ...theme };
143
+ }
144
+
145
+ function generateStyles(containerId, theme, options) {
146
+ const fontSize = options.fontSize || 14;
147
+ const fontFamily = options.fontFamily || "'Cascadia Code', 'Fira Code', 'Consolas', 'Monaco', monospace";
148
+ const cursorStyle = options.cursorStyle || 'block';
149
+ const cursorBlink = options.cursorBlink !== false;
150
+ let cursorWidth = '8px';
151
+ let cursorHeight = '100%';
152
+ if (cursorStyle === 'underline') {
153
+ cursorWidth = '8px';
154
+ cursorHeight = '2px';
155
+ }
156
+ else if (cursorStyle === 'bar') {
157
+ cursorWidth = '2px';
158
+ cursorHeight = '100%';
159
+ }
160
+ return `
161
+ #${containerId} {
162
+ background-color: ${theme.background};
163
+ color: ${theme.foreground};
164
+ font-family: ${fontFamily};
165
+ font-size: ${fontSize}px;
166
+ line-height: 1.5;
167
+ padding: 16px;
168
+ overflow-y: auto;
169
+ height: 100%;
170
+ box-sizing: border-box;
171
+ position: relative;
172
+ }
173
+
174
+ #${containerId} * {
175
+ box-sizing: border-box;
176
+ }
177
+
178
+ #${containerId}::-webkit-scrollbar {
179
+ width: 10px;
180
+ }
181
+
182
+ #${containerId}::-webkit-scrollbar-track {
183
+ background: ${theme.background};
184
+ }
185
+
186
+ #${containerId}::-webkit-scrollbar-thumb {
187
+ background: ${theme.foreground}33;
188
+ border-radius: 5px;
189
+ }
190
+
191
+ #${containerId}::-webkit-scrollbar-thumb:hover {
192
+ background: ${theme.foreground}55;
193
+ }
194
+
195
+ #${containerId} .terminal-output {
196
+ white-space: pre-wrap;
197
+ word-wrap: break-word;
198
+ }
199
+
200
+ #${containerId} .terminal-line {
201
+ display: flex;
202
+ flex-wrap: wrap;
203
+ margin: 0;
204
+ padding: 0;
205
+ }
206
+
207
+ #${containerId} .terminal-prompt {
208
+ color: ${theme.green || '#00ff00'};
209
+ margin-right: 8px;
210
+ user-select: none;
211
+ flex-shrink: 0;
212
+ }
213
+
214
+ #${containerId} .terminal-input-wrapper {
215
+ display: flex;
216
+ flex: 1;
217
+ min-width: 0;
218
+ position: relative;
219
+ }
220
+
221
+ #${containerId} .terminal-input {
222
+ background: transparent;
223
+ border: none;
224
+ outline: none;
225
+ color: ${theme.foreground};
226
+ font-family: inherit;
227
+ font-size: inherit;
228
+ flex: 1;
229
+ min-width: 0;
230
+ caret-color: transparent;
231
+ padding: 0;
232
+ margin: 0;
233
+ }
234
+
235
+ #${containerId} .terminal-input::selection {
236
+ background: ${theme.selection || 'rgba(255, 255, 255, 0.3)'};
237
+ }
238
+
239
+ #${containerId} .terminal-cursor {
240
+ display: inline-block;
241
+ width: ${cursorWidth};
242
+ height: ${cursorHeight};
243
+ background-color: ${theme.cursor};
244
+ position: absolute;
245
+ pointer-events: none;
246
+ ${cursorStyle === 'underline' ? 'bottom: 2px;' : ''}
247
+ ${cursorBlink ? `animation: terminal-cursor-blink-${containerId} 1s step-end infinite;` : ''}
248
+ }
249
+
250
+ @keyframes terminal-cursor-blink-${containerId} {
251
+ 0%, 100% { opacity: 1; }
252
+ 50% { opacity: 0; }
253
+ }
254
+
255
+ #${containerId} .terminal-hidden-input {
256
+ position: absolute;
257
+ left: -9999px;
258
+ opacity: 0;
259
+ }
260
+
261
+ #${containerId} .terminal-history-line {
262
+ margin: 0;
263
+ padding: 0;
264
+ }
265
+
266
+ #${containerId} .terminal-error {
267
+ color: ${theme.red || '#ff0000'};
268
+ }
269
+
270
+ #${containerId} .terminal-success {
271
+ color: ${theme.green || '#00ff00'};
272
+ }
273
+
274
+ #${containerId} .terminal-warning {
275
+ color: ${theme.yellow || '#ffff00'};
276
+ }
277
+
278
+ #${containerId} .terminal-info {
279
+ color: ${theme.blue || '#0000ff'};
280
+ }
281
+
282
+ #${containerId} .text-bold {
283
+ font-weight: bold;
284
+ }
285
+
286
+ #${containerId} .text-italic {
287
+ font-style: italic;
288
+ }
289
+
290
+ #${containerId} .text-underline {
291
+ text-decoration: underline;
292
+ }
293
+
294
+ #${containerId}.terminal-disabled {
295
+ opacity: 0.7;
296
+ pointer-events: none;
297
+ }
298
+ `;
299
+ }
300
+
301
+ class HistoryManager {
302
+ constructor(maxSize = 100) {
303
+ this.history = [];
304
+ this.historyIndex = -1;
305
+ this.tempInput = '';
306
+ this.maxSize = maxSize;
307
+ }
308
+ add(command) {
309
+ if (!command.trim())
310
+ return;
311
+ // Avoid duplicates at the end
312
+ if (this.history[this.history.length - 1] === command) {
313
+ this.historyIndex = -1;
314
+ return;
315
+ }
316
+ this.history.push(command);
317
+ if (this.history.length > this.maxSize) {
318
+ this.history.shift();
319
+ }
320
+ this.historyIndex = -1;
321
+ this.tempInput = '';
322
+ }
323
+ navigateUp(currentInput) {
324
+ if (this.history.length === 0)
325
+ return null;
326
+ if (this.historyIndex === -1) {
327
+ this.tempInput = currentInput;
328
+ this.historyIndex = this.history.length - 1;
329
+ }
330
+ else if (this.historyIndex > 0) {
331
+ this.historyIndex--;
332
+ }
333
+ return this.history[this.historyIndex];
334
+ }
335
+ navigateDown() {
336
+ if (this.historyIndex === -1)
337
+ return null;
338
+ if (this.historyIndex < this.history.length - 1) {
339
+ this.historyIndex++;
340
+ return this.history[this.historyIndex];
341
+ }
342
+ else {
343
+ this.historyIndex = -1;
344
+ return this.tempInput;
345
+ }
346
+ }
347
+ reset() {
348
+ this.historyIndex = -1;
349
+ this.tempInput = '';
350
+ }
351
+ getAll() {
352
+ return [...this.history];
353
+ }
354
+ clear() {
355
+ this.history = [];
356
+ this.historyIndex = -1;
357
+ this.tempInput = '';
358
+ }
359
+ setHistory(history) {
360
+ this.history = history.slice(-this.maxSize);
361
+ this.historyIndex = -1;
362
+ }
363
+ }
364
+
365
+ class CommandRegistry {
366
+ constructor() {
367
+ this.commands = new Map();
368
+ this.aliases = new Map();
369
+ }
370
+ register(definition) {
371
+ this.commands.set(definition.name, definition);
372
+ if (definition.aliases) {
373
+ definition.aliases.forEach(alias => {
374
+ this.aliases.set(alias, definition.name);
375
+ });
376
+ }
377
+ }
378
+ registerSimple(name, handler, description) {
379
+ this.register({
380
+ name,
381
+ handler,
382
+ description,
383
+ });
384
+ }
385
+ unregister(name) {
386
+ const command = this.commands.get(name);
387
+ if (!command)
388
+ return false;
389
+ if (command.aliases) {
390
+ command.aliases.forEach(alias => {
391
+ this.aliases.delete(alias);
392
+ });
393
+ }
394
+ return this.commands.delete(name);
395
+ }
396
+ get(name) {
397
+ const commandName = this.aliases.get(name) || name;
398
+ return this.commands.get(commandName);
399
+ }
400
+ has(name) {
401
+ return this.commands.has(name) || this.aliases.has(name);
402
+ }
403
+ getAll() {
404
+ return Array.from(this.commands.values());
405
+ }
406
+ getNames() {
407
+ return Array.from(this.commands.keys());
408
+ }
409
+ parseInput(input) {
410
+ const parts = this.tokenize(input);
411
+ const command = parts[0] || '';
412
+ const args = [];
413
+ const flags = {};
414
+ for (let i = 1; i < parts.length; i++) {
415
+ const part = parts[i];
416
+ if (part.startsWith('--')) {
417
+ const [key, value] = part.slice(2).split('=');
418
+ flags[key] = value !== undefined ? value : true;
419
+ }
420
+ else if (part.startsWith('-') && part.length === 2) {
421
+ flags[part.slice(1)] = true;
422
+ }
423
+ else {
424
+ args.push(part);
425
+ }
426
+ }
427
+ return { command, args, flags };
428
+ }
429
+ tokenize(input) {
430
+ const tokens = [];
431
+ let current = '';
432
+ let inQuote = false;
433
+ let quoteChar = '';
434
+ for (let i = 0; i < input.length; i++) {
435
+ const char = input[i];
436
+ if ((char === '"' || char === "'") && !inQuote) {
437
+ inQuote = true;
438
+ quoteChar = char;
439
+ }
440
+ else if (char === quoteChar && inQuote) {
441
+ inQuote = false;
442
+ quoteChar = '';
443
+ }
444
+ else if (char === ' ' && !inQuote) {
445
+ if (current) {
446
+ tokens.push(current);
447
+ current = '';
448
+ }
449
+ }
450
+ else {
451
+ current += char;
452
+ }
453
+ }
454
+ if (current) {
455
+ tokens.push(current);
456
+ }
457
+ return tokens;
458
+ }
459
+ }
460
+
461
+ class OutputManager {
462
+ constructor(container, maxLines = 1000) {
463
+ this.container = container;
464
+ this.maxLines = maxLines;
465
+ this.outputElement = document.createElement('div');
466
+ this.outputElement.className = 'terminal-output';
467
+ this.container.appendChild(this.outputElement);
468
+ }
469
+ write(text, options = {}) {
470
+ const span = document.createElement('span');
471
+ if (options.html) {
472
+ span.innerHTML = text;
473
+ }
474
+ else {
475
+ span.textContent = text;
476
+ }
477
+ this.applyStyles(span, options);
478
+ this.outputElement.appendChild(span);
479
+ this.trimOutput();
480
+ this.scrollToBottom();
481
+ return span;
482
+ }
483
+ writeln(text, options = {}) {
484
+ const div = document.createElement('div');
485
+ div.className = 'terminal-history-line';
486
+ if (options.html) {
487
+ div.innerHTML = text;
488
+ }
489
+ else {
490
+ div.textContent = text;
491
+ }
492
+ this.applyStyles(div, options);
493
+ this.outputElement.appendChild(div);
494
+ this.trimOutput();
495
+ this.scrollToBottom();
496
+ return div;
497
+ }
498
+ writeCommand(prompt, command) {
499
+ const line = document.createElement('div');
500
+ line.className = 'terminal-line terminal-history-line';
501
+ const promptSpan = document.createElement('span');
502
+ promptSpan.className = 'terminal-prompt';
503
+ promptSpan.textContent = prompt;
504
+ const commandSpan = document.createElement('span');
505
+ commandSpan.textContent = command;
506
+ line.appendChild(promptSpan);
507
+ line.appendChild(commandSpan);
508
+ this.outputElement.appendChild(line);
509
+ this.trimOutput();
510
+ this.scrollToBottom();
511
+ }
512
+ writeError(text) {
513
+ return this.writeln(text, { color: 'inherit', className: 'terminal-error' });
514
+ }
515
+ writeSuccess(text) {
516
+ return this.writeln(text, { color: 'inherit', className: 'terminal-success' });
517
+ }
518
+ writeWarning(text) {
519
+ return this.writeln(text, { color: 'inherit', className: 'terminal-warning' });
520
+ }
521
+ writeInfo(text) {
522
+ return this.writeln(text, { color: 'inherit', className: 'terminal-info' });
523
+ }
524
+ writeTable(data, columns) {
525
+ if (data.length === 0)
526
+ return;
527
+ const cols = columns || Object.keys(data[0]);
528
+ const widths = {};
529
+ // Calculate column widths
530
+ cols.forEach(col => {
531
+ widths[col] = col.length;
532
+ data.forEach(row => {
533
+ const val = String(row[col] ?? '');
534
+ widths[col] = Math.max(widths[col], val.length);
535
+ });
536
+ });
537
+ // Header
538
+ const header = cols.map(col => col.padEnd(widths[col])).join(' | ');
539
+ const separator = cols.map(col => '-'.repeat(widths[col])).join('-+-');
540
+ this.writeln(header);
541
+ this.writeln(separator);
542
+ // Rows
543
+ data.forEach(row => {
544
+ const line = cols.map(col => String(row[col] ?? '').padEnd(widths[col])).join(' | ');
545
+ this.writeln(line);
546
+ });
547
+ }
548
+ clear() {
549
+ this.outputElement.innerHTML = '';
550
+ }
551
+ applyStyles(element, options) {
552
+ if (options.color) {
553
+ element.style.color = options.color;
554
+ }
555
+ if (options.backgroundColor) {
556
+ element.style.backgroundColor = options.backgroundColor;
557
+ }
558
+ if (options.bold) {
559
+ element.classList.add('text-bold');
560
+ }
561
+ if (options.italic) {
562
+ element.classList.add('text-italic');
563
+ }
564
+ if (options.underline) {
565
+ element.classList.add('text-underline');
566
+ }
567
+ if (options.className) {
568
+ element.classList.add(options.className);
569
+ }
570
+ }
571
+ trimOutput() {
572
+ const lines = this.outputElement.children;
573
+ while (lines.length > this.maxLines) {
574
+ this.outputElement.removeChild(lines[0]);
575
+ }
576
+ }
577
+ scrollToBottom() {
578
+ this.container.scrollTop = this.container.scrollHeight;
579
+ }
580
+ getElement() {
581
+ return this.outputElement;
582
+ }
583
+ }
584
+
585
+ class Terminal {
586
+ constructor(options) {
587
+ this.isEnabled = true;
588
+ this.eventListeners = new Map();
589
+ this.terminalId = `terminal-${Math.random().toString(36).substr(2, 9)}`;
590
+ // Get container
591
+ if (typeof options.container === 'string') {
592
+ const el = document.querySelector(options.container);
593
+ if (!el)
594
+ throw new Error(`Container not found: ${options.container}`);
595
+ this.container = el;
596
+ }
597
+ else {
598
+ this.container = options.container;
599
+ }
600
+ // Set default options
601
+ this.options = {
602
+ container: this.container,
603
+ prompt: options.prompt ?? '$ ',
604
+ welcomeMessage: options.welcomeMessage ?? '',
605
+ theme: options.theme ?? 'default',
606
+ fontSize: options.fontSize ?? 14,
607
+ fontFamily: options.fontFamily ?? "'Cascadia Code', 'Fira Code', Consolas, monospace",
608
+ cursorStyle: options.cursorStyle ?? 'block',
609
+ cursorBlink: options.cursorBlink ?? true,
610
+ history: options.history ?? true,
611
+ historySize: options.historySize ?? 100,
612
+ autoFocus: options.autoFocus ?? true,
613
+ scrollback: options.scrollback ?? 1000,
614
+ tabSize: options.tabSize ?? 4,
615
+ };
616
+ this.theme = getTheme(this.options.theme);
617
+ this.history = new HistoryManager(this.options.historySize);
618
+ this.commands = new CommandRegistry();
619
+ this.init();
620
+ this.output = new OutputManager(this.container, this.options.scrollback);
621
+ this.createInputLine();
622
+ this.registerBuiltInCommands();
623
+ this.showWelcomeMessage();
624
+ this.emit('ready');
625
+ }
626
+ init() {
627
+ this.container.id = this.terminalId;
628
+ this.container.innerHTML = '';
629
+ // Inject styles
630
+ this.styleElement = document.createElement('style');
631
+ this.styleElement.textContent = generateStyles(this.terminalId, this.theme, this.options);
632
+ document.head.appendChild(this.styleElement);
633
+ // Click to focus
634
+ this.container.addEventListener('click', () => {
635
+ if (this.isEnabled) {
636
+ this.focus();
637
+ }
638
+ });
639
+ }
640
+ createInputLine() {
641
+ this.inputLine = document.createElement('div');
642
+ this.inputLine.className = 'terminal-line';
643
+ this.promptElement = document.createElement('span');
644
+ this.promptElement.className = 'terminal-prompt';
645
+ this.promptElement.textContent = this.options.prompt;
646
+ const inputWrapper = document.createElement('div');
647
+ inputWrapper.className = 'terminal-input-wrapper';
648
+ this.inputElement = document.createElement('input');
649
+ this.inputElement.type = 'text';
650
+ this.inputElement.className = 'terminal-input';
651
+ this.inputElement.spellcheck = false;
652
+ this.inputElement.autocomplete = 'off';
653
+ this.inputElement.autocapitalize = 'off';
654
+ this.cursorElement = document.createElement('span');
655
+ this.cursorElement.className = 'terminal-cursor';
656
+ inputWrapper.appendChild(this.inputElement);
657
+ inputWrapper.appendChild(this.cursorElement);
658
+ this.inputLine.appendChild(this.promptElement);
659
+ this.inputLine.appendChild(inputWrapper);
660
+ this.container.appendChild(this.inputLine);
661
+ this.setupInputHandlers();
662
+ this.updateCursor();
663
+ if (this.options.autoFocus) {
664
+ this.focus();
665
+ }
666
+ }
667
+ setupInputHandlers() {
668
+ this.inputElement.addEventListener('input', () => {
669
+ this.updateCursor();
670
+ this.emit('input', this.inputElement.value);
671
+ });
672
+ this.inputElement.addEventListener('keydown', (e) => {
673
+ this.emit('key', e);
674
+ this.handleKeyDown(e);
675
+ });
676
+ this.inputElement.addEventListener('blur', () => {
677
+ this.cursorElement.style.display = 'none';
678
+ });
679
+ this.inputElement.addEventListener('focus', () => {
680
+ this.cursorElement.style.display = 'inline-block';
681
+ });
682
+ }
683
+ handleKeyDown(e) {
684
+ switch (e.key) {
685
+ case 'Enter':
686
+ e.preventDefault();
687
+ this.executeCurrentInput();
688
+ break;
689
+ case 'ArrowUp':
690
+ e.preventDefault();
691
+ if (this.options.history) {
692
+ const prev = this.history.navigateUp(this.inputElement.value);
693
+ if (prev !== null) {
694
+ this.inputElement.value = prev;
695
+ this.updateCursor();
696
+ // Move cursor to end
697
+ setTimeout(() => {
698
+ this.inputElement.selectionStart = this.inputElement.selectionEnd = this.inputElement.value.length;
699
+ }, 0);
700
+ }
701
+ }
702
+ break;
703
+ case 'ArrowDown':
704
+ e.preventDefault();
705
+ if (this.options.history) {
706
+ const next = this.history.navigateDown();
707
+ if (next !== null) {
708
+ this.inputElement.value = next;
709
+ this.updateCursor();
710
+ }
711
+ }
712
+ break;
713
+ case 'Tab':
714
+ e.preventDefault();
715
+ this.handleTabCompletion();
716
+ break;
717
+ case 'c':
718
+ if (e.ctrlKey) {
719
+ e.preventDefault();
720
+ this.output.writeCommand(this.options.prompt, this.inputElement.value + '^C');
721
+ this.inputElement.value = '';
722
+ this.updateCursor();
723
+ this.history.reset();
724
+ }
725
+ break;
726
+ case 'l':
727
+ if (e.ctrlKey) {
728
+ e.preventDefault();
729
+ this.clear();
730
+ }
731
+ break;
732
+ case 'u':
733
+ if (e.ctrlKey) {
734
+ e.preventDefault();
735
+ this.inputElement.value = '';
736
+ this.updateCursor();
737
+ }
738
+ break;
739
+ }
740
+ }
741
+ handleTabCompletion() {
742
+ const input = this.inputElement.value;
743
+ const parts = input.split(' ');
744
+ if (parts.length === 1) {
745
+ // Command completion
746
+ const partial = parts[0].toLowerCase();
747
+ const matches = this.commands.getNames().filter(name => name.toLowerCase().startsWith(partial));
748
+ if (matches.length === 1) {
749
+ this.inputElement.value = matches[0] + ' ';
750
+ this.updateCursor();
751
+ }
752
+ else if (matches.length > 1) {
753
+ this.output.writeCommand(this.options.prompt, input);
754
+ this.output.writeln(matches.join(' '));
755
+ }
756
+ }
757
+ }
758
+ async executeCurrentInput() {
759
+ const input = this.inputElement.value.trim();
760
+ // Show the command in output
761
+ this.output.writeCommand(this.options.prompt, this.inputElement.value);
762
+ // Clear input
763
+ this.inputElement.value = '';
764
+ this.updateCursor();
765
+ if (!input)
766
+ return;
767
+ // Add to history
768
+ if (this.options.history) {
769
+ this.history.add(input);
770
+ }
771
+ // Parse and execute
772
+ const { command, args, flags } = this.commands.parseInput(input);
773
+ this.emit('command', command, args);
774
+ const commandDef = this.commands.get(command);
775
+ if (commandDef) {
776
+ try {
777
+ const context = {
778
+ terminal: this.getInterface(),
779
+ args,
780
+ rawInput: input,
781
+ flags,
782
+ };
783
+ const result = await commandDef.handler(context);
784
+ if (typeof result === 'string') {
785
+ this.output.writeln(result);
786
+ }
787
+ }
788
+ catch (error) {
789
+ const message = error instanceof Error ? error.message : String(error);
790
+ this.output.writeError(`Error: ${message}`);
791
+ }
792
+ }
793
+ else if (command) {
794
+ this.output.writeError(`Command not found: ${command}`);
795
+ this.output.writeln("Type 'help' for available commands.");
796
+ }
797
+ this.output.scrollToBottom();
798
+ }
799
+ updateCursor() {
800
+ const text = this.inputElement.value;
801
+ const selectionStart = this.inputElement.selectionStart || 0;
802
+ // Create a temporary span to measure text width
803
+ const measurer = document.createElement('span');
804
+ measurer.style.font = getComputedStyle(this.inputElement).font;
805
+ measurer.style.visibility = 'hidden';
806
+ measurer.style.position = 'absolute';
807
+ measurer.style.whiteSpace = 'pre';
808
+ measurer.textContent = text.substring(0, selectionStart);
809
+ document.body.appendChild(measurer);
810
+ const width = measurer.offsetWidth;
811
+ document.body.removeChild(measurer);
812
+ this.cursorElement.style.left = `${width}px`;
813
+ }
814
+ showWelcomeMessage() {
815
+ const message = this.options.welcomeMessage;
816
+ if (!message)
817
+ return;
818
+ if (Array.isArray(message)) {
819
+ message.forEach(line => this.output.writeln(line));
820
+ }
821
+ else {
822
+ this.output.writeln(message);
823
+ }
824
+ }
825
+ registerBuiltInCommands() {
826
+ // Help command
827
+ this.commands.register({
828
+ name: 'help',
829
+ description: 'Show available commands',
830
+ aliases: ['?', 'commands'],
831
+ handler: ({ args }) => {
832
+ if (args.length > 0) {
833
+ const cmd = this.commands.get(args[0]);
834
+ if (cmd) {
835
+ this.output.writeln(`${cmd.name}: ${cmd.description || 'No description'}`);
836
+ if (cmd.usage) {
837
+ this.output.writeln(`Usage: ${cmd.usage}`);
838
+ }
839
+ if (cmd.aliases && cmd.aliases.length > 0) {
840
+ this.output.writeln(`Aliases: ${cmd.aliases.join(', ')}`);
841
+ }
842
+ }
843
+ else {
844
+ this.output.writeError(`Unknown command: ${args[0]}`);
845
+ }
846
+ }
847
+ else {
848
+ this.output.writeln('Available commands:');
849
+ this.output.writeln('');
850
+ const commands = this.commands.getAll();
851
+ const maxLength = Math.max(...commands.map(c => c.name.length));
852
+ commands.forEach(cmd => {
853
+ const name = cmd.name.padEnd(maxLength + 2);
854
+ const desc = cmd.description || '';
855
+ this.output.writeln(` ${name}${desc}`);
856
+ });
857
+ }
858
+ },
859
+ });
860
+ // Clear command
861
+ this.commands.register({
862
+ name: 'clear',
863
+ description: 'Clear the terminal',
864
+ aliases: ['cls'],
865
+ handler: () => {
866
+ this.clear();
867
+ },
868
+ });
869
+ // History command
870
+ this.commands.register({
871
+ name: 'history',
872
+ description: 'Show command history',
873
+ handler: () => {
874
+ const hist = this.history.getAll();
875
+ if (hist.length === 0) {
876
+ this.output.writeln('No history');
877
+ }
878
+ else {
879
+ hist.forEach((cmd, i) => {
880
+ this.output.writeln(` ${(i + 1).toString().padStart(4)} ${cmd}`);
881
+ });
882
+ }
883
+ },
884
+ });
885
+ // Echo command
886
+ this.commands.register({
887
+ name: 'echo',
888
+ description: 'Print text to the terminal',
889
+ usage: 'echo <text>',
890
+ handler: ({ args }) => {
891
+ return args.join(' ');
892
+ },
893
+ });
894
+ }
895
+ getInterface() {
896
+ return {
897
+ write: (text, options) => this.write(text, options),
898
+ writeln: (text, options) => this.writeln(text, options),
899
+ clear: () => this.clear(),
900
+ setPrompt: (prompt) => this.setPrompt(prompt),
901
+ focus: () => this.focus(),
902
+ blur: () => this.blur(),
903
+ disable: () => this.disable(),
904
+ enable: () => this.enable(),
905
+ getHistory: () => this.history.getAll(),
906
+ destroy: () => this.destroy(),
907
+ };
908
+ }
909
+ // Public API
910
+ write(text, options) {
911
+ this.output.write(text, options);
912
+ }
913
+ writeln(text, options) {
914
+ this.output.writeln(text, options);
915
+ }
916
+ writeError(text) {
917
+ this.output.writeError(text);
918
+ }
919
+ writeSuccess(text) {
920
+ this.output.writeSuccess(text);
921
+ }
922
+ writeWarning(text) {
923
+ this.output.writeWarning(text);
924
+ }
925
+ writeInfo(text) {
926
+ this.output.writeInfo(text);
927
+ }
928
+ writeTable(data, columns) {
929
+ this.output.writeTable(data, columns);
930
+ }
931
+ clear() {
932
+ this.output.clear();
933
+ this.emit('clear');
934
+ }
935
+ focus() {
936
+ this.inputElement.focus();
937
+ }
938
+ blur() {
939
+ this.inputElement.blur();
940
+ }
941
+ disable() {
942
+ this.isEnabled = false;
943
+ this.inputElement.disabled = true;
944
+ this.container.classList.add('terminal-disabled');
945
+ }
946
+ enable() {
947
+ this.isEnabled = true;
948
+ this.inputElement.disabled = false;
949
+ this.container.classList.remove('terminal-disabled');
950
+ }
951
+ setPrompt(prompt) {
952
+ this.options.prompt = prompt;
953
+ this.promptElement.textContent = prompt;
954
+ }
955
+ getPrompt() {
956
+ return this.options.prompt;
957
+ }
958
+ setValue(value) {
959
+ this.inputElement.value = value;
960
+ this.updateCursor();
961
+ }
962
+ getValue() {
963
+ return this.inputElement.value;
964
+ }
965
+ // Command registration
966
+ registerCommand(definition) {
967
+ this.commands.register(definition);
968
+ }
969
+ registerCommands(definitions) {
970
+ definitions.forEach(def => this.commands.register(def));
971
+ }
972
+ addCommand(name, handler, description) {
973
+ this.commands.registerSimple(name, handler, description);
974
+ }
975
+ removeCommand(name) {
976
+ return this.commands.unregister(name);
977
+ }
978
+ hasCommand(name) {
979
+ return this.commands.has(name);
980
+ }
981
+ getCommands() {
982
+ return this.commands.getAll();
983
+ }
984
+ // Execute command programmatically
985
+ async exec(command) {
986
+ const originalValue = this.inputElement.value;
987
+ this.inputElement.value = command;
988
+ await this.executeCurrentInput();
989
+ this.inputElement.value = originalValue;
990
+ }
991
+ // Theme
992
+ setTheme(theme) {
993
+ this.theme = typeof theme === 'string' ? getTheme(theme) : theme;
994
+ this.styleElement.textContent = generateStyles(this.terminalId, this.theme, this.options);
995
+ }
996
+ getTheme() {
997
+ return { ...this.theme };
998
+ }
999
+ // History
1000
+ getHistory() {
1001
+ return this.history.getAll();
1002
+ }
1003
+ clearHistory() {
1004
+ this.history.clear();
1005
+ }
1006
+ setHistory(history) {
1007
+ this.history.setHistory(history);
1008
+ }
1009
+ // Events
1010
+ on(event, callback) {
1011
+ if (!this.eventListeners.has(event)) {
1012
+ this.eventListeners.set(event, new Set());
1013
+ }
1014
+ this.eventListeners.get(event).add(callback);
1015
+ }
1016
+ off(event, callback) {
1017
+ this.eventListeners.get(event)?.delete(callback);
1018
+ }
1019
+ emit(event, ...args) {
1020
+ this.eventListeners.get(event)?.forEach(callback => {
1021
+ try {
1022
+ callback(...args);
1023
+ }
1024
+ catch (e) {
1025
+ console.error('Event listener error:', e);
1026
+ }
1027
+ });
1028
+ }
1029
+ // Cleanup
1030
+ destroy() {
1031
+ this.styleElement.remove();
1032
+ this.container.innerHTML = '';
1033
+ this.eventListeners.clear();
1034
+ }
1035
+ }
1036
+
1037
+ export { CommandRegistry, HistoryManager, OutputManager, Terminal, Terminal as default, getTheme, themes };
1038
+ //# sourceMappingURL=index.esm.js.map