codexmate 0.0.28 → 0.0.30

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 (50) hide show
  1. package/cli/builtin-proxy.js +107 -2
  2. package/cli/config-bootstrap.js +30 -12
  3. package/cli/config-health.js +117 -1
  4. package/cli/local-bridge.js +324 -0
  5. package/cli/openai-bridge.js +195 -31
  6. package/cli.js +245 -28
  7. package/lib/cli-webhook.js +126 -0
  8. package/package.json +1 -1
  9. package/web-ui/app.js +28 -8
  10. package/web-ui/index.html +1 -0
  11. package/web-ui/logic.codex.mjs +13 -0
  12. package/web-ui/modules/app.computed.dashboard.mjs +25 -2
  13. package/web-ui/modules/app.computed.session.mjs +22 -17
  14. package/web-ui/modules/app.methods.claude-config.mjs +12 -2
  15. package/web-ui/modules/app.methods.codex-config.mjs +25 -0
  16. package/web-ui/modules/app.methods.index.mjs +2 -0
  17. package/web-ui/modules/app.methods.navigation.mjs +39 -8
  18. package/web-ui/modules/app.methods.providers.mjs +125 -8
  19. package/web-ui/modules/app.methods.session-actions.mjs +1 -1
  20. package/web-ui/modules/app.methods.session-browser.mjs +1 -1
  21. package/web-ui/modules/app.methods.session-trash.mjs +3 -4
  22. package/web-ui/modules/app.methods.startup-claude.mjs +1 -0
  23. package/web-ui/modules/app.methods.webhook.mjs +79 -0
  24. package/web-ui/modules/i18n.dict.mjs +1109 -72
  25. package/web-ui/modules/i18n.mjs +9 -3
  26. package/web-ui/modules/skills.methods.mjs +1 -0
  27. package/web-ui/partials/index/layout-header.html +25 -0
  28. package/web-ui/partials/index/modals-basic.html +0 -3
  29. package/web-ui/partials/index/panel-config-claude.html +8 -2
  30. package/web-ui/partials/index/panel-config-codex.html +28 -3
  31. package/web-ui/partials/index/panel-dashboard.html +33 -0
  32. package/web-ui/partials/index/panel-market.html +3 -3
  33. package/web-ui/partials/index/panel-plugins.html +2 -2
  34. package/web-ui/partials/index/panel-sessions.html +1 -9
  35. package/web-ui/partials/index/panel-settings.html +71 -134
  36. package/web-ui/partials/index/panel-trash.html +88 -0
  37. package/web-ui/session-helpers.mjs +20 -2
  38. package/web-ui/styles/dashboard.css +132 -0
  39. package/web-ui/styles/docs-panel.css +63 -39
  40. package/web-ui/styles/layout-shell.css +54 -34
  41. package/web-ui/styles/plugins-panel.css +121 -80
  42. package/web-ui/styles/sessions-list.css +41 -43
  43. package/web-ui/styles/sessions-preview.css +34 -38
  44. package/web-ui/styles/sessions-toolbar-trash.css +31 -27
  45. package/web-ui/styles/settings-panel.css +197 -33
  46. package/web-ui/styles/skills-list.css +12 -10
  47. package/web-ui/styles/skills-market.css +67 -44
  48. package/web-ui/styles/trash-panel.css +90 -0
  49. package/web-ui/styles/webhook.css +81 -0
  50. package/web-ui/styles.css +2 -0
