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.
- package/dist/components/Sidebar.d.ts +8 -0
- package/dist/components/Sidebar.d.ts.map +1 -0
- package/dist/components/Sidebar.js +6 -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/Sidebar.js +225 -0
- package/dist/static/components/SimpleLivePreview.js +73 -53
- package/dist/static/components/Terminal.js +141 -61
- package/dist/static/signals/SidebarSignal.js +123 -0
- package/dist/static/signals/TerminalSignal.js +137 -2
- package/dist/static/styles/main.css +111 -0
- package/package.json +1 -1
- package/src/components/Sidebar.tsx +92 -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/Sidebar.js +225 -0
- package/src/static/components/SimpleLivePreview.js +73 -53
- package/src/static/components/Terminal.js +141 -61
- package/src/static/signals/SidebarSignal.js +123 -0
- package/src/static/signals/TerminalSignal.js +137 -2
- package/src/static/styles/main.css +111 -0
- package/tailwind.config.ts +10 -0
|
@@ -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
|
@@ -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
|
|
32
|
-
title="
|
|
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>
|
package/src/pages/Code.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Layout from "../layouts/Layout.js";
|
|
2
|
-
import {
|
|
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
|
-
{/*
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
{/*
|
|
67
|
-
<
|
|
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
|
+
});
|