dsh-remote-control-plugin 0.0.0-stage → 0.3.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/locale/en.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Remote Control",
4
+ "description": "Discover other DSH desktops on your network, approve them once, and drive their sessions from a Devices panel — plus a pairing-friendly browser page for phones."
5
+ }
6
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "远程控制",
4
+ "description": "自动发现同一网络中的其他 DSH 桌面端,授权一次后即可在“设备”面板中控制它们的会话;同时提供适合手机的配对浏览页面。"
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,6 +1,48 @@
1
1
  {
2
2
  "name": "dsh-remote-control-plugin",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.3.1",
4
+ "description": "Remote control for DSH: a Devices panel that discovers other DSH desktops, pairs with them once, and drives their sessions across LAN or a relay.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "keywords": [
8
+ "dsh",
9
+ "deepseek-harness",
10
+ "cordis",
11
+ "cordis-plugin",
12
+ "remote-control",
13
+ "devices"
14
+ ],
15
+ "exports": {
16
+ ".": "./index.js",
17
+ "./client": "./client.js",
18
+ "./locale/*.json": "./locale/*.json",
19
+ "./icon": "./icon.svg"
20
+ },
21
+ "files": [
22
+ "index.js",
23
+ "client.js",
24
+ "host/*.js",
25
+ "cordis.patch.yml",
26
+ "locale/*.json",
27
+ "icon.svg",
28
+ "README.md",
29
+ "INSTALL.md",
30
+ "LICENSE",
31
+ "web/index.html",
32
+ "web/app.js",
33
+ "web/app.css",
34
+ "web/icon.svg"
35
+ ],
36
+ "engines": {
37
+ "node": ">=20"
38
+ },
39
+ "dsh": {
40
+ "bundle": {
41
+ "patch": "./cordis.patch.yml"
42
+ },
43
+ "client": {
44
+ "platform": "web",
45
+ "immediately": true
46
+ }
47
+ }
48
+ }
package/web/app.css ADDED
@@ -0,0 +1,420 @@
1
+ :root {
2
+ color-scheme: dark light;
3
+ --bg: #0b0d12;
4
+ --bg-raised: #141821;
5
+ --bg-sunken: #0f131b;
6
+ --line: #232936;
7
+ --text: #e8ecf4;
8
+ --text-muted: #93a0b5;
9
+ --accent: #5b8bff;
10
+ --accent-ink: #ffffff;
11
+ --user-bubble: #26324a;
12
+ --assistant-bubble: #161b24;
13
+ --danger: #ff6b6b;
14
+ --ok: #45d19a;
15
+ --radius: 16px;
16
+ --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
17
+ --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
18
+ }
19
+
20
+ @media (prefers-color-scheme: light) {
21
+ :root {
22
+ --bg: #f5f7fb;
23
+ --bg-raised: #ffffff;
24
+ --bg-sunken: #eef1f7;
25
+ --line: #dde2ec;
26
+ --text: #14181f;
27
+ --text-muted: #5d6779;
28
+ --accent: #2f6bff;
29
+ --user-bubble: #dfe7ff;
30
+ --assistant-bubble: #ffffff;
31
+ --danger: #c53030;
32
+ --ok: #14855f;
33
+ }
34
+ }
35
+
36
+ * { box-sizing: border-box; }
37
+
38
+ html, body {
39
+ height: 100%;
40
+ margin: 0;
41
+ }
42
+
43
+ body {
44
+ background: var(--bg);
45
+ color: var(--text);
46
+ font-family: var(--sans);
47
+ font-size: 16px;
48
+ line-height: 1.45;
49
+ -webkit-text-size-adjust: 100%;
50
+ overscroll-behavior-y: none;
51
+ }
52
+
53
+ .muted { color: var(--text-muted); }
54
+ .tiny { font-size: 12px; }
55
+ .sr-only {
56
+ position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
57
+ overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
58
+ }
59
+ code { font-family: var(--mono); font-size: 0.9em; }
60
+
61
+ /* ---------- login ---------- */
62
+
63
+ .login {
64
+ min-height: 100dvh;
65
+ display: grid;
66
+ place-items: center;
67
+ padding: 24px;
68
+ background:
69
+ radial-gradient(1200px 600px at 50% -10%, color-mix(in srgb, var(--accent) 22%, transparent), transparent 70%),
70
+ var(--bg);
71
+ }
72
+
73
+ .login-card {
74
+ width: 100%;
75
+ max-width: 380px;
76
+ background: var(--bg-raised);
77
+ border: 1px solid var(--line);
78
+ border-radius: 22px;
79
+ padding: 28px 22px;
80
+ box-shadow: 0 24px 60px rgb(0 0 0 / 0.25);
81
+ text-align: center;
82
+ }
83
+
84
+ .login-logo { border-radius: 14px; }
85
+ .login h1 { font-size: 20px; margin: 12px 0 8px; }
86
+ .login p { margin: 0 0 16px; }
87
+
88
+ #pair-form { display: grid; gap: 10px; }
89
+
90
+ #code {
91
+ width: 100%;
92
+ padding: 14px 16px;
93
+ font-size: 26px;
94
+ font-family: var(--mono);
95
+ letter-spacing: 0.32em;
96
+ text-align: center;
97
+ color: var(--text);
98
+ background: var(--bg-sunken);
99
+ border: 1px solid var(--line);
100
+ border-radius: 14px;
101
+ }
102
+ #code:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
103
+
104
+ button {
105
+ font: inherit;
106
+ cursor: pointer;
107
+ border-radius: 12px;
108
+ border: 1px solid transparent;
109
+ }
110
+
111
+ #pair-submit {
112
+ padding: 13px 16px;
113
+ background: var(--accent);
114
+ color: var(--accent-ink);
115
+ font-weight: 600;
116
+ }
117
+ #pair-submit:disabled { opacity: 0.6; cursor: progress; }
118
+
119
+ .error { color: var(--danger); font-size: 14px; margin: 12px 0 0; }
120
+
121
+ /* ---------- app shell ---------- */
122
+
123
+ .app {
124
+ display: flex;
125
+ flex-direction: column;
126
+ height: 100dvh;
127
+ }
128
+
129
+ .bar {
130
+ position: sticky;
131
+ top: 0;
132
+ z-index: 20;
133
+ display: flex;
134
+ align-items: center;
135
+ gap: 10px;
136
+ padding: calc(env(safe-area-inset-top) + 10px) 12px 10px;
137
+ background: color-mix(in srgb, var(--bg) 82%, transparent);
138
+ backdrop-filter: blur(14px);
139
+ border-bottom: 1px solid var(--line);
140
+ }
141
+
142
+ .bar-title {
143
+ display: flex;
144
+ flex-direction: column;
145
+ min-width: 0;
146
+ flex: 1;
147
+ }
148
+ #session-title {
149
+ font-weight: 600;
150
+ font-size: 15px;
151
+ white-space: nowrap;
152
+ overflow: hidden;
153
+ text-overflow: ellipsis;
154
+ }
155
+ #session-meta {
156
+ white-space: nowrap;
157
+ overflow: hidden;
158
+ text-overflow: ellipsis;
159
+ }
160
+
161
+ .icon-button {
162
+ display: grid;
163
+ place-items: center;
164
+ width: 40px; height: 40px;
165
+ background: transparent;
166
+ color: var(--text);
167
+ border: 1px solid var(--line);
168
+ }
169
+
170
+ .status {
171
+ display: inline-flex;
172
+ align-items: center;
173
+ gap: 6px;
174
+ font-size: 12px;
175
+ color: var(--text-muted);
176
+ padding: 5px 9px;
177
+ border: 1px solid var(--line);
178
+ border-radius: 999px;
179
+ }
180
+ .status i {
181
+ width: 8px; height: 8px; border-radius: 50%;
182
+ background: var(--text-muted);
183
+ }
184
+ .status[data-state="live"] i { background: var(--ok); box-shadow: 0 0 0 4px color-mix(in srgb, var(--ok) 18%, transparent); }
185
+ .status[data-state="error"] i { background: var(--danger); }
186
+ .status[data-state="connecting"] i { background: var(--accent); }
187
+
188
+ /* ---------- drawer ---------- */
189
+
190
+ .scrim {
191
+ position: fixed; inset: 0; z-index: 30;
192
+ background: rgb(0 0 0 / 0.45);
193
+ }
194
+
195
+ .drawer {
196
+ position: fixed;
197
+ top: 0; bottom: 0; left: 0;
198
+ z-index: 40;
199
+ width: min(84vw, 340px);
200
+ display: flex;
201
+ flex-direction: column;
202
+ background: var(--bg-raised);
203
+ border-right: 1px solid var(--line);
204
+ padding-top: env(safe-area-inset-top);
205
+ animation: slide-in 160ms ease-out;
206
+ }
207
+
208
+ @keyframes slide-in {
209
+ from { transform: translateX(-12px); opacity: 0; }
210
+ to { transform: translateX(0); opacity: 1; }
211
+ }
212
+
213
+ .drawer-head {
214
+ display: flex; align-items: center; justify-content: space-between;
215
+ padding: 14px 14px 10px;
216
+ }
217
+ .ghost-button {
218
+ padding: 7px 12px;
219
+ background: transparent;
220
+ border: 1px solid var(--line);
221
+ color: var(--text);
222
+ font-size: 13px;
223
+ }
224
+
225
+ .session-list { flex: 1; overflow-y: auto; padding: 4px 8px 12px; }
226
+
227
+ .session-item {
228
+ display: block;
229
+ width: 100%;
230
+ text-align: left;
231
+ padding: 10px 12px;
232
+ background: transparent;
233
+ border: none;
234
+ border-radius: 12px;
235
+ color: var(--text);
236
+ }
237
+ .session-item + .session-item { margin-top: 2px; }
238
+ .session-item[aria-current="true"] { background: var(--bg-sunken); }
239
+ .session-item:hover { background: var(--bg-sunken); }
240
+ .session-item .row1 {
241
+ display: flex; align-items: center; gap: 8px;
242
+ font-size: 14px; font-weight: 600;
243
+ }
244
+ .session-item .row1 span:first-child {
245
+ flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
246
+ }
247
+ .session-item .row2 {
248
+ font-size: 12px; color: var(--text-muted); margin-top: 2px;
249
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
250
+ }
251
+ .dot {
252
+ width: 7px; height: 7px; border-radius: 50%; background: var(--ok); flex: none;
253
+ }
254
+ .dot.stale { background: var(--text-muted); }
255
+
256
+ .drawer-foot {
257
+ border-top: 1px solid var(--line);
258
+ padding: 12px 14px calc(env(safe-area-inset-bottom) + 12px);
259
+ }
260
+ .link-button {
261
+ background: none; border: none; color: var(--text-muted);
262
+ font-size: 13px; text-decoration: underline; padding: 0;
263
+ }
264
+
265
+ /* ---------- transcript ---------- */
266
+
267
+ .log {
268
+ flex: 1;
269
+ overflow-y: auto;
270
+ padding: 16px 12px 8px;
271
+ display: flex;
272
+ flex-direction: column;
273
+ gap: 10px;
274
+ scroll-behavior: smooth;
275
+ }
276
+
277
+ .empty {
278
+ margin: auto;
279
+ text-align: center;
280
+ color: var(--text-muted);
281
+ font-size: 14px;
282
+ padding: 24px;
283
+ }
284
+
285
+ .bubble {
286
+ max-width: min(88%, 640px);
287
+ padding: 10px 13px;
288
+ border-radius: var(--radius);
289
+ border: 1px solid var(--line);
290
+ white-space: pre-wrap;
291
+ overflow-wrap: anywhere;
292
+ font-size: 15px;
293
+ }
294
+ .bubble.user {
295
+ align-self: flex-end;
296
+ background: var(--user-bubble);
297
+ border-color: color-mix(in srgb, var(--accent) 30%, var(--line));
298
+ }
299
+ .bubble.assistant {
300
+ align-self: flex-start;
301
+ background: var(--assistant-bubble);
302
+ }
303
+ .bubble.live { border-style: dashed; }
304
+ .bubble.interrupted { opacity: 0.75; }
305
+
306
+ .bubble .label {
307
+ display: block;
308
+ font-size: 11px;
309
+ letter-spacing: 0.06em;
310
+ text-transform: uppercase;
311
+ color: var(--text-muted);
312
+ margin-bottom: 4px;
313
+ }
314
+
315
+ details.tool {
316
+ align-self: flex-start;
317
+ max-width: min(92%, 720px);
318
+ width: 100%;
319
+ background: var(--bg-sunken);
320
+ border: 1px solid var(--line);
321
+ border-radius: 12px;
322
+ font-size: 13px;
323
+ }
324
+ details.tool summary {
325
+ cursor: pointer;
326
+ padding: 8px 12px;
327
+ color: var(--text-muted);
328
+ list-style: none;
329
+ display: flex;
330
+ gap: 8px;
331
+ align-items: baseline;
332
+ }
333
+ details.tool summary::-webkit-details-marker { display: none; }
334
+ details.tool summary .tool-name { color: var(--text); font-weight: 600; font-family: var(--mono); }
335
+ details.tool summary .tool-hint {
336
+ flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
337
+ }
338
+ details.tool pre {
339
+ margin: 0;
340
+ padding: 0 12px 10px;
341
+ font-family: var(--mono);
342
+ font-size: 12px;
343
+ white-space: pre-wrap;
344
+ overflow-wrap: anywhere;
345
+ color: var(--text-muted);
346
+ max-height: 320px;
347
+ overflow: auto;
348
+ }
349
+ details.tool.error { border-color: color-mix(in srgb, var(--danger) 45%, var(--line)); }
350
+ details.tool.error summary .tool-name { color: var(--danger); }
351
+
352
+ .note {
353
+ align-self: center;
354
+ font-size: 12px;
355
+ color: var(--text-muted);
356
+ padding: 2px 8px;
357
+ }
358
+
359
+ .strip {
360
+ align-self: stretch;
361
+ font-size: 13px;
362
+ padding: 8px 12px;
363
+ border-radius: 10px;
364
+ border: 1px solid var(--line);
365
+ background: color-mix(in srgb, var(--danger) 12%, transparent);
366
+ color: var(--text);
367
+ }
368
+
369
+ /* ---------- composer ---------- */
370
+
371
+ .composer {
372
+ position: sticky;
373
+ bottom: 0;
374
+ display: flex;
375
+ gap: 8px;
376
+ align-items: flex-end;
377
+ padding: 10px 12px calc(env(safe-area-inset-bottom) + 10px);
378
+ background: color-mix(in srgb, var(--bg) 88%, transparent);
379
+ backdrop-filter: blur(14px);
380
+ border-top: 1px solid var(--line);
381
+ }
382
+
383
+ #input {
384
+ flex: 1;
385
+ resize: none;
386
+ max-height: 40vh;
387
+ padding: 11px 13px;
388
+ font: inherit;
389
+ font-size: 15px;
390
+ line-height: 1.4;
391
+ color: var(--text);
392
+ background: var(--bg-raised);
393
+ border: 1px solid var(--line);
394
+ border-radius: 14px;
395
+ }
396
+ #input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
397
+
398
+ .send {
399
+ padding: 11px 16px;
400
+ background: var(--accent);
401
+ color: var(--accent-ink);
402
+ font-weight: 600;
403
+ border-radius: 14px;
404
+ min-height: 44px;
405
+ }
406
+ .send:disabled { opacity: 0.55; cursor: default; }
407
+
408
+ .stop {
409
+ padding: 11px 14px;
410
+ background: color-mix(in srgb, var(--danger) 18%, transparent);
411
+ border: 1px solid color-mix(in srgb, var(--danger) 45%, var(--line));
412
+ color: var(--text);
413
+ min-height: 44px;
414
+ }
415
+
416
+ @media (min-width: 720px) {
417
+ .drawer { box-shadow: 0 20px 60px rgb(0 0 0 / 0.4); }
418
+ .log { padding-inline: max(12px, calc((100% - 760px) / 2)); }
419
+ .composer { padding-inline: max(12px, calc((100% - 760px) / 2)); }
420
+ }