@@ -0,0 +1,88 @@
1
+  <!-- 回收站面板 — 极简·本质 -->
2
+ <div
3
+ v-show="mainTab === 'trash'"
4
+ class="mode-content"
5
+ id="panel-trash"
6
+ role="tabpanel"
7
+ aria-labelledby="tab-trash">
8
+ <div v-if="!loading" class="trash-panel-shell">
9
+ <!-- 空态 — 宁静的留白 -->
10
+ <div v-if="getSessionTrashViewState() === 'empty'" class="trash-empty-state">
11
+ <div class="trash-empty-icon">🗑</div>
12
+ <div class="trash-empty-title">{{ t('settings.trash.empty') }}</div>
13
+ <div class="trash-empty-hint">删除的会话将出现在这里,保留 {{ sessionTrashRetentionDays }} 天后自动清理</div>
14
+ </div>
15
+
16
+ <!-- 加载态 -->
17
+ <div v-else-if="getSessionTrashViewState() === 'loading'" class="trash-empty-state">
18
+ <div class="trash-empty-icon">⟳</div>
19
+ <div class="trash-empty-title">{{ t('settings.trash.loading') }}</div>
20
+ </div>
21
+
22
+ <!-- 错误态 -->
23
+ <div v-else-if="getSessionTrashViewState() === 'retry'" class="trash-empty-state">
24
+ <div class="trash-empty-icon">⚠</div>
25
+ <div class="trash-empty-title">{{ t('settings.trash.retry') }}</div>
26
+ <button class="btn-tool" @click="loadSessionTrash({ forceRefresh: true })">重试</button>
27
+ </div>
28
+
29
+ <!-- 列表态 -->
30
+ <template v-else>
31
+ <div class="trash-toolbar">
32
+ <div class="trash-toolbar-left">
33
+ <span class="trash-toolbar-count">{{ sessionTrashCount }} 个已删除会话</span>
34
+ <span class="trash-toolbar-retention">· {{ sessionTrashRetentionDays }} 天后自动清理</span>
35
+ </div>
36
+ <div class="trash-toolbar-right">
37
+ <button class="btn-mini" @click="loadSessionTrash({ forceRefresh: true })" :disabled="sessionTrashLoading">
38
+ {{ sessionTrashLoading ? '刷新中…' : '刷新' }}
39
+ </button>
40
+ <button class="btn-mini delete" @click="clearSessionTrash" :disabled="sessionTrashClearing || sessionTrashLoading || !(Number(sessionTrashCount) > 0)">
41
+ {{ sessionTrashClearing ? '清空中…' : '清空回收站' }}
42
+ </button>
43
+ </div>
44
+ </div>
45
+
46
+ <div class="trash-list">
47
+ <div v-for="item in visibleSessionTrashItems" :key="item.trashId" class="trash-item session-item session-card">
48
+ <div class="trash-item-header session-item-header">
49
+ <div class="trash-item-main">
50
+ <div class="trash-item-mainline">
51
+ <div class="trash-item-title">{{ item.title || item.sessionId }}</div>
52
+ <span class="session-count-badge">{{ item.messageCount == null ? 0 : item.messageCount }}</span>
53
+ </div>
54
+ <div class="trash-item-meta session-item-meta">
55
+ <span class="session-source">{{ item.sourceLabel }}</span>
56
+ <span class="trash-item-dot">·</span>
57
+ <span class="trash-item-time">{{ item.deletedAt || item.updatedAt || t('sessions.unknownTime') }}</span>
58
+ </div>
59
+ </div>
60
+ <div class="trash-item-side">
61
+ <div class="trash-item-actions session-item-actions">
62
+ <button class="btn-mini" @click="restoreSessionTrash(item)" :disabled="sessionTrashLoading || sessionTrashClearing || isSessionTrashActionBusy(item)">
63
+ {{ sessionTrashRestoring[getSessionTrashActionKey(item)] ? '恢复中…' : '恢复' }}
64
+ </button>
65
+ <button class="btn-mini delete" @click="purgeSessionTrash(item)" :disabled="sessionTrashLoading || sessionTrashClearing || isSessionTrashActionBusy(item)">
66
+ {{ sessionTrashPurging[getSessionTrashActionKey(item)] ? '删除中…' : '彻底删除' }}
67
+ </button>
68
+ </div>
69
+ </div>
70
+ </div>
71
+ <div v-if="item.cwd" class="trash-item-path session-item-sub session-item-wrap">
72
+ <span class="trash-item-label">{{ t('settings.trash.workspace') }}</span>
73
+ <span>{{ item.cwd }}</span>
74
+ </div>
75
+ <div class="trash-item-path session-item-sub session-item-wrap">
76
+ <span class="trash-item-label">{{ t('settings.trash.originalFile') }}</span>
77
+ <span>{{ item.originalFilePath }}</span>
78
+ </div>
79
+ </div>
80
+ <div v-if="sessionTrashHasMoreItems" class="trash-list-footer">
81
+ <button class="btn-tool btn-tool-compact" @click="loadMoreSessionTrashItems" :disabled="sessionTrashLoading || sessionTrashClearing">
82
+ 加载更多(还有 {{ sessionTrashHiddenCount }} 条)
83
+ </button>
84
+ </div>
85
+ </div>
86
+ </template>
87
+ </div>
88
+ </div>
@@ -187,9 +187,7 @@ export function switchMainTab(tab) {
187
187
  void Promise.resolve(marketOverviewLoad).catch(() => {});
188
188
  }
