agent-loft 0.2.34 → 0.2.36

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.
@@ -0,0 +1,415 @@
1
+ /* ---------- 远程控制(views/remote.js、desktop.js) ---------- */
2
+
3
+ /* 侧栏入口:没权限、没有能用的时藏起来(.side-nav 的 display 会盖过 hidden) */
4
+ .side-nav[hidden] {
5
+ display: none;
6
+ }
7
+
8
+ /* 后台连着时右边一个小绿点 */
9
+ .side-nav .live-dot {
10
+ display: none;
11
+ width: 7px;
12
+ height: 7px;
13
+ margin-left: auto;
14
+ border-radius: 50%;
15
+ background: var(--ok);
16
+ }
17
+
18
+ .side-nav.live .live-dot {
19
+ display: block;
20
+ }
21
+
22
+ /* 远程控制页:对话、输入框都藏起来(只是藏,回来时还在),这一页占满顶栏下面 */
23
+ .main.remote-view .messages,
24
+ .main.remote-view .msg-nav,
25
+ .main.remote-view .composer-wrap,
26
+ .main.remote-view .to-bottom,
27
+ .main.remote-view > .term-dock {
28
+ display: none;
29
+ }
30
+
31
+ .remote-pane {
32
+ flex: 1;
33
+ min-height: 0;
34
+ display: flex;
35
+ flex-direction: column;
36
+ margin-top: calc(52px + env(safe-area-inset-top));
37
+ }
38
+
39
+ /* 软键盘盖住的高度(views/remote.js),iOS 上才不为 0 */
40
+ .remote-pane,
41
+ .main > .term-dock {
42
+ padding-bottom: var(--kb-inset, 0px);
43
+ }
44
+
45
+ .remote-pane[hidden] {
46
+ display: none;
47
+ }
48
+
49
+ /* iOS 主屏 PWA 里顶栏是 sticky 的、本来就占位置 */
50
+ @media (display-mode: standalone) and (max-width: 768px) {
51
+ .remote-pane {
52
+ margin-top: 0;
53
+ }
54
+ }
55
+
56
+ .remote-head {
57
+ display: flex;
58
+ align-items: center;
59
+ gap: 8px;
60
+ padding: 0 12px 6px;
61
+ min-height: 36px;
62
+ }
63
+
64
+ .remote-head .grow {
65
+ flex: 1;
66
+ }
67
+
68
+ .remote-switch {
69
+ display: flex;
70
+ gap: 2px;
71
+ padding: 2px;
72
+ border-radius: 9px;
73
+ background: var(--bg-soft);
74
+ }
75
+
76
+ .remote-switch:empty {
77
+ display: none;
78
+ }
79
+
80
+ .remote-tab {
81
+ display: flex;
82
+ white-space: nowrap;
83
+ flex-shrink: 0;
84
+ align-items: center;
85
+ gap: 6px;
86
+ padding: 5px 12px;
87
+ border: none;
88
+ border-radius: 7px;
89
+ background: transparent;
90
+ color: var(--fg-soft);
91
+ font: inherit;
92
+ font-size: 13px;
93
+ cursor: pointer;
94
+ }
95
+
96
+ .remote-tab svg {
97
+ width: 15px;
98
+ height: 15px;
99
+ }
100
+
101
+ .remote-tab.on {
102
+ background: var(--bg);
103
+ color: var(--fg);
104
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
105
+ }
106
+
107
+ .remote-tools {
108
+ display: flex;
109
+ gap: 2px;
110
+ min-width: 0;
111
+ overflow-x: auto;
112
+ scrollbar-width: none;
113
+ }
114
+
115
+ /* 手机:切换只留图标,工具栏挤一挤 */
116
+ @media (max-width: 768px) {
117
+ .remote-head {
118
+ padding: 0 8px 6px;
119
+ gap: 4px;
120
+ }
121
+
122
+ .remote-tab {
123
+ padding: 6px 10px;
124
+ }
125
+
126
+ .remote-tab span {
127
+ display: none;
128
+ }
129
+
130
+ .desk-tool {
131
+ width: 30px;
132
+ flex-shrink: 0;
133
+ }
134
+ }
135
+
136
+ .desk-tool {
137
+ width: 32px;
138
+ height: 32px;
139
+ }
140
+
141
+ .desk-tool svg {
142
+ width: 17px;
143
+ height: 17px;
144
+ }
145
+
146
+ .desk-tool.on {
147
+ background: var(--bg-hover);
148
+ color: var(--accent);
149
+ }
150
+
151
+ .desk-tool:disabled {
152
+ opacity: 0.35;
153
+ pointer-events: none;
154
+ }
155
+
156
+ .remote-body {
157
+ flex: 1;
158
+ min-height: 0;
159
+ display: flex;
160
+ flex-direction: column;
161
+ position: relative;
162
+ }
163
+
164
+ .remote-body[hidden] {
165
+ display: none;
166
+ }
167
+
168
+ /* ---------- 桌面 ---------- */
169
+
170
+ .desk-status {
171
+ display: flex;
172
+ align-items: center;
173
+ justify-content: center;
174
+ gap: 10px;
175
+ padding: 6px 12px;
176
+ font-size: 13px;
177
+ }
178
+
179
+ .desk-status[hidden] {
180
+ display: none;
181
+ }
182
+
183
+ /* 外框负责裁剪;里面那块交给 noVNC,手机上在它上面做 CSS 放大(views/desktop-touch.js) */
184
+ .desk-viewport {
185
+ flex: 1;
186
+ min-height: 0;
187
+ position: relative;
188
+ overflow: hidden;
189
+ background: #111;
190
+ /* 手势全由页面自己处理:别让浏览器拿去缩放、滚动页面 */
191
+ touch-action: none;
192
+ -webkit-user-select: none;
193
+ user-select: none;
194
+ -webkit-touch-callout: none;
195
+ }
196
+
197
+ .desk-screen {
198
+ position: relative;
199
+ width: 100%;
200
+ height: 100%;
201
+ transform-origin: 0 0;
202
+ transition: opacity 0.2s;
203
+ }
204
+
205
+ /* 触控板模式下的指针(远程那边的指针不画在画面里) */
206
+ .desk-cursor {
207
+ position: absolute;
208
+ left: 0;
209
+ top: 0;
210
+ width: 20px;
211
+ height: 24px;
212
+ margin: -1px 0 0 -2px;
213
+ pointer-events: none;
214
+ filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
215
+ z-index: 2;
216
+ }
217
+
218
+ .desk-cursor[hidden] {
219
+ display: none;
220
+ }
221
+
222
+ .desk-cursor svg {
223
+ width: 100%;
224
+ height: 100%;
225
+ }
226
+
227
+ /* 长按到了:要开始拖了 */
228
+ .desk-cursor.armed::after,
229
+ .desk-cursor.pressed::after {
230
+ content: "";
231
+ position: absolute;
232
+ left: -12px;
233
+ top: -12px;
234
+ width: 26px;
235
+ height: 26px;
236
+ border-radius: 50%;
237
+ border: 2px solid var(--accent);
238
+ background: color-mix(in srgb, var(--accent) 25%, transparent);
239
+ }
240
+
241
+ /* 放大了:右下角「2.0× · 还原」 */
242
+ .desk-zoom {
243
+ position: absolute;
244
+ right: 10px;
245
+ bottom: 10px;
246
+ z-index: 3;
247
+ padding: 5px 10px;
248
+ border: none;
249
+ border-radius: 999px;
250
+ background: rgba(0, 0, 0, 0.6);
251
+ color: #fff;
252
+ font: 12px var(--font);
253
+ }
254
+
255
+ .desk-zoom[hidden],
256
+ .desk-hint[hidden] {
257
+ display: none;
258
+ }
259
+
260
+ .desk-hint {
261
+ position: absolute;
262
+ left: 50%;
263
+ top: 12px;
264
+ transform: translateX(-50%);
265
+ z-index: 3;
266
+ padding: 6px 12px;
267
+ border-radius: 999px;
268
+ background: rgba(0, 0, 0, 0.7);
269
+ color: #fff;
270
+ font-size: 13px;
271
+ white-space: nowrap;
272
+ pointer-events: none;
273
+ }
274
+
275
+ .desk-keys .term-key.icon {
276
+ display: grid;
277
+ place-items: center;
278
+ }
279
+
280
+ .desk-keys .term-key.icon svg {
281
+ width: 20px;
282
+ height: 20px;
283
+ }
284
+
285
+ .desk-keys .term-key.on {
286
+ background: var(--accent);
287
+ border-color: var(--accent);
288
+ color: var(--accent-fg);
289
+ }
290
+
291
+ .gesture-sec h4 {
292
+ margin: 4px 0 8px;
293
+ font-size: 14px;
294
+ }
295
+
296
+ .gesture-list {
297
+ display: grid;
298
+ grid-template-columns: max-content 1fr;
299
+ gap: 6px 14px;
300
+ margin-bottom: 14px;
301
+ font-size: 13px;
302
+ }
303
+
304
+ .gesture-row {
305
+ display: contents;
306
+ }
307
+
308
+ .gesture-row span {
309
+ color: var(--fg-soft);
310
+ }
311
+
312
+ .desk-screen.dim {
313
+ opacity: 0.4;
314
+ }
315
+
316
+ /* 浏览器全屏时只剩画面 */
317
+ .remote-body:fullscreen .desk-status {
318
+ display: none;
319
+ }
320
+
321
+ /* 不支持元素全屏(iPhone):铺满整个页面,盖住顶栏、侧栏,弹窗和提示还在上面 */
322
+ .remote-body.desk-full {
323
+ position: fixed;
324
+ inset: 0;
325
+ z-index: 35;
326
+ background: #111;
327
+ padding: env(safe-area-inset-top) env(safe-area-inset-right) calc(env(safe-area-inset-bottom) + var(--kb-inset, 0px)) env(safe-area-inset-left);
328
+ }
329
+
330
+ /* 没连着时画面中间的「连接桌面」 */
331
+ .desk-idle {
332
+ position: absolute;
333
+ inset: 0;
334
+ z-index: 3;
335
+ display: flex;
336
+ flex-direction: column;
337
+ align-items: center;
338
+ justify-content: center;
339
+ gap: 12px;
340
+ background: var(--bg);
341
+ }
342
+
343
+ .desk-idle[hidden],
344
+ .desk-exitfull[hidden] {
345
+ display: none;
346
+ }
347
+
348
+ .desk-idle > span svg {
349
+ width: 44px;
350
+ height: 44px;
351
+ color: var(--fg-soft);
352
+ }
353
+
354
+ .desk-idle .btn {
355
+ display: inline-flex;
356
+ align-items: center;
357
+ gap: 6px;
358
+ }
359
+
360
+ .desk-idle .btn svg {
361
+ width: 16px;
362
+ height: 16px;
363
+ }
364
+
365
+ /* 全屏时左上角「退出全屏」 */
366
+ .desk-exitfull {
367
+ position: absolute;
368
+ left: 10px;
369
+ top: 10px;
370
+ z-index: 3;
371
+ display: grid;
372
+ place-items: center;
373
+ width: 34px;
374
+ height: 34px;
375
+ border: none;
376
+ border-radius: 50%;
377
+ background: rgba(0, 0, 0, 0.55);
378
+ color: #fff;
379
+ }
380
+
381
+ .desk-exitfull svg {
382
+ width: 18px;
383
+ height: 18px;
384
+ }
385
+
386
+ .desk-clip {
387
+ width: 100%;
388
+ margin-top: 10px;
389
+ font: 13px var(--mono);
390
+ resize: vertical;
391
+ }
392
+
393
+ /* 手机上收软键盘输入用:看不见,但要能拿到焦点 */
394
+ .desk-keyinput {
395
+ position: absolute;
396
+ left: 0;
397
+ bottom: 0;
398
+ width: 1px;
399
+ height: 1px;
400
+ opacity: 0;
401
+ border: none;
402
+ padding: 0;
403
+ resize: none;
404
+ /* 小于 16px 的输入框,iOS 聚焦时会把整页放大 */
405
+ font-size: 16px;
406
+ }
407
+
408
+ /* 设置里按选项显示的一组输入框:照常吃 section 的间距 */
409
+ .field-group {
410
+ display: contents;
411
+ }
412
+
413
+ .field-group[hidden] {
414
+ display: none;
415
+ }
@@ -0,0 +1,243 @@
1
+ /* ---------- 终端(views/terminal.js) ---------- */
2
+
3
+ /* 底部面板:在输入框下面,高度可拖动 */
4
+ .term-dock {
5
+ --term-h: 38vh;
6
+ display: flex;
7
+ flex-direction: column;
8
+ height: var(--term-h);
9
+ min-height: 120px;
10
+ flex-shrink: 0;
11
+ border-top: 1px solid var(--border);
12
+ background: var(--bg);
13
+ position: relative;
14
+ z-index: 6;
15
+ }
16
+
17
+ .term-dock[hidden] {
18
+ display: none;
19
+ }
20
+
21
+ .term-resizer {
22
+ position: absolute;
23
+ top: -3px;
24
+ left: 0;
25
+ right: 0;
26
+ height: 6px;
27
+ cursor: row-resize;
28
+ z-index: 1;
29
+ }
30
+
31
+ body.resizing-v {
32
+ cursor: row-resize;
33
+ user-select: none;
34
+ }
35
+
36
+ .term-head {
37
+ display: flex;
38
+ align-items: center;
39
+ gap: 2px;
40
+ padding: 4px 8px 0;
41
+ min-height: 36px;
42
+ }
43
+
44
+ .term-head .grow {
45
+ flex: 1;
46
+ }
47
+
48
+ .term-act {
49
+ width: 30px;
50
+ height: 30px;
51
+ }
52
+
53
+ .term-act[hidden] {
54
+ display: none;
55
+ }
56
+
57
+ .term-act svg {
58
+ width: 16px;
59
+ height: 16px;
60
+ }
61
+
62
+ .term-tabs {
63
+ display: flex;
64
+ gap: 2px;
65
+ min-width: 0;
66
+ overflow-x: auto;
67
+ scrollbar-width: none;
68
+ }
69
+
70
+ .term-tab {
71
+ display: flex;
72
+ align-items: center;
73
+ gap: 6px;
74
+ max-width: 200px;
75
+ padding: 5px 6px 5px 10px;
76
+ border-radius: 7px;
77
+ font-size: 13px;
78
+ color: var(--fg-soft);
79
+ cursor: pointer;
80
+ white-space: nowrap;
81
+ flex-shrink: 0;
82
+ }
83
+
84
+ .term-tab:hover {
85
+ background: var(--bg-hover);
86
+ }
87
+
88
+ .term-tab.on {
89
+ background: var(--bg-soft);
90
+ color: var(--fg);
91
+ }
92
+
93
+ .term-tab.ended .term-tab-name {
94
+ text-decoration: line-through;
95
+ color: var(--fg-faint);
96
+ }
97
+
98
+ .term-tab-name {
99
+ overflow: hidden;
100
+ text-overflow: ellipsis;
101
+ }
102
+
103
+ .term-tab-x {
104
+ display: grid;
105
+ place-items: center;
106
+ width: 18px;
107
+ height: 18px;
108
+ padding: 0;
109
+ border: none;
110
+ border-radius: 4px;
111
+ background: transparent;
112
+ color: inherit;
113
+ opacity: 0;
114
+ cursor: pointer;
115
+ }
116
+
117
+ .term-tab-x svg {
118
+ width: 12px;
119
+ height: 12px;
120
+ }
121
+
122
+ .term-tab:hover .term-tab-x,
123
+ .term-tab.on .term-tab-x {
124
+ opacity: 0.7;
125
+ }
126
+
127
+ /* 触屏上没有悬停:× 一直显示 */
128
+ @media (hover: none) {
129
+ .term-tab .term-tab-x {
130
+ opacity: 0.7;
131
+ }
132
+ }
133
+
134
+ .term-tab-x:hover {
135
+ opacity: 1;
136
+ background: var(--bg-hover);
137
+ }
138
+
139
+ .term-panes {
140
+ flex: 1;
141
+ min-height: 0;
142
+ position: relative;
143
+ padding: 4px 4px 4px 10px;
144
+ }
145
+
146
+ .term-pane {
147
+ position: absolute;
148
+ inset: 4px 4px 4px 10px;
149
+ }
150
+
151
+ .term-pane[hidden] {
152
+ display: none;
153
+ }
154
+
155
+ .term-pane .xterm {
156
+ height: 100%;
157
+ }
158
+
159
+ .term-pane .xterm-viewport {
160
+ background: transparent !important;
161
+ }
162
+
163
+ .term-empty {
164
+ display: flex;
165
+ flex-direction: column;
166
+ align-items: center;
167
+ justify-content: center;
168
+ gap: 12px;
169
+ height: 100%;
170
+ padding: 16px;
171
+ text-align: center;
172
+ }
173
+
174
+ /* 手机上的快捷键行 */
175
+ .term-keys {
176
+ display: flex;
177
+ gap: 6px;
178
+ padding: 6px 8px calc(6px + env(safe-area-inset-bottom));
179
+ overflow-x: auto;
180
+ scrollbar-width: none;
181
+ border-top: 1px solid var(--border);
182
+ }
183
+
184
+ .term-key {
185
+ flex-shrink: 0;
186
+ min-width: 40px;
187
+ height: 34px;
188
+ padding: 0 10px;
189
+ border: 1px solid var(--border);
190
+ border-radius: 8px;
191
+ background: var(--bg-soft);
192
+ color: var(--fg);
193
+ font: 14px var(--mono);
194
+ }
195
+
196
+ .term-key.on {
197
+ background: var(--accent);
198
+ border-color: var(--accent);
199
+ color: var(--accent-fg);
200
+ }
201
+
202
+ /* 放在远程控制页里:占满那一块,不能拖高度 */
203
+ .term-dock.embedded {
204
+ flex: 1;
205
+ height: auto;
206
+ min-height: 0;
207
+ border-top: none;
208
+ }
209
+
210
+ .term-dock.embedded .term-resizer {
211
+ display: none;
212
+ }
213
+
214
+ /* 手机上长按选了字:上面一条「复制 / 全选 / 取消」(views/terminal-touch.js) */
215
+ .term-selbar {
216
+ position: absolute;
217
+ top: 6px;
218
+ right: 6px;
219
+ z-index: 5;
220
+ display: flex;
221
+ gap: 1px;
222
+ border-radius: 10px;
223
+ overflow: hidden;
224
+ box-shadow: var(--shadow);
225
+ background: var(--border);
226
+ }
227
+
228
+ .term-selbar[hidden] {
229
+ display: none;
230
+ }
231
+
232
+ .term-selbar button {
233
+ padding: 8px 14px;
234
+ border: none;
235
+ background: var(--bg-input);
236
+ color: var(--fg);
237
+ font: 14px var(--font);
238
+ }
239
+
240
+ /* 手指翻历史时别让浏览器去滚页面 */
241
+ .term-pane {
242
+ touch-action: none;
243
+ }
package/web/index.html CHANGED
@@ -26,6 +26,8 @@
26
26
  <link rel="stylesheet" href="/css/schedule.css" />
