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.
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,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
- });