|
1 | 1 | <script lang="ts"> |
2 | | - import '@xterm/xterm/css/xterm.css'; |
3 | | -
|
4 | | - import { FitAddon } from '@xterm/addon-fit'; |
5 | | - import { Terminal } from '@xterm/xterm'; |
6 | | - import { onDestroy, onMount } from 'svelte'; |
7 | | -
|
8 | | - import { getTerminalTheme } from './terminal-theme'; |
9 | | - import TerminalSearchControls from './TerminalSearchControls.svelte'; |
10 | | -
|
11 | | - interface Props { |
12 | | - terminal?: Terminal; |
13 | | - convertEol?: boolean; |
14 | | - disableStdIn?: boolean; |
15 | | - screenReaderMode?: boolean; |
16 | | - showCursor?: boolean; |
17 | | - search?: boolean; |
18 | | - class?: string; |
19 | | - fontSize?: number; |
20 | | - } |
21 | | -
|
22 | | - let { |
23 | | - terminal = $bindable(), |
24 | | - convertEol, |
25 | | - disableStdIn = true, |
26 | | - screenReaderMode, |
27 | | - showCursor = false, |
28 | | - search = false, |
29 | | - class: className, |
30 | | - fontSize = 10, |
31 | | - }: Props = $props(); |
32 | | -
|
33 | | - let logsXtermDiv: HTMLDivElement | undefined; |
34 | | - let resizeHandler: () => void; |
35 | | - let fitAddon: FitAddon; |
| 2 | + import '@xterm/xterm/css/xterm.css'; |
| 3 | +
|
| 4 | + import { FitAddon } from '@xterm/addon-fit'; |
| 5 | + import { Terminal } from '@xterm/xterm'; |
| 6 | + import { onDestroy, onMount } from 'svelte'; |
| 7 | +
|
| 8 | + import { getTerminalTheme } from './terminal-theme'; |
| 9 | + import TerminalSearchControls from './TerminalSearchControls.svelte'; |
| 10 | +
|
| 11 | + interface Props { |
| 12 | + terminal?: Terminal; |
| 13 | + convertEol?: boolean; |
| 14 | + disableStdIn?: boolean; |
| 15 | + screenReaderMode?: boolean; |
| 16 | + showCursor?: boolean; |
| 17 | + search?: boolean; |
| 18 | + class?: string; |
| 19 | + fontSize?: number; |
| 20 | + } |
36 | 21 |
|
37 | | - async function refreshTerminal(): Promise<void> { |
| 22 | + let { |
| 23 | + terminal = $bindable(), |
| 24 | + convertEol, |
| 25 | + disableStdIn = true, |
| 26 | + screenReaderMode, |
| 27 | + showCursor = false, |
| 28 | + search = false, |
| 29 | + class: className, |
| 30 | + fontSize = 10, |
| 31 | + }: Props = $props(); |
| 32 | +
|
| 33 | + let logsXtermDiv: HTMLDivElement | undefined; |
| 34 | + let resizeHandler: () => void; |
| 35 | + let fitAddon: FitAddon; |
| 36 | +
|
| 37 | + async function refreshTerminal(): Promise<void> { |
38 | 38 | // missing element, return |
39 | 39 | if (!logsXtermDiv) { |
40 | 40 | return; |
41 | 41 | } |
42 | | - |
| 42 | + |
43 | 43 | const lineHeight = 1; // TODO: get from configuration |
44 | 44 |
|
45 | 45 | terminal = new Terminal({ |
|
49 | 49 | theme: getTerminalTheme(), |
50 | 50 | convertEol: convertEol, |
51 | 51 | screenReaderMode: screenReaderMode, |
52 | | - rightClickSelectsWord: true, |
| 52 | + rightClickSelectsWord: true, |
53 | 53 | }); |
54 | 54 | fitAddon = new FitAddon(); |
55 | 55 | terminal.loadAddon(fitAddon); |
|
60 | 60 | terminal.write('\x1b[?25l'); |
61 | 61 | } |
62 | 62 |
|
63 | | - // Handle text selection and copy to clipboard |
64 | | - terminal.onSelectionChange(() => { |
65 | | - const selection = terminal.getSelection(); |
66 | | - if (selection) { |
67 | | - const textarea = document.createElement('textarea'); |
68 | | - textarea.value = selection; |
69 | | - textarea.style.position = 'fixed'; |
70 | | - textarea.style.opacity = '0'; |
71 | | - document.body.appendChild(textarea); |
72 | | - textarea.select(); |
73 | | - try { |
74 | | - document.execCommand('copy'); |
75 | | - } catch (err) { |
76 | | - console.error('Failed to copy:', err); |
77 | | - } |
78 | | - document.body.removeChild(textarea); |
79 | | - } |
80 | | - }); |
81 | | -
|
82 | 63 | // call fit addon each time we resize the window |
83 | 64 | resizeHandler = (): void => { |
84 | 65 | fitAddon.fit(); |
|
0 commit comments