treesap 0.1.9 → 0.1.10

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.
@@ -1093,6 +1093,10 @@ video {
1093
1093
  margin-bottom: 0;
1094
1094
  }
1095
1095
 
1096
+ .pointer-events-none {
1097
+ pointer-events: none;
1098
+ }
1099
+
1096
1100
  .visible {
1097
1101
  visibility: visible;
1098
1102
  }
@@ -1101,6 +1105,10 @@ video {
1101
1105
  position: static;
1102
1106
  }
1103
1107
 
1108
+ .fixed {
1109
+ position: fixed;
1110
+ }
1111
+
1104
1112
  .absolute {
1105
1113
  position: absolute;
1106
1114
  }
@@ -1109,10 +1117,38 @@ video {
1109
1117
  position: relative;
1110
1118
  }
1111
1119
 
1120
+ .inset-0 {
1121
+ inset: 0px;
1122
+ }
1123
+
1124
+ .left-0 {
1125
+ left: 0px;
1126
+ }
1127
+
1128
+ .left-4 {
1129
+ left: 1rem;
1130
+ }
1131
+
1132
+ .top-0 {
1133
+ top: 0px;
1134
+ }
1135
+
1136
+ .top-4 {
1137
+ top: 1rem;
1138
+ }
1139
+
1140
+ .z-40 {
1141
+ z-index: 40;
1142
+ }
1143
+
1112
1144
  .z-50 {
1113
1145
  z-index: 50;
1114
1146
  }
1115
1147
 
1148
+ .z-60 {
1149
+ z-index: 60;
1150
+ }
1151
+
1116
1152
  .mb-12 {
1117
1153
  margin-bottom: 3rem;
1118
1154
  }
@@ -1217,6 +1253,20 @@ video {
1217
1253
  flex-shrink: 0;
1218
1254
  }
1219
1255
 
