treesap 0.1.9 → 0.1.11
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/dist/components/ChatInput.d.ts +7 -0
- package/dist/components/ChatInput.d.ts.map +1 -0
- package/dist/components/ChatInput.js +11 -0
- package/dist/components/ChatInput.js.map +1 -0
- package/dist/components/Sidebar.d.ts +8 -0
- package/dist/components/Sidebar.d.ts.map +1 -0
- package/dist/components/Sidebar.js +7 -0
- package/dist/components/Sidebar.js.map +1 -0
- package/dist/components/SimpleLivePreview.js +1 -1
- package/dist/components/SimpleLivePreview.js.map +1 -1
- package/dist/pages/Code.d.ts.map +1 -1
- package/dist/pages/Code.js +2 -2
- package/dist/pages/Code.js.map +1 -1
- package/dist/services/websocket.d.ts.map +1 -1
- package/dist/services/websocket.js +1 -2
- package/dist/services/websocket.js.map +1 -1
- package/dist/static/components/ChatInput.js +237 -0
- package/dist/static/components/Sidebar.js +225 -0
- package/dist/static/components/SimpleLivePreview.js +73 -53
- package/dist/static/components/Terminal.js +143 -61
- package/dist/static/signals/SidebarSignal.js +123 -0
- package/dist/static/signals/TerminalSignal.js +137 -2
- package/dist/static/styles/main.css +180 -0
- package/package.json +1 -1
- package/src/components/ChatInput.tsx +56 -0
- package/src/components/Sidebar.tsx +99 -0
- package/src/components/SimpleLivePreview.tsx +4 -4
- package/src/pages/Code.tsx +18 -55
- package/src/services/websocket.ts +1 -5
- package/src/static/components/ChatInput.js +237 -0
- package/src/static/components/Sidebar.js +225 -0
- package/src/static/components/SimpleLivePreview.js +73 -53
- package/src/static/components/Terminal.js +143 -61
- package/src/static/signals/SidebarSignal.js +123 -0
- package/src/static/signals/TerminalSignal.js +137 -2
- package/src/static/styles/main.css +180 -0
- package/tailwind.config.ts +10 -0
|
@@ -0,0 +1,225 @@
|
|
|
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,4 +1,6 @@
|
|
|
1
1
|
// SimpleLivePreview component JavaScript
|
|
2
|
+
import { sidebarStore } from '/signals/SidebarSignal.js';
|
|
3
|
+
|
|
2
4
|
class SimpleLivePreviewManager {
|
|
3
5
|
constructor(id = 'simple-preview') {
|
|
4
6
|
this.id = id;
|
|
@@ -17,8 +19,8 @@ class SimpleLivePreviewManager {
|
|
|
17
19
|
// Get preview port from iframe data attribute
|
|
18
20
|
this.previewPort = this.iframe?.getAttribute('data-preview-port') || 5173;
|
|
19
21
|
|
|
20
|
-
//
|
|
21
|
-
this.
|
|
22
|
+
// Reference to the sidebar store
|
|
23
|
+
this.store = sidebarStore;
|
|
22
24
|
|
|
23
25
|
this.init();
|
|
24
26
|
}
|
|
@@ -34,12 +36,15 @@ class SimpleLivePreviewManager {
|
|
|
34
36
|
|
|
35
37
|
// Set up event listeners
|
|
36
38
|
this.setupEventListeners();
|
|
39
|
+
|
|
40
|
+
// Subscribe to sidebar store changes
|
|
41
|
+
this.subscribeToStore();
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
setupEventListeners() {
|
|
40
45
|
// Hide sidebar toggle (both sidebar and floating button)
|
|
41
|
-
this.hideSidebarBtn?.addEventListener('click', () => this.
|
|
42
|
-
this.floatingHideSidebarBtn?.addEventListener('click', () => this.
|
|
46
|
+
this.hideSidebarBtn?.addEventListener('click', () => this.store.toggle());
|
|
47
|
+
this.floatingHideSidebarBtn?.addEventListener('click', () => this.store.toggle());
|
|
43
48
|
|
|
44
49
|
// Refresh button
|
|
45
50
|
this.refreshBtn?.addEventListener('click', () => this.refreshIframe());
|
|
@@ -62,6 +67,21 @@ class SimpleLivePreviewManager {
|
|
|
62
67
|
this.loadUrl();
|
|
63
68
|
});
|
|
64
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
|
+
|
|
65
85
|
// Keyboard shortcuts
|
|
66
86
|
document.addEventListener('keydown', (e) => {
|
|
67
87
|
// Cmd/Ctrl + R for refresh
|
|
@@ -69,11 +89,6 @@ class SimpleLivePreviewManager {
|
|
|
69
89
|
e.preventDefault();
|
|
70
90
|
this.refreshIframe();
|
|
71
91
|
}
|
|
72
|
-
// Cmd/Ctrl + B to toggle sidebar panel
|
|
73
|
-
if ((e.metaKey || e.ctrlKey) && e.key === 'b') {
|
|
74
|
-
e.preventDefault();
|
|
75
|
-
this.toggleSidebarVisibility();
|
|
76
|
-
}
|
|
77
92
|
});
|
|
78
93
|
|
|
79
94
|
// Handle iframe load errors (for X-Frame-Options violations)
|
|
@@ -129,55 +144,41 @@ class SimpleLivePreviewManager {
|
|
|
129
144
|
}
|
|
130
145
|
}
|
|
131
146
|
|
|
132
|
-
|
|
133
|
-
|
|
147
|
+
subscribeToStore() {
|
|
148
|
+
// Subscribe to sidebar store changes
|
|
149
|
+
this.store.shouldShowFloatingButton.subscribe(() => this.updateFloatingButton());
|
|
150
|
+
this.store.isOpen.subscribe(() => this.updateFloatingButton());
|
|
134
151
|
|
|
135
|
-
|
|
136
|
-
|
|
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;
|
|
137
160
|
|
|
138
|
-
if (
|
|
139
|
-
if (
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
// Update button icons and titles
|
|
146
|
-
if (this.hideSidebarIcon) {
|
|
147
|
-
this.hideSidebarIcon.setAttribute('icon', 'ph:sidebar-simple-fill');
|
|
148
|
-
}
|
|
149
|
-
if (this.floatingHideSidebarIcon) {
|
|
150
|
-
this.floatingHideSidebarIcon.setAttribute('icon', 'ph:sidebar-simple-fill');
|
|
151
|
-
}
|
|
152
|
-
if (this.hideSidebarBtn) {
|
|
153
|
-
this.hideSidebarBtn.setAttribute('title', 'Show Sidebar');
|
|
154
|
-
}
|
|
155
|
-
if (this.floatingHideSidebarBtn) {
|
|
156
|
-
this.floatingHideSidebarBtn.setAttribute('title', 'Show Sidebar');
|
|
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');
|
|
157
168
|
}
|
|
169
|
+
floatingBtn.setAttribute('title', 'Show Sidebar');
|
|
158
170
|
} else {
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
previewPane.classList.remove('w-full');
|
|
162
|
-
previewPane.classList.add('flex-1');
|
|
163
|
-
|
|
164
|
-
// Update button icons and titles
|
|
165
|
-
if (this.hideSidebarIcon) {
|
|
166
|
-
this.hideSidebarIcon.setAttribute('icon', 'ph:sidebar-simple');
|
|
167
|
-
}
|
|
168
|
-
if (this.floatingHideSidebarIcon) {
|
|
169
|
-
this.floatingHideSidebarIcon.setAttribute('icon', 'ph:sidebar-simple');
|
|
170
|
-
}
|
|
171
|
-
if (this.hideSidebarBtn) {
|
|
172
|
-
this.hideSidebarBtn.setAttribute('title', 'Hide Sidebar');
|
|
173
|
-
}
|
|
174
|
-
if (this.floatingHideSidebarBtn) {
|
|
175
|
-
this.floatingHideSidebarBtn.setAttribute('title', 'Hide Sidebar');
|
|
176
|
-
}
|
|
171
|
+
// Hide floating button when sidebar is open
|
|
172
|
+
floatingBtn.style.display = 'none';
|
|
177
173
|
}
|
|
178
174
|
}
|
|
179
175
|
}
|
|
180
176
|
|
|
177
|
+
toggleSidebarVisibility() {
|
|
178
|
+
// Use the store to toggle
|
|
179
|
+
this.store.toggle();
|
|
180
|
+
}
|
|
181
|
+
|
|
181
182
|
refreshIframe() {
|
|
182
183
|
if (this.iframe) {
|
|
183
184
|
console.log('Refreshing iframe...');
|
|
@@ -191,7 +192,13 @@ class SimpleLivePreviewManager {
|
|
|
191
192
|
loadUrl() {
|
|
192
193
|
if (this.urlInput && this.iframe) {
|
|
193
194
|
const path = this.urlInput.value.trim();
|
|
194
|
-
|
|
195
|
+
this.loadUrlFromPath(path);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
loadUrlFromPath(path) {
|
|
200
|
+
if (this.iframe) {
|
|
201
|
+
console.log('loadUrlFromPath called with path:', path);
|
|
195
202
|
|
|
196
203
|
// Check if it's an external URL (starts with http:// or https://)
|
|
197
204
|
if (path.startsWith('http://') || path.startsWith('https://')) {
|
|
@@ -201,8 +208,10 @@ class SimpleLivePreviewManager {
|
|
|
201
208
|
// Open external URLs in a new tab
|
|
202
209
|
console.log('Opening external URL in new tab:', path);
|
|
203
210
|
window.open(path, '_blank');
|
|
204
|
-
// Clear the input
|
|
205
|
-
this.urlInput
|
|
211
|
+
// Clear the input if it exists
|
|
212
|
+
if (this.urlInput) {
|
|
213
|
+
this.urlInput.value = '';
|
|
214
|
+
}
|
|
206
215
|
return;
|
|
207
216
|
}
|
|
208
217
|
}
|
|
@@ -211,9 +220,20 @@ class SimpleLivePreviewManager {
|
|
|
211
220
|
const newUrl = path ? baseUrl + '/' + path.replace(/^\//, '') : baseUrl;
|
|
212
221
|
console.log('Loading URL in iframe:', newUrl);
|
|
213
222
|
this.iframe.src = newUrl;
|
|
223
|
+
|
|
224
|
+
// Update input if it exists
|
|
225
|
+
if (this.urlInput) {
|
|
226
|
+
this.urlInput.value = path;
|
|
227
|
+
}
|
|
214
228
|
}
|
|
215
229
|
}
|
|
216
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
|
+
|
|
217
237
|
handleIframeError() {
|
|
218
238
|
// Get the current iframe src and open it in a new tab if it's external
|
|
219
239
|
if (this.iframe && this.iframe.src) {
|