@nordbyte/nordrelay 0.8.1 → 0.8.3

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 (179) hide show
  1. package/.env.example +9 -0
  2. package/README.md +84 -1205
  3. package/dist/{access-control.js → access/access-control.js} +1 -1
  4. package/dist/{audit-log.js → access/audit-log.js} +32 -15
  5. package/dist/{session-locks.js → access/session-locks.js} +1 -1
  6. package/dist/{user-management.js → access/user-management.js} +1 -1
  7. package/dist/{claude-code-cli.js → agents/claude-code/claude-code-cli.js} +2 -2
  8. package/dist/{claude-code-session.js → agents/claude-code/claude-code-session.js} +1 -1
  9. package/dist/{codex-cli.js → agents/codex/codex-cli.js} +14 -5
  10. package/dist/{codex-session.js → agents/codex/codex-session.js} +2 -4
  11. package/dist/{hermes-cli.js → agents/hermes/hermes-cli.js} +2 -2
  12. package/dist/{hermes-launch.js → agents/hermes/hermes-launch.js} +1 -1
  13. package/dist/{hermes-session.js → agents/hermes/hermes-session.js} +1 -1
  14. package/dist/{openclaw-cli.js → agents/openclaw/openclaw-cli.js} +2 -2
  15. package/dist/{openclaw-launch.js → agents/openclaw/openclaw-launch.js} +1 -1
  16. package/dist/{openclaw-session.js → agents/openclaw/openclaw-session.js} +1 -1
  17. package/dist/{pi-cli.js → agents/pi/pi-cli.js} +2 -2
  18. package/dist/{pi-launch.js → agents/pi/pi-launch.js} +1 -1
  19. package/dist/{pi-session.js → agents/pi/pi-session.js} +1 -1
  20. package/dist/{adapter-conformance.js → agents/shared/adapter-conformance.js} +2 -2
  21. package/dist/{agent-activity.js → agents/shared/agent-activity.js} +5 -5
  22. package/dist/agents/shared/agent-auth-commands.js +30 -0
  23. package/dist/{agent-factory.js → agents/shared/agent-factory.js} +5 -5
  24. package/dist/{agent-feature-matrix.js → agents/shared/agent-feature-matrix.js} +2 -2
  25. package/dist/{agent-updates.js → agents/shared/agent-updates.js} +7 -7
  26. package/dist/{discord-artifacts.js → channels/discord/discord-artifacts.js} +4 -4
  27. package/dist/{discord-bot.js → channels/discord/discord-bot.js} +176 -451
  28. package/dist/{discord-channel-runtime.js → channels/discord/discord-channel-runtime.js} +2 -2
  29. package/dist/{discord-command-surface.js → channels/discord/discord-command-surface.js} +3 -3
  30. package/dist/{bot-rendering.js → channels/shared/bot-rendering.js} +6 -6
  31. package/dist/{channel-actions.js → channels/shared/channel-actions.js} +4 -4
  32. package/dist/channels/shared/channel-bridge-controller.js +69 -0
  33. package/dist/channels/shared/channel-cli-artifacts.js +51 -0
  34. package/dist/{channel-command-service.js → channels/shared/channel-command-service.js} +51 -28
  35. package/dist/channels/shared/channel-external-mirror-controller.js +193 -0
  36. package/dist/channels/shared/channel-external-monitor.js +52 -0
  37. package/dist/{channel-mirror-registry.js → channels/shared/channel-mirror-registry.js} +14 -6
  38. package/dist/{channel-peer-prompt.js → channels/shared/channel-peer-prompt.js} +3 -3
  39. package/dist/channels/shared/channel-prompt-queue.js +37 -0
  40. package/dist/{channel-turn-service.js → channels/shared/channel-turn-service.js} +25 -11
  41. package/dist/{context-key.js → channels/shared/context-key.js} +1 -1
  42. package/dist/{session-format.js → channels/shared/session-format.js} +2 -2
  43. package/dist/{slack-artifacts.js → channels/slack/slack-artifacts.js} +4 -4
  44. package/dist/{slack-bot.js → channels/slack/slack-bot.js} +171 -309
  45. package/dist/{slack-channel-runtime.js → channels/slack/slack-channel-runtime.js} +2 -2
  46. package/dist/{slack-command-surface.js → channels/slack/slack-command-surface.js} +2 -2
  47. package/dist/{slack-diagnostics.js → channels/slack/slack-diagnostics.js} +2 -2
  48. package/dist/{bot-ui.js → channels/telegram/bot-ui.js} +1 -1
  49. package/dist/{bot.js → channels/telegram/bot.js} +195 -430
  50. package/dist/{telegram-access-commands.js → channels/telegram/telegram-access-commands.js} +3 -3
  51. package/dist/{telegram-access-middleware.js → channels/telegram/telegram-access-middleware.js} +4 -4
  52. package/dist/{telegram-agent-commands.js → channels/telegram/telegram-agent-commands.js} +9 -9
  53. package/dist/{telegram-artifact-commands.js → channels/telegram/telegram-artifact-commands.js} +4 -4
  54. package/dist/{telegram-channel-runtime.js → channels/telegram/telegram-channel-runtime.js} +2 -2
  55. package/dist/{telegram-command-menu.js → channels/telegram/telegram-command-menu.js} +1 -1
  56. package/dist/{telegram-diagnostics-command.js → channels/telegram/telegram-diagnostics-command.js} +7 -7
  57. package/dist/{telegram-general-commands.js → channels/telegram/telegram-general-commands.js} +4 -4
  58. package/dist/{telegram-operational-commands.js → channels/telegram/telegram-operational-commands.js} +5 -5
  59. package/dist/{telegram-output.js → channels/telegram/telegram-output.js} +2 -2
  60. package/dist/{telegram-preference-commands.js → channels/telegram/telegram-preference-commands.js} +3 -3
  61. package/dist/{telegram-queue-commands.js → channels/telegram/telegram-queue-commands.js} +6 -6
  62. package/dist/{telegram-support-command.js → channels/telegram/telegram-support-command.js} +4 -4
  63. package/dist/{telegram-update-commands.js → channels/telegram/telegram-update-commands.js} +5 -5
  64. package/dist/{config-metadata.js → core/config-metadata.js} +8 -0
  65. package/dist/{config.js → core/config.js} +11 -3
  66. package/dist/core/pagination.js +22 -0
  67. package/dist/index.js +27 -23
  68. package/dist/peers/peer-discovery-jobs.js +206 -0
  69. package/dist/peers/peer-discovery.js +223 -0
  70. package/dist/peers/peer-health-monitor.js +49 -0
  71. package/dist/{peer-identity.js → peers/peer-identity.js} +50 -1
  72. package/dist/{peer-runtime-service.js → peers/peer-runtime-service.js} +29 -7
  73. package/dist/{peer-server.js → peers/peer-server.js} +3 -2
  74. package/dist/{peer-store.js → peers/peer-store.js} +96 -9
  75. package/dist/{peer-types.js → peers/peer-types.js} +28 -0
  76. package/dist/peers/peer-web-proxy-contract.js +129 -0
  77. package/dist/{metrics.js → runtime/metrics.js} +5 -3
  78. package/dist/{relay-artifact-service.js → runtime/relay-artifact-service.js} +1 -1
  79. package/dist/runtime/relay-auth-service.js +63 -0
  80. package/dist/runtime/relay-dashboard-service.js +139 -0
  81. package/dist/{relay-external-activity-monitor.js → runtime/relay-external-activity-monitor.js} +155 -53
  82. package/dist/{relay-queue-service.js → runtime/relay-queue-service.js} +1 -0
  83. package/dist/runtime/relay-runtime-active-sessions.js +387 -0
  84. package/dist/runtime/relay-runtime-dashboard.js +204 -0
  85. package/dist/{relay-runtime-helpers.js → runtime/relay-runtime-helpers.js} +3 -0
  86. package/dist/runtime/relay-runtime-prompt-queue-artifacts.js +311 -0
  87. package/dist/runtime/relay-runtime-sessions.js +631 -0
  88. package/dist/runtime/relay-runtime-trace.js +92 -0
  89. package/dist/runtime/relay-runtime-types.js +1 -0
  90. package/dist/runtime/relay-runtime-updates-jobs.js +366 -0
  91. package/dist/runtime/relay-runtime.js +461 -0
  92. package/dist/runtime/runtime-cache.js +117 -0
  93. package/dist/{prompt-store.js → state/prompt-store.js} +13 -1
  94. package/dist/{session-registry.js → state/session-registry.js} +3 -3
  95. package/dist/{operations.js → support/operations.js} +7 -7
  96. package/dist/{support-bundle.js → support/support-bundle.js} +1 -1
  97. package/dist/{web-api-contract.js → web/web-api-contract.js} +19 -3
  98. package/dist/web/web-api-types.js +1 -0
  99. package/dist/{web-dashboard-access-routes.js → web/web-dashboard-access-routes.js} +17 -14
  100. package/dist/{web-dashboard-artifact-routes.js → web/web-dashboard-artifact-routes.js} +6 -2
  101. package/dist/{web-dashboard-assets.js → web/web-dashboard-assets.js} +25 -2
  102. package/dist/{web-dashboard-http.js → web/web-dashboard-http.js} +41 -5
  103. package/dist/{web-dashboard-pages.js → web/web-dashboard-pages.js} +95 -30
  104. package/dist/{web-dashboard-peer-routes.js → web/web-dashboard-peer-routes.js} +121 -7
  105. package/dist/{web-dashboard-runtime-routes.js → web/web-dashboard-runtime-routes.js} +8 -1
  106. package/dist/web/web-dashboard-security.js +14 -0
  107. package/dist/{web-dashboard-session-routes.js → web/web-dashboard-session-routes.js} +29 -13
  108. package/dist/web/web-dashboard-ui.js +56 -0
  109. package/dist/{web-dashboard.js → web/web-dashboard.js} +132 -48
  110. package/dist/web/web-performance.js +62 -0
  111. package/dist/web/web-rate-limit.js +19 -0
  112. package/dist/{web-state.js → web/web-state.js} +107 -9
  113. package/dist/webui-assets/dashboard.css +398 -49
  114. package/dist/webui-assets/dashboard.js +1239 -103
  115. package/dist/webui-assets/favicon.ico +0 -0
  116. package/dist/webui-assets/favicon.png +0 -0
  117. package/dist/webui-assets/logo.png +0 -0
  118. package/package.json +6 -3
  119. package/plugins/nordrelay/scripts/nordrelay.mjs +346 -12
  120. package/plugins/nordrelay/scripts/service-installer.mjs +183 -0
  121. package/{launchd/start.sh → scripts/launchd-start.sh} +1 -1
  122. package/scripts/postinstall.mjs +122 -0
  123. package/dist/relay-runtime.js +0 -1916
  124. package/dist/runtime-cache.js +0 -57
  125. package/dist/web-dashboard-ui.js +0 -20
  126. /package/dist/{user-management-crypto.js → access/user-management-crypto.js} +0 -0
  127. /package/dist/{user-management-normalize.js → access/user-management-normalize.js} +0 -0
  128. /package/dist/{user-management-types.js → access/user-management-types.js} +0 -0
  129. /package/dist/{claude-code-auth.js → agents/claude-code/claude-code-auth.js} +0 -0
  130. /package/dist/{claude-code-launch.js → agents/claude-code/claude-code-launch.js} +0 -0
  131. /package/dist/{claude-code-state.js → agents/claude-code/claude-code-state.js} +0 -0
  132. /package/dist/{codex-auth.js → agents/codex/codex-auth.js} +0 -0
  133. /package/dist/{codex-config.js → agents/codex/codex-config.js} +0 -0
  134. /package/dist/{codex-launch.js → agents/codex/codex-launch.js} +0 -0
  135. /package/dist/{codex-state.js → agents/codex/codex-state.js} +0 -0
  136. /package/dist/{hermes-api.js → agents/hermes/hermes-api.js} +0 -0
  137. /package/dist/{hermes-auth.js → agents/hermes/hermes-auth.js} +0 -0
  138. /package/dist/{hermes-state.js → agents/hermes/hermes-state.js} +0 -0
  139. /package/dist/{openclaw-auth.js → agents/openclaw/openclaw-auth.js} +0 -0
  140. /package/dist/{openclaw-gateway.js → agents/openclaw/openclaw-gateway.js} +0 -0
  141. /package/dist/{openclaw-state.js → agents/openclaw/openclaw-state.js} +0 -0
  142. /package/dist/{pi-auth.js → agents/pi/pi-auth.js} +0 -0
  143. /package/dist/{pi-rpc.js → agents/pi/pi-rpc.js} +0 -0
  144. /package/dist/{pi-state.js → agents/pi/pi-state.js} +0 -0
  145. /package/dist/{agent-adapter.js → agents/shared/agent-adapter.js} +0 -0
  146. /package/dist/{agent.js → agents/shared/agent.js} +0 -0
  147. /package/dist/{artifacts.js → artifacts/artifacts.js} +0 -0
  148. /package/dist/{attachments.js → artifacts/attachments.js} +0 -0
  149. /package/dist/{voice.js → artifacts/voice.js} +0 -0
  150. /package/dist/{discord-rate-limit.js → channels/discord/discord-rate-limit.js} +0 -0
  151. /package/dist/{channel-adapter.js → channels/shared/channel-adapter.js} +0 -0
  152. /package/dist/{relay-runtime-types.js → channels/shared/channel-bridge-state.js} +0 -0
  153. /package/dist/{channel-command-catalog.js → channels/shared/channel-command-catalog.js} +0 -0
  154. /package/dist/{channel-command-core.js → channels/shared/channel-command-core.js} +0 -0
  155. /package/dist/{channel-prompt-engine.js → channels/shared/channel-prompt-engine.js} +0 -0
  156. /package/dist/{channel-runtime.js → channels/shared/channel-runtime.js} +0 -0
  157. /package/dist/{channel-turn-lifecycle.js → channels/shared/channel-turn-lifecycle.js} +0 -0
  158. /package/dist/{slack-rate-limit.js → channels/slack/slack-rate-limit.js} +0 -0
  159. /package/dist/{telegram-command-types.js → channels/telegram/telegram-command-types.js} +0 -0
  160. /package/dist/{telegram-rate-limit.js → channels/telegram/telegram-rate-limit.js} +0 -0
  161. /package/dist/{activity-events.js → core/activity-events.js} +0 -0
  162. /package/dist/{error-messages.js → core/error-messages.js} +0 -0
  163. /package/dist/{format.js → core/format.js} +0 -0
  164. /package/dist/{logger.js → core/logger.js} +0 -0
  165. /package/dist/{redaction.js → core/redaction.js} +0 -0
  166. /package/dist/{settings-service.js → core/settings-service.js} +0 -0
  167. /package/dist/{settings-wizard-test.js → core/settings-wizard-test.js} +0 -0
  168. /package/dist/{workspace-policy.js → core/workspace-policy.js} +0 -0
  169. /package/dist/{peer-auth.js → peers/peer-auth.js} +0 -0
  170. /package/dist/{peer-client.js → peers/peer-client.js} +0 -0
  171. /package/dist/{peer-context.js → peers/peer-context.js} +0 -0
  172. /package/dist/{peer-readiness.js → peers/peer-readiness.js} +0 -0
  173. /package/dist/{web-api-types.js → runtime/relay-runtime-delegate.js} +0 -0
  174. /package/dist/{remote-prompt.js → runtime/remote-prompt.js} +0 -0
  175. /package/dist/{bot-preferences.js → state/bot-preferences.js} +0 -0
  176. /package/dist/{job-store.js → state/job-store.js} +0 -0
  177. /package/dist/{persistence.js → state/persistence.js} +0 -0
  178. /package/dist/{state-backend.js → state/state-backend.js} +0 -0
  179. /package/dist/{zip-writer.js → support/zip-writer.js} +0 -0