27
27
  <link rel="stylesheet" href="/css/tasks.css" />
28
28
  <link rel="stylesheet" href="/css/skills.css" />
29
+ <link rel="stylesheet" href="/css/terminal.css" />
30
+ <link rel="stylesheet" href="/css/remote.css" />
29
31
  <link rel="stylesheet" href="/css/mobile.css" />
30
32
  </head>
31
33
  <body>
package/web/lib/dom.js CHANGED
@@ -46,6 +46,21 @@ export const ICON = {
46
46
  trash: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M8 6V4h8v2"/><path d="M19 6l-1 14H6L5 6"/><path d="M10 11v6M14 11v6"/></svg>',
47
47
  folder: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7.5A2.5 2.5 0 0 1 5.5 5h3.6l2 2.2h7.4A2.5 2.5 0 0 1 21 9.7v7.8a2.5 2.5 0 0 1-2.5 2.5h-13A2.5 2.5 0 0 1 3 17.5Z"/><path d="M3 10h18"/></svg>',
48
48
  folderOpen: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3 17.5V7.5A2.5 2.5 0 0 1 5.5 5h3.6l2 2.2h6.4A2.5 2.5 0 0 1 20 9.7V11"/><path d="M3 17.5 5.6 12a2 2 0 0 1 1.8-1.1h13a1 1 0 0 1 .9 1.4l-2.4 6.2a2.2 2.2 0 0 1-2 1.5H5.5A2.5 2.5 0 0 1 3 17.5Z"/></svg>',
49
+ terminal: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="m7 9 3 3-3 3M13 15h4"/></svg>',
50
+ monitor: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg>',
51
+ keyboard: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="5" width="20" height="14" rx="2"/><path d="M6 9h.01M10 9h.01M14 9h.01M18 9h.01M6 13h.01M18 13h.01M10 13h4M7 16h10"/></svg>',
52
+ clipboard: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="8" y="2" width="8" height="4" rx="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg>',
53
+ command: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 6v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3V6a3 3 0 1 0-3 3h12a3 3 0 1 0-3-3"/></svg>',
54
+ eye: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7S2 12 2 12Z"/><circle cx="12" cy="12" r="3"/></svg>',
55
+ expand: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3M21 8V5a2 2 0 0 0-2-2h-3M3 16v3a2 2 0 0 0 2 2h3M16 21h3a2 2 0 0 0 2-2v-3"/></svg>',
56
+ plug: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22v-5"/><path d="M9 8V2"/><path d="M15 8V2"/><path d="M18 8v5a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V8Z"/></svg>',
57
+ unplug: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m19 5 3-3"/><path d="m2 22 3-3"/><path d="M6.3 20.3a2.4 2.4 0 0 0 3.4 0L12 18l-6-6-2.3 2.3a2.4 2.4 0 0 0 0 3.4Z"/><path d="M7.5 13.5 10 11"/><path d="M10.5 16.5 13 14"/><path d="m12 6 6 6 2.3-2.3a2.4 2.4 0 0 0 0-3.4l-2.6-2.6a2.4 2.4 0 0 0-3.4 0Z"/></svg>',
58
+ shrink: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3v3a2 2 0 0 1-2 2H3M21 8h-3a2 2 0 0 1-2-2V3M3 16h3a2 2 0 0 1 2 2v3M16 21v-3a2 2 0 0 1 2-2h3"/></svg>',
59
+ pointer: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3l14 7-6 2-2 6Z"/></svg>',
60
+ hand: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 11V5a1.5 1.5 0 0 1 3 0v5"/><path d="M12 10V4a1.5 1.5 0 0 1 3 0v6"/><path d="M15 10V6a1.5 1.5 0 0 1 3 0v8a7 7 0 0 1-7 7h-.5a6 6 0 0 1-4.6-2.2L3.4 15a1.6 1.6 0 0 1 2.4-2.1L9 15.5V11"/></svg>',
61
+ help: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M9.5 9.2a2.6 2.6 0 0 1 5 .9c0 1.7-2.5 2.3-2.5 3.9M12 17h.01"/></svg>',
62
+ maximize: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg>',
63
+ restore: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 14h6v6M20 10h-6V4M14 10l7-7M3 21l7-7"/></svg>',
49
64
  laptop: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="5" width="16" height="11" rx="1.5"/><path d="M2 19h20"/></svg>',
50
65
  logo: '<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.5 8.5a8 8 0 0 1 13.2 0 8 8 0 0 1 9 9.4 8 8 0 0 1 0 12.2 8 8 0 0 1-9 9.4 8 8 0 0 1-13.2 0 8 8 0 0 1-9-9.4 8 8 0 0 1 0-12.2 8 8 0 0 1 9-9.4Z"/><path d="m17 18 5 5-5 5M25 30h7"/></svg>',
51
66
  feishu:
package/web/lib/ws.js CHANGED
@@ -98,7 +98,8 @@ export const WS = {
98
98
  */
99
99
  presence() {
100
100
  const visible = document.visibilityState === "visible" && document.hasFocus() && Date.now() - lastInput < IDLE_MS;
101
- this.raw({ t: "presence", visible, key: S.current ?? "" });
101
+ // 在定时任务、远程控制页上:没在看哪条对话(远程控制页不清 S.current,回来时对话还在)
102
+ this.raw({ t: "presence", visible, key: S.view ? "" : (S.current ?? "") });
102
103
  },
103
104
  };
104
105
 
@@ -241,6 +242,10 @@ function onWsMessage(msg) {
241
242
  // 自己的定时任务变了:定时任务页开着的话会去重新拉(views/tasks.js)
242
243
  document.dispatchEvent(new Event("aim:tasks"));
243
244
  return;
245
+ case "remote":
246
+ // 自己的终端开了 / 关了,或者远程控制的权限、设置变了:重新问一下(views/layout.js)
247
+ document.dispatchEvent(new Event("aim:remote"));
248
+ return;
244
249
  case "skills":
245
250
  // 技能变了:设置里的技能页开着就重新拉,@ 补全的缓存作废
246
251
  document.dispatchEvent(new Event("aim:skills"));