webserial-core 2.0.0-dev.1 → 2.0.1
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/demo-style.css +255 -0
- package/dist/demos/assets/demo-shared-DLFsukQx.css +1 -0
- package/dist/demos/assets/demo-shared-DnvFynUr.js +272 -0
- package/dist/demos/assets/web-bluetooth-VVwTClLx.js +1 -0
- package/dist/demos/assets/web-serial-CsBUUDvz.js +1 -0
- package/dist/demos/assets/web-usb-D7sSHjku.js +1 -0
- package/dist/demos/assets/websocket-DREvCVt-.js +1 -0
- package/dist/demos/web-bluetooth.html +408 -0
- package/dist/demos/web-serial.html +483 -0
- package/dist/demos/web-usb.html +526 -0
- package/dist/demos/websocket.html +498 -0
- package/dist/dist/webserial-core.umd.js +1 -0
- package/dist/favicon.svg +10 -0
- package/dist/images/cover.svg +145 -0
- package/dist/images/icons/adapters.svg +13 -0
- package/dist/images/icons/events.svg +4 -0
- package/dist/images/icons/parsers.svg +7 -0
- package/dist/images/icons/queue.svg +8 -0
- package/dist/images/icons/reconnect.svg +7 -0
- package/dist/images/icons/typed.svg +5 -0
- package/dist/images/logo.svg +10 -0
- package/dist/robots.txt +4 -0
- package/dist/types/index.d.ts +6 -30
- package/package.json +5 -4
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/* Shared styles for all webserial-core demos.
|
|
2
|
+
* Each demo HTML sets its own CSS custom properties in an inline <style> block:
|
|
3
|
+
* --accent, --accent-dark, --accent-badge, --log-event
|
|
4
|
+
* --notice-bg, --notice-border, --notice-text, --notice-code-bg (optional)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
*,
|
|
8
|
+
*::before,
|
|
9
|
+
*::after {
|
|
10
|
+
box-sizing: border-box;
|
|
11
|
+
margin: 0;
|
|
12
|
+
padding: 0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
body {
|
|
16
|
+
font-family:
|
|
17
|
+
"Segoe UI",
|
|
18
|
+
system-ui,
|
|
19
|
+
-apple-system,
|
|
20
|
+
sans-serif;
|
|
21
|
+
background: #0f0f12;
|
|
22
|
+
color: #e4e4e7;
|
|
23
|
+
min-height: 100vh;
|
|
24
|
+
display: flex;
|
|
25
|
+
align-items: center;
|
|
26
|
+
justify-content: center;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
#app {
|
|
30
|
+
width: min(640px, 94vw);
|
|
31
|
+
background: #18181b;
|
|
32
|
+
border: 1px solid #27272a;
|
|
33
|
+
border-radius: 16px;
|
|
34
|
+
overflow: hidden;
|
|
35
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.45);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
header {
|
|
39
|
+
padding: 20px 24px;
|
|
40
|
+
border-bottom: 1px solid #27272a;
|
|
41
|
+
display: flex;
|
|
42
|
+
align-items: center;
|
|
43
|
+
gap: 12px;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
header h1 {
|
|
47
|
+
font-size: 1.15rem;
|
|
48
|
+
font-weight: 600;
|
|
49
|
+
color: var(--accent);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
header span {
|
|
53
|
+
font-size: 0.8rem;
|
|
54
|
+
color: #71717a;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.badge {
|
|
58
|
+
background: var(--accent);
|
|
59
|
+
color: var(--accent-badge);
|
|
60
|
+
font-size: 0.65rem;
|
|
61
|
+
font-weight: 700;
|
|
62
|
+
padding: 2px 8px;
|
|
63
|
+
border-radius: 999px;
|
|
64
|
+
text-transform: uppercase;
|
|
65
|
+
letter-spacing: 0.05em;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.controls {
|
|
69
|
+
display: flex;
|
|
70
|
+
gap: 8px;
|
|
71
|
+
padding: 16px 24px;
|
|
72
|
+
border-bottom: 1px solid #27272a;
|
|
73
|
+
flex-wrap: wrap;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
button {
|
|
77
|
+
padding: 8px 18px;
|
|
78
|
+
border: none;
|
|
79
|
+
border-radius: 8px;
|
|
80
|
+
cursor: pointer;
|
|
81
|
+
font-size: 0.85rem;
|
|
82
|
+
font-weight: 500;
|
|
83
|
+
transition:
|
|
84
|
+
background 0.2s,
|
|
85
|
+
opacity 0.2s;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
button:disabled {
|
|
89
|
+
opacity: 0.4;
|
|
90
|
+
cursor: not-allowed;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
#btn-connect {
|
|
94
|
+
background: var(--accent);
|
|
95
|
+
color: var(--accent-badge);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
#btn-connect:hover:not(:disabled) {
|
|
99
|
+
background: var(--accent-dark);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
#btn-disconnect {
|
|
103
|
+
background: #ef4444;
|
|
104
|
+
color: #fff;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
#btn-disconnect:hover:not(:disabled) {
|
|
108
|
+
background: #dc2626;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.notice {
|
|
112
|
+
margin: 0 24px 16px;
|
|
113
|
+
padding: 14px 16px;
|
|
114
|
+
background: var(--notice-bg, #161616);
|
|
115
|
+
border: 1px solid var(--notice-border, #3f3f46);
|
|
116
|
+
border-left: 3px solid var(--accent);
|
|
117
|
+
border-radius: 8px;
|
|
118
|
+
font-size: 0.78rem;
|
|
119
|
+
line-height: 1.6;
|
|
120
|
+
color: var(--notice-text, #e4e4e7);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.notice code {
|
|
124
|
+
background: var(--notice-code-bg, #0f0f0f);
|
|
125
|
+
padding: 1px 5px;
|
|
126
|
+
border-radius: 4px;
|
|
127
|
+
font-family: monospace;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.info-text {
|
|
131
|
+
margin: 0 24px 4px;
|
|
132
|
+
padding: 10px 0;
|
|
133
|
+
font-size: 0.8rem;
|
|
134
|
+
line-height: 1.6;
|
|
135
|
+
color: #a1a1aa;
|
|
136
|
+
border-bottom: 1px solid #27272a;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.info-text code {
|
|
140
|
+
background: #09090b;
|
|
141
|
+
color: #e4e4e7;
|
|
142
|
+
padding: 1px 5px;
|
|
143
|
+
border-radius: 4px;
|
|
144
|
+
font-family: monospace;
|
|
145
|
+
font-size: 0.78rem;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.send-row {
|
|
149
|
+
display: flex;
|
|
150
|
+
gap: 8px;
|
|
151
|
+
padding: 0 24px 16px;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.send-row input {
|
|
155
|
+
flex: 1;
|
|
156
|
+
padding: 8px 12px;
|
|
157
|
+
border-radius: 8px;
|
|
158
|
+
border: 1px solid #3f3f46;
|
|
159
|
+
background: #09090b;
|
|
160
|
+
color: #e4e4e7;
|
|
161
|
+
font-size: 0.85rem;
|
|
162
|
+
outline: none;
|
|
163
|
+
transition: border-color 0.2s;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.send-row input:focus {
|
|
167
|
+
border-color: var(--accent);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.send-row input:disabled {
|
|
171
|
+
opacity: 0.4;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
#btn-send {
|
|
175
|
+
background: #6366f1;
|
|
176
|
+
color: #fff;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
#btn-send:hover:not(:disabled) {
|
|
180
|
+
background: #4f46e5;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
#mode-toggle {
|
|
184
|
+
background: var(--accent);
|
|
185
|
+
color: var(--accent-badge);
|
|
186
|
+
font-weight: 700;
|
|
187
|
+
min-width: 54px;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
#mode-toggle:hover {
|
|
191
|
+
background: var(--accent-dark);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
#log {
|
|
195
|
+
height: 340px;
|
|
196
|
+
overflow-y: auto;
|
|
197
|
+
padding: 16px 24px;
|
|
198
|
+
font-family: "Fira Code", "Cascadia Code", monospace;
|
|
199
|
+
font-size: 0.8rem;
|
|
200
|
+
line-height: 1.7;
|
|
201
|
+
background: #09090b;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.log-line {
|
|
205
|
+
white-space: pre-wrap;
|
|
206
|
+
word-break: break-all;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.log-info {
|
|
210
|
+
color: #a1a1aa;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.log-event {
|
|
214
|
+
color: var(--log-event, #38bdf8);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.log-data {
|
|
218
|
+
color: #4ade80;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.log-error {
|
|
222
|
+
color: #f87171;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
#log::-webkit-scrollbar {
|
|
226
|
+
width: 6px;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
#log::-webkit-scrollbar-track {
|
|
230
|
+
background: transparent;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
#log::-webkit-scrollbar-thumb {
|
|
234
|
+
background: #3f3f46;
|
|
235
|
+
border-radius: 3px;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.nav-link {
|
|
239
|
+
display: block;
|
|
240
|
+
text-align: center;
|
|
241
|
+
padding: 10px;
|
|
242
|
+
color: #71717a;
|
|
243
|
+
font-size: 0.78rem;
|
|
244
|
+
text-decoration: none;
|
|
245
|
+
border-top: 1px solid #27272a;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.nav-link:hover {
|
|
249
|
+
color: var(--accent);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.nav-link + .nav-link {
|
|
253
|
+
border-top: none;
|
|
254
|
+
padding-top: 0;
|
|
255
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*,:before,:after{box-sizing:border-box;margin:0;padding:0}html,body{height:100%;overflow:hidden}:root{--bg:#f4f4f5;--surface:#fff;--surface-2:#f4f4f5;--surface-3:#e4e4e7;--border:#e4e4e7;--border-hi:#d1d5db;--text:#18181b;--text-muted:#71717a;--text-faint:#a1a1aa;--accent:#22c55e;--accent-dark:#16a34a;--accent-glow:#22c55e1f;--accent-badge:#fff;--sent-bg:var(--accent);--sent-text:#fff;--recv-bg:#e4e4e7;--recv-text:#18181b;--sys-bg:#fef9c3;--sys-text:#713f12;--sys-border:#fbbf24;--err-bg:#fee2e2;--err-text:#991b1b;--err-border:#f87171;--topbar-h:56px;--sidebar-w:320px;--code-w:370px;--code-bg:#1e1e1e;--code-text:#d4d4d4;--code-gutter:#858585;--code-hover:#ffffff0a;--code-border:#3f3f46;--scrollbar:#d1d5db}[data-theme=dark]{--bg:#0f0f12;--surface:#18181b;--surface-2:#1c1c1f;--surface-3:#27272a;--border:#27272a;--border-hi:#3f3f46;--text:#e4e4e7;--text-muted:#71717a;--text-faint:#52525b;--recv-bg:#27272a;--recv-text:#e4e4e7;--sys-bg:#1c1a07;--sys-text:#fcd34d;--sys-border:#854d0e;--err-bg:#1f0707;--err-text:#f87171;--err-border:#7f1d1d;--scrollbar:#3f3f46}body{background:var(--bg);color:var(--text);flex-direction:column;font-family:Segoe UI,system-ui,-apple-system,sans-serif;transition:background .2s,color .2s;display:flex}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:var(--scrollbar);border-radius:3px}.topbar{height:var(--topbar-h);background:var(--surface);border-bottom:1px solid var(--border);z-index:100;flex-shrink:0;align-items:center;gap:10px;padding:0 14px;display:flex}.topbar-brand{flex-shrink:0;align-items:center;gap:7px;text-decoration:none;display:flex}.brand-icon{font-size:1.1rem}.brand-name{color:var(--text);white-space:nowrap;font-size:.88rem;font-weight:700}.brand-ver{color:var(--text-faint);border:1px solid var(--border);border-radius:4px;padding:1px 6px;font-size:.6rem}.provider-pill{background:var(--accent);color:var(--accent-badge);text-transform:uppercase;letter-spacing:.05em;white-space:nowrap;border-radius:999px;padding:2px 8px;font-size:.6rem;font-weight:700}.topbar-sep{background:var(--border);flex-shrink:0;width:1px;height:22px}.flex-1{flex:1}.topbar-nav{scrollbar-width:none;flex-shrink:0;gap:2px;display:flex;overflow-x:auto}.topbar-nav::-webkit-scrollbar{display:none}.nav-item{color:var(--text-muted);white-space:nowrap;border-radius:7px;align-items:center;gap:5px;padding:5px 10px;font-size:.77rem;font-weight:500;text-decoration:none;transition:background .15s,color .15s;display:flex}.nav-item:hover{background:var(--surface-2);color:var(--text)}.nav-item.active{background:var(--accent-glow);color:var(--accent)}.topbar-actions{flex-shrink:0;align-items:center;gap:6px;display:flex}.status-pill{border:1px solid var(--border);color:var(--text-muted);white-space:nowrap;border-radius:999px;align-items:center;gap:5px;padding:4px 9px;font-size:.7rem;display:flex}.status-dot{background:var(--text-faint);border-radius:50%;flex-shrink:0;width:7px;height:7px;transition:background .2s,box-shadow .2s}.status-dot.connected{background:var(--accent);box-shadow:0 0 5px var(--accent)}.status-dot.connecting{background:#f59e0b;animation:1s infinite blink}.status-dot.error{background:#ef4444}@keyframes blink{0%,to{opacity:1}50%{opacity:.3}}.btn{cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none;border:1px solid #0000;border-radius:8px;justify-content:center;align-items:center;gap:5px;padding:6px 13px;font-size:.77rem;font-weight:500;line-height:1;transition:background .15s,opacity .15s,transform .1s,border-color .15s;display:inline-flex}.btn:active:not(:disabled){transform:scale(.97)}.btn:disabled{opacity:.4;cursor:not-allowed}.btn-connect{background:var(--accent);color:#fff}.btn-connect:hover:not(:disabled){background:var(--accent-dark)}.btn-disconnect{color:#fff;background:#ef4444}.btn-disconnect:hover:not(:disabled){background:#dc2626}.btn-send{color:#fff;background:#6366f1}.btn-send:hover:not(:disabled){background:#4f46e5}.btn-ghost{border-color:var(--border);color:var(--text-muted);background:0 0}.btn-ghost:hover:not(:disabled){background:var(--surface-2);color:var(--text)}.btn-mode{background:var(--accent);color:var(--accent-badge);border-color:#0000;min-width:46px;font-weight:700}.btn-mode:hover{background:var(--accent-dark)}.btn-dl{background:var(--surface);border-color:var(--border);color:var(--text)}.btn-dl:hover:not(:disabled){background:var(--surface-2)}.icon-btn{border:1px solid var(--border);cursor:pointer;width:32px;height:32px;color:var(--text-muted);background:0 0;border-radius:7px;flex-shrink:0;justify-content:center;align-items:center;font-size:.88rem;transition:background .15s,color .15s;display:flex}.icon-btn:hover{background:var(--surface-2);color:var(--text)}.app-layout{flex:1;display:flex;overflow:hidden}.sidebar{width:var(--sidebar-w);background:var(--surface);border-right:1px solid var(--border);flex-direction:column;flex-shrink:0;transition:width .22s;display:flex;overflow:hidden}.sidebar.collapsed{width:0}.sidebar-scroll{flex:1;overflow-y:auto}.sb-section{padding:12px 13px 9px}.sb-section+.sb-section{border-top:1px solid var(--border)}.sb-title{text-transform:uppercase;letter-spacing:.08em;color:var(--text-faint);margin-bottom:9px;font-size:.59rem;font-weight:700}.field{flex-direction:column;gap:3px;margin-bottom:8px;display:flex}.field:last-child{margin-bottom:0}.field-row{gap:7px;display:flex}.field-row .field{flex:1;min-width:0}.field label{color:var(--text-muted);font-size:.68rem;font-weight:500}.field select,.field input[type=text],.field input[type=number],.field input[type=url]{border:1px solid var(--border-hi);background:var(--surface-2);width:100%;color:var(--text);appearance:textfield;border-radius:6px;outline:none;padding:5px 8px;font-size:.75rem;transition:border-color .15s}.field input::-webkit-inner-spin-button{-webkit-appearance:none}.field select:focus,.field input:focus{border-color:var(--accent)}.field-toggle{justify-content:space-between;align-items:center;margin-bottom:8px;padding:1px 0;display:flex}.field-toggle label{color:var(--text-muted);cursor:pointer;font-size:.74rem}.sw{width:34px;height:18px;display:inline-block;position:relative}.sw input{opacity:0;width:0;height:0}.sw-knob{background:var(--surface-3);cursor:pointer;border-radius:999px;transition:background .2s;position:absolute;inset:0}.sw-knob:before{content:"";background:#fff;border-radius:50%;width:12px;height:12px;transition:transform .2s;position:absolute;bottom:3px;left:3px;box-shadow:0 1px 3px #00000040}.sw input:checked+.sw-knob{background:var(--accent)}.sw input:checked+.sw-knob:before{transform:translate(16px)}.dl-lang-row,.dl-type-row{flex-wrap:wrap;gap:5px;margin-bottom:7px;display:flex}.dl-opt{border:1px solid var(--border);background:var(--surface-2);color:var(--text-muted);cursor:pointer;border-radius:6px;align-items:center;gap:4px;padding:3px 9px;font-size:.71rem;transition:all .15s;display:inline-flex}.dl-opt:has(input:checked){border-color:var(--accent);color:var(--accent);background:var(--accent-glow)}.dl-opt:hover{background:var(--surface-3)}.dl-opt input{accent-color:var(--accent)}.dl-btns{flex-wrap:wrap;gap:5px;margin-top:7px;display:flex}.dl-btns .btn{flex:1;font-size:.72rem}.console-area{flex-direction:column;flex:1;min-width:0;display:flex;overflow:hidden}.console-hdr{background:var(--surface);border-bottom:1px solid var(--border);flex-shrink:0;align-items:center;gap:8px;padding:7px 13px;display:flex}.console-hdr .status-pill{font-size:.7rem}.messages{flex-direction:column;flex:1;gap:3px;padding:12px 13px;display:flex;position:relative;overflow-y:auto}.msg{flex-direction:column;max-width:78%;animation:.15s both msgIn;display:flex}@keyframes msgIn{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.msg.sent{align-self:flex-end;align-items:flex-end}.msg.received{align-self:flex-start;align-items:flex-start}.msg.system,.msg.error{align-self:center;max-width:88%}.msg-label{text-transform:uppercase;letter-spacing:.04em;color:var(--text-faint);margin-bottom:2px;padding:0 4px;font-size:.59rem;font-weight:700}.msg-bubble{word-break:break-word;white-space:pre-wrap;border-radius:14px;padding:7px 12px;font-family:Cascadia Code,Fira Code,monospace;font-size:.79rem;line-height:1.5}.msg.sent .msg-bubble{background:var(--sent-bg);color:var(--sent-text);border-bottom-right-radius:3px}.msg.received .msg-bubble{background:var(--recv-bg);color:var(--recv-text);border-bottom-left-radius:3px}.msg.system .msg-bubble{background:var(--sys-bg);color:var(--sys-text);border-left:3px solid var(--sys-border);border-radius:8px;font-family:inherit;font-size:.73rem}.msg.error .msg-bubble{background:var(--err-bg);color:var(--err-text);border-left:3px solid var(--err-border);border-radius:8px;font-family:inherit;font-size:.73rem}.msg-time{color:var(--text-faint);margin-top:2px;padding:0 4px;font-size:.59rem}.empty-state{color:var(--text-faint);-webkit-user-select:none;user-select:none;pointer-events:none;flex-direction:column;flex:1;justify-content:center;align-items:center;gap:8px;font-size:.78rem;display:flex}.empty-icon{opacity:.3;font-size:2.4rem}.input-bar{background:var(--surface);border-top:1px solid var(--border);flex-shrink:0;align-items:center;gap:7px;padding:10px 12px;display:flex}.msg-input{border:1px solid var(--border-hi);background:var(--surface-2);color:var(--text);border-radius:22px;outline:none;flex:1;padding:8px 14px;font-size:.82rem;transition:border-color .15s}.msg-input:focus{border-color:var(--accent)}.msg-input:disabled{opacity:.4}.code-panel{width:var(--code-w);background:var(--code-bg);border-left:1px solid var(--code-border);flex-direction:column;flex-shrink:0;transition:width .22s;display:flex;overflow:hidden}.code-panel.collapsed{width:0}.cp-hdr{border-bottom:1px solid var(--code-border);background:#252526;flex-shrink:0;justify-content:space-between;align-items:center;gap:8px;padding:7px 11px;display:flex}.cp-title{color:#ccc;align-items:center;gap:6px;font-size:.68rem;font-weight:600;display:flex}.cp-tab{color:#bbb;cursor:default;background:#37373d;border:none;border-radius:4px;padding:2px 8px;font-size:.67rem}.cp-actions{gap:4px;display:flex}.cp-btn{color:#888;cursor:pointer;background:#37373d;border:none;border-radius:4px;padding:3px 8px;font-size:.67rem;transition:all .15s}.cp-btn:hover{color:#ccc;background:#4a4a4f}.cp-btn.copied{color:#4ade80;background:#1e4d2b}.code-view{background:var(--code-bg);flex:1;padding:6px 0;font-family:Cascadia Code,Fira Code,Consolas,monospace;font-size:.72rem;line-height:1.65;overflow:auto}.code-line{min-height:1.65em;display:flex}.code-line:hover{background:var(--code-hover)}.cl-num{text-align:right;width:42px;color:var(--code-gutter);-webkit-user-select:none;user-select:none;flex-shrink:0;padding-right:14px;font-size:.67rem;line-height:1.65}.cl-txt{color:var(--code-text);white-space:pre;flex:1;padding:0 14px}.t-kw{color:#569cd6}.t-str{color:#ce9178}.t-num{color:#b5cea8}.t-cmt{color:#6a9955;font-style:italic}.t-fn{color:#dcdcaa}.t-cls{color:#4ec9b0}.t-var{color:#9cdcfe}.t-typ{color:#4ec9b0}.notice{background:var(--surface-2);border:1px solid var(--border);border-left:3px solid var(--accent);color:var(--text-muted);border-radius:7px;margin-bottom:3px;padding:8px 11px;font-size:.71rem;line-height:1.6}.notice code{background:var(--surface-3);border-radius:3px;padding:1px 5px;font-family:monospace;font-size:.69rem}.mob-toggle{display:none!important}.mob-nav-drawer{top:var(--topbar-h);background:var(--surface);border-bottom:1px solid var(--border);z-index:85;max-height:0;transition:max-height .26s,box-shadow .26s;position:fixed;left:0;right:0;overflow:hidden;box-shadow:0 6px 28px #00000038}.mob-nav-drawer.open{max-height:340px;box-shadow:0 6px 28px #0000004d}.mob-nav-inner{flex-direction:column;gap:9px;padding:10px 12px 12px;display:flex}.mob-nav-links{flex-wrap:wrap;gap:6px;display:flex}.mob-nav-links .nav-item{border:1px solid var(--border);background:var(--surface-2);border-radius:8px;flex:1;justify-content:center;min-width:72px;padding:8px 10px;font-size:.78rem}.mob-nav-links .nav-lbl{display:inline!important}.mob-nav-sep{background:var(--border);height:1px}.mob-nav-actions{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.mob-nav-actions .btn{flex:1;min-width:90px}@media (width<=1200px){:root{--code-w:320px}.mob-toggle{display:flex!important}.topbar-nav{display:none!important}.topbar-sep{display:none}}@media (width<=960px){:root{--sidebar-w:290px}.code-panel{right:0;top:var(--topbar-h);z-index:70;width:min(90vw,400px);transition:transform .28s;position:fixed;bottom:0;transform:translate(calc(100% + 1px));box-shadow:-4px 0 24px #0006}.code-panel:not(.collapsed){transform:translate(0)}.code-toggle{display:flex!important}.resize-handle{display:none!important}}@media (width<=720px){.brand-ver,.topbar-brand{display:none}}@media (width<=640px){.topbar-actions .btn-connect,.topbar-actions .btn-disconnect{display:none}.sidebar{top:var(--topbar-h);z-index:50;position:fixed;bottom:0;left:0;box-shadow:4px 0 24px #0000004d}.sidebar.collapsed{box-shadow:none}}.resize-handle,.sidebar-resize-handle{cursor:col-resize;z-index:10;background:0 0;flex-shrink:0;width:4px;transition:background .15s}.resize-handle:hover,.resize-handle.dragging,.sidebar-resize-handle:hover,.sidebar-resize-handle.dragging{background:var(--accent)}.chip-list{flex-direction:column;gap:5px;margin-top:5px;display:flex}.chip{background:var(--surface-2);border:1px solid var(--border);border-radius:8px;align-items:center;gap:5px;min-width:0;padding:5px 8px;font-size:.71rem;display:flex}.chip-badge{background:var(--accent-glow);color:var(--accent);text-transform:uppercase;letter-spacing:.03em;border-radius:4px;flex-shrink:0;padding:1px 5px;font-size:.57rem;font-weight:700}.chip-name{color:var(--text);text-overflow:ellipsis;white-space:nowrap;flex-shrink:0;max-width:72px;font-weight:600;overflow:hidden}.chip-val{color:var(--text-muted);text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;font-family:Cascadia Code,Fira Code,monospace;font-size:.68rem;overflow:hidden}.chip-send,.chip-del{cursor:pointer;background:var(--surface-3);width:20px;height:20px;color:var(--text-muted);border:none;border-radius:5px;flex-shrink:0;justify-content:center;align-items:center;padding:0;font-size:.68rem;line-height:1;transition:background .15s,color .15s;display:flex}.chip-send:hover{background:var(--accent-glow);color:var(--accent)}.chip-del:hover{color:#dc2626;background:#fee2e2}[data-theme=dark] .chip-del:hover{color:#f87171;background:#3f0707}[data-lucide]{stroke-width:2px;vertical-align:-2px;flex-shrink:0;width:14px;height:14px;display:inline-block}.icon-btn [data-lucide]{width:16px;height:16px}.app-footer{color:var(--text-faint);text-align:center;border-top:1px solid var(--border);background:var(--surface);letter-spacing:.02em;flex-shrink:0;padding:5px 16px;font-size:.63rem}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e=class{listeners={};on(e,t){return this.listeners[e]||(this.listeners[e]=new Set),this.listeners[e].add(t),this}off(e,t){return this.listeners[e]&&this.listeners[e].delete(t),this}emit(e,...t){let n=this.listeners[e];if(!n||n.size===0)return!1;for(let e of n)e(...t);return!0}},t=class{static instances=new Set;static portInstanceMap=new WeakMap;static getInstances(){return Array.from(this.instances)}static register(e){this.instances.add(e)}static unregister(e){this.instances.delete(e)}static isPortInUse(e,t){let n=this.portInstanceMap.get(e);return n!==void 0&&n!==t}static lockPort(e,t){this.portInstanceMap.set(e,t)}static unlockPort(e){this.portInstanceMap.delete(e)}},n=class{queue=[];isProcessing=!1;isPaused=!0;timeoutId=null;commandTimeout;onSend;onTimeout;constructor(e){this.commandTimeout=e.commandTimeout,this.onSend=e.onSend,this.onTimeout=e.onTimeout}get queueSize(){return this.queue.length}enqueue(e){this.queue.push(e),this.tryProcessNext()}advance(){this.clearCommandTimeout(),this.isProcessing=!1,this.tryProcessNext()}pause(){this.isPaused=!0,this.clearCommandTimeout(),this.isProcessing=!1}resume(){this.isPaused=!1,this.tryProcessNext()}clear(){this.queue=[],this.clearCommandTimeout(),this.isProcessing=!1}snapshot(){return[...this.queue]}restore(e){this.queue=[...e,...this.queue]}tryProcessNext(){if(this.isPaused||this.isProcessing||this.queue.length===0)return;this.isProcessing=!0;let e=this.queue.shift();this.commandTimeout>0&&(this.timeoutId=setTimeout(()=>{this.timeoutId=null,this.onTimeout(e),this.advance()},this.commandTimeout)),this.onSend(e).catch(()=>{this.advance()})}clearCommandTimeout(){this.timeoutId!==null&&(clearTimeout(this.timeoutId),this.timeoutId=null)}},r=class e extends Error{constructor(t){super(t),this.name=`SerialPermissionError`,Object.setPrototypeOf(this,e.prototype)}},i=class e extends Error{constructor(t){super(t),this.name=`SerialReadError`,Object.setPrototypeOf(this,e.prototype)}},a=class e extends Error{constructor(t){super(t),this.name=`SerialWriteError`,Object.setPrototypeOf(this,e.prototype)}},o=class o extends e{port=null;reader=null;writer=null;queue;options;isConnecting=!1;abortController=null;userInitiatedDisconnect=!1;reconnectTimerId=null;isHandshaking=!1;static customProvider=null;static polyfillOptions;constructor(e){super(),this.options={baudRate:e.baudRate,dataBits:e.dataBits??8,stopBits:e.stopBits??1,parity:e.parity??`none`,bufferSize:e.bufferSize??255,flowControl:e.flowControl??`none`,filters:e.filters??[],commandTimeout:e.commandTimeout??0,parser:e.parser,autoReconnect:e.autoReconnect??!1,autoReconnectInterval:e.autoReconnectInterval??1500,handshakeTimeout:e.handshakeTimeout??2e3,provider:e.provider,polyfillOptions:e.polyfillOptions},this.queue=new n({commandTimeout:this.options.commandTimeout,onSend:async e=>{await this.writeToPort(e),this.emit(`serial:sent`,e,this)},onTimeout:e=>{this.emit(`serial:timeout`,e,this)}}),this.on(`serial:data`,()=>{this.queue.advance()}),t.register(this)}async handshake(){return!0}async connect(){if(!this.isConnecting&&!this.port){this.isConnecting=!0,this.emit(`serial:connecting`,this);try{let e=this.getSerial();if(!e)throw Error(`Web Serial API is not supported in this browser. Use AbstractSerialDevice.setProvider() to set a WebUSB polyfill.`);if(this.port=await this.findAndValidatePort(),!this.port){let t;try{t=await e.requestPort({filters:this.options.filters},this.options.polyfillOptions??o.polyfillOptions)}catch(e){throw e instanceof DOMException&&(e.name===`NotFoundError`||e.name===`SecurityError`||e.name===`AbortError`)?new r(e instanceof Error?e.message:String(e)):e instanceof Error?e:Error(String(e))}if(!await this.openAndHandshake(t))throw Error(`Handshake failed: the selected device did not respond correctly.`);this.port=t}this.abortController=new AbortController,this.queue.resume(),this.emit(`serial:connected`,this)}catch(e){if(e instanceof r?this.emit(`serial:need-permission`,this):this.emit(`serial:error`,e instanceof Error?e:Error(String(e)),this),this.port){t.unlockPort(this.port);try{await this.port.close()}catch{}this.port=null}throw e}finally{this.isConnecting=!1}}}async disconnect(){this.port&&(this.userInitiatedDisconnect=!0,this.stopReconnecting(),await this.cleanupPort())}async cleanupPort(){if(this.port){this.queue.pause(),this.abortController?.abort(),this.abortController=null;try{let e=this.reader,t=this.writer;if(this.reader=null,this.writer=null,e){try{await e.cancel()}catch{}try{e.releaseLock()}catch{}}if(t){try{await t.close()}catch{}try{t.releaseLock()}catch{}}try{await this.port.close()}catch{}}catch(e){this.emit(`serial:error`,e instanceof Error?e:Error(String(e)),this)}finally{this.port&&t.unlockPort(this.port),this.port=null,this.options.parser?.reset?.(),this.emit(`serial:disconnected`,this),!this.userInitiatedDisconnect&&this.options.autoReconnect&&this.startReconnecting(),this.userInitiatedDisconnect=!1}}}async forget(){await this.disconnect(),this.port&&typeof this.port.forget==`function`&&await this.port.forget(),t.unregister(this)}async send(e){let t;t=typeof e==`string`?new TextEncoder().encode(e):e,t.length>0&&this.queue.enqueue(t)}clearQueue(){this.queue.clear(),this.emit(`serial:queue-empty`,this)}async writeToPort(e){if(!this.port||!this.port.writable)throw new a(`Port not writable.`);this.writer=this.port.writable.getWriter();try{await this.writer.write(e)}catch(e){throw new a(e instanceof Error?e.message:String(e))}finally{this.writer.releaseLock(),this.writer=null}}async readLoop(){if(!(!this.port||!this.port.readable)&&!this.reader){this.reader=this.port.readable.getReader();try{for(;;){let{value:e,done:t}=await this.reader.read();if(t)break;e&&(this.options.parser?this.options.parser.parse(e,e=>{this.emit(`serial:data`,e,this)}):this.emit(`serial:data`,e,this))}}catch(e){if(this.port)throw new i(e instanceof Error?e.message:String(e))}finally{if(this.reader){try{this.reader.releaseLock()}catch{}this.reader=null}}}}async openAndHandshake(e){let n=this;if(t.isPortInUse(e,n))return!1;t.lockPort(e,n);try{await e.open({baudRate:this.options.baudRate,dataBits:this.options.dataBits,stopBits:this.options.stopBits,parity:this.options.parity,bufferSize:this.options.bufferSize,flowControl:this.options.flowControl})}catch(n){throw t.unlockPort(e),n instanceof Error?n:Error(String(n))}this.port=e,this.abortController=new AbortController;let r=this.queue.snapshot();this.isHandshaking=!0,this.readLoop().catch(e=>{!this.isHandshaking&&this.port&&(this.emit(`serial:error`,e,this),this.cleanupPort())}),this.queue.resume();try{let t=await this.runHandshakeWithTimeout();return this.isHandshaking=!1,t?(this.queue.pause(),this.queue.clear(),this.queue.restore(r),this.options.parser?.reset?.(),!0):(await this.teardownHandshake(e,r),!1)}catch{return this.isHandshaking=!1,await this.teardownHandshake(e,r),!1}}async teardownHandshake(e,n){this.queue.pause(),this.queue.clear(),this.queue.restore(n),await this.stopReader(),this.port=null,this.abortController=null,this.options.parser?.reset?.();try{await e.close()}catch{}t.unlockPort(e)}async stopReader(){let e=this.reader;if(this.reader=null,e){try{await e.cancel()}catch{}try{e.releaseLock()}catch{}}}async runHandshakeWithTimeout(){let e=this.options.handshakeTimeout??2e3;return Promise.race([this.handshake(),new Promise(t=>setTimeout(()=>t(!1),e))])}async findAndValidatePort(){let e=this.getSerial();if(!e)return null;let n=await e.getPorts(this.options.polyfillOptions??o.polyfillOptions);if(n.length===0)return null;let r=this.options.filters??[],i=this;for(let e of n)if(!t.isPortInUse(e,i)){if(r.length>0){let t=e.getInfo();if(!r.some(e=>{let n=e.usbVendorId===void 0||e.usbVendorId===t.usbVendorId,r=e.usbProductId===void 0||e.usbProductId===t.usbProductId;return n&&r}))continue}try{if(await this.openAndHandshake(e))return e}catch{}}return null}startReconnecting(){this.reconnectTimerId||=(this.emit(`serial:reconnecting`,this),setInterval(async()=>{if(this.port||this.isConnecting){this.stopReconnecting();return}try{let e=await this.findAndValidatePort();e&&(this.stopReconnecting(),await this.reconnect(e))}catch{}},this.options.autoReconnectInterval))}stopReconnecting(){this.reconnectTimerId&&=(clearInterval(this.reconnectTimerId),null)}async reconnect(e){if(!(this.isConnecting||this.port)){this.isConnecting=!0,this.emit(`serial:connecting`,this);try{this.port=e,this.abortController=new AbortController,this.queue.resume(),this.emit(`serial:connected`,this)}catch(e){this.emit(`serial:error`,e instanceof Error?e:Error(String(e)),this),this.port&&=(t.unlockPort(this.port),null),this.options.autoReconnect&&this.startReconnecting()}finally{this.isConnecting=!1}}}static getInstances(){return t.getInstances()}static async connectAll(){let e=t.getInstances();for(let t of e)try{await t.connect()}catch{}}static setProvider(e,t){o.customProvider=e,o.polyfillOptions=t}getSerial(){return this.options.provider?this.options.provider:o.customProvider?o.customProvider:typeof navigator<`u`&&navigator.serial?navigator.serial:null}};function s(e){let t=``,n=new TextDecoder;return{parse(r,i){t+=n.decode(r,{stream:!0});let a;for(;(a=t.indexOf(e))!==-1;)i(t.slice(0,a)),t=t.slice(a+e.length)},reset(){t=``,n=new TextDecoder}}}var c=`wsc-demo-theme`;function l(){return window.matchMedia(`(prefers-color-scheme: dark)`).matches?`dark`:`light`}function u(){let e=localStorage.getItem(c)??l();return document.documentElement.setAttribute(`data-theme`,e),window.matchMedia(`(prefers-color-scheme: dark)`).addEventListener(`change`,e=>{localStorage.getItem(c)||document.documentElement.setAttribute(`data-theme`,e.matches?`dark`:`light`)}),e}function d(){let e=(document.documentElement.getAttribute(`data-theme`)??l())===`dark`?`light`:`dark`;return localStorage.setItem(c,e),document.documentElement.setAttribute(`data-theme`,e),e}function f(e,t,n){let r=e.querySelector(`.empty-state`);r&&r.remove();let{kind:i,label:a,time:o=new Date}=n,s=document.createElement(`div`);if(s.className=`msg ${i}`,a&&(i===`sent`||i===`received`)){let e=document.createElement(`div`);e.className=`msg-label`,e.textContent=a,s.appendChild(e)}let c=document.createElement(`div`);c.className=`msg-bubble`,c.textContent=t,s.appendChild(c);let l=document.createElement(`div`);l.className=`msg-time`,l.textContent=o.toLocaleTimeString([],{hour:`2-digit`,minute:`2-digit`,second:`2-digit`}),s.appendChild(l),e.appendChild(s),e.scrollTop=e.scrollHeight}function p(e){e.innerHTML=`
|
|
2
|
+
<div class="empty-state">
|
|
3
|
+
<div class="empty-icon">💬</div>
|
|
4
|
+
<span>Messages cleared</span>
|
|
5
|
+
</div>`}var m=new Set(`import.export.from.default.as.class.extends.implements.constructor.super.new.this.return.const.let.var.async.await.function.protected.public.private.static.abstract.interface.type.enum.namespace.declare.readonly.true.false.null.undefined.void.never.any.unknown.if.else.for.while.do.switch.case.break.continue.try.catch.finally.throw.typeof.instanceof.in.of.keyof.infer.string.number.boolean.object.symbol.bigint.Promise.Array.Set.Map`.split(`.`));function h(e){return e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`)}function g(e){let t=``,n=0,r=e.length;for(;n<r;){if(e[n]===`/`&&e[n+1]===`/`){t+=`<span class="t-cmt">${h(e.slice(n))}</span>`;break}let i=e[n];if(i===`'`||i===`"`||i==="`"){let a=n+1;for(;a<r;){if(e[a]===`\\`&&a+1<r){a+=2;continue}if(e[a]===i){a++;break}a++}t+=`<span class="t-str">${h(e.slice(n,a))}</span>`,n=a;continue}if(/\d/.test(e[n])&&(n===0||!/\w/.test(e[n-1]))){let i=n;for(;i<r&&/[\d.xXa-fA-F_n]/.test(e[i]);)i++;t+=`<span class="t-num">${h(e.slice(n,i))}</span>`,n=i;continue}if(/[a-zA-Z_$]/.test(e[n])){let i=n;for(;i<r&&/[\w$]/.test(e[i]);)i++;let a=e.slice(n,i);m.has(a)?t+=`<span class="t-kw">${h(a)}</span>`:i<r&&e[i]===`(`?t+=`<span class="t-fn">${h(a)}</span>`:/^[A-Z]/.test(a)?t+=`<span class="t-cls">${h(a)}</span>`:t+=`<span class="t-var">${h(a)}</span>`,n=i;continue}t+=h(e[n]),n++}return t}function _(e,t){let n=t.split(`
|
|
6
|
+
`);e.innerHTML=``,n.forEach((t,n)=>{let r=document.createElement(`div`);r.className=`code-line`;let i=document.createElement(`span`);i.className=`cl-num`,i.textContent=String(n+1);let a=document.createElement(`span`);a.className=`cl-txt`,a.innerHTML=g(t)||` `,r.appendChild(i),r.appendChild(a),e.appendChild(r)})}function v(e){return`'${e.replace(/\\(?![nrt0\\'])/g,`\\\\`).replace(/'/g,`\\'`)}'`}function y(e){return e.replace(/\\n/g,`
|
|
7
|
+
`).replace(/\\r/g,`\r`).replace(/\\t/g,` `).replace(/\\0/g,`\0`)}function b(e){return!e||e.length===0?`[]`:`[${e.map(e=>{let t=[];return e.usbVendorId!==void 0&&t.push(`usbVendorId: 0x${e.usbVendorId.toString(16).padStart(4,`0`)}`),e.usbProductId!==void 0&&t.push(`usbProductId: 0x${e.usbProductId.toString(16).padStart(4,`0`)}`),`{ ${t.join(`, `)} }`}).join(`, `)}]`}function x(e){return e.trim().split(/\s+/).filter(Boolean).map(e=>parseInt(e,16)).filter(e=>!isNaN(e))}function S(e){let t=x(e);return t.length===0?`new Uint8Array([])`:`new Uint8Array([${t.map(e=>`0x${e.toString(16).padStart(2,`0`)}`).join(`, `)}])`}function C(e){return e.replace(/[^a-zA-Z0-9 _-]/g,``).split(/[\s_-]+/).filter(Boolean).map((e,t)=>t===0?e.charAt(0).toLowerCase()+e.slice(1).toLowerCase():e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join(``)||`cmd`}function w(e,t){if(e.length===0)return``;let n=t.charAt(0).toLowerCase()+t.slice(1);return`
|
|
8
|
+
declare module 'webserial-core' {
|
|
9
|
+
interface SerialEventMap<T> {
|
|
10
|
+
`+e.map(e=>` '${n}:${C(e.name||`listener`)}': (data: string) => void;`).join(`
|
|
11
|
+
`)+`
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
`}function T(e,t){if(e.length===0)return``;let n=t?`: Promise<void>`:``,r=t?`public `:``;return`
|
|
15
|
+
`+e.map(e=>{let t=C(e.name),i=`send`+t.charAt(0).toUpperCase()+t.slice(1);return e.mode===`hex`?` ${r}${i}()${n} { return this.send(${S(e.value)}); } // ${e.name}`:` ${r}${i}()${n} { return this.send('${e.value.replace(/'/g,`\\'`)}'); } // ${e.name}`}).join(`
|
|
16
|
+
`)+`
|
|
17
|
+
`}function E(e,t,n){if(e.length===0)return``;let r=t.charAt(0).toLowerCase()+t.slice(1),i=n?`(data: string)`:`(data)`,a=n?`: void`:``,o=e.map(e=>{let t=e.name||`listener`,n=`${r}:${C(t)}`,i;switch(e.match){case`contains`:i=`String(data).includes('${e.pattern.replace(/'/g,`\\'`)}')`;break;case`startsWith`:i=`String(data).startsWith('${e.pattern.replace(/'/g,`\\'`)}')`;break;case`hex`:i=`(() => { const enc = new TextEncoder().encode(String(data)); const ex = ${`[${x(e.pattern).map(e=>`0x${e.toString(16).padStart(2,`0`)}`).join(`, `)}]`}; return enc.length === ex.length && enc.every((b, i) => b === ex[i]); })()`;break;default:i=`String(data).trim() === '${e.pattern.replace(/'/g,`\\'`)}' `}return` // ${t}\n if (${i}) {\n this.emit('${n}', data);\n }`}).join(`
|
|
18
|
+
`);return`\n ${n?`private `:``}startListening()${a} {\n this.on('serial:data', ${i} => {\n${o}\n });\n }\n`}function D(e,t,n,r,i){if(!e)return` // No handshake configured — accept any device.
|
|
19
|
+
return true;`;let a;if(a=t===`hex`?` await this.send(${S(e)});`:` await this.send('${e.replace(/'/g,`\\'`)}');`,!n)return`${a}\n return true;`;let o=i?`(data: string)`:`(data)`,s;return s=r===`hex`?`(() => { const enc = new TextEncoder().encode(String(data)); const ex = ${`[${x(n).map(e=>`0x${e.toString(16).padStart(2,`0`)}`).join(`, `)}]`}; return enc.length === ex.length && enc.every((b, i) => b === ex[i]); })()`:`String(data).trim() === '${n.replace(/'/g,`\\'`)}'`,`${a}\n return new Promise((resolve) => {\n const _h = ${o} => {\n this.off('serial:data', _h);\n resolve(${s});\n };\n this.on('serial:data', _h);\n });`}function O(e,t,n,r=[],i=[]){let a=n?`ts`:`js`,o=v(e.delimiter||`\\n`),s=n?`: Promise<boolean>`:``,c=n?`: SerialPortFilter[]`:``,l=n?`
|
|
20
|
+
import type { SerialPortFilter } from 'webserial-core';`:``,u=b(e.filters);return`// device.${a} — Generated by webserial-core demo
|
|
21
|
+
import { AbstractSerialDevice, delimiter } from 'webserial-core';${l}
|
|
22
|
+
${n?w(i,t):``}
|
|
23
|
+
export class ${t} extends AbstractSerialDevice${n?`<string>`:``} {
|
|
24
|
+
constructor(filters${n?`${c}`:` = []`}) {
|
|
25
|
+
super({
|
|
26
|
+
baudRate: ${e.baudRate},
|
|
27
|
+
dataBits: ${e.dataBits},
|
|
28
|
+
stopBits: ${e.stopBits},
|
|
29
|
+
parity: '${e.parity}',
|
|
30
|
+
flowControl: '${e.flowControl}',
|
|
31
|
+
bufferSize: ${e.bufferSize},
|
|
32
|
+
commandTimeout: ${e.commandTimeout},
|
|
33
|
+
parser: delimiter(${o}),
|
|
34
|
+
autoReconnect: ${e.autoReconnect},
|
|
35
|
+
autoReconnectInterval: ${e.autoReconnectInterval},
|
|
36
|
+
handshakeTimeout: ${e.handshakeTimeout},
|
|
37
|
+
filters,
|
|
38
|
+
});${i.length>0?`
|
|
39
|
+
this.startListening();`:``}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
${n?`protected `:``}async handshake()${s} {
|
|
43
|
+
${D(e.hsCmd,e.hsCmdMode,e.hsExpect,e.hsExpectMode,n)}
|
|
44
|
+
}
|
|
45
|
+
${T(r,n)}${E(i,t,n)}}
|
|
46
|
+
|
|
47
|
+
// ── Usage ────────────────────────────────────────────────────────
|
|
48
|
+
const device = new ${t}(${u});
|
|
49
|
+
|
|
50
|
+
device.on('serial:connected', () => console.log('Connected!'));
|
|
51
|
+
device.on('serial:disconnected', () => console.log('Disconnected.'));
|
|
52
|
+
device.on('serial:data', (data) => console.log('← ', data));
|
|
53
|
+
device.on('serial:error', (err) => console.error(err.message));
|
|
54
|
+
|
|
55
|
+
// Must be called from a user-gesture (click handler):
|
|
56
|
+
// await device.connect();
|
|
57
|
+
|
|
58
|
+
// Send a message (prepend/append applied in your UI layer):
|
|
59
|
+
// await device.send('${e.prepend}COMMAND${e.append}');
|
|
60
|
+
|
|
61
|
+
// Disconnect:
|
|
62
|
+
// await device.disconnect();
|
|
63
|
+
`}function k(e,t,n,r=[],i=[]){let a=n?`ts`:`js`,o=v(e.delimiter||`\\n`),s=n?`: Promise<boolean>`:``;return`// device.${a} — Generated by webserial-core demo
|
|
64
|
+
import { AbstractSerialDevice, delimiter, createBluetoothProvider } from 'webserial-core';
|
|
65
|
+
|
|
66
|
+
// Inject the BLE provider before creating any device instance.
|
|
67
|
+
AbstractSerialDevice.setProvider(createBluetoothProvider());
|
|
68
|
+
${n?w(i,t):``}
|
|
69
|
+
export class ${t} extends AbstractSerialDevice${n?`<string>`:``} {
|
|
70
|
+
constructor() {
|
|
71
|
+
super({
|
|
72
|
+
baudRate: 9600, // Nominal — not used over BLE GATT
|
|
73
|
+
bufferSize: ${e.bufferSize},
|
|
74
|
+
commandTimeout: ${e.commandTimeout},
|
|
75
|
+
parser: delimiter(${o}),
|
|
76
|
+
autoReconnect: false, // BLE requires user gesture to reconnect
|
|
77
|
+
handshakeTimeout: ${e.handshakeTimeout},
|
|
78
|
+
});${i.length>0?`
|
|
79
|
+
this.startListening();`:``}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
${n?`protected `:``}async handshake()${s} {
|
|
83
|
+
${D(e.hsCmd,e.hsCmdMode,e.hsExpect,e.hsExpectMode,n)}
|
|
84
|
+
}
|
|
85
|
+
${T(r,n)}${E(i,t,n)}}
|
|
86
|
+
|
|
87
|
+
// ── Usage ────────────────────────────────────────────────────────
|
|
88
|
+
const device = new ${t}();
|
|
89
|
+
|
|
90
|
+
device.on('serial:connected', () => console.log('BLE connected!'));
|
|
91
|
+
device.on('serial:disconnected', () => console.log('Disconnected.'));
|
|
92
|
+
device.on('serial:data', (data) => console.log('← ', data));
|
|
93
|
+
device.on('serial:error', (err) => console.error(err.message));
|
|
94
|
+
|
|
95
|
+
// Must be called from a user-gesture:
|
|
96
|
+
// await device.connect();
|
|
97
|
+
// await device.send('${e.prepend}COMMAND${e.append}');
|
|
98
|
+
// await device.disconnect();
|
|
99
|
+
`}function A(e,t,n,r=[],i=[]){let a=n?`ts`:`js`,o=v(e.delimiter||`\\n`),s=n?`: Promise<boolean>`:``,c=n?`: SerialPortFilter[]`:``,l=n?`
|
|
100
|
+
import type { SerialPortFilter } from 'webserial-core';`:``,u=b(e.filters);return`// device.${a} — Generated by webserial-core demo
|
|
101
|
+
import { AbstractSerialDevice, delimiter, WebUsbProvider } from 'webserial-core';${l}
|
|
102
|
+
|
|
103
|
+
// Inject the WebUSB polyfill provider.
|
|
104
|
+
AbstractSerialDevice.setProvider(
|
|
105
|
+
new WebUsbProvider({
|
|
106
|
+
usbControlInterfaceClass: ${e.usbControlInterfaceClass},
|
|
107
|
+
usbTransferInterfaceClass: ${e.usbTransferInterfaceClass},
|
|
108
|
+
})
|
|
109
|
+
);
|
|
110
|
+
${n?w(i,t):``}
|
|
111
|
+
export class ${t} extends AbstractSerialDevice${n?`<string>`:``} {
|
|
112
|
+
constructor(filters${n?`${c}`:` = []`}) {
|
|
113
|
+
super({
|
|
114
|
+
baudRate: ${e.baudRate},
|
|
115
|
+
dataBits: ${e.dataBits},
|
|
116
|
+
stopBits: ${e.stopBits},
|
|
117
|
+
parity: '${e.parity}',
|
|
118
|
+
flowControl: '${e.flowControl}',
|
|
119
|
+
bufferSize: ${e.bufferSize},
|
|
120
|
+
commandTimeout: ${e.commandTimeout},
|
|
121
|
+
parser: delimiter(${o}),
|
|
122
|
+
autoReconnect: ${e.autoReconnect},
|
|
123
|
+
autoReconnectInterval: ${e.autoReconnectInterval},
|
|
124
|
+
handshakeTimeout: ${e.handshakeTimeout},
|
|
125
|
+
filters,
|
|
126
|
+
});${i.length>0?`
|
|
127
|
+
this.startListening();`:``}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
${n?`protected `:``}async handshake()${s} {
|
|
131
|
+
${D(e.hsCmd,e.hsCmdMode,e.hsExpect,e.hsExpectMode,n)}
|
|
132
|
+
}
|
|
133
|
+
${T(r,n)}${E(i,t,n)}}
|
|
134
|
+
|
|
135
|
+
// ── Usage ────────────────────────────────────────────────────────
|
|
136
|
+
// CP2102/ESP32: { usbVendorId: 0x10c4, usbProductId: 0xea60 }
|
|
137
|
+
// CH340/Arduino: { usbVendorId: 0x1a86, usbProductId: 0x7523 }
|
|
138
|
+
const device = new ${t}(${u});
|
|
139
|
+
|
|
140
|
+
device.on('serial:connected', () => console.log('USB connected!'));
|
|
141
|
+
device.on('serial:disconnected', () => console.log('Disconnected.'));
|
|
142
|
+
device.on('serial:data', (data) => console.log('← ', data));
|
|
143
|
+
device.on('serial:error', (err) => console.error(err.message));
|
|
144
|
+
|
|
145
|
+
// await device.connect();
|
|
146
|
+
// await device.send('${e.prepend}COMMAND${e.append}');
|
|
147
|
+
// await device.disconnect();
|
|
148
|
+
`}function j(e,t,n,r=[],i=[]){let a=n?`ts`:`js`,o=v(e.delimiter||`\\n`),s=n?`: Promise<boolean>`:``;return`// device.${a} — Generated by webserial-core demo
|
|
149
|
+
import { AbstractSerialDevice, delimiter, createWebSocketProvider } from 'webserial-core';
|
|
150
|
+
|
|
151
|
+
// Inject the WebSocket bridge provider.
|
|
152
|
+
// Start the Node.js bridge first: cd demos/websocket && node server.js
|
|
153
|
+
const wsProvider = createWebSocketProvider('${e.wsUrl}');
|
|
154
|
+
AbstractSerialDevice.setProvider(wsProvider);
|
|
155
|
+
${n?w(i,t):``}
|
|
156
|
+
export class ${t} extends AbstractSerialDevice${n?`<string>`:``} {
|
|
157
|
+
constructor() {
|
|
158
|
+
super({
|
|
159
|
+
baudRate: ${e.baudRate},
|
|
160
|
+
dataBits: ${e.dataBits},
|
|
161
|
+
stopBits: ${e.stopBits},
|
|
162
|
+
parity: '${e.parity}',
|
|
163
|
+
flowControl: '${e.flowControl}',
|
|
164
|
+
bufferSize: ${e.bufferSize},
|
|
165
|
+
commandTimeout: ${e.commandTimeout},
|
|
166
|
+
parser: delimiter(${o}),
|
|
167
|
+
autoReconnect: ${e.autoReconnect},
|
|
168
|
+
autoReconnectInterval: ${e.autoReconnectInterval},
|
|
169
|
+
handshakeTimeout: ${e.handshakeTimeout},
|
|
170
|
+
});${i.length>0?`
|
|
171
|
+
this.startListening();`:``}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
${n?`protected `:``}async handshake()${s} {
|
|
175
|
+
${D(e.hsCmd,e.hsCmdMode,e.hsExpect,e.hsExpectMode,n)}
|
|
176
|
+
}
|
|
177
|
+
${T(r,n)}${E(i,t,n)}}
|
|
178
|
+
|
|
179
|
+
// ── Usage ────────────────────────────────────────────────────────
|
|
180
|
+
const device = new ${t}();
|
|
181
|
+
|
|
182
|
+
device.on('serial:connected', () => console.log('WS connected!'));
|
|
183
|
+
device.on('serial:disconnected', () => console.log('Disconnected.'));
|
|
184
|
+
device.on('serial:data', (data) => console.log('← ', data));
|
|
185
|
+
device.on('serial:error', (err) => console.error(err.message));
|
|
186
|
+
|
|
187
|
+
// await device.connect();
|
|
188
|
+
// await device.send('${e.prepend}COMMAND${e.append}');
|
|
189
|
+
// await device.disconnect();
|
|
190
|
+
`}function M(e,t,n){let r=n===`ws`?{ws:`^8.0.0`}:{},i={vite:`^8.0.0`,...t?{typescript:`~5.9.3`}:{}};return JSON.stringify({name:e.toLowerCase().replace(/\s+/g,`-`).replace(/[^a-z0-9-]/g,``),version:`1.0.0`,type:`module`,scripts:{dev:`vite`,build:`vite build`,preview:`vite preview`},dependencies:{"webserial-core":`^2.0.0`,...r},devDependencies:i},null,2)}function N(){return JSON.stringify({compilerOptions:{target:`ES2022`,useDefineForClassFields:!0,lib:[`ES2022`,`DOM`,`DOM.Iterable`],module:`ESNext`,skipLibCheck:!0,moduleResolution:`bundler`,allowImportingTsExtensions:!0,strict:!0,noEmit:!0},include:[`**/*.ts`]},null,2)}function P(e,t,n){return`<!doctype html>
|
|
191
|
+
<html lang="en">
|
|
192
|
+
<head>
|
|
193
|
+
<meta charset="UTF-8" />
|
|
194
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
195
|
+
<title>${e} — ${n}</title>
|
|
196
|
+
<style>
|
|
197
|
+
body { font-family: system-ui, sans-serif; max-width: 600px; margin: 40px auto; padding: 0 16px; }
|
|
198
|
+
pre { background: #0f0f0f; color: #4ade80; padding: 12px; border-radius: 8px; font-size: 0.82rem; min-height: 120px; overflow-y: auto; }
|
|
199
|
+
.controls { display: flex; gap: 8px; margin-bottom: 12px; }
|
|
200
|
+
button { padding: 8px 16px; border-radius: 6px; border: none; cursor: pointer; }
|
|
201
|
+
#btn-connect { background: #22c55e; color: #fff; }
|
|
202
|
+
#btn-disconnect { background: #ef4444; color: #fff; }
|
|
203
|
+
#btn-send { background: #6366f1; color: #fff; }
|
|
204
|
+
input { flex: 1; padding: 8px 12px; border: 1px solid #ddd; border-radius: 6px; }
|
|
205
|
+
</style>
|
|
206
|
+
</head>
|
|
207
|
+
<body>
|
|
208
|
+
<h2>${e}</h2>
|
|
209
|
+
<div class="controls">
|
|
210
|
+
<button id="btn-connect">Connect</button>
|
|
211
|
+
<button id="btn-disconnect" disabled>Disconnect</button>
|
|
212
|
+
</div>
|
|
213
|
+
<div class="controls">
|
|
214
|
+
<input id="input-send" type="text" placeholder="Command, e.g. LED_ON" disabled />
|
|
215
|
+
<button id="btn-send" disabled>Send</button>
|
|
216
|
+
</div>
|
|
217
|
+
<pre id="log">Waiting for connection...</pre>
|
|
218
|
+
|
|
219
|
+
<script type="module">
|
|
220
|
+
import { ${e} } from './device.${t}';
|
|
221
|
+
const device = new ${e}();
|
|
222
|
+
const log = (msg) => {
|
|
223
|
+
document.getElementById('log').textContent += msg + '\\n';
|
|
224
|
+
};
|
|
225
|
+
device.on('serial:connected', () => { log('✓ Connected'); document.getElementById('btn-disconnect').disabled = false; document.getElementById('btn-send').disabled = false; document.getElementById('input-send').disabled = false; });
|
|
226
|
+
device.on('serial:disconnected', () => { log('✗ Disconnected'); document.getElementById('btn-disconnect').disabled = true; document.getElementById('btn-send').disabled = true; document.getElementById('input-send').disabled = true; });
|
|
227
|
+
device.on('serial:data', (data) => log('← ' + data));
|
|
228
|
+
device.on('serial:error', (err) => log('⚠ ' + err.message));
|
|
229
|
+
document.getElementById('btn-connect').onclick = () => device.connect();
|
|
230
|
+
document.getElementById('btn-disconnect').onclick = () => device.disconnect();
|
|
231
|
+
document.getElementById('btn-send').onclick = () => {
|
|
232
|
+
const v = document.getElementById('input-send').value.trim();
|
|
233
|
+
if (v) { device.send(v + '\\n'); document.getElementById('input-send').value = ''; }
|
|
234
|
+
};
|
|
235
|
+
document.getElementById('input-send').onkeydown = (e) => { if (e.key === 'Enter') document.getElementById('btn-send').click(); };
|
|
236
|
+
<\/script>
|
|
237
|
+
</body>
|
|
238
|
+
</html>
|
|
239
|
+
`}function F(e,t,n,r){return`# ${e}
|
|
240
|
+
|
|
241
|
+
A ${n} device using [webserial-core](https://github.com/danidoble/webserial-core).
|
|
242
|
+
|
|
243
|
+
${r}
|
|
244
|
+
|
|
245
|
+
## Setup
|
|
246
|
+
|
|
247
|
+
\`\`\`bash
|
|
248
|
+
npm install
|
|
249
|
+
npm run dev
|
|
250
|
+
\`\`\`
|
|
251
|
+
|
|
252
|
+
## Usage
|
|
253
|
+
|
|
254
|
+
\`\`\`typescript
|
|
255
|
+
import { ${e} } from './device.${t}';
|
|
256
|
+
|
|
257
|
+
const device = new ${e}();
|
|
258
|
+
|
|
259
|
+
device.on('serial:data', (data) => {
|
|
260
|
+
console.log('Received:', data);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// Must be called from a user-gesture (button click):
|
|
264
|
+
await device.connect();
|
|
265
|
+
|
|
266
|
+
// Send data:
|
|
267
|
+
await device.send('LED_ON\\n');
|
|
268
|
+
|
|
269
|
+
// Disconnect:
|
|
270
|
+
await device.disconnect();
|
|
271
|
+
\`\`\`
|
|
272
|
+
`}function I(e,t){let n=new Blob([t],{type:`text/plain;charset=utf-8`}),r=URL.createObjectURL(n),i=document.createElement(`a`);i.href=r,i.download=e,document.body.appendChild(i),i.click(),document.body.removeChild(i),URL.revokeObjectURL(r)}function L(e,t){let n=JSON.stringify(e,null,2),r=new Blob([n],{type:`application/json;charset=utf-8`}),i=URL.createObjectURL(r),a=document.createElement(`a`);a.href=i,a.download=t.endsWith(`.json`)?t:t+`.json`,document.body.appendChild(a),a.click(),document.body.removeChild(a),URL.revokeObjectURL(i)}function R(e,t){let n=new TextEncoder,r=W(e.map(e=>({name:e.name,data:n.encode(e.content)}))),i=new Blob([r.buffer],{type:`application/zip`}),a=URL.createObjectURL(i),o=document.createElement(`a`);o.href=a,o.download=t.endsWith(`.zip`)?t:t+`.zip`,document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(a)}var z=null;function B(){if(z)return z;z=new Uint32Array(256);for(let e=0;e<256;e++){let t=e;for(let e=0;e<8;e++)t=t&1?3988292384^t>>>1:t>>>1;z[e]=t>>>0}return z}function V(e){let t=B(),n=4294967295;for(let r=0;r<e.length;r++)n=n>>>8^t[(n^e[r])&255];return(n^4294967295)>>>0}function H(e,t,n){new DataView(e.buffer,e.byteOffset+t).setUint16(0,n,!0)}function U(e,t,n){new DataView(e.buffer,e.byteOffset+t).setUint32(0,n,!0)}function W(e){let t=new TextEncoder,n=[],r=0;for(let i of e){let e=t.encode(i.name),a=V(i.data),o=new Uint8Array(30+e.length);U(o,0,67324752),H(o,4,20),H(o,6,0),H(o,8,0),H(o,10,0),H(o,12,0),U(o,14,a),U(o,18,i.data.length),U(o,22,i.data.length),H(o,26,e.length),H(o,28,0),o.set(e,30);let s=new Uint8Array(46+e.length);U(s,0,33639248),H(s,4,20),H(s,6,20),H(s,8,0),H(s,10,0),H(s,12,0),H(s,14,0),U(s,16,a),U(s,20,i.data.length),U(s,24,i.data.length),H(s,28,e.length),H(s,30,0),H(s,32,0),H(s,34,0),H(s,36,0),U(s,38,0),U(s,42,r),s.set(e,46),n.push({localHdr:o,data:i.data,cdHdr:s,offset:r}),r+=o.length+i.data.length}let i=n.reduce((e,t)=>e+t.cdHdr.length,0),a=new Uint8Array(22);U(a,0,101010256),H(a,4,0),H(a,6,0),H(a,8,n.length),H(a,10,n.length),U(a,12,i),U(a,16,r),H(a,20,0);let o=new Uint8Array(r+i+a.length),s=0;for(let e of n)o.set(e.localHdr,s),s+=e.localHdr.length,o.set(e.data,s),s+=e.data.length;for(let e of n)o.set(e.cdHdr,s),s+=e.cdHdr.length;return o.set(a,s),o}function G(e){let t=document.createElement(`canvas`);t.style.cssText=`position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:0;opacity:0.55;`,e.insertBefore(t,e.firstChild);let n=t.getContext(`2d`),r=window.devicePixelRatio||1,i=[],a=[],o=[],s=0,c=0;function l(){let l=e.getBoundingClientRect();s=Math.max(l.width,1),c=Math.max(l.height,1),t.width=Math.round(s*r),t.height=Math.round(c*r),t.style.width=`${s}px`,t.style.height=`${c}px`,n.setTransform(r,0,0,r,0,0);let u=Math.ceil(s/52),d=Math.ceil(c/52);i=[];let f=new Map,p=(e,t)=>{f.set(`${e},${t}`,(f.get(`${e},${t}`)??0)+1)};for(let e=0;e<=d;e++)for(let t=0;t<=u;t++){let n=t*52,r=e*52;t<u&&Math.random()>.22&&(i.push({x1:n,y1:r,x2:n+52,y2:r}),p(n,r),p(n+52,r)),e<d&&Math.random()>.22&&(i.push({x1:n,y1:r,x2:n,y2:r+52}),p(n,r),p(n,r+52))}a=Array.from(f.keys()).map(e=>{let[t,n]=e.split(`,`).map(Number);return[t,n]});let m=Math.max(10,Math.min(35,Math.floor(s*c/11e3)));o=Array.from({length:m},()=>({seg:Math.floor(Math.random()*Math.max(1,i.length)),t:Math.random(),speed:.004+Math.random()*.009,sz:1.5+Math.random()*2}))}function u(){let e=getComputedStyle(document.documentElement).getPropertyValue(`--accent`).trim();if(e.startsWith(`#`)){let t=e.length===4?e.slice(1).split(``).map(e=>e+e).join(``):e.slice(1);return[0,2,4].map(e=>parseInt(t.slice(e,e+2),16)).join(`,`)}return`34,197,94`}let d=0;function f(){n.clearRect(0,0,s,c);let e=u();n.strokeStyle=`rgba(${e},0.07)`,n.lineWidth=.8;for(let e of i)n.beginPath(),n.moveTo(e.x1,e.y1),n.lineTo(e.x2,e.y2),n.stroke();for(let[t,r]of a)n.beginPath(),n.arc(t,r,3.8,0,Math.PI*2),n.fillStyle=`rgba(${e},0.05)`,n.fill(),n.strokeStyle=`rgba(${e},0.2)`,n.lineWidth=.7,n.stroke(),n.beginPath(),n.arc(t,r,1.4,0,Math.PI*2),n.fillStyle=`rgba(${e},0.25)`,n.fill();for(let t of o){let r=i[t.seg];if(!r)continue;let a=r.x1+(r.x2-r.x1)*t.t,o=r.y1+(r.y2-r.y1)*t.t,s=Math.max(0,t.t-.24),c=r.x1+(r.x2-r.x1)*s,l=r.y1+(r.y2-r.y1)*s,u=n.createLinearGradient(c,l,a,o);u.addColorStop(0,`rgba(${e},0)`),u.addColorStop(1,`rgba(${e},0.42)`),n.strokeStyle=u,n.lineWidth=2,n.beginPath(),n.moveTo(c,l),n.lineTo(a,o),n.stroke();let d=n.createRadialGradient(a,o,0,a,o,t.sz*7);d.addColorStop(0,`rgba(${e},0.5)`),d.addColorStop(.4,`rgba(${e},0.15)`),d.addColorStop(1,`rgba(${e},0)`),n.fillStyle=d,n.beginPath(),n.arc(a,o,t.sz*7,0,Math.PI*2),n.fill(),n.fillStyle=`rgba(${e},1)`,n.beginPath(),n.arc(a,o,t.sz,0,Math.PI*2),n.fill(),n.fillStyle=`rgba(255,255,255,0.75)`,n.beginPath(),n.arc(a,o,t.sz*.4,0,Math.PI*2),n.fill(),t.t+=t.speed,t.t>=1&&(t.t=0,t.seg=Math.floor(Math.random()*i.length))}d=requestAnimationFrame(f)}l(),f(),new ResizeObserver(()=>{cancelAnimationFrame(d),l(),f()}).observe(e)}function K(){let e=document.querySelector(`.topbar`);if(!e)return;let t=document.createElement(`button`);t.className=`icon-btn mob-toggle`,t.title=`Navigation menu`,t.innerHTML=`<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2.2" fill="none" stroke-linecap="round"><circle cx="12" cy="5" r="1.3"/><circle cx="12" cy="12" r="1.3"/><circle cx="12" cy="19" r="1.3"/></svg>`,e.appendChild(t);let n=document.createElement(`div`);n.className=`mob-nav-drawer`;let r=document.createElement(`div`);r.className=`mob-nav-inner`;let i=e.querySelector(`.topbar-nav`);if(i){let e=document.createElement(`div`);e.className=`mob-nav-links`,i.querySelectorAll(`.nav-item`).forEach(t=>{e.appendChild(t.cloneNode(!0))}),r.appendChild(e)}let a=document.createElement(`div`);a.className=`mob-nav-sep`,r.appendChild(a);let o=document.createElement(`div`);o.className=`mob-nav-actions`;let s=document.getElementById(`btn-connect`),c=document.getElementById(`btn-disconnect`),l=(e,t,r)=>{let i=document.createElement(`button`);return i.className=`btn ${t}`,i.textContent=r,e&&(i.disabled=e.disabled,new MutationObserver(()=>{i.disabled=e.disabled}).observe(e,{attributes:!0,attributeFilter:[`disabled`]}),i.addEventListener(`click`,()=>{e.click(),n.classList.remove(`open`)})),i};o.appendChild(l(s,`btn-connect`,`Connect`)),o.appendChild(l(c,`btn-disconnect`,`Disconnect`)),r.appendChild(o),n.appendChild(r),document.body.appendChild(n),t.addEventListener(`click`,e=>{e.stopPropagation(),n.classList.toggle(`open`)}),document.addEventListener(`click`,()=>n.classList.remove(`open`)),n.addEventListener(`click`,e=>e.stopPropagation())}export{N as _,I as a,s as b,O as c,G as d,K as f,F as g,M as h,L as i,A as l,P as m,p as n,R as o,u as p,y as r,k as s,f as t,j as u,_ as v,o as x,d as y};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{_ as e,a as t,b as n,d as r,f as i,g as a,h as o,i as s,m as ee,n as c,o as te,p as l,r as u,s as ne,t as d,v as re,x as f,y as ie}from"./demo-shared-DnvFynUr.js";var p=`6e400001-b5a3-f393-e0a9-e50e24dcca9e`,ae=`6e400003-b5a3-f393-e0a9-e50e24dcca9e`,oe=`6e400002-b5a3-f393-e0a9-e50e24dcca9e`,m=20,se=10;function ce(e){let t=null,n=null,r=null;return{get readable(){return t},get writable(){return n},getInfo(){return{}},async open(){if(!e.gatt)throw Error(`GATT not available on this Bluetooth device.`);r=await e.gatt.connect();let i=await r.getPrimaryService(p),a=await i.getCharacteristic(ae),o=await i.getCharacteristic(oe);await a.startNotifications(),t=new ReadableStream({start(e){a.addEventListener(`characteristicvaluechanged`,t=>{let n=t.target.value.buffer;e.enqueue(new Uint8Array(n))})}}),n=new WritableStream({async write(e){for(let t=0;t<e.length;t+=m){let n=e.slice(t,t+m);await o.writeValueWithoutResponse(n),t+m<e.length&&await new Promise(e=>setTimeout(e,se))}}})},async close(){r?.connected&&r.disconnect(),t=null,n=null}}}function le(){return{async requestPort(){if(!navigator.bluetooth)throw Error(`Web Bluetooth API is not supported in this browser. Use Chrome on Android, macOS, or ChromeOS.`);return ce(await navigator.bluetooth.requestDevice({filters:[{services:[p]}]}))},async getPorts(){return[]}}}f.setProvider(le());var h=[],g=[],ue=class extends f{_hsCmd;_hsCmdMode;_hsExpect;_hsExpectMode;constructor(e,t,n,r,i){super(e),this._hsCmd=t,this._hsCmdMode=n,this._hsExpect=r,this._hsExpectMode=i}async handshake(){if(!this._hsCmd||(this._hsCmdMode===`hex`?await this.send(I(this._hsCmd)):await this.send(u(this._hsCmd)),!this._hsExpect))return!0;let e=this._hsExpect.trim();return new Promise(t=>{let n=r=>{if(this.off(`serial:data`,n),this._hsExpectMode===`hex`){let e=new TextEncoder().encode(String(r)),n=I(this._hsExpect);t(e.length===n.length&&e.every((e,t)=>e===n[t]))}else t(String(r).trim()===e)};this.on(`serial:data`,n)})}},_=e=>document.getElementById(e),v=_(`messages`),y=_(`btn-connect`),b=_(`btn-disconnect`),x=_(`btn-send`),S=_(`input-send`),C=_(`mode-toggle`),de=_(`status-dot`),w=_(`status-text`),fe=_(`console-dot`),T=_(`console-text`),E=_(`sidebar`),D=_(`code-panel`),O=_(`code-view`),k=_(`code-tab`),pe=_(`menu-btn`),me=_(`code-toggle-btn`),A=_(`theme-btn`),he=_(`clear-btn`),j=_(`copy-btn`),ge=_(`dl-btn`),_e=_(`cfg-export-btn`),M=_(`cfg-import-input`);function N(){let e=e=>(_(e)?.value??``).trim(),t=(t,n)=>parseInt(e(t))||n,n=e=>_(e)?.value??``;return{bufferSize:t(`cfg-bufsize`,255),commandTimeout:t(`cfg-timeout`,3e3),handshakeTimeout:t(`cfg-handshake`,2e3),delimiter:e(`cfg-delim`)||`\\n`,prepend:e(`cfg-prepend`),append:e(`cfg-append`),hsCmd:e(`cfg-hs-cmd`),hsCmdMode:n(`cfg-hs-cmd-mode`)||`text`,hsExpect:e(`cfg-hs-expect`),hsExpectMode:n(`cfg-hs-expect-mode`)||`text`}}function ve(e){let t=(e,t)=>{let n=document.getElementById(e);n&&(n instanceof HTMLInputElement&&n.type===`checkbox`?n.checked=!!t:(n instanceof HTMLInputElement||n instanceof HTMLSelectElement)&&(n.value=String(t??``)))};t(`cfg-bufsize`,e.bufferSize??255),t(`cfg-timeout`,e.commandTimeout??3e3),t(`cfg-handshake`,e.handshakeTimeout??2e3),t(`cfg-delim`,e.delimiter??`\\n`),t(`cfg-prepend`,e.prepend??``),t(`cfg-append`,e.append??``),t(`cfg-hs-cmd`,e.hsCmd??``),t(`cfg-hs-cmd-mode`,e.hsCmdMode??`text`),t(`cfg-hs-expect`,e.hsExpect??``),t(`cfg-hs-expect-mode`,e.hsExpectMode??`text`)}function P(e){let t=e.replace(/[^a-zA-Z0-9_$]/g,``).replace(/^[^a-zA-Z_$]/,`C`);return t.charAt(0).toUpperCase()+t.slice(1)||`MyDevice`}function F(e){return Array.from(e).map(e=>e.toString(16).toUpperCase().padStart(2,`0`)).join(` `)}function I(e){let t=e.replace(/\s+/g,``);if(t.length%2!=0)throw Error(`Odd number of hex characters.`);let n=new Uint8Array(t.length/2);for(let e=0;e<t.length;e+=2)n[e/2]=parseInt(t.substring(e,e+2),16);return n}function L(e,t){[de,fe].forEach(t=>{t&&(t.className=`status-dot`,e!==`disconnected`&&t.classList.add(e))}),w&&(w.textContent=t),T&&(T.textContent=t)}var R=null;function z(){R&&clearTimeout(R),R=setTimeout(()=>{let e=N(),t=P((_(`dl-name`)?.value??`MyBleDevice`).trim()),n=(document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`)===`ts`,r=n?`ts`:`js`;re(O,ne(e,t,n,h,g)),k&&(k.textContent=`${t.substring(0,10).toLowerCase()}.${r}`)},180)}var ye=l();A&&(A.textContent=ye===`dark`?`☀️`:`🌙`),pe?.addEventListener(`click`,()=>E.classList.toggle(`collapsed`)),me?.addEventListener(`click`,()=>D.classList.toggle(`collapsed`));var B=_(`resize-handle`);if(B){let e=0,t=0,n=n=>{let r=Math.max(180,Math.min(700,t+(e-n.clientX)));document.documentElement.style.setProperty(`--code-w`,`${r}px`)},r=()=>{B.classList.remove(`dragging`),document.removeEventListener(`pointermove`,n)};B.addEventListener(`pointerdown`,i=>{e=i.clientX,t=D.getBoundingClientRect().width,B.classList.add(`dragging`),document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r,{once:!0}),i.preventDefault()})}var V=_(`sidebar-resize-handle`);if(V){let e=0,t=0,n=n=>{let r=Math.max(200,Math.min(600,t+(n.clientX-e)));document.documentElement.style.setProperty(`--sidebar-w`,`${r}px`)},r=()=>{V.classList.remove(`dragging`),document.removeEventListener(`pointermove`,n)};V.addEventListener(`pointerdown`,i=>{e=i.clientX,t=E.getBoundingClientRect().width,V.classList.add(`dragging`),document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r,{once:!0}),i.preventDefault()})}window.innerWidth<=960&&D.classList.add(`collapsed`),window.innerWidth<=640&&E.classList.add(`collapsed`),A?.addEventListener(`click`,()=>{let e=ie();A&&(A.textContent=e===`dark`?`☀️`:`🌙`)}),he?.addEventListener(`click`,()=>c(v)),j?.addEventListener(`click`,async()=>{let e=O?.textContent??``;try{await navigator.clipboard.writeText(e),j&&(j.textContent=`Copied!`,j.classList.add(`copied`),setTimeout(()=>{j.textContent=`Copy`,j.classList.remove(`copied`)},1500))}catch{}}),ge?.addEventListener(`click`,()=>{let n=N(),r=(_(`dl-name`)?.value??`my-ble-device`).trim(),i=P(r),s=(document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`)===`ts`,c=document.querySelector(`input[name='dl-type']:checked`)?.value??`file`,l=s?`ts`:`js`,u=ne(n,i,s,h,g);c===`project`?te([{name:`device.${l}`,content:u},{name:`package.json`,content:o(r,s,`ble`)},{name:`index.html`,content:ee(i,l,`Web Bluetooth`)},{name:`README.md`,content:a(i,l,`Web Bluetooth`,`Requires a Chromium browser. The device must expose a Nordic UART Service (NUS) via BLE GATT.`)},...s?[{name:`tsconfig.json`,content:e()}]:[]],`${r}-project`):t(`${r}.${l}`,u)}),_e?.addEventListener(`click`,()=>{let e=(_(`dl-name`)?.value??`my-ble-device`).trim(),t=P(e);s({$version:1,provider:`ble`,className:e,language:document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`,dlType:document.querySelector(`input[name='dl-type']:checked`)?.value??`file`,cfg:N(),commands:h,listeners:g},t+`-config`)}),M?.addEventListener(`change`,()=>{let e=M.files?.[0];if(!e)return;let t=new FileReader;t.onload=e=>{try{let t=JSON.parse(e.target?.result);if(t.$version!==1||t.provider!==`ble`)return;let n=_(`dl-name`);n&&(n.value=t.className),document.querySelectorAll(`input[name='dl-lang']`).forEach(e=>{e.checked=e.value===t.language}),document.querySelectorAll(`input[name='dl-type']`).forEach(e=>{e.checked=e.value===t.dlType}),ve(t.cfg),h=t.commands.map(e=>({...e,id:crypto.randomUUID()})),g=t.listeners.map(e=>({...e,id:crypto.randomUUID()})),Q(),$(),z()}catch{}M.value=``},t.readAsText(e)}),[`cfg-bufsize`,`cfg-timeout`,`cfg-handshake`,`cfg-delim`,`cfg-prepend`,`cfg-append`,`cfg-hs-cmd`,`cfg-hs-cmd-mode`,`cfg-hs-expect`,`cfg-hs-expect-mode`,`dl-name`].forEach(e=>{let t=document.getElementById(e);t?.addEventListener(`change`,z),t?.addEventListener(`input`,z)}),document.querySelectorAll(`input[name='dl-lang']`).forEach(e=>e.addEventListener(`change`,z)),z();var H=`text`;C?.addEventListener(`click`,()=>{H=H===`text`?`hex`:`text`,C.textContent=H===`text`?`TXT`:`HEX`,S.placeholder=H===`text`?`Type a command, e.g. LED_ON`:`Hex bytes, e.g. FF 01 A3`});var U=null;function W(e){x.disabled=!e,S.disabled=!e,b.disabled=!e,y.disabled=e}y?.addEventListener(`click`,async()=>{if(U){try{await U.disconnect()}catch{}U=null}let e=N(),t=u(e.delimiter);U=new ue({baudRate:9600,bufferSize:e.bufferSize,commandTimeout:e.commandTimeout,parser:n(t),autoReconnect:!1,handshakeTimeout:e.handshakeTimeout},e.hsCmd,e.hsCmdMode,e.hsExpect,e.hsExpectMode),U.on(`serial:connecting`,()=>{L(`connecting`,`Connecting…`),y.disabled=!0,d(v,`Initiating Web Bluetooth connection…`,{kind:`system`})}),U.on(`serial:connected`,()=>{L(`connected`,`Connected`),W(!0),d(v,`Connected via Web Bluetooth!`,{kind:`system`})}),U.on(`serial:disconnected`,()=>{L(`disconnected`,`Disconnected`),W(!1),d(v,`Disconnected.`,{kind:`system`}),U=null}),U.on(`serial:data`,e=>{d(v,String(e),{kind:`received`,label:`Device`})}),U.on(`serial:error`,e=>{L(`error`,`Error`),d(v,`Error: ${e.message}`,{kind:`error`}),y.disabled=!1}),U.on(`serial:need-permission`,()=>{L(`error`,`Permission denied`),d(v,`Permission denied — select a valid BLE device and allow access.`,{kind:`error`}),y.disabled=!1}),U.on(`serial:timeout`,e=>{d(v,`Timeout: ${F(e)}`,{kind:`error`})});try{await U.connect()}catch{}}),b?.addEventListener(`click`,async()=>{await U?.disconnect()});async function G(){let e=S.value.trim();if(!e||!U)return;let t=N(),n=t.append?u(t.append):u(t.delimiter);try{if(H===`hex`){let t=I(e);d(v,`HEX: ${F(t)}`,{kind:`sent`,label:`You`}),await U.send(t)}else{let r=t.prepend+e+n;d(v,e,{kind:`sent`,label:`You`}),await U.send(r)}S.value=``,S.focus()}catch(e){d(v,`Send error: ${e instanceof Error?e.message:String(e)}`,{kind:`error`})}}x?.addEventListener(`click`,G),S?.addEventListener(`keydown`,e=>{e.key===`Enter`&&G()});var K=_(`cmd-name`),q=_(`cmd-value`),be=_(`cmd-mode`),xe=_(`cmd-add`),J=_(`cmd-list`),Y=_(`lst-name`),X=_(`lst-pattern`),Se=_(`lst-match`),Ce=_(`lst-add`),Z=_(`lst-list`);function Q(){if(J){J.innerHTML=``;for(let e of h){let t=document.createElement(`div`);t.className=`chip`;let n=document.createElement(`span`);n.className=`chip-badge`,n.textContent=e.mode.toUpperCase();let r=document.createElement(`span`);r.className=`chip-name`,r.textContent=e.name;let i=document.createElement(`span`);i.className=`chip-val`,i.textContent=e.value;let a=document.createElement(`button`);a.className=`chip-send`,a.title=`Send now`,a.textContent=`▶`,a.addEventListener(`click`,()=>{if(U)if(e.mode===`hex`){let t=I(e.value);U.send(t).catch(()=>{}),d(v,`HEX: ${F(t)}`,{kind:`sent`,label:`You`})}else{let t=N(),n=t.append?u(t.append):u(t.delimiter);U.send(t.prepend+e.value+n).catch(()=>{}),d(v,e.name,{kind:`sent`,label:`You`})}});let o=document.createElement(`button`);o.className=`chip-del`,o.title=`Remove`,o.textContent=`×`,o.addEventListener(`click`,()=>{h=h.filter(t=>t.id!==e.id),Q(),z()}),t.append(n,r,i,a,o),J.appendChild(t)}}}function $(){if(Z){Z.innerHTML=``;for(let e of g){let t=document.createElement(`div`);t.className=`chip`;let n=document.createElement(`span`);n.className=`chip-badge`,n.textContent=e.match;let r=document.createElement(`span`);r.className=`chip-name`,r.textContent=e.name;let i=document.createElement(`span`);i.className=`chip-val`,i.textContent=e.pattern;let a=document.createElement(`button`);a.className=`chip-del`,a.title=`Remove`,a.textContent=`×`,a.addEventListener(`click`,()=>{g=g.filter(t=>t.id!==e.id),$(),z()}),t.append(n,r,i,a),Z.appendChild(t)}}}xe?.addEventListener(`click`,()=>{let e=K?.value.trim(),t=q?.value.trim();if(!e||!t)return;let n=be?.value??`text`;h.push({id:crypto.randomUUID(),name:e,value:t,mode:n}),K&&(K.value=``),q&&(q.value=``),Q(),z()}),Ce?.addEventListener(`click`,()=>{let e=Y?.value.trim(),t=X?.value.trim();if(!e||!t)return;let n=Se?.value??`exact`;g.push({id:crypto.randomUUID(),name:e,pattern:t,match:n}),Y&&(Y.value=``),X&&(X.value=``),$(),z()}),navigator.bluetooth?d(v,`Web Bluetooth demo ready — configure settings and click Connect.`,{kind:`system`}):(d(v,`Web Bluetooth is NOT supported in this browser or OS.`,{kind:`error`}),y.disabled=!0),r(v),i();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{_ as e,a as t,b as n,c as r,d as i,f as a,g as o,h as ee,i as s,m as te,n as c,o as ne,p as l,r as u,t as d,v as f,x as re,y as ie}from"./demo-shared-DnvFynUr.js";var p=[],m=[],h=class extends re{_hsCmd;_hsCmdMode;_hsExpect;_hsExpectMode;constructor(e,t,n,r,i){super(e),this._hsCmd=t,this._hsCmdMode=n,this._hsExpect=r,this._hsExpectMode=i}async handshake(){if(!this._hsCmd||(this._hsCmdMode===`hex`?await this.send(F(this._hsCmd)):await this.send(u(this._hsCmd)),!this._hsExpect))return!0;let e=this._hsExpect.trim();return new Promise(t=>{let n=r=>{if(this.off(`serial:data`,n),this._hsExpectMode===`hex`){let e=new TextEncoder().encode(String(r)),n=F(this._hsExpect);t(e.length===n.length&&e.every((e,t)=>e===n[t]))}else t(String(r).trim()===e)};this.on(`serial:data`,n)})}},g=e=>document.getElementById(e),_=g(`messages`),v=g(`btn-connect`),y=g(`btn-disconnect`),b=g(`btn-send`),x=g(`input-send`),S=g(`mode-toggle`),ae=g(`status-dot`),C=g(`status-text`),oe=g(`console-dot`),w=g(`console-text`),T=g(`sidebar`),E=g(`code-panel`),D=g(`code-view`),O=g(`code-tab`),se=g(`menu-btn`),ce=g(`code-toggle-btn`),k=g(`theme-btn`),le=g(`clear-btn`),A=g(`copy-btn`),ue=g(`dl-btn`),de=g(`cfg-export-btn`),j=g(`cfg-import-input`);function M(){let e=e=>(g(e)?.value??``).trim(),t=(t,n)=>parseInt(e(t))||n,n=e=>g(e)?.value??``,r=e=>g(e)?.checked??!1,i=e(`cfg-vendor`),a=e(`cfg-product`),o=[];if(i||a){let e={};i&&(e.usbVendorId=parseInt(i,16)),a&&(e.usbProductId=parseInt(a,16)),o.push(e)}return{baudRate:t(`cfg-baud`,9600),dataBits:t(`cfg-databits`,8),stopBits:t(`cfg-stopbits`,1),parity:n(`cfg-parity`)||`none`,flowControl:n(`cfg-flow`)||`none`,bufferSize:t(`cfg-bufsize`,255),commandTimeout:t(`cfg-timeout`,3e3),autoReconnect:r(`cfg-autoreconnect`),autoReconnectInterval:t(`cfg-reconnect-ms`,1500),handshakeTimeout:t(`cfg-handshake`,2e3),delimiter:e(`cfg-delim`)||`\\n`,prepend:e(`cfg-prepend`),append:e(`cfg-append`),hsCmd:e(`cfg-hs-cmd`),hsCmdMode:n(`cfg-hs-cmd-mode`)||`text`,hsExpect:e(`cfg-hs-expect`),hsExpectMode:n(`cfg-hs-expect-mode`)||`text`,filters:o}}function fe(e){let t=(e,t)=>{let n=document.getElementById(e);n&&(n instanceof HTMLInputElement&&n.type===`checkbox`?n.checked=!!t:(n instanceof HTMLInputElement||n instanceof HTMLSelectElement)&&(n.value=String(t??``)))};t(`cfg-baud`,e.baudRate??9600),t(`cfg-databits`,e.dataBits??8),t(`cfg-stopbits`,e.stopBits??1),t(`cfg-parity`,e.parity??`none`),t(`cfg-flow`,e.flowControl??`none`),t(`cfg-bufsize`,e.bufferSize??255),t(`cfg-timeout`,e.commandTimeout??3e3),t(`cfg-handshake`,e.handshakeTimeout??2e3),t(`cfg-reconnect-ms`,e.autoReconnectInterval??1500),t(`cfg-autoreconnect`,e.autoReconnect??!1),t(`cfg-vendor`,e.filters?.[0]?.usbVendorId==null?``:e.filters[0].usbVendorId.toString(16)),t(`cfg-product`,e.filters?.[0]?.usbProductId==null?``:e.filters[0].usbProductId.toString(16)),t(`cfg-delim`,e.delimiter??`\\n`),t(`cfg-prepend`,e.prepend??``),t(`cfg-append`,e.append??``),t(`cfg-hs-cmd`,e.hsCmd??``),t(`cfg-hs-cmd-mode`,e.hsCmdMode??`text`),t(`cfg-hs-expect`,e.hsExpect??``),t(`cfg-hs-expect-mode`,e.hsExpectMode??`text`)}function N(e){let t=e.replace(/[^a-zA-Z0-9_$]/g,``).replace(/^[^a-zA-Z_$]/,`C`);return t.charAt(0).toUpperCase()+t.slice(1)||`MyDevice`}function P(e){return Array.from(e).map(e=>e.toString(16).toUpperCase().padStart(2,`0`)).join(` `)}function F(e){let t=e.replace(/\s+/g,``);if(t.length%2!=0)throw Error(`Odd number of hex characters.`);let n=new Uint8Array(t.length/2);for(let e=0;e<t.length;e+=2)n[e/2]=parseInt(t.substring(e,e+2),16);return n}function I(e,t){[ae,oe].forEach(t=>{t&&(t.className=`status-dot`,e!==`disconnected`&&t.classList.add(e))}),C&&(C.textContent=t),w&&(w.textContent=t)}var L=null;function R(){L&&clearTimeout(L),L=setTimeout(()=>{let e=M(),t=N((g(`dl-name`)?.value??`MySerialDevice`).trim()),n=(document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`)===`ts`,i=n?`ts`:`js`;f(D,r(e,t,n,p,m)),O&&(O.textContent=`${t.substring(0,10).toLowerCase()}.${i}`)},180)}var z=l();k&&(k.textContent=z===`dark`?`☀️`:`🌙`),se?.addEventListener(`click`,()=>T.classList.toggle(`collapsed`)),ce?.addEventListener(`click`,()=>E.classList.toggle(`collapsed`));var B=g(`resize-handle`);if(B){let e=0,t=0,n=n=>{let r=Math.max(180,Math.min(700,t+(e-n.clientX)));document.documentElement.style.setProperty(`--code-w`,`${r}px`)},r=()=>{B.classList.remove(`dragging`),document.removeEventListener(`pointermove`,n)};B.addEventListener(`pointerdown`,i=>{e=i.clientX,t=E.getBoundingClientRect().width,B.classList.add(`dragging`),document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r,{once:!0}),i.preventDefault()})}var V=g(`sidebar-resize-handle`);if(V){let e=0,t=0,n=n=>{let r=Math.max(200,Math.min(600,t+(n.clientX-e)));document.documentElement.style.setProperty(`--sidebar-w`,`${r}px`)},r=()=>{V.classList.remove(`dragging`),document.removeEventListener(`pointermove`,n)};V.addEventListener(`pointerdown`,i=>{e=i.clientX,t=T.getBoundingClientRect().width,V.classList.add(`dragging`),document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r,{once:!0}),i.preventDefault()})}window.innerWidth<=960&&E.classList.add(`collapsed`),window.innerWidth<=640&&T.classList.add(`collapsed`),k?.addEventListener(`click`,()=>{let e=ie();k&&(k.textContent=e===`dark`?`☀️`:`🌙`)}),le?.addEventListener(`click`,()=>c(_)),A?.addEventListener(`click`,async()=>{let e=D?.textContent??``;try{await navigator.clipboard.writeText(e),A&&(A.textContent=`Copied!`,A.classList.add(`copied`),setTimeout(()=>{A.textContent=`Copy`,A.classList.remove(`copied`)},1500))}catch{}}),ue?.addEventListener(`click`,()=>{let n=M(),i=(g(`dl-name`)?.value??`my-device`).trim(),a=N(i),s=(document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`)===`ts`,c=document.querySelector(`input[name='dl-type']:checked`)?.value??`file`,l=s?`ts`:`js`,u=r(n,a,s,p,m);c===`project`?ne([{name:`device.${l}`,content:u},{name:`package.json`,content:ee(i,s,`serial`)},{name:`index.html`,content:te(a,l,`Web Serial`)},{name:`README.md`,content:o(a,l,`Web Serial`,`Requires a Chromium browser with Web Serial API support.`)},...s?[{name:`tsconfig.json`,content:e()}]:[]],`${i}-project`):t(`${i}.${l}`,u)}),de?.addEventListener(`click`,()=>{let e=(g(`dl-name`)?.value??`my-device`).trim(),t=N(e);s({$version:1,provider:`serial`,className:e,language:document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`,dlType:document.querySelector(`input[name='dl-type']:checked`)?.value??`file`,cfg:M(),commands:p,listeners:m},t+`-config`)}),j?.addEventListener(`change`,()=>{let e=j.files?.[0];if(!e)return;let t=new FileReader;t.onload=e=>{try{let t=JSON.parse(e.target?.result);if(t.$version!==1||t.provider!==`serial`)return;let n=g(`dl-name`);n&&(n.value=t.className),document.querySelectorAll(`input[name='dl-lang']`).forEach(e=>{e.checked=e.value===t.language}),document.querySelectorAll(`input[name='dl-type']`).forEach(e=>{e.checked=e.value===t.dlType}),fe(t.cfg),p=t.commands.map(e=>({...e,id:crypto.randomUUID()})),m=t.listeners.map(e=>({...e,id:crypto.randomUUID()})),Q(),$(),R()}catch{}j.value=``},t.readAsText(e)}),[`cfg-baud`,`cfg-databits`,`cfg-stopbits`,`cfg-parity`,`cfg-flow`,`cfg-bufsize`,`cfg-timeout`,`cfg-handshake`,`cfg-reconnect-ms`,`cfg-autoreconnect`,`cfg-vendor`,`cfg-product`,`cfg-delim`,`cfg-prepend`,`cfg-append`,`cfg-hs-cmd`,`cfg-hs-cmd-mode`,`cfg-hs-expect`,`cfg-hs-expect-mode`,`dl-name`].forEach(e=>{let t=document.getElementById(e);t?.addEventListener(`change`,R),t?.addEventListener(`input`,R)}),document.querySelectorAll(`input[name='dl-lang']`).forEach(e=>e.addEventListener(`change`,R)),R();var H=`text`;S?.addEventListener(`click`,()=>{H=H===`text`?`hex`:`text`,S.textContent=H===`text`?`TXT`:`HEX`,x.placeholder=H===`text`?`Type a command, e.g. LED_ON`:`Hex bytes, e.g. FF 01 A3`});var U=null;function W(e){b.disabled=!e,x.disabled=!e,y.disabled=!e,v.disabled=e}v?.addEventListener(`click`,async()=>{if(U){try{await U.disconnect()}catch{}U=null}let e=M(),t=u(e.delimiter);U=new h({baudRate:e.baudRate,dataBits:e.dataBits,stopBits:e.stopBits,parity:e.parity,flowControl:e.flowControl,bufferSize:e.bufferSize,commandTimeout:e.commandTimeout,parser:n(t),autoReconnect:e.autoReconnect,autoReconnectInterval:e.autoReconnectInterval,handshakeTimeout:e.handshakeTimeout,filters:e.filters??[]},e.hsCmd,e.hsCmdMode,e.hsExpect,e.hsExpectMode),U.on(`serial:connecting`,()=>{I(`connecting`,`Connecting…`),v.disabled=!0,d(_,`Connecting to serial device…`,{kind:`system`})}),U.on(`serial:connected`,()=>{I(`connected`,`Connected`),W(!0),d(_,`Connected via Web Serial!`,{kind:`system`})}),U.on(`serial:disconnected`,()=>{I(`disconnected`,`Disconnected`),W(!1),d(_,`Disconnected.`,{kind:`system`}),U=null}),U.on(`serial:data`,e=>{d(_,String(e),{kind:`received`,label:`Device`})}),U.on(`serial:error`,e=>{I(`error`,`Error`),d(_,`Error: ${e.message}`,{kind:`error`}),v.disabled=!1}),U.on(`serial:need-permission`,()=>{I(`error`,`Permission denied`),d(_,`Permission denied — select a port and allow access.`,{kind:`error`}),v.disabled=!1}),U.on(`serial:timeout`,e=>{d(_,`Timeout: ${P(e)}`,{kind:`error`})}),U.on(`serial:reconnecting`,()=>{I(`connecting`,`Reconnecting…`),d(_,`Auto-reconnecting…`,{kind:`system`})});try{await U.connect()}catch{}}),y?.addEventListener(`click`,async()=>{await U?.disconnect()});async function G(){let e=x.value.trim();if(!e||!U)return;let t=M(),n=t.append?u(t.append):u(t.delimiter);try{if(H===`hex`){let t=F(e);d(_,`HEX: ${P(t)}`,{kind:`sent`,label:`You`}),await U.send(t)}else{let r=t.prepend+e+n;d(_,e,{kind:`sent`,label:`You`}),await U.send(r)}x.value=``,x.focus()}catch(e){d(_,`Send error: ${e instanceof Error?e.message:String(e)}`,{kind:`error`})}}b?.addEventListener(`click`,G),x?.addEventListener(`keydown`,e=>{e.key===`Enter`&&G()});var K=g(`cmd-name`),q=g(`cmd-value`),pe=g(`cmd-mode`),me=g(`cmd-add`),J=g(`cmd-list`),Y=g(`lst-name`),X=g(`lst-pattern`),he=g(`lst-match`),ge=g(`lst-add`),Z=g(`lst-list`);function Q(){if(J){J.innerHTML=``;for(let e of p){let t=document.createElement(`div`);t.className=`chip`;let n=document.createElement(`span`);n.className=`chip-badge`,n.textContent=e.mode.toUpperCase();let r=document.createElement(`span`);r.className=`chip-name`,r.textContent=e.name;let i=document.createElement(`span`);i.className=`chip-val`,i.textContent=e.value;let a=document.createElement(`button`);a.className=`chip-send`,a.title=`Send now`,a.textContent=`▶`,a.addEventListener(`click`,()=>{if(U)if(e.mode===`hex`){let t=F(e.value);U.send(t).catch(()=>{}),d(_,`HEX: ${P(t)}`,{kind:`sent`,label:`You`})}else{let t=M(),n=t.append?u(t.append):u(t.delimiter);U.send(t.prepend+e.value+n).catch(()=>{}),d(_,e.name,{kind:`sent`,label:`You`})}});let o=document.createElement(`button`);o.className=`chip-del`,o.title=`Remove`,o.textContent=`×`,o.addEventListener(`click`,()=>{p=p.filter(t=>t.id!==e.id),Q(),R()}),t.append(n,r,i,a,o),J.appendChild(t)}}}function $(){if(Z){Z.innerHTML=``;for(let e of m){let t=document.createElement(`div`);t.className=`chip`;let n=document.createElement(`span`);n.className=`chip-badge`,n.textContent=e.match;let r=document.createElement(`span`);r.className=`chip-name`,r.textContent=e.name;let i=document.createElement(`span`);i.className=`chip-val`,i.textContent=e.pattern;let a=document.createElement(`button`);a.className=`chip-del`,a.title=`Remove`,a.textContent=`×`,a.addEventListener(`click`,()=>{m=m.filter(t=>t.id!==e.id),$(),R()}),t.append(n,r,i,a),Z.appendChild(t)}}}me?.addEventListener(`click`,()=>{let e=K?.value.trim(),t=q?.value.trim();if(!e||!t)return;let n=pe?.value??`text`;p.push({id:crypto.randomUUID(),name:e,value:t,mode:n}),K&&(K.value=``),q&&(q.value=``),Q(),R()}),ge?.addEventListener(`click`,()=>{let e=Y?.value.trim(),t=X?.value.trim();if(!e||!t)return;let n=he?.value??`exact`;m.push({id:crypto.randomUUID(),name:e,pattern:t,match:n}),Y&&(Y.value=``),X&&(X.value=``),$(),R()}),d(_,`Web Serial demo ready — configure settings and click Connect.`,{kind:`system`}),i(_),a();
|