treesap 0.1.13 → 0.2.1
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/README.md +31 -192
- package/dist/app.d.ts +28 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +184 -0
- package/dist/app.js.map +1 -0
- package/dist/context.d.ts +36 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +95 -0
- package/dist/context.js.map +1 -0
- package/dist/index.d.ts +5 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -9
- package/dist/index.js.map +1 -1
- package/dist/middleware/cors.d.ts +11 -0
- package/dist/middleware/cors.d.ts.map +1 -0
- package/dist/middleware/cors.js +34 -0
- package/dist/middleware/cors.js.map +1 -0
- package/dist/middleware/serve-static.d.ts +6 -0
- package/dist/middleware/serve-static.d.ts.map +1 -0
- package/dist/middleware/serve-static.js +68 -0
- package/dist/middleware/serve-static.js.map +1 -0
- package/dist/node.d.ts +8 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +52 -0
- package/dist/node.js.map +1 -0
- package/dist/path.d.ts +10 -0
- package/dist/path.d.ts.map +1 -0
- package/dist/path.js +45 -0
- package/dist/path.js.map +1 -0
- package/dist/vite.d.ts +31 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +278 -0
- package/dist/vite.js.map +1 -0
- package/package.json +33 -40
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -137
- package/dist/cli.js.map +0 -1
- package/dist/components/BaseHead.d.ts +0 -5
- package/dist/components/BaseHead.d.ts.map +0 -1
- package/dist/components/BaseHead.js +0 -161
- package/dist/components/BaseHead.js.map +0 -1
- package/dist/components/ChatInput.d.ts +0 -7
- package/dist/components/ChatInput.d.ts.map +0 -1
- package/dist/components/ChatInput.js +0 -11
- package/dist/components/ChatInput.js.map +0 -1
- package/dist/components/Sidebar.d.ts +0 -8
- package/dist/components/Sidebar.d.ts.map +0 -1
- package/dist/components/Sidebar.js +0 -7
- package/dist/components/Sidebar.js.map +0 -1
- package/dist/components/SimpleLivePreview.d.ts +0 -7
- package/dist/components/SimpleLivePreview.d.ts.map +0 -1
- package/dist/components/SimpleLivePreview.js +0 -7
- package/dist/components/SimpleLivePreview.js.map +0 -1
- package/dist/components/Terminal.d.ts +0 -7
- package/dist/components/Terminal.d.ts.map +0 -1
- package/dist/components/Terminal.js +0 -14
- package/dist/components/Terminal.js.map +0 -1
- package/dist/components/VoiceRecorder.d.ts +0 -4
- package/dist/components/VoiceRecorder.d.ts.map +0 -1
- package/dist/components/VoiceRecorder.js +0 -5
- package/dist/components/VoiceRecorder.js.map +0 -1
- package/dist/components/icons/GeminiLogo.d.ts +0 -7
- package/dist/components/icons/GeminiLogo.d.ts.map +0 -1
- package/dist/components/icons/GeminiLogo.js +0 -5
- package/dist/components/icons/GeminiLogo.js.map +0 -1
- package/dist/components/icons/OllamaLogo.d.ts +0 -2
- package/dist/components/icons/OllamaLogo.d.ts.map +0 -1
- package/dist/components/icons/OllamaLogo.js +0 -5
- package/dist/components/icons/OllamaLogo.js.map +0 -1
- package/dist/layouts/Layout.d.ts +0 -9
- package/dist/layouts/Layout.d.ts.map +0 -1
- package/dist/layouts/Layout.js +0 -9
- package/dist/layouts/Layout.js.map +0 -1
- package/dist/layouts/NotFoundLayout.d.ts +0 -2
- package/dist/layouts/NotFoundLayout.d.ts.map +0 -1
- package/dist/layouts/NotFoundLayout.js +0 -6
- package/dist/layouts/NotFoundLayout.js.map +0 -1
- package/dist/pages/Code.d.ts +0 -7
- package/dist/pages/Code.d.ts.map +0 -1
- package/dist/pages/Code.js +0 -8
- package/dist/pages/Code.js.map +0 -1
- package/dist/pages/Home.d.ts +0 -7
- package/dist/pages/Home.d.ts.map +0 -1
- package/dist/pages/Home.js +0 -8
- package/dist/pages/Home.js.map +0 -1
- package/dist/pages/Welcome.d.ts +0 -2
- package/dist/pages/Welcome.d.ts.map +0 -1
- package/dist/pages/Welcome.js +0 -6
- package/dist/pages/Welcome.js.map +0 -1
- package/dist/server.d.ts +0 -11
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js +0 -434
- package/dist/server.js.map +0 -1
- package/dist/services/dev-server.d.ts +0 -29
- package/dist/services/dev-server.d.ts.map +0 -1
- package/dist/services/dev-server.js +0 -201
- package/dist/services/dev-server.js.map +0 -1
- package/dist/services/terminal.d.ts +0 -46
- package/dist/services/terminal.d.ts.map +0 -1
- package/dist/services/terminal.js +0 -264
- package/dist/services/terminal.js.map +0 -1
- package/dist/services/websocket.d.ts +0 -48
- package/dist/services/websocket.d.ts.map +0 -1
- package/dist/services/websocket.js +0 -332
- package/dist/services/websocket.js.map +0 -1
- package/dist/static/components/ChatInput.js +0 -237
- package/dist/static/components/Sidebar.js +0 -225
- package/dist/static/components/SimpleLivePreview.js +0 -305
- package/dist/static/components/Terminal.js +0 -461
- package/dist/static/components/TerminalTabs.js +0 -383
- package/dist/static/favicon.svg +0 -14
- package/dist/static/signals/LivePreviewSignal.js +0 -71
- package/dist/static/signals/SidebarSignal.js +0 -123
- package/dist/static/signals/TerminalSignal.js +0 -273
- package/dist/static/styles/main.css +0 -1761
- package/src/cli.ts +0 -155
- package/src/components/BaseHead.ts +0 -164
- package/src/components/ChatInput.tsx +0 -56
- package/src/components/Sidebar.tsx +0 -99
- package/src/components/SimpleLivePreview.tsx +0 -40
- package/src/components/Terminal.tsx +0 -40
- package/src/components/VoiceRecorder.tsx +0 -33
- package/src/components/icons/GeminiLogo.tsx +0 -10
- package/src/components/icons/OllamaLogo.tsx +0 -5
- package/src/index.tsx +0 -12
- package/src/layouts/Layout.tsx +0 -41
- package/src/layouts/NotFoundLayout.tsx +0 -15
- package/src/pages/Code.tsx +0 -34
- package/src/pages/Welcome.tsx +0 -56
- package/src/server.tsx +0 -519
- package/src/services/dev-server.ts +0 -234
- package/src/services/terminal.ts +0 -325
- package/src/services/websocket.ts +0 -405
- package/src/static/components/ChatInput.js +0 -237
- package/src/static/components/Sidebar.js +0 -225
- package/src/static/components/SimpleLivePreview.js +0 -305
- package/src/static/components/Terminal.js +0 -461
- package/src/static/components/TerminalTabs.js +0 -383
- package/src/static/favicon.svg +0 -14
- package/src/static/signals/LivePreviewSignal.js +0 -71
- package/src/static/signals/SidebarSignal.js +0 -123
- package/src/static/signals/TerminalSignal.js +0 -273
- package/src/static/styles/main.css +0 -1761
- package/src/styles/input.css +0 -3
- package/tailwind.config.ts +0 -22
- package/tsconfig.json +0 -37
|
@@ -1,461 +0,0 @@
|
|
|
1
|
-
// Terminal component JavaScript using Xterm.js with WebSocket
|
|
2
|
-
import { Terminal } from 'https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/+esm';
|
|
3
|
-
import { terminalStore } from '/signals/TerminalSignal.js';
|
|
4
|
-
|
|
5
|
-
class TerminalManager {
|
|
6
|
-
constructor(terminalId) {
|
|
7
|
-
this.terminalId = terminalId;
|
|
8
|
-
this.container = document.getElementById(terminalId);
|
|
9
|
-
this.xtermContainer = document.getElementById(`${terminalId}-xterm`);
|
|
10
|
-
this.resetBtn = document.getElementById(`${terminalId}-reset-btn`);
|
|
11
|
-
this.status = document.getElementById(`${terminalId}-status`);
|
|
12
|
-
|
|
13
|
-
// Get terminal data from window
|
|
14
|
-
const terminalData = window[`terminalData_${terminalId.replace(/-/g, '_')}`];
|
|
15
|
-
if (!terminalData) {
|
|
16
|
-
console.error(`No terminal data found for ${terminalId}`);
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
this.sessionId = terminalData.sessionId;
|
|
21
|
-
this.index = terminalData.index;
|
|
22
|
-
|
|
23
|
-
console.log(`Terminal ${terminalId} initialized with:`, terminalData);
|
|
24
|
-
|
|
25
|
-
// Register terminal in store
|
|
26
|
-
terminalStore.addTerminal(this.index);
|
|
27
|
-
terminalStore.updateTerminalStatus(terminalId, 'connecting');
|
|
28
|
-
|
|
29
|
-
// WebSocket connection
|
|
30
|
-
this.websocket = null;
|
|
31
|
-
this.terminal = null;
|
|
32
|
-
this.reconnectAttempts = 0;
|
|
33
|
-
this.maxReconnectAttempts = 5;
|
|
34
|
-
this.reconnectDelay = 1000; // Start with 1 second
|
|
35
|
-
|
|
36
|
-
this.init();
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
init() {
|
|
40
|
-
console.log('Terminal init called with ID:', this.terminalId);
|
|
41
|
-
console.log('Container found:', !!this.container);
|
|
42
|
-
console.log('Xterm container found:', !!this.xtermContainer);
|
|
43
|
-
|
|
44
|
-
if (!this.container || !this.xtermContainer) {
|
|
45
|
-
console.error('Terminal containers not found! Looking for ID:', this.terminalId);
|
|
46
|
-
terminalStore.updateTerminalStatus(this.terminalId, 'error');
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Initialize Xterm.js
|
|
51
|
-
this.setupXterm();
|
|
52
|
-
|
|
53
|
-
// Set up event listeners
|
|
54
|
-
this.setupEventListeners();
|
|
55
|
-
|
|
56
|
-
// Connect to terminal via WebSocket
|
|
57
|
-
this.connectToTerminal();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
setupXterm() {
|
|
61
|
-
// Detect mobile devices for responsive terminal setup
|
|
62
|
-
const isMobile = window.innerWidth <= 768 || /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
63
|
-
|
|
64
|
-
// Create terminal instance with VS Code-like theme and responsive settings
|
|
65
|
-
this.terminal = new Terminal({
|
|
66
|
-
cursorBlink: true,
|
|
67
|
-
fontSize: 12,
|
|
68
|
-
fontFamily: 'Monaco, Menlo, "Ubuntu Mono", monospace',
|
|
69
|
-
theme: {
|
|
70
|
-
background: '#1e1e1e',
|
|
71
|
-
foreground: '#cccccc',
|
|
72
|
-
cursor: '#ffffff',
|
|
73
|
-
cursorAccent: '#1e1e1e',
|
|
74
|
-
selection: '#ffffff40',
|
|
75
|
-
black: '#000000',
|
|
76
|
-
red: '#f14c4c',
|
|
77
|
-
green: '#23d18b',
|
|
78
|
-
yellow: '#f5f543',
|
|
79
|
-
blue: '#3b8eea',
|
|
80
|
-
magenta: '#d670d6',
|
|
81
|
-
cyan: '#29b8db',
|
|
82
|
-
white: '#e5e5e5',
|
|
83
|
-
brightBlack: '#666666',
|
|
84
|
-
brightRed: '#f14c4c',
|
|
85
|
-
brightGreen: '#23d18b',
|
|
86
|
-
brightYellow: '#f5f543',
|
|
87
|
-
brightBlue: '#3b8eea',
|
|
88
|
-
brightMagenta: '#d670d6',
|
|
89
|
-
brightCyan: '#29b8db',
|
|
90
|
-
brightWhite: '#ffffff'
|
|
91
|
-
},
|
|
92
|
-
scrollback: isMobile ? 500 : 1000, // Smaller scrollback for mobile performance
|
|
93
|
-
tabStopWidth: 4,
|
|
94
|
-
allowTransparency: false,
|
|
95
|
-
// Mobile-specific options
|
|
96
|
-
...(isMobile && {
|
|
97
|
-
convertEol: true,
|
|
98
|
-
disableStdin: false
|
|
99
|
-
})
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
// Open terminal in container
|
|
103
|
-
this.terminal.open(this.xtermContainer);
|
|
104
|
-
|
|
105
|
-
// Focus terminal
|
|
106
|
-
this.terminal.focus();
|
|
107
|
-
|
|
108
|
-
// Handle terminal input - pass through to shell
|
|
109
|
-
this.terminal.onData((data) => {
|
|
110
|
-
// Log the input data for debugging
|
|
111
|
-
console.log('Terminal input (manual typing):', JSON.stringify(data), 'char codes:', data.split('').map(c => c.charCodeAt(0)));
|
|
112
|
-
// Send all input directly to the shell session
|
|
113
|
-
this.sendInput(data);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// Fit terminal to container
|
|
117
|
-
this.fitTerminal();
|
|
118
|
-
|
|
119
|
-
// Resize handler
|
|
120
|
-
window.addEventListener('resize', () => {
|
|
121
|
-
// Add a small delay for mobile orientation changes
|
|
122
|
-
clearTimeout(this.resizeTimeout);
|
|
123
|
-
this.resizeTimeout = setTimeout(() => {
|
|
124
|
-
this.fitTerminal();
|
|
125
|
-
}, 100);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
// Mobile orientation change handler
|
|
129
|
-
window.addEventListener('orientationchange', () => {
|
|
130
|
-
// Longer delay for orientation changes as they can be slower
|
|
131
|
-
clearTimeout(this.orientationTimeout);
|
|
132
|
-
this.orientationTimeout = setTimeout(() => {
|
|
133
|
-
this.fitTerminal();
|
|
134
|
-
}, 300);
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
fitTerminal() {
|
|
139
|
-
if (this.terminal && this.xtermContainer) {
|
|
140
|
-
// Wait a moment to ensure container is properly sized
|
|
141
|
-
setTimeout(() => {
|
|
142
|
-
const containerRect = this.xtermContainer.getBoundingClientRect();
|
|
143
|
-
if (containerRect.width > 0 && containerRect.height > 0) {
|
|
144
|
-
// Detect mobile devices for responsive sizing
|
|
145
|
-
const isMobile = window.innerWidth <= 768 || /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
146
|
-
|
|
147
|
-
// Use consistent font size
|
|
148
|
-
const fontSize = 12;
|
|
149
|
-
|
|
150
|
-
// Calculate character dimensions more accurately
|
|
151
|
-
// Create a temporary element to measure actual character size
|
|
152
|
-
const testElement = document.createElement('div');
|
|
153
|
-
testElement.style.fontFamily = this.terminal.options.fontFamily;
|
|
154
|
-
testElement.style.fontSize = `${fontSize}px`;
|
|
155
|
-
testElement.style.position = 'absolute';
|
|
156
|
-
testElement.style.visibility = 'hidden';
|
|
157
|
-
testElement.style.whiteSpace = 'pre';
|
|
158
|
-
testElement.textContent = 'M'; // Use 'M' as it's typically the widest character
|
|
159
|
-
document.body.appendChild(testElement);
|
|
160
|
-
|
|
161
|
-
const charWidth = testElement.getBoundingClientRect().width;
|
|
162
|
-
const charHeight = testElement.getBoundingClientRect().height;
|
|
163
|
-
document.body.removeChild(testElement);
|
|
164
|
-
|
|
165
|
-
// Calculate columns and rows based on actual character dimensions
|
|
166
|
-
const cols = Math.max(20, Math.floor(containerRect.width / charWidth)); // Minimum 20 columns
|
|
167
|
-
const rows = Math.max(10, Math.floor(containerRect.height / charHeight)); // Minimum 10 rows
|
|
168
|
-
|
|
169
|
-
// Store previous dimensions to detect significant changes
|
|
170
|
-
const prevCols = this.lastCols || 0;
|
|
171
|
-
const prevRows = this.lastRows || 0;
|
|
172
|
-
const significantChange = Math.abs(cols - prevCols) > Math.max(10, prevCols * 0.3) ||
|
|
173
|
-
Math.abs(rows - prevRows) > Math.max(5, prevRows * 0.3);
|
|
174
|
-
|
|
175
|
-
console.log(`Fitting terminal ${this.terminalId}: ${cols}x${rows} (container: ${containerRect.width}x${containerRect.height}, charSize: ${charWidth}x${charHeight}, mobile: ${isMobile})`);
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
// Resize the terminal
|
|
179
|
-
this.terminal.resize(cols, rows);
|
|
180
|
-
|
|
181
|
-
// Send resize notification to backend PTY
|
|
182
|
-
if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
|
|
183
|
-
const resizeMessage = {
|
|
184
|
-
type: 'resize',
|
|
185
|
-
sessionId: this.sessionId,
|
|
186
|
-
terminalId: this.terminalId,
|
|
187
|
-
cols: cols,
|
|
188
|
-
rows: rows
|
|
189
|
-
};
|
|
190
|
-
this.websocket.send(JSON.stringify(resizeMessage));
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Force a refresh if there was a significant size change (like mobile rotation)
|
|
194
|
-
if (significantChange && (prevCols > 0 || prevRows > 0)) {
|
|
195
|
-
console.log(`Significant terminal size change detected, forcing refresh`);
|
|
196
|
-
// Small delay to ensure resize is processed, then refresh
|
|
197
|
-
setTimeout(() => {
|
|
198
|
-
if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
|
|
199
|
-
// Send Ctrl+L to clear and refresh the display
|
|
200
|
-
const refreshMessage = {
|
|
201
|
-
type: 'input',
|
|
202
|
-
sessionId: this.sessionId,
|
|
203
|
-
terminalId: this.terminalId,
|
|
204
|
-
data: '\x0C' // Ctrl+L (form feed) to refresh
|
|
205
|
-
};
|
|
206
|
-
this.websocket.send(JSON.stringify(refreshMessage));
|
|
207
|
-
}
|
|
208
|
-
}, 100);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Store current dimensions for next comparison
|
|
212
|
-
this.lastCols = cols;
|
|
213
|
-
this.lastRows = rows;
|
|
214
|
-
|
|
215
|
-
} else {
|
|
216
|
-
console.warn(`Terminal ${this.terminalId} container has zero dimensions, retrying...`);
|
|
217
|
-
// Retry after a short delay
|
|
218
|
-
setTimeout(() => this.fitTerminal(), 100);
|
|
219
|
-
}
|
|
220
|
-
}, 10);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
setupEventListeners() {
|
|
225
|
-
// Handle reset button
|
|
226
|
-
if (this.resetBtn) {
|
|
227
|
-
this.resetBtn.addEventListener('click', () => {
|
|
228
|
-
this.clearTerminal();
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
sendInput(data) {
|
|
234
|
-
// Send input via WebSocket
|
|
235
|
-
if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
|
|
236
|
-
const message = {
|
|
237
|
-
type: 'input',
|
|
238
|
-
sessionId: this.sessionId,
|
|
239
|
-
terminalId: this.terminalId,
|
|
240
|
-
data: data
|
|
241
|
-
};
|
|
242
|
-
this.websocket.send(JSON.stringify(message));
|
|
243
|
-
} else {
|
|
244
|
-
console.error('WebSocket not connected, cannot send input');
|
|
245
|
-
this.updateStatus('Disconnected');
|
|
246
|
-
terminalStore.updateTerminalStatus(this.terminalId, 'disconnected');
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
connectToTerminal() {
|
|
251
|
-
if (this.websocket) {
|
|
252
|
-
this.websocket.close();
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
this.updateStatus('Connecting...');
|
|
256
|
-
|
|
257
|
-
// Create WebSocket connection
|
|
258
|
-
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
259
|
-
const wsUrl = `${protocol}//${window.location.host}/terminal/ws`;
|
|
260
|
-
|
|
261
|
-
console.log(`Connecting to WebSocket: ${wsUrl}`);
|
|
262
|
-
this.websocket = new WebSocket(wsUrl);
|
|
263
|
-
|
|
264
|
-
this.websocket.onopen = () => {
|
|
265
|
-
console.log('WebSocket connected, joining terminal session');
|
|
266
|
-
this.reconnectAttempts = 0;
|
|
267
|
-
this.reconnectDelay = 1000;
|
|
268
|
-
|
|
269
|
-
// Join the terminal session
|
|
270
|
-
const joinMessage = {
|
|
271
|
-
type: 'join',
|
|
272
|
-
sessionId: this.sessionId,
|
|
273
|
-
terminalId: this.terminalId
|
|
274
|
-
};
|
|
275
|
-
this.websocket.send(JSON.stringify(joinMessage));
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
this.websocket.onmessage = (event) => {
|
|
279
|
-
try {
|
|
280
|
-
const data = JSON.parse(event.data);
|
|
281
|
-
console.log('Received WebSocket message:', data.type);
|
|
282
|
-
|
|
283
|
-
switch (data.type) {
|
|
284
|
-
case 'connected':
|
|
285
|
-
this.updateStatus('Ready');
|
|
286
|
-
terminalStore.updateTerminalStatus(this.terminalId, 'connected');
|
|
287
|
-
console.log('Terminal session joined successfully');
|
|
288
|
-
// Dispatch global status for cross-tab sync
|
|
289
|
-
document.dispatchEvent(new CustomEvent('terminal:global_status', {
|
|
290
|
-
detail: { status: 'connected' }
|
|
291
|
-
}));
|
|
292
|
-
break;
|
|
293
|
-
|
|
294
|
-
case 'output':
|
|
295
|
-
if (data.content) {
|
|
296
|
-
this.terminal.write(data.content);
|
|
297
|
-
}
|
|
298
|
-
break;
|
|
299
|
-
|
|
300
|
-
case 'error':
|
|
301
|
-
if (data.content) {
|
|
302
|
-
this.terminal.write(`\x1b[31m${data.content}\x1b[0m`);
|
|
303
|
-
} else if (data.data) {
|
|
304
|
-
this.terminal.write(`\x1b[31m${data.data}\x1b[0m`);
|
|
305
|
-
}
|
|
306
|
-
break;
|
|
307
|
-
|
|
308
|
-
case 'exit':
|
|
309
|
-
this.terminal.writeln(`\x1b[90mProcess exited with code ${data.code || 0}\x1b[0m`);
|
|
310
|
-
this.terminal.write('\x1b[32m$ \x1b[0m');
|
|
311
|
-
break;
|
|
312
|
-
|
|
313
|
-
case 'clients_count':
|
|
314
|
-
console.log(`${data.count} clients connected to this session`);
|
|
315
|
-
// Dispatch event for TerminalSignal to handle cross-tab sync
|
|
316
|
-
document.dispatchEvent(new CustomEvent('terminal:clients_count', {
|
|
317
|
-
detail: {
|
|
318
|
-
sessionId: this.sessionId,
|
|
319
|
-
count: data.count
|
|
320
|
-
}
|
|
321
|
-
}));
|
|
322
|
-
break;
|
|
323
|
-
|
|
324
|
-
case 'session_closed':
|
|
325
|
-
console.log('Terminal session was closed');
|
|
326
|
-
this.updateStatus('Session Closed');
|
|
327
|
-
terminalStore.updateTerminalStatus(this.terminalId, 'closed');
|
|
328
|
-
// Dispatch event for TerminalSignal to handle cross-tab sync
|
|
329
|
-
document.dispatchEvent(new CustomEvent('terminal:session_closed', {
|
|
330
|
-
detail: {
|
|
331
|
-
sessionId: this.sessionId
|
|
332
|
-
}
|
|
333
|
-
}));
|
|
334
|
-
break;
|
|
335
|
-
|
|
336
|
-
case 'pong':
|
|
337
|
-
// Connection health check response
|
|
338
|
-
break;
|
|
339
|
-
|
|
340
|
-
default:
|
|
341
|
-
console.log('Unknown message type:', data.type, data);
|
|
342
|
-
}
|
|
343
|
-
} catch (error) {
|
|
344
|
-
console.error('Error parsing WebSocket message:', error);
|
|
345
|
-
}
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
this.websocket.onerror = (error) => {
|
|
349
|
-
console.error('WebSocket error:', error);
|
|
350
|
-
this.updateStatus('Connection Error');
|
|
351
|
-
terminalStore.updateTerminalStatus(this.terminalId, 'error');
|
|
352
|
-
};
|
|
353
|
-
|
|
354
|
-
this.websocket.onclose = (event) => {
|
|
355
|
-
console.log('WebSocket closed:', event.code, event.reason);
|
|
356
|
-
this.updateStatus('Disconnected');
|
|
357
|
-
terminalStore.updateTerminalStatus(this.terminalId, 'disconnected');
|
|
358
|
-
|
|
359
|
-
// Attempt to reconnect with exponential backoff
|
|
360
|
-
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
361
|
-
this.reconnectAttempts++;
|
|
362
|
-
console.log(`Attempting to reconnect (${this.reconnectAttempts}/${this.maxReconnectAttempts}) in ${this.reconnectDelay}ms`);
|
|
363
|
-
|
|
364
|
-
setTimeout(() => {
|
|
365
|
-
this.connectToTerminal();
|
|
366
|
-
}, this.reconnectDelay);
|
|
367
|
-
|
|
368
|
-
// Exponential backoff: double the delay each time, max 10 seconds
|
|
369
|
-
this.reconnectDelay = Math.min(this.reconnectDelay * 2, 10000);
|
|
370
|
-
} else {
|
|
371
|
-
console.error('Max reconnection attempts reached');
|
|
372
|
-
this.updateStatus('Connection Failed');
|
|
373
|
-
terminalStore.updateTerminalStatus(this.terminalId, 'failed');
|
|
374
|
-
}
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
updateStatus(status) {
|
|
379
|
-
if (this.status) {
|
|
380
|
-
this.status.textContent = status;
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
clearTerminal() {
|
|
385
|
-
if (this.terminal) {
|
|
386
|
-
this.terminal.clear();
|
|
387
|
-
this.terminal.writeln('\x1b[36mTerminal cleared\x1b[0m');
|
|
388
|
-
this.terminal.write('\x1b[32m$ \x1b[0m');
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
async destroy() {
|
|
393
|
-
// Send leave message to WebSocket before closing
|
|
394
|
-
if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
|
|
395
|
-
const leaveMessage = {
|
|
396
|
-
type: 'leave',
|
|
397
|
-
sessionId: this.sessionId,
|
|
398
|
-
terminalId: this.terminalId
|
|
399
|
-
};
|
|
400
|
-
this.websocket.send(JSON.stringify(leaveMessage));
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
if (this.websocket) {
|
|
404
|
-
this.websocket.close();
|
|
405
|
-
}
|
|
406
|
-
if (this.terminal) {
|
|
407
|
-
this.terminal.dispose();
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// Note: We don't destroy the server-side session anymore since other tabs might be using it
|
|
411
|
-
// The WebSocket service will manage session cleanup when all clients disconnect
|
|
412
|
-
|
|
413
|
-
// Remove from store
|
|
414
|
-
terminalStore.removeTerminal(this.terminalId);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// Auto-initialize when script loads
|
|
419
|
-
console.log('Terminal.js loaded, looking for terminal containers...');
|
|
420
|
-
let terminalManagers = new Map();
|
|
421
|
-
|
|
422
|
-
function initializeTerminals() {
|
|
423
|
-
// Look for all sapling-islands and find the ones with terminal content
|
|
424
|
-
const saplingIslands = document.querySelectorAll('sapling-island');
|
|
425
|
-
|
|
426
|
-
for (const island of saplingIslands) {
|
|
427
|
-
// Look for a div with terminal ID (updated selector for new background class)
|
|
428
|
-
const terminalDiv = island.querySelector('div[id^="terminal-"]');
|
|
429
|
-
if (terminalDiv && terminalDiv.id && terminalDiv.id.startsWith('terminal-')) {
|
|
430
|
-
const terminalId = terminalDiv.id;
|
|
431
|
-
console.log('Found terminal component with ID:', terminalId);
|
|
432
|
-
|
|
433
|
-
// Check if we already have a manager for this terminal
|
|
434
|
-
if (!terminalManagers.has(terminalId)) {
|
|
435
|
-
const manager = new TerminalManager(terminalId);
|
|
436
|
-
terminalManagers.set(terminalId, manager);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
console.log(`Initialized ${terminalManagers.size} terminal(s)`);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// Try immediate initialization
|
|
445
|
-
if (document.readyState === 'loading') {
|
|
446
|
-
document.addEventListener('DOMContentLoaded', initializeTerminals);
|
|
447
|
-
} else {
|
|
448
|
-
initializeTerminals();
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
// Make available globally
|
|
452
|
-
window.terminalManagers = terminalManagers;
|
|
453
|
-
window.TerminalManager = TerminalManager;
|
|
454
|
-
window.initializeTerminals = initializeTerminals;
|
|
455
|
-
|
|
456
|
-
// Cleanup on page unload
|
|
457
|
-
window.addEventListener('beforeunload', () => {
|
|
458
|
-
for (const manager of terminalManagers.values()) {
|
|
459
|
-
manager.destroy();
|
|
460
|
-
}
|
|
461
|
-
});
|