WACATW commited on
Commit
b667d54
·
verified ·
1 Parent(s): 8f17206

Upload static/index.html

Browse files
Files changed (1) hide show
  1. static/index.html +152 -0
static/index.html ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>终端 - 虚拟电脑</title>
7
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css" />
8
+ <script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.js"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.js"></script>
10
+ <style>
11
+ * { margin: 0; padding: 0; box-sizing: border-box; }
12
+ html, body { width: 100%; height: 100%; background: #1e1e1e; overflow: hidden; }
13
+ #terminal-container { width: 100%; height: 100%; padding: 8px; }
14
+ .xterm { height: 100%; }
15
+ </style>
16
+ </head>
17
+ <body>
18
+ <div id="terminal-container"></div>
19
+ <script>
20
+ const term = new Terminal({
21
+ cursorBlink: true,
22
+ cursorStyle: 'block',
23
+ fontSize: 14,
24
+ fontFamily: '"Cascadia Code", "Fira Code", "JetBrains Mono", "Consolas", monospace',
25
+ theme: {
26
+ background: '#1e1e1e',
27
+ foreground: '#d4d4d4',
28
+ cursor: '#ffffff',
29
+ selectionBackground: '#264f78',
30
+ black: '#000000',
31
+ red: '#cd3131',
32
+ green: '#0dbc79',
33
+ yellow: '#e5e510',
34
+ blue: '#2472c8',
35
+ magenta: '#bc3fbc',
36
+ cyan: '#11a8cd',
37
+ white: '#e5e5e5',
38
+ },
39
+ scrollback: 5000,
40
+ convertEol: true,
41
+ });
42
+ const fitAddon = new FitAddon.FitAddon();
43
+ term.loadAddon(fitAddon);
44
+ term.open(document.getElementById('terminal-container'));
45
+ fitAddon.fit();
46
+ window.addEventListener('resize', () => fitAddon.fit());
47
+
48
+ let ws = null;
49
+ let currentLine = '';
50
+ let commandHistory = [];
51
+ let historyIndex = -1;
52
+
53
+ function connectWebSocket() {
54
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
55
+ const wsUrl = `${protocol}//${window.location.host}/terminal/ws/terminal`;
56
+ ws = new WebSocket(wsUrl);
57
+
58
+ ws.onopen = () => {
59
+ term.writeln('\x1b[1;32m✓ 已连接到虚拟电脑终端\x1b[0m');
60
+ term.writeln('\x1b[90m输入 Shell 命令,按 Enter 执行。\x1b[0m');
61
+ term.writeln('');
62
+ showPrompt();
63
+ };
64
+
65
+ ws.onmessage = (event) => {
66
+ const output = event.data;
67
+ term.write(output);
68
+ if (!output.endsWith('\n')) term.write('\r\n');
69
+ showPrompt();
70
+ };
71
+
72
+ ws.onclose = () => {
73
+ term.writeln('\r\n\x1b[1;31m✗ 连接已断开。请刷新页面重连。\x1b[0m');
74
+ };
75
+
76
+ ws.onerror = () => {
77
+ term.writeln('\r\n\x1b[1;31m✗ 连接错误\x1b[0m');
78
+ };
79
+ }
80
+
81
+ function showPrompt() {
82
+ term.write('\x1b[1;36m$\x1b[0m ');
83
+ }
84
+
85
+ term.onData((data) => {
86
+ if (data === '\r') {
87
+ term.write('\r\n');
88
+ if (currentLine.trim()) {
89
+ commandHistory.push(currentLine);
90
+ historyIndex = commandHistory.length;
91
+ if (currentLine.trim() === 'clear') {
92
+ term.clear();
93
+ showPrompt();
94
+ } else if (currentLine.trim() === 'exit') {
95
+ term.writeln('\x1b[90m再见!\x1b[0m');
96
+ } else {
97
+ if (ws && ws.readyState === WebSocket.OPEN) {
98
+ ws.send(currentLine);
99
+ } else {
100
+ term.writeln('\x1b[1;31m✗ 未连接到后端\x1b[0m');
101
+ showPrompt();
102
+ }
103
+ }
104
+ } else {
105
+ showPrompt();
106
+ }
107
+ currentLine = '';
108
+ return;
109
+ }
110
+ if (data === '\x7f') {
111
+ if (currentLine.length > 0) {
112
+ currentLine = currentLine.slice(0, -1);
113
+ term.write('\b \b');
114
+ }
115
+ return;
116
+ }
117
+ if (data === '\x1b[A') {
118
+ if (historyIndex > 0) {
119
+ historyIndex--;
120
+ term.write('\r\x1b[K');
121
+ showPrompt();
122
+ currentLine = commandHistory[historyIndex];
123
+ term.write(currentLine);
124
+ }
125
+ return;
126
+ }
127
+ if (data === '\x1b[B') {
128
+ if (historyIndex < commandHistory.length - 1) {
129
+ historyIndex++;
130
+ term.write('\r\x1b[K');
131
+ showPrompt();
132
+ currentLine = commandHistory[historyIndex];
133
+ term.write(currentLine);
134
+ } else {
135
+ historyIndex = commandHistory.length;
136
+ term.write('\r\x1b[K');
137
+ showPrompt();
138
+ currentLine = '';
139
+ }
140
+ return;
141
+ }
142
+ if (data >= ' ' || data === '\t') {
143
+ currentLine += data;
144
+ term.write(data);
145
+ }
146
+ });
147
+
148
+ connectWebSocket();
149
+ setTimeout(() => term.focus(), 100);
150
+ </script>
151
+ </body>
152
+ </html>