treesap 0.1.13 → 0.2.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/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,225 +0,0 @@
|
|
|
1
|
-
// Sidebar component JavaScript for responsive behavior
|
|
2
|
-
import { sidebarStore } from '/signals/SidebarSignal.js';
|
|
3
|
-
|
|
4
|
-
class SidebarManager {
|
|
5
|
-
constructor(id = 'sidebar') {
|
|
6
|
-
this.id = id;
|
|
7
|
-
|
|
8
|
-
// DOM elements
|
|
9
|
-
this.backdrop = document.getElementById(`${id}-backdrop`);
|
|
10
|
-
this.pane = document.getElementById(`${id}-pane`);
|
|
11
|
-
this.closeBtn = document.getElementById(`${id}-close-btn`);
|
|
12
|
-
this.refreshBtn = document.getElementById('live-preview-refresh-btn');
|
|
13
|
-
this.urlInput = document.getElementById('live-preview-url-input');
|
|
14
|
-
this.loadBtn = document.getElementById('live-preview-load-btn');
|
|
15
|
-
|
|
16
|
-
// Reference to the sidebar store
|
|
17
|
-
this.store = sidebarStore;
|
|
18
|
-
|
|
19
|
-
this.init();
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
init() {
|
|
23
|
-
console.log('Initializing Sidebar:', this.id);
|
|
24
|
-
console.log('Elements found:', {
|
|
25
|
-
backdrop: !!this.backdrop,
|
|
26
|
-
pane: !!this.pane,
|
|
27
|
-
closeBtn: !!this.closeBtn,
|
|
28
|
-
refreshBtn: !!this.refreshBtn,
|
|
29
|
-
urlInput: !!this.urlInput,
|
|
30
|
-
loadBtn: !!this.loadBtn
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
// Set up event listeners
|
|
34
|
-
this.setupEventListeners();
|
|
35
|
-
|
|
36
|
-
// Subscribe to store state changes
|
|
37
|
-
this.subscribeToStore();
|
|
38
|
-
|
|
39
|
-
// Initial state update
|
|
40
|
-
this.updateSidebarState();
|
|
41
|
-
this.updateMobileToggle();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
setupEventListeners() {
|
|
45
|
-
// Mobile close button
|
|
46
|
-
this.closeBtn?.addEventListener('click', () => this.store.close());
|
|
47
|
-
|
|
48
|
-
// Backdrop click to close
|
|
49
|
-
this.backdrop?.addEventListener('click', () => this.store.close());
|
|
50
|
-
|
|
51
|
-
// Mobile toggle button (in main layout)
|
|
52
|
-
const mobileToggle = document.getElementById('mobile-sidebar-toggle');
|
|
53
|
-
mobileToggle?.addEventListener('click', () => this.store.toggle());
|
|
54
|
-
|
|
55
|
-
// Refresh button
|
|
56
|
-
this.refreshBtn?.addEventListener('click', () => this.refreshPreview());
|
|
57
|
-
|
|
58
|
-
// URL navigation
|
|
59
|
-
this.loadBtn?.addEventListener('click', (e) => {
|
|
60
|
-
e.preventDefault();
|
|
61
|
-
this.loadUrl();
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
this.urlInput?.addEventListener('keypress', (e) => {
|
|
65
|
-
if (e.key === 'Enter') {
|
|
66
|
-
e.preventDefault();
|
|
67
|
-
this.loadUrl();
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
// Keyboard shortcuts
|
|
72
|
-
document.addEventListener('keydown', (e) => {
|
|
73
|
-
// Escape key to close sidebar on mobile
|
|
74
|
-
if (e.key === 'Escape' && this.store.isMobile.value && this.store.isOpen.value) {
|
|
75
|
-
e.preventDefault();
|
|
76
|
-
this.store.close();
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Cmd/Ctrl + B to toggle sidebar
|
|
80
|
-
if ((e.metaKey || e.ctrlKey) && e.key === 'b') {
|
|
81
|
-
e.preventDefault();
|
|
82
|
-
this.store.toggle();
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// Listen for custom events to maintain backward compatibility
|
|
87
|
-
document.addEventListener('sidebar:toggle', () => this.store.toggle());
|
|
88
|
-
document.addEventListener('sidebar:open', () => this.store.open());
|
|
89
|
-
document.addEventListener('sidebar:close', () => this.store.close());
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
subscribeToStore() {
|
|
93
|
-
// Subscribe to store changes and update UI accordingly
|
|
94
|
-
this.store.isOpen.subscribe(() => this.updateSidebarState());
|
|
95
|
-
this.store.isMobile.subscribe(() => this.updateSidebarState());
|
|
96
|
-
this.store.shouldShowBackdrop.subscribe(() => this.updateSidebarState());
|
|
97
|
-
this.store.shouldShowMobileToggle.subscribe(() => this.updateMobileToggle());
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
updateSidebarState() {
|
|
101
|
-
if (!this.pane || !this.backdrop) return;
|
|
102
|
-
|
|
103
|
-
const isOpen = this.store.isOpen.value;
|
|
104
|
-
const isMobile = this.store.isMobile.value;
|
|
105
|
-
const shouldShowBackdrop = this.store.shouldShowBackdrop.value;
|
|
106
|
-
|
|
107
|
-
if (isMobile) {
|
|
108
|
-
// Mobile behavior: overlay
|
|
109
|
-
if (shouldShowBackdrop) {
|
|
110
|
-
// Show backdrop
|
|
111
|
-
this.backdrop.classList.remove('opacity-0', 'pointer-events-none');
|
|
112
|
-
this.backdrop.classList.add('opacity-100');
|
|
113
|
-
|
|
114
|
-
// Prevent body scroll
|
|
115
|
-
document.body.style.overflow = 'hidden';
|
|
116
|
-
} else {
|
|
117
|
-
// Hide backdrop
|
|
118
|
-
this.backdrop.classList.remove('opacity-100');
|
|
119
|
-
this.backdrop.classList.add('opacity-0', 'pointer-events-none');
|
|
120
|
-
|
|
121
|
-
// Restore body scroll
|
|
122
|
-
document.body.style.overflow = '';
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (isOpen) {
|
|
126
|
-
// Show sidebar
|
|
127
|
-
this.pane.classList.remove('-translate-x-full');
|
|
128
|
-
this.pane.classList.add('translate-x-0');
|
|
129
|
-
} else {
|
|
130
|
-
// Hide sidebar
|
|
131
|
-
this.pane.classList.remove('translate-x-0');
|
|
132
|
-
this.pane.classList.add('-translate-x-full');
|
|
133
|
-
}
|
|
134
|
-
} else {
|
|
135
|
-
// Desktop behavior: side panel
|
|
136
|
-
// Hide backdrop (not needed on desktop)
|
|
137
|
-
this.backdrop.classList.add('opacity-0', 'pointer-events-none');
|
|
138
|
-
|
|
139
|
-
// Restore body scroll
|
|
140
|
-
document.body.style.overflow = '';
|
|
141
|
-
|
|
142
|
-
if (isOpen) {
|
|
143
|
-
// Show sidebar
|
|
144
|
-
this.pane.classList.remove('-translate-x-full');
|
|
145
|
-
this.pane.classList.add('translate-x-0');
|
|
146
|
-
this.pane.style.display = '';
|
|
147
|
-
} else {
|
|
148
|
-
// Hide sidebar completely on desktop
|
|
149
|
-
this.pane.style.display = 'none';
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
updateMobileToggle() {
|
|
155
|
-
const mobileToggle = document.getElementById('mobile-sidebar-toggle');
|
|
156
|
-
const shouldShow = this.store.shouldShowMobileToggle.value;
|
|
157
|
-
|
|
158
|
-
if (mobileToggle) {
|
|
159
|
-
mobileToggle.style.display = shouldShow ? 'flex' : 'none';
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
refreshPreview() {
|
|
164
|
-
// Dispatch event for SimpleLivePreview to handle
|
|
165
|
-
document.dispatchEvent(new CustomEvent('preview:refresh'));
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
loadUrl() {
|
|
169
|
-
if (this.urlInput) {
|
|
170
|
-
const path = this.urlInput.value.trim();
|
|
171
|
-
// Dispatch event for SimpleLivePreview to handle
|
|
172
|
-
document.dispatchEvent(new CustomEvent('preview:loadUrl', {
|
|
173
|
-
detail: { path }
|
|
174
|
-
}));
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Public API methods
|
|
179
|
-
getState() {
|
|
180
|
-
return this.store.getState();
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
destroy() {
|
|
184
|
-
// Restore body scroll
|
|
185
|
-
document.body.style.overflow = '';
|
|
186
|
-
|
|
187
|
-
// Clean up is handled by the signal store
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Auto-initialize when script loads
|
|
192
|
-
console.log('Sidebar.js loaded, looking for sidebar containers...');
|
|
193
|
-
|
|
194
|
-
function initializeSidebar() {
|
|
195
|
-
// Look for sapling-islands containing sidebar content
|
|
196
|
-
const saplingIslands = document.querySelectorAll('sapling-island');
|
|
197
|
-
|
|
198
|
-
for (const island of saplingIslands) {
|
|
199
|
-
// Look for sidebar pane div
|
|
200
|
-
const sidebarPane = island.querySelector('div[id$="-pane"]');
|
|
201
|
-
if (sidebarPane && sidebarPane.id.includes('sidebar')) {
|
|
202
|
-
const sidebarId = sidebarPane.id.replace('-pane', '');
|
|
203
|
-
console.log('Found Sidebar component with ID:', sidebarId);
|
|
204
|
-
|
|
205
|
-
// Create and store manager
|
|
206
|
-
const manager = new SidebarManager(sidebarId);
|
|
207
|
-
window.sidebarManager = manager; // Make globally available
|
|
208
|
-
|
|
209
|
-
break; // Only one sidebar per page
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Initialize immediately since Sapling islands are ready
|
|
215
|
-
initializeSidebar();
|
|
216
|
-
|
|
217
|
-
// Make available globally
|
|
218
|
-
window.SidebarManager = SidebarManager;
|
|
219
|
-
|
|
220
|
-
// Cleanup on page unload
|
|
221
|
-
window.addEventListener('beforeunload', () => {
|
|
222
|
-
if (window.sidebarManager) {
|
|
223
|
-
window.sidebarManager.destroy();
|
|
224
|
-
}
|
|
225
|
-
});
|
|
@@ -1,305 +0,0 @@
|
|
|
1
|
-
// SimpleLivePreview component JavaScript
|
|
2
|
-
import { sidebarStore } from '/signals/SidebarSignal.js';
|
|
3
|
-
|
|
4
|
-
class SimpleLivePreviewManager {
|
|
5
|
-
constructor(id = 'simple-preview') {
|
|
6
|
-
this.id = id;
|
|
7
|
-
|
|
8
|
-
// DOM elements
|
|
9
|
-
this.container = document.getElementById(id);
|
|
10
|
-
this.hideSidebarBtn = document.getElementById(`${id}-hide-sidebar-btn`);
|
|
11
|
-
this.hideSidebarIcon = document.getElementById(`${id}-hide-sidebar-icon`);
|
|
12
|
-
this.floatingHideSidebarBtn = document.getElementById(`${id}-floating-hide-sidebar-btn`);
|
|
13
|
-
this.floatingHideSidebarIcon = document.getElementById(`${id}-floating-hide-sidebar-icon`);
|
|
14
|
-
this.refreshBtn = document.getElementById(`${id}-refresh-btn`);
|
|
15
|
-
this.urlInput = document.getElementById(`${id}-url-input`);
|
|
16
|
-
this.loadBtn = document.getElementById(`${id}-load-btn`);
|
|
17
|
-
this.iframe = document.getElementById(`${id}-iframe`);
|
|
18
|
-
|
|
19
|
-
// Get preview port from iframe data attribute
|
|
20
|
-
this.previewPort = this.iframe?.getAttribute('data-preview-port') || 5173;
|
|
21
|
-
|
|
22
|
-
// Reference to the sidebar store
|
|
23
|
-
this.store = sidebarStore;
|
|
24
|
-
|
|
25
|
-
this.init();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
init() {
|
|
29
|
-
console.log('Initializing SimpleLivePreview:', this.id);
|
|
30
|
-
console.log('Elements found:', {
|
|
31
|
-
container: !!this.container,
|
|
32
|
-
urlInput: !!this.urlInput,
|
|
33
|
-
loadBtn: !!this.loadBtn,
|
|
34
|
-
iframe: !!this.iframe
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
// Set up event listeners
|
|
38
|
-
this.setupEventListeners();
|
|
39
|
-
|
|
40
|
-
// Subscribe to sidebar store changes
|
|
41
|
-
this.subscribeToStore();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
setupEventListeners() {
|
|
45
|
-
// Hide sidebar toggle (both sidebar and floating button)
|
|
46
|
-
this.hideSidebarBtn?.addEventListener('click', () => this.store.toggle());
|
|
47
|
-
this.floatingHideSidebarBtn?.addEventListener('click', () => this.store.toggle());
|
|
48
|
-
|
|
49
|
-
// Refresh button
|
|
50
|
-
this.refreshBtn?.addEventListener('click', () => this.refreshIframe());
|
|
51
|
-
|
|
52
|
-
// URL navigation
|
|
53
|
-
this.loadBtn?.addEventListener('click', (e) => {
|
|
54
|
-
e.preventDefault();
|
|
55
|
-
this.loadUrl();
|
|
56
|
-
});
|
|
57
|
-
this.urlInput?.addEventListener('keypress', (e) => {
|
|
58
|
-
if (e.key === 'Enter') {
|
|
59
|
-
e.preventDefault();
|
|
60
|
-
this.loadUrl();
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
// Prevent form submission if input is in a form
|
|
65
|
-
this.urlInput?.addEventListener('submit', (e) => {
|
|
66
|
-
e.preventDefault();
|
|
67
|
-
this.loadUrl();
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
// Listen for events from Sidebar component
|
|
71
|
-
document.addEventListener('preview:refresh', () => this.refreshIframe());
|
|
72
|
-
document.addEventListener('preview:loadUrl', (e) => {
|
|
73
|
-
if (e.detail && e.detail.path !== undefined) {
|
|
74
|
-
this.loadUrlFromPath(e.detail.path);
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
// Legacy: Listen for sidebar state changes (for backward compatibility)
|
|
79
|
-
document.addEventListener('sidebar:stateChanged', (e) => {
|
|
80
|
-
if (e.detail) {
|
|
81
|
-
this.handleSidebarStateChange(e.detail);
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
// Keyboard shortcuts
|
|
86
|
-
document.addEventListener('keydown', (e) => {
|
|
87
|
-
// Cmd/Ctrl + R for refresh
|
|
88
|
-
if ((e.metaKey || e.ctrlKey) && e.key === 'r' && this.iframe && this.iframe.closest(`#${this.id}`)) {
|
|
89
|
-
e.preventDefault();
|
|
90
|
-
this.refreshIframe();
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
// Handle iframe load errors (for X-Frame-Options violations)
|
|
95
|
-
if (this.iframe) {
|
|
96
|
-
// Store the current src to detect external navigation
|
|
97
|
-
let lastSrc = this.iframe.src;
|
|
98
|
-
|
|
99
|
-
this.iframe.addEventListener('error', (e) => {
|
|
100
|
-
console.log('Iframe load error, possibly due to X-Frame-Options');
|
|
101
|
-
this.handleIframeError();
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
// Monitor src changes to catch navigation
|
|
105
|
-
const observer = new MutationObserver((mutations) => {
|
|
106
|
-
mutations.forEach((mutation) => {
|
|
107
|
-
if (mutation.type === 'attributes' && mutation.attributeName === 'src') {
|
|
108
|
-
const newSrc = this.iframe.src;
|
|
109
|
-
console.log('Iframe src changed from', lastSrc, 'to', newSrc);
|
|
110
|
-
|
|
111
|
-
// Check if it's an external URL
|
|
112
|
-
if (newSrc && (newSrc.startsWith('http://') || newSrc.startsWith('https://'))) {
|
|
113
|
-
const localServerUrl = `http://localhost:${this.previewPort}`;
|
|
114
|
-
if (!newSrc.startsWith(localServerUrl)) {
|
|
115
|
-
console.log('Detected external navigation, redirecting to new tab:', newSrc);
|
|
116
|
-
// Prevent the navigation by restoring the previous src
|
|
117
|
-
this.iframe.src = lastSrc;
|
|
118
|
-
// Open in new tab
|
|
119
|
-
window.open(newSrc, '_blank');
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
lastSrc = newSrc;
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
observer.observe(this.iframe, { attributes: true, attributeFilter: ['src'] });
|
|
129
|
-
|
|
130
|
-
// Also listen for load events to detect if content failed to load
|
|
131
|
-
this.iframe.addEventListener('load', () => {
|
|
132
|
-
try {
|
|
133
|
-
// Try to access the iframe's location - this will throw if blocked by X-Frame-Options
|
|
134
|
-
const iframeUrl = this.iframe.contentWindow?.location?.href;
|
|
135
|
-
if (!iframeUrl || iframeUrl === 'about:blank') {
|
|
136
|
-
// Might be a blocked frame
|
|
137
|
-
setTimeout(() => this.checkIframeContent(), 100);
|
|
138
|
-
}
|
|
139
|
-
} catch (error) {
|
|
140
|
-
console.log('Cannot access iframe content, likely blocked by security policy');
|
|
141
|
-
this.handleIframeError();
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
subscribeToStore() {
|
|
148
|
-
// Subscribe to sidebar store changes
|
|
149
|
-
this.store.shouldShowFloatingButton.subscribe(() => this.updateFloatingButton());
|
|
150
|
-
this.store.isOpen.subscribe(() => this.updateFloatingButton());
|
|
151
|
-
|
|
152
|
-
// Initial update
|
|
153
|
-
this.updateFloatingButton();
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
updateFloatingButton() {
|
|
157
|
-
const floatingBtn = this.floatingHideSidebarBtn;
|
|
158
|
-
const floatingIcon = this.floatingHideSidebarIcon;
|
|
159
|
-
const shouldShow = this.store.shouldShowFloatingButton.value;
|
|
160
|
-
|
|
161
|
-
if (floatingBtn) {
|
|
162
|
-
if (shouldShow) {
|
|
163
|
-
// Show floating button when sidebar is closed
|
|
164
|
-
floatingBtn.style.display = 'flex';
|
|
165
|
-
// Update icon and title
|
|
166
|
-
if (floatingIcon) {
|
|
167
|
-
floatingIcon.setAttribute('icon', 'ph:sidebar-simple-fill');
|
|
168
|
-
}
|
|
169
|
-
floatingBtn.setAttribute('title', 'Show Sidebar');
|
|
170
|
-
} else {
|
|
171
|
-
// Hide floating button when sidebar is open
|
|
172
|
-
floatingBtn.style.display = 'none';
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
toggleSidebarVisibility() {
|
|
178
|
-
// Use the store to toggle
|
|
179
|
-
this.store.toggle();
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
refreshIframe() {
|
|
183
|
-
if (this.iframe) {
|
|
184
|
-
console.log('Refreshing iframe...');
|
|
185
|
-
// Force reload by adding timestamp
|
|
186
|
-
const url = new URL(this.iframe.src);
|
|
187
|
-
url.searchParams.set('_t', Date.now().toString());
|
|
188
|
-
this.iframe.src = url.toString();
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
loadUrl() {
|
|
193
|
-
if (this.urlInput && this.iframe) {
|
|
194
|
-
const path = this.urlInput.value.trim();
|
|
195
|
-
this.loadUrlFromPath(path);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
loadUrlFromPath(path) {
|
|
200
|
-
if (this.iframe) {
|
|
201
|
-
console.log('loadUrlFromPath called with path:', path);
|
|
202
|
-
|
|
203
|
-
// Check if it's an external URL (starts with http:// or https://)
|
|
204
|
-
if (path.startsWith('http://') || path.startsWith('https://')) {
|
|
205
|
-
// Check if it's NOT our local server
|
|
206
|
-
const localServerUrl = `http://localhost:${this.previewPort}`;
|
|
207
|
-
if (!path.startsWith(localServerUrl)) {
|
|
208
|
-
// Open external URLs in a new tab
|
|
209
|
-
console.log('Opening external URL in new tab:', path);
|
|
210
|
-
window.open(path, '_blank');
|
|
211
|
-
// Clear the input if it exists
|
|
212
|
-
if (this.urlInput) {
|
|
213
|
-
this.urlInput.value = '';
|
|
214
|
-
}
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const baseUrl = `http://localhost:${this.previewPort}`;
|
|
220
|
-
const newUrl = path ? baseUrl + '/' + path.replace(/^\//, '') : baseUrl;
|
|
221
|
-
console.log('Loading URL in iframe:', newUrl);
|
|
222
|
-
this.iframe.src = newUrl;
|
|
223
|
-
|
|
224
|
-
// Update input if it exists
|
|
225
|
-
if (this.urlInput) {
|
|
226
|
-
this.urlInput.value = path;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
handleSidebarStateChange(state) {
|
|
232
|
-
// Legacy method for backward compatibility
|
|
233
|
-
console.log('Legacy sidebar state changed:', state);
|
|
234
|
-
// The floating button is now handled by updateFloatingButton()
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
handleIframeError() {
|
|
238
|
-
// Get the current iframe src and open it in a new tab if it's external
|
|
239
|
-
if (this.iframe && this.iframe.src) {
|
|
240
|
-
const src = this.iframe.src;
|
|
241
|
-
if (src.startsWith('http://') || src.startsWith('https://')) {
|
|
242
|
-
if (!src.startsWith(`http://localhost:${this.previewPort}`)) {
|
|
243
|
-
console.log('Opening blocked external URL in new tab:', src);
|
|
244
|
-
window.open(src, '_blank');
|
|
245
|
-
// Reset iframe to local server
|
|
246
|
-
this.iframe.src = `http://localhost:${this.previewPort}`;
|
|
247
|
-
// Clear input if it exists
|
|
248
|
-
if (this.urlInput) {
|
|
249
|
-
this.urlInput.value = '';
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
checkIframeContent() {
|
|
257
|
-
// Additional check for iframe content accessibility
|
|
258
|
-
if (this.iframe) {
|
|
259
|
-
try {
|
|
260
|
-
const doc = this.iframe.contentDocument;
|
|
261
|
-
if (!doc || doc.body.innerHTML === '') {
|
|
262
|
-
this.handleIframeError();
|
|
263
|
-
}
|
|
264
|
-
} catch (error) {
|
|
265
|
-
this.handleIframeError();
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
destroy() {
|
|
271
|
-
// Clean up event listeners if needed
|
|
272
|
-
// (Optional since Sapling handles cleanup)
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Auto-initialize when script loads
|
|
277
|
-
console.log('SimpleLivePreview.js loaded, looking for preview containers...');
|
|
278
|
-
|
|
279
|
-
function initializeSimpleLivePreview() {
|
|
280
|
-
// Look for all sapling-islands and find the ones with SimpleLivePreview content
|
|
281
|
-
const saplingIslands = document.querySelectorAll('sapling-island');
|
|
282
|
-
|
|
283
|
-
for (const island of saplingIslands) {
|
|
284
|
-
// Look for a div with an iframe that has data-preview-port
|
|
285
|
-
const previewDiv = island.querySelector('div[id] iframe[data-preview-port]');
|
|
286
|
-
if (previewDiv) {
|
|
287
|
-
const parentDiv = previewDiv.closest('div[id]');
|
|
288
|
-
if (parentDiv && parentDiv.id) {
|
|
289
|
-
console.log('Found SimpleLivePreview component with ID:', parentDiv.id);
|
|
290
|
-
new SimpleLivePreviewManager(parentDiv.id);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// Initialize immediately since Sapling islands are ready
|
|
297
|
-
initializeSimpleLivePreview();
|
|
298
|
-
|
|
299
|
-
// Make available globally
|
|
300
|
-
window.SimpleLivePreviewManager = SimpleLivePreviewManager;
|
|
301
|
-
|
|
302
|
-
// Cleanup on page unload
|
|
303
|
-
window.addEventListener('beforeunload', () => {
|
|
304
|
-
// Cleanup handled by Sapling automatically
|
|
305
|
-
});
|