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.
Files changed (147) hide show
  1. package/README.md +31 -192
  2. package/dist/app.d.ts +28 -0
  3. package/dist/app.d.ts.map +1 -0
  4. package/dist/app.js +184 -0
  5. package/dist/app.js.map +1 -0
  6. package/dist/context.d.ts +36 -0
  7. package/dist/context.d.ts.map +1 -0
  8. package/dist/context.js +95 -0
  9. package/dist/context.js.map +1 -0
  10. package/dist/index.d.ts +5 -7
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +5 -9
  13. package/dist/index.js.map +1 -1
  14. package/dist/middleware/cors.d.ts +11 -0
  15. package/dist/middleware/cors.d.ts.map +1 -0
  16. package/dist/middleware/cors.js +34 -0
  17. package/dist/middleware/cors.js.map +1 -0
  18. package/dist/middleware/serve-static.d.ts +6 -0
  19. package/dist/middleware/serve-static.d.ts.map +1 -0
  20. package/dist/middleware/serve-static.js +68 -0
  21. package/dist/middleware/serve-static.js.map +1 -0
  22. package/dist/node.d.ts +8 -0
  23. package/dist/node.d.ts.map +1 -0
  24. package/dist/node.js +52 -0
  25. package/dist/node.js.map +1 -0
  26. package/dist/path.d.ts +10 -0
  27. package/dist/path.d.ts.map +1 -0
  28. package/dist/path.js +45 -0
  29. package/dist/path.js.map +1 -0
  30. package/dist/vite.d.ts +31 -0
  31. package/dist/vite.d.ts.map +1 -0
  32. package/dist/vite.js +278 -0
  33. package/dist/vite.js.map +1 -0
  34. package/package.json +33 -40
  35. package/dist/cli.d.ts +0 -3
  36. package/dist/cli.d.ts.map +0 -1
  37. package/dist/cli.js +0 -137
  38. package/dist/cli.js.map +0 -1
  39. package/dist/components/BaseHead.d.ts +0 -5
  40. package/dist/components/BaseHead.d.ts.map +0 -1
  41. package/dist/components/BaseHead.js +0 -161
  42. package/dist/components/BaseHead.js.map +0 -1
  43. package/dist/components/ChatInput.d.ts +0 -7
  44. package/dist/components/ChatInput.d.ts.map +0 -1
  45. package/dist/components/ChatInput.js +0 -11
  46. package/dist/components/ChatInput.js.map +0 -1
  47. package/dist/components/Sidebar.d.ts +0 -8
  48. package/dist/components/Sidebar.d.ts.map +0 -1
  49. package/dist/components/Sidebar.js +0 -7
  50. package/dist/components/Sidebar.js.map +0 -1
  51. package/dist/components/SimpleLivePreview.d.ts +0 -7
  52. package/dist/components/SimpleLivePreview.d.ts.map +0 -1
  53. package/dist/components/SimpleLivePreview.js +0 -7
  54. package/dist/components/SimpleLivePreview.js.map +0 -1
  55. package/dist/components/Terminal.d.ts +0 -7
  56. package/dist/components/Terminal.d.ts.map +0 -1
  57. package/dist/components/Terminal.js +0 -14
  58. package/dist/components/Terminal.js.map +0 -1
  59. package/dist/components/VoiceRecorder.d.ts +0 -4
  60. package/dist/components/VoiceRecorder.d.ts.map +0 -1
  61. package/dist/components/VoiceRecorder.js +0 -5
  62. package/dist/components/VoiceRecorder.js.map +0 -1
  63. package/dist/components/icons/GeminiLogo.d.ts +0 -7
  64. package/dist/components/icons/GeminiLogo.d.ts.map +0 -1
  65. package/dist/components/icons/GeminiLogo.js +0 -5
  66. package/dist/components/icons/GeminiLogo.js.map +0 -1
  67. package/dist/components/icons/OllamaLogo.d.ts +0 -2
  68. package/dist/components/icons/OllamaLogo.d.ts.map +0 -1
  69. package/dist/components/icons/OllamaLogo.js +0 -5
  70. package/dist/components/icons/OllamaLogo.js.map +0 -1
  71. package/dist/layouts/Layout.d.ts +0 -9
  72. package/dist/layouts/Layout.d.ts.map +0 -1
  73. package/dist/layouts/Layout.js +0 -9
  74. package/dist/layouts/Layout.js.map +0 -1
  75. package/dist/layouts/NotFoundLayout.d.ts +0 -2
  76. package/dist/layouts/NotFoundLayout.d.ts.map +0 -1
  77. package/dist/layouts/NotFoundLayout.js +0 -6
  78. package/dist/layouts/NotFoundLayout.js.map +0 -1
  79. package/dist/pages/Code.d.ts +0 -7
  80. package/dist/pages/Code.d.ts.map +0 -1
  81. package/dist/pages/Code.js +0 -8
  82. package/dist/pages/Code.js.map +0 -1
  83. package/dist/pages/Home.d.ts +0 -7
  84. package/dist/pages/Home.d.ts.map +0 -1
  85. package/dist/pages/Home.js +0 -8
  86. package/dist/pages/Home.js.map +0 -1
  87. package/dist/pages/Welcome.d.ts +0 -2
  88. package/dist/pages/Welcome.d.ts.map +0 -1
  89. package/dist/pages/Welcome.js +0 -6
  90. package/dist/pages/Welcome.js.map +0 -1
  91. package/dist/server.d.ts +0 -11
  92. package/dist/server.d.ts.map +0 -1
  93. package/dist/server.js +0 -434
  94. package/dist/server.js.map +0 -1
  95. package/dist/services/dev-server.d.ts +0 -29
  96. package/dist/services/dev-server.d.ts.map +0 -1
  97. package/dist/services/dev-server.js +0 -201
  98. package/dist/services/dev-server.js.map +0 -1
  99. package/dist/services/terminal.d.ts +0 -46
  100. package/dist/services/terminal.d.ts.map +0 -1
  101. package/dist/services/terminal.js +0 -264
  102. package/dist/services/terminal.js.map +0 -1
  103. package/dist/services/websocket.d.ts +0 -48
  104. package/dist/services/websocket.d.ts.map +0 -1
  105. package/dist/services/websocket.js +0 -332
  106. package/dist/services/websocket.js.map +0 -1
  107. package/dist/static/components/ChatInput.js +0 -237
  108. package/dist/static/components/Sidebar.js +0 -225
  109. package/dist/static/components/SimpleLivePreview.js +0 -305
  110. package/dist/static/components/Terminal.js +0 -461
  111. package/dist/static/components/TerminalTabs.js +0 -383
  112. package/dist/static/favicon.svg +0 -14
  113. package/dist/static/signals/LivePreviewSignal.js +0 -71
  114. package/dist/static/signals/SidebarSignal.js +0 -123
  115. package/dist/static/signals/TerminalSignal.js +0 -273
  116. package/dist/static/styles/main.css +0 -1761
  117. package/src/cli.ts +0 -155
  118. package/src/components/BaseHead.ts +0 -164
  119. package/src/components/ChatInput.tsx +0 -56
  120. package/src/components/Sidebar.tsx +0 -99
  121. package/src/components/SimpleLivePreview.tsx +0 -40
  122. package/src/components/Terminal.tsx +0 -40
  123. package/src/components/VoiceRecorder.tsx +0 -33
  124. package/src/components/icons/GeminiLogo.tsx +0 -10
  125. package/src/components/icons/OllamaLogo.tsx +0 -5
  126. package/src/index.tsx +0 -12
  127. package/src/layouts/Layout.tsx +0 -41
  128. package/src/layouts/NotFoundLayout.tsx +0 -15
  129. package/src/pages/Code.tsx +0 -34
  130. package/src/pages/Welcome.tsx +0 -56
  131. package/src/server.tsx +0 -519
  132. package/src/services/dev-server.ts +0 -234
  133. package/src/services/terminal.ts +0 -325
  134. package/src/services/websocket.ts +0 -405
  135. package/src/static/components/ChatInput.js +0 -237
  136. package/src/static/components/Sidebar.js +0 -225
  137. package/src/static/components/SimpleLivePreview.js +0 -305
  138. package/src/static/components/Terminal.js +0 -461
  139. package/src/static/components/TerminalTabs.js +0 -383
  140. package/src/static/favicon.svg +0 -14
  141. package/src/static/signals/LivePreviewSignal.js +0 -71
  142. package/src/static/signals/SidebarSignal.js +0 -123
  143. package/src/static/signals/TerminalSignal.js +0 -273
  144. package/src/static/styles/main.css +0 -1761
  145. package/src/styles/input.css +0 -3
  146. package/tailwind.config.ts +0 -22
  147. 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
- });