1256
+ .-translate-x-full {
1257
+ --tw-translate-x: -100%;
1258
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
1259
+ }
1260
+
1261
+ .translate-x-0 {
1262
+ --tw-translate-x: 0px;
1263
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
1264
+ }
1265
+
1266
+ .transform {
1267
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
1268
+ }
1269
+
1220
1270
  @keyframes spin {
1221
1271
  to {
1222
1272
  transform: rotate(360deg);
@@ -1359,6 +1409,10 @@ video {
1359
1409
  background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
1360
1410
  }
1361
1411
 
1412
+ .bg-opacity-50 {
1413
+ --tw-bg-opacity: 0.5;
1414
+ }
1415
+
1362
1416
  .bg-gradient-to-br {
1363
1417
  background-image: linear-gradient(to bottom right, var(--tw-gradient-stops));
1364
1418
  }
@@ -1491,6 +1545,14 @@ video {
1491
1545
  color: rgb(255 255 255 / var(--tw-text-opacity, 1));
1492
1546
  }
1493
1547
 
1548
+ .opacity-0 {
1549
+ opacity: 0;
1550
+ }
1551
+
1552
+ .opacity-100 {
1553
+ opacity: 1;
1554
+ }
1555
+
1494
1556
  .opacity-50 {
1495
1557
  opacity: 0.5;
1496
1558
  }
@@ -1505,6 +1567,12 @@ video {
1505
1567
  filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
1506
1568
  }
1507
1569
 
1570
+ .backdrop-blur-sm {
1571
+ --tw-backdrop-blur: blur(4px);
1572
+ -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
1573
+ backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
1574
+ }
1575
+
1508
1576
  .transition-all {
1509
1577
  transition-property: all;
1510
1578
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
@@ -1517,10 +1585,26 @@ video {
1517
1585
  transition-duration: 150ms;
1518
1586
  }
1519
1587
 
1588
+ .transition-opacity {
1589
+ transition-property: opacity;
1590
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1591
+ transition-duration: 150ms;
1592
+ }
1593
+
1594
+ .transition-transform {
1595
+ transition-property: transform;
1596
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1597
+ transition-duration: 150ms;
1598
+ }
1599
+
1520
1600
  .duration-300 {
1521
1601
  transition-duration: 300ms;
1522
1602
  }
1523
1603
 
1604
+ .ease-in-out {
1605
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1606
+ }
1607
+
1524
1608
  .focus-within\:border-\[\#0e639c\]:focus-within {
1525
1609
  --tw-border-opacity: 1;
1526
1610
  border-color: rgb(14 99 156 / var(--tw-border-opacity, 1));
@@ -1576,3 +1660,30 @@ video {
1576
1660
  --tw-text-opacity: 1;
1577
1661
  color: rgb(255 255 255 / var(--tw-text-opacity, 1));
1578
1662
  }
1663
+
1664
+ @media (min-width: 768px) {
1665
+ .md\:relative {
1666
+ position: relative;
1667
+ }
1668
+
1669
+ .md\:z-auto {
1670
+ z-index: auto;
1671
+ }
1672
+
1673
+ .md\:hidden {
1674
+ display: none;
1675
+ }
1676
+
1677
+ .md\:w-2\/5 {
1678
+ width: 40%;
1679
+ }
1680
+
1681
+ .md\:flex-1 {
1682
+ flex: 1 1 0%;
1683
+ }
1684
+
1685
+ .md\:translate-x-0 {
1686
+ --tw-translate-x: 0px;
1687
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
1688
+ }
1689
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "treesap",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "AI Agent Framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,92 @@
1
+ import { Terminal as TerminalComponent } from "./Terminal.js";
2
+
3
+ interface SidebarProps {
4
+ id?: string;
5
+ previewPort?: number;
6
+ workingDirectory?: string;
7
+ }
8
+
9
+ export function Sidebar({ id = "sidebar", previewPort = 1234, workingDirectory }: SidebarProps) {
10
+ return (
11
+ <sapling-island loading="visible">
12
+ <template>
13
+ <script type="module" src="https://code.iconify.design/iconify-icon/2.0.0/iconify-icon.min.js"></script>
14
+ <script type="module" src="/components/Sidebar.js"></script>
15
+ </template>
16
+
17
+ {/* Mobile backdrop */}
18
+ <div
19
+ id={`${id}-backdrop`}
20
+ class="fixed inset-0 bg-black bg-opacity-50 backdrop-blur-sm z-40 transition-opacity duration-300 opacity-0 pointer-events-none md:hidden"
21
+ ></div>
22
+
23
+ {/* Sidebar container */}
24
+ <div
25
+ id={`${id}-pane`}
26
+ class="fixed left-0 top-0 h-full w-full z-50 transform -translate-x-full transition-transform duration-300 ease-in-out md:relative md:translate-x-0 md:w-2/5 md:z-auto border-r border-[#3c3c3c] flex flex-col bg-[#252526]"
27
+ >
28
+ {/* Preview Controls */}
29
+ <div class="p-3 border-b border-[#3c3c3c] bg-[#2d2d30] flex-shrink-0">
30
+ <div class="flex items-center gap-2">
31
+ {/* Mobile close button */}
32
+ <button
33
+ type="button"
34
+ id={`${id}-close-btn`}
35
+ class="p-2 hover:bg-[#3c3c3c] rounded-md transition-colors flex-shrink-0 text-[#cccccc] hover:text-white md:hidden"
36
+ title="Close Sidebar"
37
+ >
38
+ <iconify-icon icon="tabler:x" width="16" height="16"></iconify-icon>
39
+ </button>
40
+
41
+ {/* Back to Home */}
42
+ <a
43
+ href="/"
44
+ class="p-2 hover:bg-[#3c3c3c] rounded-md transition-colors flex-shrink-0 text-[#cccccc] hover:text-white"
45
+ title="Back to Home"
46
+ >
47
+ <iconify-icon icon="tabler:arrow-left" width="16" height="16"></iconify-icon>
48
+ </a>
49
+
50
+ {/* Refresh button */}
51
+ <button
52
+ type="button"
53
+ id="live-preview-refresh-btn"
54
+ class="p-2 hover:bg-[#3c3c3c] rounded-md transition-colors flex-shrink-0 text-[#cccccc] hover:text-white"
55
+ title="Reload"
56
+ >
57
+ <iconify-icon icon="tabler:refresh" width="16" height="16"></iconify-icon>
58
+ </button>
59
+
60
+ {/* URL input */}
61
+ <div class="flex-1 flex items-center bg-[#1e1e1e] border border-[#3c3c3c] rounded px-3 py-2 hover:border-[#0e639c] focus-within:border-[#0e639c] transition-all">
62
+ <iconify-icon icon="tabler:world" width="16" height="16" class="text-[#cccccc] mr-2"></iconify-icon>
63
+ <span class="text-[#cccccc] text-sm">localhost:{previewPort}/</span>
64
+ <input
65
+ id="live-preview-url-input"
66
+ type="text"
67
+ placeholder="path"
68
+ defaultValue=""
69
+ class="flex-1 bg-transparent text-sm focus:outline-none text-[#cccccc] ml-1"
70
+ />
71
+ <button
72
+ type="button"
73
+ id="live-preview-load-btn"
74
+ class="ml-2 p-1 hover:bg-[#3c3c3c] rounded transition-colors flex-shrink-0 text-[#cccccc] hover:text-white"
75
+ title="Go"
76
+ >
77
+ <iconify-icon icon="tabler:chevron-right" width="16" height="16"></iconify-icon>
78
+ </button>
79
+ </div>
80
+ </div>
81
+ </div>
82
+
83
+ {/* Terminal Content */}
84
+ <div class="flex-1 overflow-hidden bg-[#1e1e1e]">
85
+ <div class="h-full">
86
+ <TerminalComponent index={1} />
87
+ </div>
88
+ </div>
89
+ </div>
90
+ </sapling-island>
91
+ );
92
+ }
@@ -25,14 +25,14 @@ export function SimpleLivePreview({ id = "simple-preview", previewPort = 5173 }:
25
25
  data-preview-port={previewPort}
26
26
  ></iframe>
27
27
 
28
- {/* Floating Sidebar Toggle */}
28
+ {/* Floating Sidebar Toggle - Only show when sidebar is closed */}
29
29
  <button
30
30
  id={`${id}-floating-hide-sidebar-btn`}
31
- class="absolute p-3 bg-white border-2 border-gray-400 rounded-lg shadow-xl hover:bg-gray-50 hover:shadow-2xl transition-all flex items-center justify-center z-50"
32
- title="Toggle Sidebar"
31
+ class="absolute p-3 bg-white border-2 border-gray-400 rounded-lg shadow-xl hover:bg-gray-50 hover:shadow-2xl transition-all items-center justify-center z-50 hidden"
32
+ title="Show Sidebar"
33
33
  style="position: absolute !important; z-index: 9999 !important; bottom: 16px; left: 16px;"
34
34
  >
35
- <iconify-icon id={`${id}-floating-hide-sidebar-icon`} icon="ph:sidebar-simple" width="20" height="20" class="text-gray-800"></iconify-icon>
35
+ <iconify-icon id={`${id}-floating-hide-sidebar-icon`} icon="ph:sidebar-simple-fill" width="20" height="20" class="text-gray-800"></iconify-icon>
36
36
  </button>
37
37
  </div>
38
38
  </sapling-island>
@@ -1,5 +1,5 @@
1
1
  import Layout from "../layouts/Layout.js";
2
- import { Terminal as TerminalComponent } from "../components/Terminal.js";
2
+ import { Sidebar } from "../components/Sidebar.js";
3
3
  import { SimpleLivePreview } from "../components/SimpleLivePreview.js";
4
4
 
5
5
  interface TerminalProps {
@@ -10,61 +10,24 @@ interface TerminalProps {
10
10
  export function Code({ previewPort = 1234, workingDirectory }: TerminalProps) {
11
11
  return (
12
12
  <Layout title="Code Editor">
13
- <div id="code-container" class="h-screen flex bg-[#1e1e1e]">
14
- {/* Left Pane - Tabbed Sidebar */}
15
- <div id="sidebar-pane" class="w-2/5-plus border-r border-[#3c3c3c] transition-all duration-300 flex flex-col bg-[#252526]">
16
- {/* Preview Controls */}
17
- <div class="p-3 border-b border-[#3c3c3c] bg-[#2d2d30]">
18
- <div class="flex items-center gap-2">
19
- <a
20
- href="/"
21
- class="p-2 hover:bg-[#3c3c3c] rounded-md transition-colors flex-shrink-0 text-[#cccccc] hover:text-white"
22
- title="Back to Home"
23
- >
24
- <iconify-icon icon="tabler:arrow-left" width="16" height="16"></iconify-icon>
25
- </a>
26
- <button
27
- type="button"
28
- id="live-preview-refresh-btn"
29
- class="p-2 hover:bg-[#3c3c3c] rounded-md transition-colors flex-shrink-0 text-[#cccccc] hover:text-white"
30
- title="Reload"
31
- >
32
- <iconify-icon icon="tabler:refresh" width="16" height="16"></iconify-icon>
33
- </button>
34
- <div class="flex-1 flex items-center bg-[#1e1e1e] border border-[#3c3c3c] rounded px-3 py-2 hover:border-[#0e639c] focus-within:border-[#0e639c] transition-all">
35
- <iconify-icon icon="tabler:world" width="16" height="16" class="text-[#cccccc] mr-2"></iconify-icon>
36
- <span class="text-[#cccccc] text-sm">localhost:{previewPort}/</span>
37
- <input
38
- id="live-preview-url-input"
39
- type="text"
40
- placeholder="path"
41
- defaultValue=""
42
- class="flex-1 bg-transparent text-sm focus:outline-none text-[#cccccc] ml-1"
43
- />
44
- <button
45
- type="button"
46
- id="live-preview-load-btn"
47
- class="ml-2 p-1 hover:bg-[#3c3c3c] rounded transition-colors flex-shrink-0 text-[#cccccc] hover:text-white"
48
- title="Go"
49
- >
50
- <iconify-icon icon="tabler:chevron-right" width="16" height="16"></iconify-icon>
51
- </button>
52
- </div>
53
- </div>
54
- </div>
55
-
56
-
57
- {/* Tab Content */}
58
- <div class="flex-1 overflow-hidden bg-[#1e1e1e]">
59
- {/* Single Terminal Content */}
60
- <div class="h-full">
61
- <TerminalComponent index={1} />
62
- </div>
63
- </div>
64
- </div>
13
+ <div id="code-container" class="h-screen flex bg-[#1e1e1e] relative">
14
+ {/* Mobile toggle button */}
15
+ <button
16
+ type="button"
17
+ id="mobile-sidebar-toggle"
18
+ class="fixed top-4 left-4 z-60 p-3 bg-[#2d2d30] border border-[#3c3c3c] rounded-lg shadow-xl hover:bg-[#3c3c3c] transition-all md:hidden"
19
+ title="Toggle Sidebar"
20
+ >
21
+ <iconify-icon icon="tabler:menu-2" width="20" height="20" class="text-[#cccccc]"></iconify-icon>
22
+ </button>
23
+
24
+ {/* Sidebar */}
25
+ <Sidebar id="sidebar" previewPort={previewPort} workingDirectory={workingDirectory} />
65
26
 
66
- {/* Right Pane - Live Preview */}
67
- <SimpleLivePreview id="live-preview" previewPort={previewPort} />
27
+ {/* Main Content - Live Preview */}
28
+ <div class="flex-1 md:flex-1">
29
+ <SimpleLivePreview id="live-preview" previewPort={previewPort} />
30
+ </div>
68
31
  </div>
69
32
  </Layout>
70
33
  );
@@ -104,8 +104,6 @@ export class WebSocketTerminalService {
104
104
  return;
105
105
  }
106
106
 
107
- console.log(`Received message from ${clientId}:`, message.type, message.sessionId);
108
-
109
107
  switch (message.type) {
110
108
  case 'join':
111
109
  this.handleJoin(clientId, message);
@@ -186,9 +184,7 @@ export class WebSocketTerminalService {
186
184
 
187
185
  private static handleInput(clientId: string, message: WebSocketMessage) {
188
186
  const client = this.clients.get(clientId);
189
- if (!client || !message.sessionId || message.data === undefined) return;
190
-
191
- console.log(`Input from client ${clientId} to session ${message.sessionId}`);
187
+ if (!client || !message.sessionId || message.data === undefined) return;;
192
188
 
193
189
  // Get the terminal session
194
190
  const session = TerminalService.getSession(message.sessionId);
@@ -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
+ });