@walkhi/code-relax 0.1.0-beta.1 → 0.1.0-beta.2

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.
Files changed (62) hide show
  1. package/README.md +5 -5
  2. package/dist/bin/self-relay-server.mjs +5 -2
  3. package/dist/shared/app-server-events.cjs +45 -19
  4. package/dist/shared/p2p-data-channel.cjs +50 -0
  5. package/dist/src/app-server-tasks.mjs +28 -25
  6. package/dist/src/lan-socket-server.mjs +8 -2
  7. package/dist/src/platform/windows/IsolatedProcess.cs +7 -3
  8. package/dist/src/platform/windows/background-process.mjs +7 -2
  9. package/dist/src/platform/windows/launch-worker.ps1 +13 -6
  10. package/dist/src/platform/windows/start-hidden-console.ps1 +11 -6
  11. package/dist/src/self-relay/admin-state.mjs +61 -0
  12. package/dist/src/self-relay/client.mjs +2 -0
  13. package/dist/src/self-relay/connector.mjs +23 -8
  14. package/dist/src/self-relay/demo.mjs +2 -2
  15. package/dist/src/self-relay/lifecycle.mjs +1 -1
  16. package/dist/src/self-relay/p2p-probe.mjs +123 -83
  17. package/dist/src/self-relay/server.mjs +137 -11
  18. package/dist/src/server.mjs +337 -255
  19. package/dist/src/shared-app-server.mjs +180 -24
  20. package/dist/src/shared-catalog.mjs +4 -14
  21. package/dist/src/thread-catalog.mjs +12 -7
  22. package/dist/web/activity-view.js +283 -0
  23. package/dist/web/capabilities.js +2 -2
  24. package/dist/web/chat-transport.js +15 -9
  25. package/dist/web/chat.css +139 -93
  26. package/dist/web/chat.js +1451 -2770
  27. package/dist/web/community-view.js +20 -0
  28. package/dist/web/community.css +77 -0
  29. package/dist/web/community.html +37 -0
  30. package/dist/web/composer-controller.js +101 -0
  31. package/dist/web/conversation-controller.js +99 -0
  32. package/dist/web/disclosure-state-controller.js +95 -0
  33. package/dist/web/draft-controller.js +103 -0
  34. package/dist/web/harmony-platform.js +3 -2
  35. package/dist/web/history-cache.js +112 -18
  36. package/dist/web/history-controller.js +167 -0
  37. package/dist/web/index.html +141 -38
  38. package/dist/web/link-action-controller.js +212 -0
  39. package/dist/web/message-send-controller.js +177 -0
  40. package/dist/web/message-view.js +98 -0
  41. package/dist/web/p2p-data-channel.js +50 -0
  42. package/dist/web/p2p-probe.js +67 -27
  43. package/dist/web/page-resume.js +28 -0
  44. package/dist/web/pending-message-store.js +108 -0
  45. package/dist/web/queue-controller.js +82 -0
  46. package/dist/web/resources.json +1 -1
  47. package/dist/web/self-relay-session.js +28 -18
  48. package/dist/web/station-connection-controller.js +75 -0
  49. package/dist/web/task-list-view.js +296 -0
  50. package/dist/web/thread-attention-controller.js +124 -0
  51. package/dist/web/thread-context-controller.js +30 -0
  52. package/dist/web/thread-list-controller.js +61 -0
  53. package/dist/web/thread-list-sync.js +86 -0
  54. package/dist/web/thread-title-controller.js +57 -0
  55. package/dist/web/timeline-formatters.js +249 -0
  56. package/dist/web/timeline-reducer.js +81 -0
  57. package/dist/web/timeline-renderer.js +161 -0
  58. package/dist/web/timeline-scroll-controller.js +50 -0
  59. package/dist/web/usage-controller.js +258 -0
  60. package/dist/web/vendor/lucide.LICENSE.txt +17 -0
  61. package/package.json +1 -1
  62. package/tools/postinstall.mjs +66 -2
@@ -2,8 +2,9 @@
2
2
  const capabilities = root.CodexCapabilities;
3
3
  const selfRelay = root.codexSelfRelayConfig;
4
4
  const lanOrigin = selfRelay ? new URL(selfRelay.url).origin.replace(/^wss:/, 'https:').replace(/^ws:/, 'http:') : root.codexLanConfig?.address || root.location?.origin;
5
- const workstationScope = selfRelay
5
+ const stationScope = selfRelay
6
6
  ? JSON.stringify([selfRelay.url, selfRelay.room, selfRelay.clientId])
7
+ // workstationScope is the released native/Web wire field; stationScope is the domain name used here.
7
8
  : root.codexLanConfig?.workstationScope || '';
8
9
 
9
10
  class LanTransport extends (selfRelay ? root.CodexSelfRelaySession : root.CodexLanSession) {
@@ -37,7 +38,7 @@
37
38
  return this.subscribe(`/api/threads/${encodeURIComponent(thread.id)}/events?${query}`);
38
39
  }
39
40
  async cacheScope() {
40
- if (workstationScope) return `workstation-v1:${workstationScope}`;
41
+ if (stationScope) return `workstation-v1:${stationScope}`;
41
42
  if (!selfRelay) return root.codexLanConfig?.cacheScope ? `${root.codexLanConfig.cacheScope}:${this.mode}` : null;
42
43
  const identity = [selfRelay.url, selfRelay.room, selfRelay.clientId, selfRelay.revision, selfRelay.credential].join('\0');
43
44
  const bytes = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(identity));
@@ -50,7 +51,7 @@
50
51
  }
51
52
  async readCached(id, thread) {
52
53
  const scope = await this.cacheScope();
53
- return scope ? root.codexHistoryCache?.get(scope, (workstationScope || selfRelay) ? id : JSON.stringify([thread?.hostId || 'local', id])) : null;
54
+ return scope ? root.codexHistoryCache?.get(scope, (stationScope || selfRelay) ? id : JSON.stringify([thread?.hostId || 'local', id])) : null;
54
55
  }
55
56
  async readLastCached() {
56
57
  const scope = await this.cacheScope();
@@ -58,7 +59,7 @@
58
59
  }
