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