@@ -8,6 +8,7 @@
8
8
  --border:#dce3d9;
9
9
  --border-soft:#e7ede4;
10
10
  --sidebar:#17251d;
11
+ --sidebar-border:#2a3a30;
11
12
  --sidebar-text:#f4f8f2;
12
13
  --sidebar-muted:#aebcaf;
13
14
  --accent:#235c42;
@@ -30,6 +31,7 @@
30
31
  --border:#2d3830;
31
32
  --border-soft:#263128;
32
33
  --sidebar:#0c120f;
34
+ --sidebar-border:#1a271f;
33
35
  --sidebar-text:#edf7ef;
34
36
  --sidebar-muted:#8da091;
35
37
  --accent:#4fa876;
@@ -42,48 +44,68 @@
42
44
  --shadow:0 10px 28px rgba(0,0,0,.22);
43
45
  --link:#75c99a;
44
46
  }
45
- .agent-settings-nav {
47
+ .access-tab {
48
+ display: none;
49
+ }
50
+ .access-tab.active {
51
+ display: block;
52
+ }
53
+ .section-header {
46
54
  display: flex;
47
- align-items: center;
48
- gap: 8px;
49
- flex-wrap: wrap;
50
- margin: 0 0 12px;
51
- padding: 10px;
52
- border: 1px solid var(--border-soft);
53
- border-radius: 8px;
54
- background: var(--surface);
55
+ align-items: flex-end;
56
+ justify-content: space-between;
57
+ gap: 16px;
58
+ margin: -12px -16px 0;
59
+ padding: 0 16px;
55
60
  }
56
- .agent-settings-nav strong {
57
- font-size: 13px;
58
- color: var(--muted);
59
- margin-right: 4px;
61
+ .access-section-header,
62
+ .settings-section-header {
63
+ margin-bottom: 12px;
60
64
  }
61
- .agent-settings-nav button {
62
- background: var(--surface);
63
- color: var(--text);
64
- border-color: var(--border);
65
- min-height: 32px;
66
- height: 32px;
67
- line-height: 1;
65
+ .section-tabs {
66
+ display: flex;
67
+ align-items: flex-end;
68
+ gap: 2px;
69
+ width: 100%;
70
+ min-width: 0;
71
+ min-height: 43px;
72
+ overflow-x: auto;
73
+ overflow-y: hidden;
74
+ border-bottom: 1px solid var(--border);
68
75
  }
69
- .agent-settings-nav button.active {
70
- background: var(--accent);
71
- color: white;
72
- border-color: var(--accent);
76
+ .section-tabs button {
77
+ appearance: none;
78
+ -webkit-appearance: none;
79
+ display: inline-flex;
80
+ align-items: center;
81
+ justify-content: center;
82
+ min-height: 43px;
83
+ height: 43px;
84
+ margin: 0;
85
+ padding: 0 14px;
86
+ border: 0;
87
+ border-radius: 0;
88
+ background: transparent;
89
+ color: var(--muted);
90
+ font-weight: 650;
91
+ line-height: 1;
92
+ white-space: nowrap;
93
+ cursor: pointer;
94
+ box-shadow: inset 0 -2px 0 transparent;
73
95
  }
74
- @media (max-width: 560px) {
75
- .agent-settings-nav {
76
- align-items: stretch;
77
- }
78
- .agent-settings-nav button {
79
- width: 100%;
80
- }
96
+ .section-tabs button:hover {
97
+ background: color-mix(in srgb, var(--accent) 8%, transparent);
98
+ color: var(--text);
81
99
  }
82
- .access-tab {
83
- display: none;
100
+ .section-tabs button.active,
101
+ .section-tabs button[aria-selected=true] {
102
+ box-shadow: inset 0 -2px 0 var(--accent);
103
+ background: transparent;
104
+ color: var(--text);
84
105
  }
85
- .access-tab.active {
86
- display: block;
106
+ .section-tabs button:focus-visible {
107
+ outline: 2px solid var(--accent);
108
+ outline-offset: -3px;
87
109
  }
88
110
  .access-tab-heading {
89
111
  display: flex;
@@ -92,24 +114,130 @@
92
114
  gap: 12px;
93
115
  margin: 0 0 10px;
94
116
  }
95
- .access-tab-heading h2 {
96
- margin: 0;
97
- }
98
117
  .access-tab-heading input {
99
118
  max-width: 320px;
100
119
  }
120
+ .settings-subnav {
121
+ display: flex;
122
+ align-items: center;
123
+ gap: 10px;
124
+ margin: 0 0 14px;
125
+ }
126
+ .settings-subnav[hidden] {
127
+ display: none;
128
+ }
129
+ .settings-subnav label {
130
+ display: inline-flex;
131
+ align-items: center;
132
+ gap: 8px;
133
+ color: var(--muted);
134
+ font-size: 13px;
135
+ line-height: 1;
136
+ }
137
+ .settings-subnav select {
138
+ min-width: 220px;
139
+ }
140
+ .settings-actions {
141
+ margin: 0 0 14px;
142
+ }
143
+ .access-filter-row {
144
+ display: flex;
145
+ align-items: center;
146
+ gap: 8px;
147
+ flex-wrap: wrap;
148
+ }
149
+ .access-filter-row input,
150
+ .access-filter-row select {
151
+ min-width: 170px;
152
+ }
101
153
  .access-id-row {
102
154
  display: flex;
103
155
  align-items: center;
104
156
  gap: 8px;
105
157
  flex-wrap: wrap;
106
158
  }
159
+ .user-list {
160
+ gap: 10px;
161
+ }
162
+ .user-card-main {
163
+ display: grid;
164
+ grid-template-columns: minmax(0, 1fr) auto;
165
+ gap: 12px;
166
+ align-items: start;
167
+ }
168
+ .user-card-meta {
169
+ display: flex;
170
+ align-items: center;
171
+ justify-content: flex-end;
172
+ gap: 6px;
173
+ flex-wrap: wrap;
174
+ max-width: 460px;
175
+ }
176
+ .identity-actions {
177
+ margin-bottom: 10px;
178
+ }
179
+ .user-detail-tabs {
180
+ margin: 14px 0 12px;
181
+ }
182
+ .user-detail-panel {
183
+ display: none;
184
+ }
185
+ .user-detail-panel.active {
186
+ display: block;
187
+ }
188
+ .access-effective-grid {
189
+ display: grid;
190
+ grid-template-columns: repeat(2, minmax(0, 1fr));
191
+ gap: 12px;
192
+ }
193
+ .permission-category-grid {
194
+ display: grid;
195
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
196
+ gap: 10px;
197
+ }
198
+ .permission-section {
199
+ min-width: 0;
200
+ margin: 0;
201
+ border: 1px solid var(--border-soft);
202
+ border-radius: 8px;
203
+ padding: 10px;
204
+ background: var(--surface-soft);
205
+ }
206
+ .permission-section legend {
207
+ font-size: 12px;
208
+ font-weight: 700;
209
+ color: var(--muted);
210
+ padding: 0 4px;
211
+ }
212
+ .permission-section label {
213
+ display: flex !important;
214
+ margin: 5px 0;
215
+ }
216
+ .scope-section {
217
+ display: grid;
218
+ gap: 6px;
219
+ }
220
+ .scope-section small {
221
+ color: var(--muted);
222
+ }
223
+ @media (max-width: 760px) {
224
+ .user-card-main,
225
+ .access-effective-grid {
226
+ grid-template-columns: 1fr;
227
+ }
228
+ .user-card-meta {
229
+ justify-content: flex-start;
230
+ max-width: none;
231
+ }
232
+ }
107
233
  @media (max-width: 560px) {
108
234
  .access-tab-heading {
109
235
  align-items: stretch;
110
236
  flex-direction: column;
111
237
  }
112
- .access-tab-heading input {
238
+ .access-tab-heading input,
239
+ .access-filter-row input,
240
+ .access-filter-row select {
113
241
  max-width: none;
114
242
  width: 100%;
115
243
  }
@@ -393,6 +521,10 @@
393
521
  .log-line {
394
522
  display: block;
395
523
  }
524
+ .log-line.INFO {
525
+ color: var(--pre-text);
526
+ font-weight: 400;
527
+ }
396
528
  .log-line.ERROR {
397
529
  color: var(--danger);
398
530
  font-weight: 700;
@@ -521,6 +653,21 @@
521
653
  border-color: #d9c27a;
522
654
  background: var(--warn);
523
655
  }
656
+ .peer-health-history {
657
+ margin: 8px 0;
658
+ padding: 8px;
659
+ border: 1px solid var(--border-soft);
660
+ border-radius: 8px;
661
+ background: var(--surface);
662
+ }
663
+ .peer-health-history summary {
664
+ cursor: pointer;
665
+ font-weight: 700;
666
+ color: var(--muted);
667
+ }
668
+ :root {
669
+ --dashboard-header-height:93px;
670
+ }
524
671
  * {
525
672
  box-sizing: border-box;
526
673
  }
@@ -551,36 +698,48 @@ body {
551
698
  grid-template-columns: 260px 1fr;
552
699
  }
553
700
  .sidebar {
701
+ position: sticky;
702
+ top: 0;
703
+ height: 100vh;
554
704
  background: var(--sidebar);
555
705
  color: var(--sidebar-text);
556
- padding: 18px;
557
706
  display: flex;
558
707
  flex-direction: column;
559
- gap: 22px;
708
+ gap: 0;
709
+ overflow: hidden;
560
710
  }
561
711
  .brand {
562
712
  display: flex;
563
713
  align-items: center;
564
714
  gap: 12px;
715
+ height: calc(var(--dashboard-header-height) - 1px);
716
+ padding: 0 18px;
565
717
  }
566
- .mark {
567
- display: grid;
568
- place-items: center;
569
- width: 38px;
570
- height: 38px;
718
+ .brand-mark {
719
+ display: block;
720
+ width: 44px;
721
+ height: 44px;
571
722
  border-radius: 8px;
572
- background: #d7ffe5;
573
- color: #173d29;
574
- font-weight: 800;
723
+ object-fit: cover;
724
+ flex: 0 0 44px;
575
725
  }
576
726
  .brand small {
577
727
  display: block;
578
728
  color: var(--sidebar-muted);
579
729
  }
730
+ .brand-separator {
731
+ height: 1px;
732
+ background: var(--sidebar-border);
733
+ flex: 0 0 1px;
734
+ }
580
735
  nav {
581
736
  display: flex;
737
+ flex: 1;
738
+ min-height: 0;
582
739
  flex-direction: column;
583
740
  gap: 6px;
741
+ overflow: auto;
742
+ padding: 22px 18px 18px;
584
743
  }
585
744
  nav button,
586
745
  .menu {
@@ -597,6 +756,62 @@ nav button.active,
597
756
  nav button:hover {
598
757
  background: color-mix(in srgb, var(--accent) 35%, transparent);
599
758
  }
759
+ .nav-section {
760
+ display: flex;
761
+ flex-direction: column;
762
+ gap: 4px;
763
+ }
764
+ .nav-section[hidden],
765
+ .nav-section-items[hidden] {
766
+ display: none;
767
+ }
768
+ .nav-primary {
769
+ display: flex;
770
+ flex-direction: column;
771
+ gap: 4px;
772
+ }
773
+ .nav-primary + .nav-section,
774
+ .nav-section + .nav-section {
775
+ margin-top: 8px;
776
+ padding-top: 10px;
777
+ border-top: 1px solid color-mix(in srgb, var(--sidebar-border) 72%, transparent);
778
+ }
779
+ .nav-section-items {
780
+ display: flex;
781
+ flex-direction: column;
782
+ gap: 4px;
783
+ }
784
+ .nav-section-toggle {
785
+ min-height: 30px !important;
786
+ height: 30px !important;
787
+ padding: 0 8px !important;
788
+ color: var(--sidebar-muted);
789
+ font-size: 11px;
790
+ font-weight: 760;
791
+ line-height: 1;
792
+ text-transform: uppercase;
793
+ letter-spacing: .04em;
794
+ justify-content: space-between !important;
795
+ }
796
+ .nav-section-toggle::after {
797
+ content: "";
798
+ width: 7px;
799
+ height: 7px;
800
+ border-right: 1.5px solid currentColor;
801
+ border-bottom: 1.5px solid currentColor;
802
+ transform: rotate(45deg) translateY(-1px);
803
+ opacity: .8;
804
+ }
805
+ .nav-section[data-nav-open=true] .nav-section-toggle::after {
806
+ transform: rotate(225deg) translateY(-1px);
807
+ }
808
+ .nav-section-toggle.active {
809
+ color: var(--sidebar-text);
810
+ }
811
+ .nav-section-toggle.active,
812
+ .nav-section-toggle:hover {
813
+ background: color-mix(in srgb, var(--accent) 24%, transparent);
814
+ }
600
815
  main {
601
816
  min-width: 0;
602
817
  display: flex;
@@ -610,6 +825,7 @@ header {
610
825
  justify-content: space-between;
611
826
  gap: 16px;
612
827
  align-items: center;
828
+ min-height: var(--dashboard-header-height);
613
829
  padding: 16px 22px;
614
830
  background: color-mix(in srgb, var(--surface) 92%, transparent);
615
831
  backdrop-filter: blur(12px);
@@ -639,6 +855,17 @@ a {
639
855
  align-items: center;
640
856
  flex-wrap: wrap;
641
857
  }
858
+ .mirror-control {
859
+ display: inline-flex;
860
+ align-items: center;
861
+ gap: 6px;
862
+ color: var(--muted);
863
+ font-size: 13px;
864
+ line-height: 1;
865
+ }
866
+ .mirror-control select {
867
+ min-width: 86px;
868
+ }
642
869
  .menu {
643
870
  display: none;
644
871
  background: var(--surface-soft);
@@ -760,6 +987,12 @@ nav button {
760
987
  gap: 16px;
761
988
  align-items: start;
762
989
  }
990
+ .chat-layout.tools-hidden {
991
+ grid-template-columns: minmax(0, 1fr);
992
+ }
993
+ .chat-layout.tools-hidden .side-panel {
994
+ display: none;
995
+ }
763
996
  .chat-panel {
764
997
  height: calc(100vh - 170px);
765
998
  min-height: 520px;
@@ -806,6 +1039,120 @@ nav button {
806
1039
  .message.system {
807
1040
  background: var(--warn);
808
1041
  }
1042
+ .message-body {
1043
+ white-space: pre-wrap;
1044
+ }
1045
+ .message .chat-inline-code {
1046
+ appearance: none;
1047
+ -webkit-appearance: none;
1048
+ display: inline;
1049
+ min-height: 0;
1050
+ height: auto;
1051
+ margin: 0;
1052
+ padding: 0;
1053
+ border: 0;
1054
+ background: transparent;
1055
+ color: var(--link);
1056
+ font-family:
1057
+ ui-monospace,
1058
+ SFMono-Regular,
1059
+ Menlo,
1060
+ Consolas,
1061
+ monospace;
1062
+ font-size: 1em;
1063
+ line-height: inherit;
1064
+ white-space: normal;
1065
+ vertical-align: baseline;
1066
+ cursor: pointer;
1067
+ }
1068
+ .message .chat-inline-code:hover,
1069
+ .message .chat-inline-code:focus {
1070
+ background: transparent;
1071
+ color: var(--link);
1072
+ text-decoration: underline;
1073
+ outline: none;
1074
+ }
1075
+ .message .chat-code-block {
1076
+ position: relative;
1077
+ margin: 8px 0;
1078
+ padding: 10px;
1079
+ border: 1px solid var(--border);
1080
+ border-radius: 8px;
1081
+ background: var(--pre);
1082
+ color: var(--pre-text);
1083
+ font-family:
1084
+ ui-monospace,
1085
+ SFMono-Regular,
1086
+ Menlo,
1087
+ Consolas,
1088
+ monospace;
1089
+ font-size: 13px;
1090
+ line-height: 1.45;
1091
+ white-space: pre;
1092
+ overflow: auto;
1093
+ cursor: pointer;
1094
+ }
1095
+ .message .chat-code-block:hover,
1096
+ .message .chat-code-block:focus {
1097
+ border-color: var(--accent);
1098
+ outline: none;
1099
+ }
1100
+ .message .chat-code-block::after {
1101
+ content: "copy";
1102
+ position: absolute;
1103
+ top: 6px;
1104
+ right: 8px;
1105
+ padding: 1px 5px;
1106
+ border-radius: 4px;
1107
+ background: color-mix(in srgb, var(--surface) 12%, transparent);
1108
+ color: var(--pre-text);
1109
+ font-family:
1110
+ Inter,
1111
+ "Segoe UI",
1112
+ system-ui,
1113
+ sans-serif;
1114
+ font-size: 11px;
1115
+ text-transform: uppercase;
1116
+ }
1117
+ .message .chat-link {
1118
+ color: var(--link);
1119
+ text-decoration: underline;
1120
+ text-underline-offset: 2px;
1121
+ }
1122
+ .message .chat-blockquote {
1123
+ margin: 6px 0;
1124
+ padding: 4px 0 4px 10px;
1125
+ border-left: 3px solid var(--border);
1126
+ color: var(--muted);
1127
+ }
1128
+ .message .chat-list {
1129
+ margin: 6px 0;
1130
+ padding-left: 22px;
1131
+ white-space: normal;
1132
+ }
1133
+ .message .chat-list li {
1134
+ margin: 3px 0;
1135
+ white-space: pre-wrap;
1136
+ }
1137
+ .message .chat-task-list {
1138
+ list-style: none;
1139
+ padding-left: 10px;
1140
+ }
1141
+ .message .chat-task-mark {
1142
+ font-family:
1143
+ ui-monospace,
1144
+ SFMono-Regular,
1145
+ Menlo,
1146
+ Consolas,
1147
+ monospace;
1148
+ color: var(--muted);
1149
+ }
1150
+ .message .chat-heading {
1151
+ display: block;
1152
+ margin: 6px 0 2px;
1153
+ font-size: 1.05em;
1154
+ font-weight: 750;
1155
+ }
809
1156
  .composer {
810
1157
  display: grid;
811
1158
  grid-template-columns: 1fr auto;
@@ -912,6 +1259,8 @@ input[type=file] {
912
1259
  border-radius: 8px;
913
1260
  padding: 12px;
914
1261
  background: var(--surface-soft);
1262
+ content-visibility: auto;
1263
+ contain-intrinsic-size: 96px;
915
1264
  }
916
1265
  .item strong {
917
1266
  display: block;