59
60
  async saveCached(data) {
60
61
  const scope = await this.cacheScope();
61
- if (scope) return root.codexHistoryCache?.save(scope, data, value => this.cacheHash(value), (workstationScope || selfRelay) ? data.thread.id : JSON.stringify([data.thread.hostId || 'local', data.thread.id]));
62
+ if (scope) return root.codexHistoryCache?.save(scope, data, value => this.cacheHash(value), (stationScope || selfRelay) ? data.thread.id : JSON.stringify([data.thread.hostId || 'local', data.thread.id]));
62
63
  }
63
64
  async addImage(blob) {
64
65
  if (!['image/jpeg', 'image/png', 'image/webp'].includes(blob.type) || !blob.size || blob.size > 8 * 1024 * 1024) throw new Error('请选择不超过 8 MiB 的 JPEG、PNG 或 WebP 图片');
@@ -76,9 +77,14 @@
76
77
  if (this.token) url.searchParams.set('token', this.token);
77
78
  return url.href;
78
79
  }
79
- async readImage(_threadId, id) {
80
+ async readImage(threadId, id, variant = 'original', recoveryToken = '', recoveryTurnId = '') {
80
81
  if (!/^[a-f0-9]{64}$/.test(id)) throw new Error('无效的图片标识');
81
- return (await this.api(`/api/images/${id}`)).url;
82
+ const query = new URLSearchParams();
83
+ if (variant === 'thumbnail') query.set('variant', 'thumbnail');
84
+ if (recoveryToken) query.set('recovery', recoveryToken);
85
+ if (threadId) query.set('threadId', threadId);
86
+ if (recoveryTurnId) query.set('turnId', recoveryTurnId);
87
+ return (await this.api(`/api/images/${id}${query.size ? `?${query}` : ''}`)).url;
82
88
  }
83
89
  async readFile(_threadId, id) {
84
90
  if (!/^[a-f0-9]{64}$/.test(id)) throw new Error('无效的文件标识');
@@ -86,7 +92,7 @@
86
92
  }
87
93
  }
88
94
 
89
- // Relay owns login, device pairing, chunking, reconnection and phone cache.
95
+ // Relay owns login, device pairing, reconnection and phone cache.
90
96
  // The LAN adapter owns the authenticated WebSocket; both expose the same UI operations.
91
97
  root.createChatTransport = function ({ token, currentThread, relay = root.codexTransport }) {
92
98
  const adapter = relay || new LanTransport(token);
@@ -142,7 +148,7 @@
142
148
  const network = selfRelay ? compact ? 'CodeRelax中继' : 'Code Relax 中继' : '局域网';
143
149
  return serviceAdvisory() ? network : `${network} · ${adapter.mode === 'lan-shared' ? '完整控制' : '基础控制'}`;
144
150
  },
145
- draftScope: () => relay ? adapter.draftScope() : workstationScope || (selfRelay ? `${lanOrigin}/${selfRelay.room}` : lanOrigin),
151
+ draftScope: () => relay ? adapter.draftScope() : stationScope || (selfRelay ? `${lanOrigin}/${selfRelay.room}` : lanOrigin),
146
152
  capabilities: () => capabilities.describe(mode()),
147
153
  has: name => (name === 'historyCache' && Boolean(selfRelay || root.codexLanConfig?.cacheScope)) || capabilities.has(mode(), name),
148
154
  supports: (name, thread) => capabilities.has(modeFor(thread), name),
@@ -170,7 +176,7 @@
170
176
  addImage: blob => adapter.addImage(blob),
171
177
  removeImage: id => adapter.removeImage?.(id),
172
178
  clearImages: () => adapter.clearImages?.(),
173
- readImage: (id, path) => adapter.readImage(id, path),
179
+ readImage: (id, path, variant, recoveryToken, recoveryTurnId) => adapter.readImage(id, path, variant, recoveryToken, recoveryTurnId),
174
180
  readFile: (id, path) => adapter.readFile(id, path),
175
181
  readCached: async (id, thread) => adapter.readCached?.(id, thread),
176
182
  readLastCached: async () => adapter.readLastCached?.(),
package/dist/web/chat.css CHANGED
@@ -66,6 +66,7 @@
66
66
  .task-title { flex: 1; min-width: 0; overflow: hidden; font-size: 12px; font-weight: 400; line-height: 1.35; text-overflow: ellipsis; white-space: nowrap; }