189
189
  if (enteringPluginsTab && typeof this.loadPluginsOverview === 'function') {
190
- // Default behavior: always land on Prompt Templates + Compose when entering Plugins.
191
190
  this.pluginsActiveId = 'prompt-templates';
192
- this.promptTemplatesMode = 'compose';
193
191
  this.promptComposerPickerVisible = false;
194
192
  let pluginsLoad = null;
195
193
  try {
@@ -456,6 +454,26 @@ export async function loadActiveSessionDetail(api, options = {}) {
456
454
  }
457
455
  }
458
456
  this.sessionPreviewPendingVisibleCount = 0;
457
+ if (this.activeSessionDetailClipped) {
458
+ const autoFetchSession = currentActiveSession;
459
+ this.$nextTick(() => {
460
+ if (this.activeSession !== autoFetchSession) return;
461
+ if (this.sessionDetailLoading) return;
462
+ if (!this.activeSessionDetailClipped) return;
463
+ const currentLimit = Number(this.sessionDetailMessageLimit);
464
+ const fetchStep = Number.isFinite(this.sessionDetailFetchStep)
465
+ ? Math.max(1, Math.floor(this.sessionDetailFetchStep))
466
+ : 300;
467
+ const limitCap = Number.isFinite(this.sessionDetailMessageLimitCap)
468
+ ? Math.max(1, Math.floor(this.sessionDetailMessageLimitCap))
469
+ : 1000;
470
+ const nextLimit = Math.min(currentLimit + fetchStep, limitCap);
471
+ if (nextLimit <= currentLimit) return;
472
+ this.sessionDetailMessageLimit = nextLimit;
473
+ this.sessionPreviewPendingVisibleCount = nextLimit;
474
+ void this.loadActiveSessionDetail({ preserveVisibleCount: true });
475
+ });
476
+ }
459
477
  this.$nextTick(() => {
460
478
  if (this.mainTab !== 'sessions' || !this.sessionPreviewRenderEnabled) {
461
479
  return;
@@ -272,3 +272,135 @@
272
272
  grid-template-columns: 76px minmax(0, 1fr);
273
273
  }
274
274
  }
275
+
276
+ .doctor-providers-health {
277
+ margin-top: 12px;
278
+ padding: 12px;
279
+ border-radius: var(--radius-lg);
280
+ border: 1px solid var(--color-border-soft);
281
+ background: rgba(255, 255, 255, 0.78);
282
+ }
283
+
284
+ .doctor-providers-health-title {
285
+ font-size: var(--font-size-body);
286
+ font-weight: var(--font-weight-title);
287
+ color: var(--color-text-primary);
288
+ margin-bottom: 10px;
289
+ display: flex;
290
+ align-items: center;
291
+ gap: 8px;
292
+ }
293
+
294
+ .doctor-providers-health-summary {
295
+ font-size: var(--font-size-caption);
296
+ font-weight: var(--font-weight-primary);
297
+ color: var(--color-text-tertiary);
298
+ }
299
+
300
+ .doctor-providers-grid {
301
+ display: flex;
302
+ flex-wrap: wrap;
303
+ gap: 8px;
304
+ }
305
+
306
+ .doctor-provider-chip {
307
+ display: inline-flex;
308
+ align-items: center;
309
+ gap: 6px;
310
+ padding: 6px 12px;
311
+ border-radius: 999px;
312
+ border: 1px solid var(--color-border-soft);
313
+ background: rgba(255, 255, 255, 0.85);
314
+ font-size: var(--font-size-caption);
315
+ color: var(--color-text-secondary);
316
+ max-width: 100%;
317
+ overflow: hidden;
318
+ }
319
+
320
+ .doctor-provider-chip.green {
321
+ border-color: rgba(57, 181, 97, 0.25);
322
+ background: rgba(57, 181, 97, 0.08);
323
+ color: #2b6a3b;
324
+ }
325
+
326
+ .doctor-provider-chip.yellow {
327
+ border-color: rgba(246, 211, 106, 0.45);
328
+ background: rgba(246, 211, 106, 0.16);
329
+ color: #6f4b00;
330
+ }
331
+
332
+ .doctor-provider-chip.red {
333
+ border-color: rgba(220, 95, 108, 0.28);
334
+ background: rgba(220, 95, 108, 0.08);
335
+ color: #8a2f36;
336
+ }
337
+
338
+ .doctor-provider-name {
339
+ overflow: hidden;
340
+ text-overflow: ellipsis;
341
+ white-space: nowrap;
342
+ }
343
+
344
+ .doctor-provider-current {
345
+ font-size: 0.85em;
346
+ opacity: 0.7;
347
+ margin-left: 2px;
348
+ }
349
+
350
+ .doctor-provider-status-dot {
351
+ width: 8px;
352
+ height: 8px;
353
+ border-radius: 50%;
354
+ flex-shrink: 0;
355
+ }
356
+
357
+ .doctor-provider-chip.green .doctor-provider-status-dot {
358
+ background: rgba(57, 181, 97, 0.7);
359
+ }
360
+
361
+ .doctor-provider-chip.yellow .doctor-provider-status-dot {
362
+ background: rgba(246, 211, 106, 0.85);
363
+ }
364
+
365
+ .doctor-provider-chip.red .doctor-provider-status-dot {
366
+ background: rgba(220, 95, 108, 0.7);
367
+ }
368
+
369
+ .doctor-provider-issues {
370
+ margin-top: 10px;
371
+ display: flex;
372
+ flex-direction: column;
373
+ gap: 6px;
374
+ }
375
+
376
+ .doctor-provider-issue-card {
377
+ padding: 8px 10px;
378
+ border-radius: 10px;
379
+ border: 1px solid rgba(220, 95, 108, 0.15);
380
+ background: rgba(255, 255, 255, 0.7);
381
+ }
382
+
383
+ .doctor-provider-issue-name {
384
+ font-weight: var(--font-weight-secondary);
385
+ color: var(--color-text-primary);
386
+ margin-bottom: 4px;
387
+ }
388
+
389
+ .doctor-provider-issue-row {
390
+ font-size: var(--font-size-caption);
391
+ color: var(--color-text-secondary);
392
+ display: flex;
393
+ flex-direction: column;
394
+ gap: 2px;
395
+ padding-left: 8px;
396
+ margin-top: 4px;
397
+ }
398
+
399
+ .doctor-provider-issue-text {
400
+ color: #8a2f36;
401
+ }
402
+
403
+ .doctor-provider-issue-fix {
404
+ color: var(--color-text-tertiary);
405
+ font-style: italic;
406
+ }
@@ -1,7 +1,6 @@
1
1
  .docs-mode-content {
2
2
  display: grid;
3
3
  gap: 14px;
4
- /* CLI Install 是展示型面板:非输入区域不使用 I-beam(text)光标,避免误导为可编辑。 */
5
4
  cursor: default;
6
5
  }
7
6
 
@@ -41,17 +40,19 @@
41
40
  .docs-toolbar-grid {
42
41
  display: grid;
43
42
  grid-template-columns: repeat(12, minmax(0, 1fr));
44
- gap: 12px;
43
+ gap: 10px;
45
44
  }
46
45
 
47
46
  .docs-toolbar-card {
48
47
  grid-column: span 4;
49
48
  display: grid;
50
49
  gap: 8px;
51
- padding: 12px;
52
- border: 1px solid var(--color-border);
53
- border-radius: 10px;
54
- background: rgba(255, 253, 252, 0.76);
50
+ padding: 14px;
51
+ border: 1px solid var(--color-border-soft);
52
+ border-radius: var(--radius-md);
53
+ background: rgba(255, 255, 255, 0.42);
54
+ box-shadow: var(--shadow-subtle);
55
+ backdrop-filter: blur(8px);
55
56
  }
56
57
 
57
58
  .docs-toolbar-card-wide {
@@ -62,15 +63,17 @@
62
63
  display: grid;
63
64
  grid-template-columns: repeat(4, minmax(0, 1fr));
64
65
  gap: 10px;
66
+ margin-top: 2px;
65
67
  }
66
68
 
67
69
  .docs-summary-item {
68
70
  display: grid;
69
71
  gap: 4px;
70
- padding: 12px;
71
- border: 1px solid var(--color-border);
72
- border-radius: 10px;
73
- background: rgba(255, 253, 252, 0.82);
72
+ padding: 12px 14px;
73
+ border: 1px solid var(--color-border-soft);
74
+ border-radius: var(--radius-md);
75
+ background: linear-gradient(180deg, rgba(255,255,255,0.88), rgba(255,251,247,0.78));
76
+ box-shadow: var(--shadow-card);
74
77
  }
75
78
 
76
79
  .docs-summary-item-wide {
@@ -78,17 +81,21 @@
78
81
  }
79
82
 
80
83
  .docs-summary-label {
81
- font-size: 11px;
84
+ font-size: 10px;
85
+ font-weight: 700;
82
86
  color: var(--color-text-muted);
83
- letter-spacing: 0.04em;
87
+ letter-spacing: 0.06em;
84
88
  text-transform: uppercase;
89
+ opacity: 0.82;
85
90
  }
86
91
 
87
92
  .docs-summary-value {
88
- font-size: 14px;
93
+ font-size: 15px;
94
+ font-weight: 700;
89
95
  color: var(--color-text-primary);
90
- line-height: 1.4;
96
+ line-height: 1.25;
91
97
  word-break: break-word;
98
+ letter-spacing: -0.02em;
92
99
  }
93
100
 
94
101
  .docs-install-list {
@@ -97,12 +104,16 @@
97
104
 
98
105
  .docs-install-row {
99
106
  align-items: stretch;
100
- background: rgba(255, 253, 252, 0.8);
107
+ background: linear-gradient(180deg, rgba(255,255,255,0.88), rgba(255,251,247,0.78));
108
+ border: 1px solid var(--color-border-soft);
109
+ border-radius: var(--radius-md);
110
+ padding: 14px;
111
+ box-shadow: var(--shadow-card);
101
112
  }
102
113
 
103
114
  .docs-command-head {
104
115
  display: grid;
105
- gap: 4px;
116
+ gap: 6px;
106
117
  }
107
118
 
108
119
  .docs-command-row {
@@ -119,34 +130,38 @@
119
130
  grid-template-columns: 1fr auto;
120
131
  align-items: stretch;
121
132
  gap: 10px;
122
- padding: 10px 10px 10px 12px;
123
- border: 1px solid var(--color-border);
124
- border-radius: 10px;
125
- background: rgba(255, 253, 252, 0.9);
133
+ padding: 10px 10px 10px 14px;
134
+ border: 1px solid var(--color-border-soft);
135
+ border-radius: var(--radius-sm);
136
+ background: rgba(255,255,255,0.64);
137
+ box-shadow: var(--shadow-subtle);
126
138
  }
127
139
 
128
140
  .docs-command-row .install-command {
129
141
  min-width: 0;
130
- padding: 10px 12px;
131
- border-radius: 8px;
132
- background: rgba(18, 22, 35, 0.06);
142
+ padding: 10px 14px;
143
+ border-radius: var(--radius-sm);
144
+ background: rgba(18,22,35,0.04);
145
+ border: 1px solid rgba(18,22,35,0.05);
133
146
  overflow-x: auto;
134
147
  white-space: nowrap;
135
148
  user-select: text;
149
+ font-size: 13px;
150
+ line-height: 1.5;
136
151
  }
137
152
 
138
153
  .docs-command-prefix {
139
154
  display: inline-flex;
140
155
  align-items: center;
141
- padding: 2px 8px;
156
+ padding: 3px 10px;
142
157
  margin-right: 10px;
143
158
  border-radius: 999px;
144
- border: 1px solid rgba(0, 0, 0, 0.1);
145
- background: rgba(210, 107, 90, 0.10);
146
- color: var(--color-text-secondary);
159
+ border: 1px solid rgba(200,121,99,0.18);
160
+ background: var(--color-brand-light);
161
+ color: var(--color-brand-dark);
147
162
  font-size: 11px;
148
163
  font-weight: 700;
149
- letter-spacing: 0.02em;
164
+ letter-spacing: 0.03em;
150
165
  }
151
166
 
152
167
  .docs-command-meta {
@@ -160,16 +175,23 @@
160
175
  .docs-meta-pill {
161
176
  display: inline-flex;
162
177
  align-items: center;
163
- padding: 4px 8px;
178
+ padding: 3px 10px;
164
179
  border-radius: 999px;
165
- border: 1px solid rgba(0, 0, 0, 0.08);
166
- background: rgba(255, 255, 255, 0.6);
180
+ border: 1px solid var(--color-border-soft);
181
+ background: rgba(255,255,255,0.72);
182
+ font-size: 11px;
183
+ font-weight: 600;
184
+ color: var(--color-text-secondary);
185
+ letter-spacing: 0.01em;
167
186
  }
168
187
 
169
188
  .docs-copy-btn {
170
- min-width: 76px;
189
+ min-width: 72px;
171
190
  min-height: 40px;
172
191
  flex: 0 0 auto;
192
+ border-radius: var(--radius-sm);
193
+ font-weight: 600;
194
+ letter-spacing: 0.01em;
173
195
  }
174
196
 
175
197
  .docs-help-grid {
@@ -179,17 +201,19 @@
179
201
  }
180
202
 
181
203
  .docs-note-card {
182
- padding: 14px;
183
- border: 1px solid var(--color-border);
184
- border-radius: 10px;
185
- background: rgba(255, 253, 252, 0.78);
204
+ padding: 16px;
205
+ border: 1px solid var(--color-border-soft);
206
+ border-radius: var(--radius-md);
207
+ background: linear-gradient(180deg, rgba(255,255,255,0.86), rgba(255,251,247,0.76));
208
+ box-shadow: var(--shadow-card);
186
209
  }
187
210
 
188
211
  .docs-note-title {
189
212
  font-size: 13px;
190
213
  font-weight: 700;
191
214
  color: var(--color-text-primary);
192
- margin-bottom: 8px;
215
+ margin-bottom: 10px;
216
+ letter-spacing: -0.02em;
193
217
  }
194
218
 
195
219
  .docs-help-list {
@@ -201,11 +225,11 @@
201
225
  padding-left: 18px;
202
226
  color: var(--color-text-secondary);
203
227
  font-size: 13px;
204
- line-height: 1.55;
228
+ line-height: 1.6;
205
229
  }
206
230
 
207
231
  .docs-static-list li + li {
208
- margin-top: 6px;
232
+ margin-top: 8px;
209
233
  }
210
234
 
211
235
  @media (max-width: 1100px) {
@@ -48,12 +48,12 @@ body::after {
48
48
  display: flex;
49
49
  flex-direction: column;
50
50
  gap: 0;
51
- padding: 10px 8px;
51
+ padding: 14px 10px;
52
52
  background:
53
- linear-gradient(180deg, rgba(255, 253, 250, 0.92) 0%, rgba(255, 248, 241, 0.82) 100%);
54
- border-right: 1px solid rgba(137, 111, 94, 0.16);
53
+ linear-gradient(180deg, rgba(255, 252, 248, 0.95) 0%, rgba(255, 248, 241, 0.89) 100%);
54
+ border-right: 1px solid rgba(137, 111, 94, 0.10);
55
55
  border-radius: 0;
56
- box-shadow: 18px 0 42px rgba(92, 68, 52, 0.08);
56
+ box-shadow: 8px 0 28px rgba(92, 68, 52, 0.05), 1px 0 0 rgba(255, 255, 255, 0.5);
57
57
  min-height: 100vh;
58
58
  overflow-y: auto;
59
59
  scrollbar-width: none;
@@ -73,12 +73,12 @@ body::after {
73
73
  .side-section {
74
74
  display: flex;
75
75
  flex-direction: column;
76
- gap: 4px;
77
- padding: 7px 4px;
76
+ gap: 3px;
77
+ padding: 8px 4px;
78
78
  }
79
79
 
80
80
  .side-section + .side-section {
81
- border-top: 1px solid rgba(137, 111, 94, 0.12);
81
+ border-top: 1px solid rgba(137, 111, 94, 0.08);
82
82
  }
83
83
 
84
84
  .side-rail-nav {
@@ -210,10 +210,10 @@ body::after {
210
210
  bottom: 0;
211
211
  z-index: 2;
212
212
  margin-top: auto;
213
- padding: 12px 4px 4px;
214
- background: linear-gradient(180deg, rgba(255, 248, 241, 0), rgba(255, 248, 241, 0.92) 34%);
215
- border-top: 1px solid rgba(137, 111, 94, 0.12);
216
- backdrop-filter: blur(10px);
213
+ padding: 10px 4px 6px;
214
+ background: linear-gradient(180deg, rgba(255, 248, 241, 0), rgba(255, 248, 241, 0.94) 28%);
215
+ border-top: 1px solid rgba(137, 111, 94, 0.08);
216
+ backdrop-filter: blur(12px);
217
217
  display: flex;
218
218
  justify-content: center;
219
219
  }
@@ -311,28 +311,29 @@ body::after {
311
311
  }
312
312
 
313
313
  .side-section-title {
314
- font-size: 11px;
314
+ font-size: 10px;
315
315
  font-weight: 700;
316
316
  color: var(--color-text-muted);
317
- letter-spacing: 0.06em;
317
+ letter-spacing: 0.08em;
318
318
  text-transform: uppercase;
319
- padding: 0 6px;
320
- margin-bottom: 8px;
319
+ padding: 0 8px;
320
+ margin-bottom: 6px;
321
+ opacity: 0.72;
321
322
  }
322
323
 
323
324
  .side-item {
324
325
  width: 100%;
325
326
  text-align: left;
326
- padding: 8px 10px;
327
- border-radius: 13px;
327
+ padding: 9px 12px 9px 14px;
328
+ border-radius: 12px;
328
329
  border: 1px solid transparent;
329
330
  background: transparent;
330
331
  color: var(--color-text-secondary);
331
332
  cursor: pointer;
332
- transition: border-color var(--transition-fast) var(--ease-smooth), background-color var(--transition-fast) var(--ease-smooth), color var(--transition-fast) var(--ease-smooth), box-shadow var(--transition-fast) var(--ease-smooth), transform var(--transition-fast) var(--ease-smooth);
333
+ transition: border-color 180ms var(--ease-out-expo), background-color 180ms var(--ease-out-expo), color 180ms var(--ease-out-expo), box-shadow 180ms var(--ease-out-expo), transform 180ms var(--ease-out-expo);
333
334
  display: flex;
334
335
  flex-direction: column;
335
- gap: 2px;
336
+ gap: 3px;
336
337
  box-shadow: none;
337
338
  position: relative;
338
339
  }
@@ -340,30 +341,28 @@ body::after {
340
341
  .side-item::before {
341
342
  content: "";
342
343
  position: absolute;
343
- left: 0;
344
+ left: 2px;
344
345
  top: 50%;
345
346
  transform: translateY(-50%);
346
347
  width: 3px;
347
- height: 16px;
348
- border-radius: 0 2px 2px 0;
348
+ height: 18px;
349
+ border-radius: 0 3px 3px 0;
349
350
  background: transparent;
350
- transition: background-color var(--transition-fast) var(--ease-smooth);
351
- display: none;
352
351
  }
353
352
 
354
353
  .side-item:hover {
355
- background: rgba(255, 255, 255, 0.58);
354
+ background: rgba(255, 255, 255, 0.64);
356
355
  color: var(--color-text-primary);
357
- transform: translateY(-1px);
356
+ transform: translateX(2px);
358
357
  }
359
358
 
360
359
  .side-item.active,
361
360
  .side-item.nav-intent-active {
362
- border-color: rgba(200, 121, 99, 0.28);
361
+ border-color: rgba(200, 121, 99, 0.22);
363
362
  background:
364
- linear-gradient(135deg, rgba(255, 255, 255, 0.82), rgba(200, 121, 99, 0.14));
363
+ linear-gradient(135deg, rgba(255, 255, 255, 0.86), rgba(200, 121, 99, 0.10));
365
364
  color: var(--color-brand-dark);
366
- box-shadow: 0 10px 24px rgba(92, 68, 52, 0.08);
365
+ box-shadow: 0 8px 20px rgba(92, 68, 52, 0.06);
367
366
  }
368
367
 
369
368
  .side-item.nav-intent-inactive,
@@ -388,6 +387,7 @@ body::after {
388
387
  font-size: 13px;
389
388
  font-weight: 600;
390
389
  letter-spacing: -0.01em;
390
+ line-height: 1.25;
391
391
  }
392
392
 
393
393
  .side-item-meta {
@@ -424,8 +424,8 @@ body::after {
424
424
  align-items: flex-start;
425
425
  gap: 10px;
426
426
  margin-bottom: 0;
427
- padding: 16px 10px 18px;
428
- border-bottom: 1px solid rgba(137, 111, 94, 0.12);
427
+ padding: 14px 10px 16px;
428
+ border-bottom: 1px solid rgba(137, 111, 94, 0.10);
429
429
  }
430
430
 
431
431
  .brand-head {
@@ -437,10 +437,12 @@ body::after {
437
437
  .brand-logo {
438
438
  width: 38px;
439
439
  height: 38px;
440
- border-radius: 14px;
440
+ border-radius: 13px;
441
441
  object-fit: cover;
442
442
  flex-shrink: 0;
443
- box-shadow: 0 12px 26px rgba(92, 68, 52, 0.12);
443
+ box-shadow:
444
+ 0 8px 20px rgba(92, 68, 52, 0.10),
445
+ 0 0 0 1px rgba(200, 121, 99, 0.08);
444
446
  }
445
447
 
446
448
  .brand-copy {
@@ -468,10 +470,11 @@ body::after {
468
470
 
469
471
  .brand-title {
470
472
  font-size: 18px;
471
- line-height: 1.1;
473
+ line-height: 1.15;
472
474
  font-family: var(--font-family-display);
473
475
  color: var(--color-text-primary);
474
476
  letter-spacing: -0.02em;
477
+ font-weight: 700;
475
478
  }
476
479
 
477
480
  .github-badge {
@@ -604,3 +607,20 @@ body::after {
604
607
  .github-badge-rail:hover {
605
608
  border-color: var(--color-border-strong);
606
609
  }
610
+
611
+ /* 侧边栏计数徽标 */
612
+ .side-item-badge {
613
+ display: inline-flex;
614
+ align-items: center;
615
+ justify-content: center;
616
+ min-width: 20px;
617
+ height: 20px;
618
+ padding: 0 6px;
619
+ border-radius: 10px;
620
+ background: var(--color-brand);
621
+ color: #fff;
622
+ font-size: 11px;
623
+ font-weight: 700;
624
+ line-height: 1;
625
+ box-shadow: 0 2px 6px rgba(200, 121, 99, 0.28);
626
+ }