67
67
  .running-status { width: 13px; height: 13px; flex: none; border: 1.5px solid #5e6962; border-top-color: #c2cbc5; border-radius: 50%; animation: status-spin .9s linear infinite; }
68
68
  .approval-status { display: inline-flex; align-items: center; flex: none; color: #f2b65a; font-size: 12px; font-weight: 650; line-height: 1.2; white-space: nowrap; }
69
+ .archive-status { display:inline-flex; align-items:center; flex:none; color:#d9a45f; font-size:12px; font-weight:600; line-height:1.2; white-space:nowrap; }
69
70
  .unread-status { width: 9px; height: 9px; flex: none; border-radius: 50%; background: #4285e8; }
70
71
  .project-header > :is(.approval-status, .running-status, .unread-status) { margin: 0 7px 0 auto; }
71
72
  .project-actions { display: flex; flex: none; align-items: center; gap: 1px; opacity: 0; pointer-events: none; }
@@ -103,7 +104,7 @@
103
104
  .turn-error-icon { display: grid; place-items: center; width: 16px; height: 16px; flex: none; margin-top: 1px; border: 1.5px solid currentColor; border-radius: 50%; font-size: 10px; font-weight: 750; line-height: 1; }
104
105
  .turn-error-message { min-width: 0; word-break: break-word; }
105
106
  .message { max-width: 88%; border-radius: 12px; padding: 10px 12px; font-size: 13px; line-height: 1.55; word-break: break-word; }
106
- .message.user { align-self: flex-end; background: #244a35; border: 1px solid #397655; }
107
+ .message.user { align-self: flex-end; padding: 5px 12px; background: #244a35; border: 1px solid #397655; }
107
108
  .pending-message { display: flex; flex-direction: column; align-self: stretch; min-width: 0; gap: 6px; }
108
109
  .pending-message-status { align-self:flex-end; color:#94a79a; font-size:12px; }
109
110
  .pending-message-actions { align-self:flex-end; display:flex; gap:8px; }
@@ -157,8 +158,15 @@
157
158
  .message-body hr { border: 0; border-top: 1px solid #35433a; }
158
159
  .user-attachments { display: flex; align-self: flex-end; max-width: 88%; flex-wrap: wrap; justify-content: flex-end; gap: 7px; }
159
160
  .image-thumb { width: 72px; height: 72px; overflow: hidden; padding: 0; border: 1px solid #45564b; border-radius: 8px; background: #0b0e0c; }
160
- .image-thumb:hover { border-color: #6e8a78; background: #0b0e0c; }
161
- .image-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
161
+ .image-thumb:hover { border-color: #6e8a78; background: #0b0e0c; }
162
+ .image-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
163
+ .image-thumb.image-placeholder { position: relative; display: grid; place-items: center; color: #66746b; }
164
+ .image-placeholder-art { display: grid; place-items: center; width: 38px; height: 38px; }
165
+ .image-placeholder-art svg { width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 1.35; stroke-linecap: round; stroke-linejoin: round; }
166
+ .image-placeholder-refresh { position: absolute; right: 5px; bottom: 5px; display: grid; place-items: center; width: 23px; height: 23px; border: 1px solid #4b5d52; border-radius: 50%; color: #b4c1b8; background: #151b17; }
167
+ .image-placeholder-refresh svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
168
+ .image-thumb.image-placeholder.loading .image-placeholder-refresh svg { animation: image-refresh-spin .8s linear infinite; }
169
+ @keyframes image-refresh-spin { to { transform: rotate(360deg); } }
162
170
  #draftImages .image-thumb { position: relative; margin-bottom: 8px; }
163
171
  .remove-image { position: absolute; right: 2px; top: 2px; width: 20px; height: 20px; border-radius: 50%; background: #101311dd; color: white; line-height: 18px; }
164
172
  .message.agent .message-image { display: block; max-width: min(100%, 640px); max-height: 380px; margin-top: 9px; border-radius: 8px; object-fit: contain; }
@@ -224,7 +232,7 @@
224
232
  .process > summary[aria-disabled="true"] { cursor:default; pointer-events:none; }
225
233
  .disclosure-chevron { display: grid; place-items: center; width: 14px; height: 14px; flex: none; color: #758078; opacity: 0; transform: rotate(0deg); transition: transform .12s ease, opacity .12s ease; }
226
234
  .disclosure-chevron svg { display: block; width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
227
- :is(.process, .activity, .reasoning-history)[open] > summary > .disclosure-chevron { opacity: 1; }
235
+ :is(.process, .activity)[open] > summary > .disclosure-chevron { opacity: 1; }
228
236
  .process[open] > summary > .disclosure-chevron { transform: rotate(90deg); }
229
237
  .activity[open] > summary > .disclosure-chevron { transform: rotate(90deg); }
230
238
  .process.running > summary { color: #71d695; }
@@ -242,12 +250,6 @@
242
250
  .process-body .message.commentary .role { display: none; }
243
251
  .process-count { display: none; }
244
252
  .turn-duration { align-self: stretch; padding: 9px 2px 8px; border-bottom: 1px solid #252e28; color: #969f99; font-size: 12px; font-weight: 650; }
245
- .reasoning-history { color: #818c84; font-size: 12px; }
246
- .reasoning-history > summary { display: flex; align-items: center; gap: 5px; cursor: pointer; padding: 5px 2px; list-style: none; }
247
- .reasoning-history > summary::-webkit-details-marker { display: none; }
248
- .reasoning-history[open] > summary > .disclosure-chevron { transform: rotate(90deg); }
249
- .reasoning-history ol { margin: 3px 0 6px; padding-left: 23px; }
250
- .reasoning-history li { margin: 5px 0; line-height: 1.45; }
251
253
  .live-step { display: flex; align-items: center; gap: 8px; padding: 7px 2px 2px; color: #8f9a92; font-size: 12px; }
252
254
  .pulse { width: 7px; height: 7px; border-radius: 50%; background: #52cc7c; animation: pulse 1.2s ease-in-out infinite; }
253
255
  @keyframes pulse { 0%, 100% { opacity: .35; transform: scale(.85); } 50% { opacity: 1; transform: scale(1.1); } }
@@ -269,7 +271,7 @@
269
271
  .clear-pending-settings:hover { color: #e8eee9; background: #303632; }
270
272
  textarea { width: 100%; min-height: 62px; resize: none; border: 0; outline: 0; padding: 3px 1px; color: #eef4ef; background: transparent; font-size: 13px; }
271
273
  #prompt { width:calc(100% - 40px); height:48px; min-height:48px; overflow-y:auto; font-size:12px; line-height:1.5; }
272
- .voice-stop-popover { position:absolute; bottom:calc(100% + 6px); left:12px; z-index:20; padding:4px; border:1px solid #465149; border-radius:12px; background:#252d28; box-shadow:0 4px 16px #0003; }
274
+ .voice-stop-popover { position:absolute; top:-48px; left:12px; z-index:20; padding:4px; border:1px solid #465149; border-radius:12px; background:#252d28; box-shadow:0 4px 16px #0003; }
273
275
  .voice-stop-popover[hidden] { display:none; }
274
276
  #stopVoiceToEdit { min-width:112px; min-height:36px; padding:6px 16px; border:0; border-radius:8px; background:transparent; color:#dce3de; font-size:13px; }
275
277
  #stopVoiceToEdit:disabled { opacity:.6; }
@@ -423,8 +425,15 @@
423
425
  .image-viewer { max-width: min(94vw, 1200px); max-height: 92vh; padding: 0; border: 0; background: transparent; overflow: visible; }
424
426
  .image-viewer::backdrop { background: #000c; backdrop-filter: blur(3px); }
425
427
  .image-viewer-shell { position: relative; display: grid; place-items: center; max-width: 94vw; max-height: 92vh; }
426
- .image-viewer img { display: block; max-width: 94vw; max-height: 92vh; border-radius: 8px; object-fit: contain; box-shadow: 0 18px 60px #000a; }
427
- .image-viewer-close { position: absolute; top: 10px; right: 10px; width: 34px; height: 34px; padding: 0; border-radius: 50%; border-color: #ffffff55; background: #101311cc; font-size: 20px; line-height: 1; }
428
+ .image-viewer img { display: block; max-width: 94vw; max-height: 92vh; border-radius: 8px; object-fit: contain; box-shadow: 0 18px 60px #000a; }
429
+ .image-viewer-original, .image-viewer-download { position: absolute; bottom: 12px; display: inline-flex; align-items: center; justify-content: center; height: 38px; border-radius: 19px; border-color: #ffffff55; background: #101311cc; color: #fff; }
430
+ .image-viewer-original { left: 12px; gap: 7px; min-width: 112px; padding: 0 14px; }
431
+ .image-viewer-download { right: 12px; width: 38px; min-width: 38px; padding: 0; }
432
+ .image-viewer-original .codex-icon, .image-viewer-download .codex-icon { width: 18px; height: 18px; }
433
+ .image-viewer-download.loading .codex-icon { display: none; }
434
+ .image-viewer-download.loading::before { content: ""; width: 15px; height: 15px; border: 2px solid #ffffff55; border-top-color: currentColor; border-radius: 50%; animation: image-viewer-spin .8s linear infinite; }
435
+ @keyframes image-viewer-spin { to { transform: rotate(360deg); } }
436
+ .image-viewer-close { position: absolute; top: 10px; right: 10px; width: 34px; height: 34px; padding: 0; border-radius: 50%; border-color: #ffffff55; background: #101311cc; font-size: 20px; line-height: 1; }
428
437
  .new-chat-entry { display:flex; flex:none; align-items:center; gap:10px; margin:8px 0; padding:10px 8px; border:0; background:transparent; color:inherit; text-align:left; }
429
438
  #newThreadWelcome[hidden], #newThreadProjectBar[hidden], #timeline[hidden], .selected-heading[hidden] { display:none; }
430
439
  .new-thread-welcome { flex:1; min-height:0; overflow:auto; display:flex; flex-direction:column; gap:24px; align-items:center; justify-content:center; padding:24px 12px; }
@@ -445,18 +454,18 @@
445
454
  section { padding: 14px; }
446
455
  .scroll-to-bottom { right: 24px; bottom: 114px; }
447
456
  }
448
- body.embedded-client { min-height:0; height:100%; background:#101311; }
449
- .embedded-client header { display:none; }
450
- .embedded-client.native-title .selected-heading { display:none; }
457
+ body.embedded-terminal { --terminal-sidebar-width:280px; min-height:0; height:100%; background:#101311; }
458
+ .embedded-terminal header { display:none; }
459
+ .terminal-chat-header { display:none; }
451
460
  #connectionStatus { flex:none; font-size:12px; color:#42d17b; }
452
461
  #connectionStatus.failed { color:#e06c64; }
453
462
  .sidebar-brand { display:none; }
454
- .embedded-client .sidebar-brand { display:flex; align-items:center; justify-content:space-between; gap:8px; padding:1px 8px 3px; font-size:18px; font-weight:600; }
455
- .embedded-client .new-chat-entry { margin:0; padding:5px 8px; font-size:12px; }
456
- .embedded-client .toolbar { min-height:20px; margin:0 4px 1px; }
457
- .embedded-client .project { margin-bottom:1px; }
458
- .embedded-client .task { margin:0; padding:4px 9px; }
459
- @media (max-width:760px) {
463
+ .embedded-terminal .sidebar-brand { display:flex; align-items:center; justify-content:space-between; gap:8px; padding:1px 8px 3px; font-size:18px; font-weight:600; }
464
+ .embedded-terminal .new-chat-entry { margin:0; padding:5px 8px; font-size:12px; }
465
+ .embedded-terminal .toolbar { min-height:20px; margin:0 4px 1px; }
466
+ .embedded-terminal .project { margin-bottom:1px; }
467
+ .embedded-terminal .task { margin:0; padding:4px 9px; }
468
+ @media (max-width:760px) {
460
469
  .composer-row { min-width:0; }
461
470
  .composer-controls { flex:0 1 auto; min-width:0; }
462
471
  .model-control { flex:0 1 auto; }
@@ -469,39 +478,44 @@
469
478
  #permissionLabel { display:none; }
470
479
  .permission-trigger { width:26px; padding:0; justify-content:center; }
471
480
  }
472
- .embedded-client .status { margin-left:0; overflow:hidden; }
473
- .embedded-client #status { overflow:hidden; text-overflow:ellipsis; }
474
- .embedded-client .project-actions { opacity:1; pointer-events:auto; }
475
- #connectionSettings, #usageButton, #returnToComputer, #debugMenu, .app-actions { display:none; }
476
- .embedded-client .app-actions { position:relative; display:flex; align-items:stretch; gap:4px; flex-shrink:0; min-width:0; }
477
- .embedded-client .app-actions > :is(#returnToComputer:not([hidden]), #debugMenu:not([hidden])) { flex:1 1 0; min-width:0; width:auto; }
478
- .embedded-client #debugMenu:not([hidden]) { position:static; display:block; }
481
+ .embedded-terminal .status { margin-left:0; overflow:hidden; }
482
+ .embedded-terminal #status { overflow:hidden; text-overflow:ellipsis; }
483
+ .embedded-terminal .project-actions { opacity:1; pointer-events:auto; }
484
+ #connectionSettings, #usageButton, #announcementButton, #communityButton, #returnToComputer, #installedAppVersion, #debugMenu, .app-actions { display:none; }
485
+ .embedded-terminal .app-actions { position:relative; display:flex; align-items:stretch; gap:4px; flex-shrink:0; min-width:0; }
486
+ .embedded-terminal .app-actions > :is(#returnToComputer:not([hidden]), #debugMenu:not([hidden])) { flex:0 0 auto; min-width:0; width:auto; }
487
+ .embedded-terminal #installedAppVersion:not([hidden]) { display:block; flex:1 1 auto; align-self:center; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
488
+ .embedded-terminal #debugMenu:not([hidden]) { position:static; display:block; }
479
489
  #debugMenu > summary { display:flex; align-items:center; gap:9px; height:100%; box-sizing:border-box; padding:5px 8px; color:var(--muted, #94a79a); font-size:12px; cursor:pointer; list-style:none; }
480
490
  #debugMenu > summary::-webkit-details-marker { display:none; }
481
491
  #debugMenu > summary > span { flex:1; text-align:right; }
482
492
  #debugMenu[open] .debug-chevron { transform:rotate(90deg); }
483
493
  .debug-menu-items { position:absolute; z-index:30; right:0; bottom:calc(100% + 4px); box-sizing:border-box; width:260px; display:grid; gap:4px; padding:8px; border:1px solid #414a44; border-radius:10px; background:#202522; box-shadow:0 12px 32px #0008; }
484
- .debug-menu-items > button { width:100%; padding:8px; border:0; background:transparent; text-align:left; }
494
+ .debug-menu-items > button { width:100%; padding:8px; border:0; background:transparent; text-align:left; }
495
+ .installed-app-version { margin:0 2px; color:var(--muted, #94a79a); font-size:9px; line-height:1.4; text-align:center; }
485
496
  #sharedRecoveryConfirm, #sharedRecoveryResult { font-size:12px; line-height:1.5; overflow-wrap:anywhere; color:var(--muted, #94a79a); }
486
497
  #sharedRecoveryConfirm button { width:100%; margin-top:6px; text-align:left; }
487
- .embedded-client aside { display:flex; flex-direction:column; overflow:hidden; padding:2px 8px 6px; font-size:12px; }
488
- .embedded-client #tasks { flex:1; min-height:0; overflow-y:auto; overflow-anchor:none; }
489
- .embedded-client :is(#connectionSettings, #usageButton, #archivedThreads:not([hidden]), #returnToComputer:not([hidden])) { display:flex; align-items:center; gap:9px; flex-shrink:0; width:100%; padding:5px 8px; border:0; border-radius:0; background:transparent; color:#9eaca3; text-align:left; font-size:12px; }
490
- .embedded-client #archivedThreads { margin-top:3px; padding-top:5px; border-top:1px solid #ffffff12; }
491
- .embedded-client #usageButton { margin-top:0; }
492
- .embedded-client #archivedThreads[hidden] + #usageButton { margin-top:3px; padding-top:5px; border-top:1px solid #ffffff12; }
498
+ .embedded-terminal aside { display:flex; flex-direction:column; overflow:hidden; padding:2px 8px 6px; font-size:12px; }
499
+ .embedded-terminal #tasks { flex:1; min-height:0; overflow-y:auto; overflow-anchor:none; }
500
+ .embedded-terminal :is(#connectionSettings, #usageButton, #announcementButton:not([hidden]), #communityButton, #archivedThreads:not([hidden]), #returnToComputer:not([hidden])) { display:flex; align-items:center; gap:9px; flex-shrink:0; width:100%; padding:5px 8px; border:0; border-radius:0; background:transparent; color:#9eaca3; text-align:left; font-size:12px; }
501
+ .embedded-terminal #archivedThreads { margin-top:3px; padding-top:5px; border-top:1px solid #ffffff12; }
502
+ .embedded-terminal #usageButton { margin-top:0; }
503
+ .embedded-terminal #archivedThreads[hidden] + #usageButton { margin-top:3px; padding-top:5px; border-top:1px solid #ffffff12; }
493
504
  .shared-service-notice { display:none; }
494
- .embedded-client .shared-service-notice:not([hidden]) { display:grid; grid-template-columns:minmax(0, 1fr) 15px; align-items:center; flex:none; gap:7px; min-width:0; margin:3px 0 2px; padding:8px; border:1px solid #d899494f; border-radius:9px; color:#e5b779; background:#d8994914; cursor:pointer; }
495
- .embedded-client .shared-service-copy { display:flex; flex-direction:column; min-width:0; gap:2px; }
496
- .embedded-client .shared-service-notice strong { font-size:12px; line-height:1.35; font-weight:650; white-space:normal; overflow-wrap:anywhere; }
497
- .embedded-client .shared-service-notice .shared-service-copy > span { color:#c8a77c; font-size:12px; line-height:1.35; white-space:normal; overflow-wrap:anywhere; }
498
- .embedded-client .shared-service-notice > .codex-icon { width:15px; height:15px; color:#c8a77c; }
505
+ .embedded-terminal .shared-service-notice:not([hidden]) { display:grid; grid-template-columns:minmax(0, 1fr) 15px; align-items:center; flex:none; gap:7px; min-width:0; margin:3px 0 2px; padding:8px; border:1px solid #d899494f; border-radius:9px; color:#e5b779; background:#d8994914; cursor:pointer; }
506
+ .embedded-terminal .shared-service-copy { display:flex; flex-direction:column; min-width:0; gap:2px; }
507
+ .embedded-terminal .shared-service-notice strong { font-size:12px; line-height:1.35; font-weight:650; white-space:normal; overflow-wrap:anywhere; }
508
+ .embedded-terminal .shared-service-notice .shared-service-copy > span { color:#c8a77c; font-size:12px; line-height:1.35; white-space:normal; overflow-wrap:anywhere; }
509
+ .embedded-terminal .shared-service-notice > .codex-icon { width:15px; height:15px; color:#c8a77c; }
499
510
  #connectionAddress { min-width:0; flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
500
511
  .connection-control-mode { font-weight:600; }
501
512
  .connection-control-mode.complete { color:#42d17b; }
502
513
  .connection-control-mode.basic { color:#dc9b57; }
503
- .embedded-client :is(#connectionSettings, #usageButton, #archivedThreads, #returnToComputer):hover { color:#e8eee9; }
514
+ .embedded-terminal :is(#connectionSettings, #usageButton, #announcementButton, #communityButton, #archivedThreads, #returnToComputer):hover { color:#e8eee9; }
504
515
  #usageButton > span { flex:1; }
516
+ #announcementTitle { min-width:0; flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
517
+ #communityButton > span { min-width:0; flex:1; }
518
+ .embedded-terminal #communityButton[aria-current="page"] { color:#e8eee9; background:#ffffff0a; }
505
519
  #usageSummary { display:flex; align-items:center; justify-content:space-between; gap:8px; min-width:0; }
506
520
  #returnToComputer { margin-top:0; }
507
521
  .usage-heading { display:flex; align-items:center; justify-content:space-between; }
@@ -544,13 +558,14 @@
544
558
  .usage-dialog-close { width:32px; height:32px; padding:0; border:0; background:transparent; font-size:20px; }
545
559
  #usageRows { display:grid; gap:9px; min-height:18px; color:#aeb9b2; font-size:12px; }
546
560
  @media (prefers-color-scheme:light) {
547
- .embedded-client :is(#connectionSettings, #usageButton, #archivedThreads, #returnToComputer) { color:#596c60; }
548
- .embedded-client #archivedThreads { border-top-color:#17212b18; }
549
- .embedded-client #archivedThreads[hidden] + #usageButton { border-top-color:#17212b18; }
550
- .embedded-client .shared-service-notice:not([hidden]) { color:#8b5318; background:#d8994914; border-color:#b66b2c45; }
551
- .embedded-client .shared-service-notice .shared-service-copy > span,
552
- .embedded-client .shared-service-notice > .codex-icon { color:#76512c; }
553
- .embedded-client :is(#connectionSettings, #usageButton, #archivedThreads, #returnToComputer):hover { color:#17212b; }
561
+ .embedded-terminal :is(#connectionSettings, #usageButton, #announcementButton, #communityButton, #archivedThreads, #returnToComputer) { color:#596c60; }
562
+ .embedded-terminal #archivedThreads { border-top-color:#17212b18; }
563
+ .embedded-terminal #archivedThreads[hidden] + #usageButton { border-top-color:#17212b18; }
564
+ .embedded-terminal .shared-service-notice:not([hidden]) { color:#8b5318; background:#d8994914; border-color:#b66b2c45; }
565
+ .embedded-terminal .shared-service-notice .shared-service-copy > span,
566
+ .embedded-terminal .shared-service-notice > .codex-icon { color:#76512c; }
567
+ .embedded-terminal :is(#connectionSettings, #usageButton, #announcementButton, #communityButton, #archivedThreads, #returnToComputer):hover { color:#17212b; }
568
+ .embedded-terminal #communityButton[aria-current="page"] { color:#17212b; background:#17212b0a; }
554
569
  .composer-add-menu { color:#34443a; background:#f5f7f5; border-color:#d5ddd8; box-shadow:0 10px 28px #17212b24; }
555
570
  .file-action-menu { color:#17212b; background:#fff; border-color:#cfd8d2; box-shadow:0 10px 28px #17212b24; }
556
571
  .file-action-name { color:#65736a; border-color:#dce3df; }
@@ -568,34 +583,42 @@
568
583
  #wideSidebarToggle, #wideSidebarExpand { display:none; }
569
584
  #wideSidebarToggle .codex-icon, #wideSidebarExpand .codex-icon { width:24px; height:24px; stroke-width:1.8; }
570
585
  #wideSidebarToggle:hover, #wideSidebarExpand:hover { background:color-mix(in srgb, currentColor 8%, transparent); }
571
- .embedded-client #voice[aria-pressed="true"] { width:26px; height:26px; margin-inline:3px; color:#ef6868; background:#ef686814; box-shadow:inset 0 0 0 1px #ef686899; }
572
- .embedded-client #voice[aria-pressed="true"] .codex-icon { width:16px; height:16px; }
586
+ .embedded-terminal #voice[aria-pressed="true"] { width:26px; height:26px; margin-inline:3px; color:#ef6868; background:#ef686814; box-shadow:inset 0 0 0 1px #ef686899; }
587
+ .embedded-terminal #voice[aria-pressed="true"] .codex-icon { width:16px; height:16px; }
573
588
  @media (max-width:760px) {
574
- .embedded-client main { position:relative; display:grid; grid-template-columns:1fr; grid-template-rows:minmax(0,1fr); }
575
- .embedded-client aside { display:none; position:absolute; inset:0 auto 0 0; width:min(90%,320px); z-index:20; box-shadow:12px 0 30px #0008; }
576
- .embedded-client.sidebar-open aside { display:flex; }
577
- .embedded-client.sidebar-open #sidebarBackdrop { display:block; position:absolute; inset:0; z-index:19; background:#0008; border:0; border-radius:0; }
578
- .embedded-client section { padding:8px 12px; }
579
- .embedded-client .selected-heading { min-width:0; }
580
- .embedded-client textarea { font-size:16px; }
581
- .embedded-client #prompt { font-size:13px; }
582
- }
583
- @media (min-width:600px) {
584
- .embedded-client #wideSidebarToggle { display:grid; place-items:center; flex:none; align-self:center; width:40px; height:40px; padding:8px; border:0; background:transparent; color:inherit; }
585
- .embedded-client.sidebar-collapsed main { grid-template-columns:minmax(0,1fr); }
586
- .embedded-client.sidebar-collapsed aside { display:none; }
587
- .embedded-client.sidebar-collapsed #wideSidebarExpand { display:grid; place-items:center; position:absolute; top:2px; left:8px; width:32px; height:28px; padding:2px; border:0; background:transparent; color:inherit; z-index:9; }
588
- .embedded-client.sidebar-collapsed .selected-heading { padding-left:36px; }
589
- .embedded-client section { padding-top:2px; }
590
- .embedded-client.native-layout .selected-heading { display:flex; }
591
- .embedded-client.native-layout #selectedTitle { flex:1; }
592
- .embedded-client main { grid-template-columns: 280px minmax(0, 1fr); grid-template-rows:minmax(0,1fr); }
593
- .embedded-client aside { display:flex; position:static; width:auto; box-shadow:none; border-right:1px solid #2c3830; border-bottom:0; }
594
- .embedded-client.sidebar-open #sidebarBackdrop { display:none; }
589
+ .embedded-terminal main { position:relative; display:grid; grid-template-columns:1fr; grid-template-rows:minmax(0,1fr); }
590
+ .embedded-terminal aside { display:none; position:absolute; inset:0 auto 0 0; width:min(90%,var(--terminal-sidebar-width)); z-index:20; box-shadow:12px 0 30px #0008; }
591
+ .embedded-terminal.sidebar-open aside { display:flex; }
592
+ .embedded-terminal.sidebar-open #sidebarBackdrop { display:block; position:absolute; inset:0; z-index:19; background:#0008; border:0; border-radius:0; }
593
+ .embedded-terminal section { padding:8px 12px; }
594
+ .embedded-terminal .selected-heading { min-width:0; }
595
+ .embedded-terminal textarea { font-size:16px; }
596
+ .embedded-terminal #prompt { font-size:13px; }
597
+ }
598
+ @media (max-width:599px) {
599
+ .embedded-terminal .terminal-chat-header { display:flex; position:absolute; z-index:9; inset:0 0 auto; height:40px; align-items:center; background:#101311; }
600
+ .embedded-terminal #terminalSidebarToggle { display:grid; place-items:center; width:48px; height:40px; flex:none; border:0; padding:8px 12px; color:inherit; background:transparent; }
601
+ .embedded-terminal #terminalSidebarToggle .codex-icon { width:28px; height:28px; stroke-width:2; }
602
+ .embedded-terminal #terminalChatTitle { position:absolute; left:56px; right:56px; overflow:hidden; font-size:15px; font-weight:700; text-align:center; text-overflow:ellipsis; white-space:nowrap; touch-action:manipulation; user-select:none; }
603
+ .embedded-terminal .selected-heading { display:none; }
604
+ .embedded-terminal section { padding-top:48px; }
605
+ .embedded-terminal.sidebar-open #wideSidebarToggle { display:grid; place-items:center; flex:none; align-self:center; width:40px; height:40px; padding:8px; border:0; background:transparent; color:inherit; }
606
+ }
607
+ @media (min-width:600px) {
608
+ .embedded-terminal #wideSidebarToggle { display:grid; place-items:center; flex:none; align-self:center; width:40px; height:40px; padding:8px; border:0; background:transparent; color:inherit; }
609
+ .embedded-terminal.sidebar-collapsed main { grid-template-columns:minmax(0,1fr); }
610
+ .embedded-terminal.sidebar-collapsed aside { display:none; }
611
+ .embedded-terminal.sidebar-collapsed #wideSidebarExpand { display:grid; place-items:center; position:absolute; top:2px; left:8px; width:32px; height:28px; padding:2px; border:0; background:transparent; color:inherit; z-index:9; }
612
+ .embedded-terminal.sidebar-collapsed .selected-heading { padding-left:36px; }
613
+ .embedded-terminal section { padding-top:2px; }
614
+ .embedded-terminal main { grid-template-columns:var(--terminal-sidebar-width) minmax(0, 1fr); grid-template-rows:minmax(0,1fr); }
615
+ .embedded-terminal aside { display:flex; position:static; width:auto; box-shadow:none; border-right:1px solid #2c3830; border-bottom:0; }
616
+ .embedded-terminal.sidebar-open #sidebarBackdrop { display:none; }
595
617
  }
596
618
  @media (prefers-color-scheme: light) {
597
- :root { color-scheme:light; background:#f5f7fa; color:#17212b; }
598
- body, body.embedded-client { background:#f5f7fa; }
619
+ :root { color-scheme:light; background:#f5f7fa; color:#17212b; }
620
+ body, body.embedded-terminal { background:#f5f7fa; }
621
+ .embedded-terminal .terminal-chat-header { background:#f5f7fa; }
599
622
  aside, .composer, .model-menu, .new-thread-project { background:#fff; border-color:#d5ddd8; color:#17212b; }
600
623
  button, select, textarea { color:#17212b; }
601
624
  button { background:#eef2ef; border-color:#ccd6ce; }
@@ -625,7 +648,7 @@
625
648
  .message-body .hljs-addition { color:#2f7444; background:#78b98a24; }
626
649
  .message-body .hljs-deletion { color:#a23d3d; background:#d77a7a24; }
627
650
  .message-body a, .process.running > summary { color:#167343; }
628
- .activity, .activity-icon, .live-step, .reasoning-history, .turn-label, .turn-duration,
651
+ .activity, .activity-icon, .live-step, .turn-label, .turn-duration,
629
652
  .process > summary, .active-process > summary, .settled-process > summary,
630
653
  .toolbar, .refresh-icon, .project-folder, .project-action, .disclosure-chevron,
631
654
  .sub, .status, #empty, .pending-settings, .menu-title, .menu-hint, .model-option small, .chevron { color:#596c60; }
@@ -663,16 +686,25 @@
663
686
  .permission-option:hover, .permission-trigger:hover, .permission-trigger[aria-expanded="true"] { background:#00000008; }
664
687
  .permission-trigger.full-access, .permission-option[data-permission="full"], .permission-option[data-permission="full"] small { color:#bd5b16; }
665
688
  .attachment { color:#44564b; border-color:#b7c9be; }
666
- .image-viewer-close { color:#fff; background:#101311cc; }
689
+ .image-viewer-close, .image-viewer-original, .image-viewer-download { color:#fff; background:#101311cc; }
667
690
  }
668
691
 
669
692
 
670
- #connectionEntry { display:none; }
671
- body.connection-entry > header, body.connection-entry > main { display:none; }
672
- body.connection-entry #connectionEntry { display:flex; flex:1; align-items:center; justify-content:center; padding:24px; overflow:auto; }
673
- .connection-entry-card { width:100%; max-width:420px; }
674
- .connection-entry-card h1 { font-size:24px; }
675
- #entryMode { opacity:.65; }
693
+ #connectionEntry { display:none; }
694
+ body.connection-entry > header, body.connection-entry > main { display:none; }
695
+ body.connection-entry #connectionEntry { display:flex; flex:1; min-height:0; overflow:hidden; }
696
+ .connection-entry-page { display:flex; flex-direction:column; width:100%; min-height:100%; }
697
+ .connection-entry-header { display:flex; flex:none; align-items:center; width:100%; height:52px; padding:0 8px; }
698
+ .connection-entry-header h1 { flex:1; margin:0; text-align:center; font-size:18px; font-weight:600; }
699
+ .connection-entry-back, .connection-entry-header-spacer { width:40px; height:40px; flex:none; }
700
+ .connection-entry-back { display:grid; place-items:center; padding:0; border:0; border-radius:10px; background:transparent; color:inherit; }
701
+ .connection-entry-back:active { background:#ffffff12; }
702
+ .connection-entry-back svg { width:22px; height:22px; fill:none; stroke:currentColor; stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round; }
703
+ .connection-entry-content { display:flex; flex:1; min-height:0; align-items:center; justify-content:center; padding:24px; overflow:auto; }
704
+ .connection-entry-card { width:100%; max-width:420px; }
705
+ .entry-connection-mode { display:flex; align-items:baseline; gap:10px; margin:0 0 20px; }
706
+ .entry-connection-mode span { font-size:12px; opacity:.58; }
707
+ .entry-connection-mode strong { font-size:16px; font-weight:600; }
676
708
  #entryStatus { white-space:pre-wrap; overflow-wrap:anywhere; }
677
709
  .connection-entry-actions { display:flex; flex-wrap:wrap; gap:10px; margin-top:24px; }
678
710
 
@@ -689,26 +721,40 @@
689
721
  .model-menu[data-loading="true"] .menu-view { visibility:hidden; }
690
722
  .model-menu[data-loading="true"]::before { content:"正在读取模型设置…"; position:absolute; inset:0; display:grid; place-items:center; font-size:13px; color:inherit; }
691
723
 
692
- .entry-brand { font-size:12px; letter-spacing:.08em; opacity:.65; }
693
- .connection-entry-card { padding:24px; border:1px solid #ffffff18; border-radius:20px; background:#ffffff05; }
694
- .connection-entry-card h1 { margin:10px 0; }
695
- .entry-steps { display:flex; gap:8px; padding:0; margin:24px 0; list-style:none; }
696
- .entry-steps[hidden] { display:none; }
697
- .entry-steps li { flex:1; padding-top:10px; border-top:3px solid #7e898544; font-size:11px; opacity:.5; }
698
- .entry-steps li[data-state="current"] { border-color:#55bf8b; opacity:1; }
699
- .entry-steps li[data-state="done"] { border-color:#55bf8b; opacity:.7; }
700
- .entry-progress { display:flex; align-items:flex-start; gap:12px; margin-top:24px; min-height:94px; }
724
+ .connection-entry-card { padding:8px 18px 24px; }
725
+ .entry-diagram { display:flex; width:100%; align-items:flex-start; margin:8px 0 28px; color:#3f5349; }
726
+ .entry-diagram-node { display:flex; flex:1; min-width:0; flex-direction:column; align-items:center; gap:4px; }
727
+ .entry-diagram-node > svg { width:28px; height:28px; fill:none; stroke:currentColor; stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; }
728
+ .entry-diagram-node > span { min-height:24px; max-width:92px; font-size:10px; line-height:12px; text-align:center; opacity:.72; }
729
+ .entry-diagram-link { flex:none; width:26px; height:10px; margin-top:8px; fill:none; stroke:currentColor; stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; }
730
+ .entry-diagram-middle > svg { display:none; }
731
+ .entry-diagram[data-mode="lan"] [data-route="lan"],
732
+ .entry-diagram[data-mode="private"] [data-route="private"],
733
+ .entry-diagram[data-mode="official"] [data-route="official"] { display:block; }
734
+ .entry-steps { display:flex; flex-direction:column; gap:0; padding:0; margin:0; list-style:none; }
735
+ .entry-steps li { position:relative; min-height:44px; padding:0 0 16px 34px; font-size:13px; line-height:20px; opacity:.46; }
736
+ .entry-steps li::before { content:attr(data-number); position:absolute; left:0; top:0; display:grid; width:22px; height:22px; place-items:center; border:1px solid #7e898566; border-radius:50%; font-size:10px; }
737
+ .entry-steps li:not(:last-child)::after { content:""; position:absolute; left:11px; top:23px; bottom:1px; width:1px; background:#7e898544; }
738
+ .entry-steps li[data-state="current"] { color:inherit; opacity:1; font-weight:600; }
739
+ .entry-steps li[data-state="current"]::before { border-color:#55bf8b; color:#55bf8b; }
740
+ .entry-steps li[data-state="done"] { opacity:.72; }
741
+ .entry-steps li[data-state="done"]::before { content:"✓"; border-color:#55bf8b; background:#55bf8b22; color:#55bf8b; }
742
+ .entry-steps li[data-state="done"]::after { background:#55bf8b88; }
743
+ .entry-progress { display:flex; align-items:flex-start; gap:12px; margin-top:8px; min-height:76px; }
701
744
  .entry-indicator { flex:none; width:18px; height:18px; margin-top:3px; border:2px solid #7e898544; border-top-color:#55bf8b; border-radius:50%; animation:entry-spin 1s linear infinite; }
702
745
  #entryStatus { margin:0 0 8px; font-size:16px; font-weight:600; }
703
746
  #entryDetail { margin:0; font-size:13px; line-height:1.7; opacity:.7; overflow-wrap:anywhere; }
704
747
  .entry-progress[data-state="error"] .entry-indicator { animation:none; border-color:#e58a78; }
705
- .connection-entry-actions button { min-height:42px; }
748
+ .connection-entry-actions button { min-height:42px; flex:1 1 160px; }
706
749
  .diagnostic-overview { font-size:17px; font-weight:600; margin-bottom:0; }
707
750
  .diagnostic-hint { opacity:.7; line-height:1.6; }
708
751
  .usage-dialog details summary { cursor:pointer; padding:10px 0; }
709
752
  @keyframes entry-spin { to { transform:rotate(360deg); } }
710
753
  @media(prefers-reduced-motion:reduce) { .entry-indicator { animation:none; } }
711
- @media(prefers-color-scheme:light) { .connection-entry-card { background:#fff; border-color:#d5ddd8; } }
754
+ @media(prefers-color-scheme:light) {
755
+ .connection-entry-back:active { background:#1f2c240d; }
756
+ .entry-diagram { color:#3f5349; }
757
+ }
712
758
 
713
759
  .approval-panel { flex: 0 1 auto; min-height: 0; max-height: 42vh; overflow-y: auto; padding: 8px 12px; border-top: 1px solid #45564b; }
714
760
  .approval-panel[hidden] { display: none; }