cursor-guard 4.3.4 → 4.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/README.zh-CN.md +6 -0
- package/package.json +1 -1
- package/references/dashboard/public/app.js +19 -13
- package/references/dashboard/public/style.css +350 -184
- package/references/lib/auto-backup.js +0 -33
- package/references/lib/core/snapshot.js +41 -6
package/README.md
CHANGED
|
@@ -377,6 +377,12 @@ The skill activates on these signals:
|
|
|
377
377
|
|
|
378
378
|
## Changelog
|
|
379
379
|
|
|
380
|
+
### v4.3.5
|
|
381
|
+
|
|
382
|
+
- **Fix**: Backup summary now uses incremental `diff-tree` instead of `git status --porcelain` — previously summary always showed cumulative changes since HEAD, now correctly shows changes since the last auto-backup
|
|
383
|
+
- **Improve**: Dashboard backup table "Changes" column uses stacked layout (file count + trigger / intent / detail on separate rows) for better readability
|
|
384
|
+
- **Improve**: Refined color palette — deeper background contrast, softer status colors (green `#4ade80`, amber `#f59e0b`, red `#ef4444`), deeper brand blue `#3b82f6`, wider text hierarchy gap
|
|
385
|
+
|
|
380
386
|
### v4.3.4
|
|
381
387
|
|
|
382
388
|
- **Improve**: Log rotation — `backup.log` now rotates at 1MB, keeping up to 3 old files (`backup.log.1`, `.2`, `.3`). Rotation runs on watcher startup and every 100 writes
|
package/README.zh-CN.md
CHANGED
|
@@ -377,6 +377,12 @@ node references\dashboard\server.js --path "D:\MyProject"
|
|
|
377
377
|
|
|
378
378
|
## 更新日志
|
|
379
379
|
|
|
380
|
+
### v4.3.5
|
|
381
|
+
|
|
382
|
+
- **修复**:备份摘要(Summary)现使用增量 `diff-tree` 替代 `git status --porcelain`——之前 summary 始终显示自 HEAD 以来的累计差异,现在正确显示自上次 auto-backup 以来的增量变化
|
|
383
|
+
- **改进**:仪表盘备份表格"变更"列改为分层堆叠布局(文件数 + 触发方式 / 意图 / 明细分行显示),可读性更好
|
|
384
|
+
- **改进**:配色优化——背景层级差距加大,状态色柔和(绿 `#4ade80`、琥珀 `#f59e0b`、红 `#ef4444`),品牌蓝加深 `#3b82f6`,文字层级对比更清晰
|
|
385
|
+
|
|
380
386
|
### v4.3.4
|
|
381
387
|
|
|
382
388
|
- **改进**:日志轮转——`backup.log` 超过 1MB 自动轮转,保留最近 3 个旧文件。watcher 启动时和每 100 次写入时检查
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursor-guard",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.5",
|
|
4
4
|
"description": "Protects code from accidental AI overwrite or deletion in Cursor IDE — mandatory pre-write snapshots, review-before-apply, local Git safety net, and deterministic recovery. | 保护代码免受 Cursor AI 代理意外覆写或删除——强制写前快照、预览再执行、本地 Git 安全网、确定性恢复。",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cursor",
|
|
@@ -819,22 +819,28 @@ function translateSummary(raw) {
|
|
|
819
819
|
}
|
|
820
820
|
|
|
821
821
|
function formatSummaryCell(b) {
|
|
822
|
-
const
|
|
823
|
-
if (b.filesChanged != null)
|
|
824
|
-
if (b.trigger)
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
const short = translated.length > 80 ? translated.substring(0, 77) + '...' : translated;
|
|
828
|
-
parts.push(`<span class="text-muted text-sm">${esc(short)}</span>`);
|
|
829
|
-
}
|
|
822
|
+
const line1 = [];
|
|
823
|
+
if (b.filesChanged != null) line1.push(`<span class="summary-files">${b.filesChanged} ${t('summary.files')}</span>`);
|
|
824
|
+
if (b.trigger) line1.push(`<span class="badge badge-trigger">${t('trigger.' + b.trigger)}</span>`);
|
|
825
|
+
|
|
826
|
+
let line2 = '';
|
|
830
827
|
if (b.intent) {
|
|
831
|
-
const intentShort = b.intent.length >
|
|
832
|
-
|
|
828
|
+
const intentShort = b.intent.length > 70 ? b.intent.substring(0, 67) + '...' : b.intent;
|
|
829
|
+
line2 = `<div class="summary-intent">${esc(intentShort)}</div>`;
|
|
833
830
|
} else if (b.message && !b.message.startsWith('guard:')) {
|
|
834
|
-
const msgShort = b.message.length >
|
|
835
|
-
|
|
831
|
+
const msgShort = b.message.length > 70 ? b.message.substring(0, 67) + '...' : b.message;
|
|
832
|
+
line2 = `<div class="summary-message">${esc(msgShort)}</div>`;
|
|
836
833
|
}
|
|
837
|
-
|
|
834
|
+
|
|
835
|
+
let line3 = '';
|
|
836
|
+
if (b.summary) {
|
|
837
|
+
const translated = translateSummary(b.summary);
|
|
838
|
+
const short = translated.length > 90 ? translated.substring(0, 87) + '...' : translated;
|
|
839
|
+
line3 = `<div class="summary-detail">${esc(short)}</div>`;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
if (!line1.length && !line2 && !line3) return '<span class="text-muted text-sm">-</span>';
|
|
843
|
+
return `<div class="summary-stack">${line1.length ? '<div class="summary-meta">' + line1.join(' ') + '</div>' : ''}${line2}${line3}</div>`;
|
|
838
844
|
}
|
|
839
845
|
|
|
840
846
|
function renderBackupTable(backups) {
|
|
@@ -1,37 +1,50 @@
|
|
|
1
1
|
/* ═══════════════════════════════════════════════════════════════
|
|
2
|
-
Cursor Guard Dashboard — Ops Console Theme
|
|
2
|
+
Cursor Guard Dashboard — Ops Console Theme v2
|
|
3
3
|
═══════════════════════════════════════════════════════════════ */
|
|
4
4
|
|
|
5
5
|
:root {
|
|
6
|
-
--bg-primary: #
|
|
7
|
-
--bg-secondary: #
|
|
8
|
-
--bg-tertiary: #
|
|
9
|
-
--bg-
|
|
10
|
-
--
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
--
|
|
14
|
-
--
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
--
|
|
18
|
-
--
|
|
19
|
-
--
|
|
20
|
-
|
|
6
|
+
--bg-primary: #0a0f1a;
|
|
7
|
+
--bg-secondary: #141c2b;
|
|
8
|
+
--bg-tertiary: #1e293b;
|
|
9
|
+
--bg-elevated: #182030;
|
|
10
|
+
--bg-hover: rgba(148,163,184,.06);
|
|
11
|
+
|
|
12
|
+
--border: rgba(71,85,105,.5);
|
|
13
|
+
--border-subtle:rgba(71,85,105,.25);
|
|
14
|
+
--border-glow: rgba(59,130,246,.2);
|
|
15
|
+
|
|
16
|
+
--text-primary: #cbd5e1;
|
|
17
|
+
--text-secondary: #8b9cb7;
|
|
18
|
+
--text-tertiary: #475569;
|
|
19
|
+
--text-heading: #f8fafc;
|
|
20
|
+
|
|
21
|
+
--green: #4ade80;
|
|
22
|
+
--green-bg: rgba(74,222,128,.12);
|
|
23
|
+
--green-glow: rgba(74,222,128,.2);
|
|
24
|
+
--yellow: #f59e0b;
|
|
25
|
+
--yellow-bg: rgba(245,158,11,.12);
|
|
26
|
+
--yellow-glow: rgba(245,158,11,.2);
|
|
21
27
|
--red: #ef4444;
|
|
22
28
|
--red-bg: rgba(239,68,68,.12);
|
|
29
|
+
--red-glow: rgba(239,68,68,.2);
|
|
23
30
|
--blue: #3b82f6;
|
|
24
31
|
--blue-bg: rgba(59,130,246,.12);
|
|
25
|
-
--
|
|
26
|
-
--purple
|
|
32
|
+
--blue-glow: rgba(59,130,246,.2);
|
|
33
|
+
--purple: #a78bfa;
|
|
34
|
+
--purple-bg: rgba(167,139,250,.12);
|
|
27
35
|
--amber: #f59e0b;
|
|
28
36
|
--amber-bg: rgba(245,158,11,.12);
|
|
29
37
|
--gray: #64748b;
|
|
30
38
|
--gray-bg: rgba(100,116,139,.12);
|
|
31
39
|
|
|
32
|
-
--radius:
|
|
33
|
-
--
|
|
34
|
-
--
|
|
40
|
+
--radius: 10px;
|
|
41
|
+
--radius-sm: 6px;
|
|
42
|
+
--topbar-h: 56px;
|
|
43
|
+
--drawer-w: 500px;
|
|
44
|
+
--shadow-sm: 0 1px 3px rgba(0,0,0,.4), 0 1px 2px rgba(0,0,0,.25);
|
|
45
|
+
--shadow-md: 0 4px 20px rgba(0,0,0,.45);
|
|
46
|
+
--shadow-lg: 0 8px 32px rgba(0,0,0,.5);
|
|
47
|
+
--transition: .2s cubic-bezier(.4,0,.2,1);
|
|
35
48
|
}
|
|
36
49
|
|
|
37
50
|
/* ── Reset ────────────────────────────────────────────────── */
|
|
@@ -39,13 +52,17 @@
|
|
|
39
52
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
40
53
|
|
|
41
54
|
body {
|
|
42
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
55
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
43
56
|
font-size: 14px;
|
|
44
|
-
line-height: 1.
|
|
57
|
+
line-height: 1.6;
|
|
45
58
|
color: var(--text-primary);
|
|
46
59
|
background: var(--bg-primary);
|
|
60
|
+
background-image:
|
|
61
|
+
radial-gradient(ellipse 80% 50% at 50% -20%, rgba(59,130,246,.06), transparent),
|
|
62
|
+
radial-gradient(ellipse 60% 40% at 80% 100%, rgba(74,222,128,.03), transparent);
|
|
47
63
|
padding-top: var(--topbar-h);
|
|
48
64
|
-webkit-font-smoothing: antialiased;
|
|
65
|
+
-moz-osx-font-smoothing: grayscale;
|
|
49
66
|
}
|
|
50
67
|
|
|
51
68
|
/* ── Top Bar ──────────────────────────────────────────────── */
|
|
@@ -54,12 +71,14 @@ body {
|
|
|
54
71
|
position: fixed;
|
|
55
72
|
top: 0; left: 0; right: 0;
|
|
56
73
|
height: var(--topbar-h);
|
|
57
|
-
background:
|
|
74
|
+
background: rgba(14,19,30,.85);
|
|
75
|
+
backdrop-filter: blur(16px) saturate(1.5);
|
|
76
|
+
-webkit-backdrop-filter: blur(16px) saturate(1.5);
|
|
58
77
|
border-bottom: 1px solid var(--border);
|
|
59
78
|
display: flex;
|
|
60
79
|
align-items: center;
|
|
61
80
|
justify-content: space-between;
|
|
62
|
-
padding: 0
|
|
81
|
+
padding: 0 24px;
|
|
63
82
|
z-index: 50;
|
|
64
83
|
gap: 12px;
|
|
65
84
|
}
|
|
@@ -70,67 +89,89 @@ body {
|
|
|
70
89
|
gap: 10px;
|
|
71
90
|
}
|
|
72
91
|
|
|
73
|
-
.logo-icon {
|
|
74
|
-
|
|
92
|
+
.logo-icon {
|
|
93
|
+
color: var(--green);
|
|
94
|
+
flex-shrink: 0;
|
|
95
|
+
filter: drop-shadow(0 0 6px var(--green-glow));
|
|
96
|
+
}
|
|
97
|
+
.logo-text {
|
|
98
|
+
font-weight: 700;
|
|
99
|
+
font-size: 15px;
|
|
100
|
+
white-space: nowrap;
|
|
101
|
+
letter-spacing: -.01em;
|
|
102
|
+
color: var(--text-heading);
|
|
103
|
+
}
|
|
75
104
|
|
|
76
105
|
#project-select {
|
|
77
106
|
background: var(--bg-tertiary);
|
|
78
107
|
color: var(--text-primary);
|
|
79
108
|
border: 1px solid var(--border);
|
|
80
|
-
border-radius:
|
|
81
|
-
padding:
|
|
109
|
+
border-radius: var(--radius-sm);
|
|
110
|
+
padding: 5px 10px;
|
|
82
111
|
font-size: 13px;
|
|
83
|
-
max-width:
|
|
112
|
+
max-width: 220px;
|
|
84
113
|
cursor: pointer;
|
|
114
|
+
transition: border-color var(--transition);
|
|
85
115
|
}
|
|
86
|
-
#project-select:
|
|
116
|
+
#project-select:hover { border-color: var(--blue); }
|
|
117
|
+
#project-select:focus { outline: none; border-color: var(--blue); box-shadow: 0 0 0 3px var(--blue-bg); }
|
|
87
118
|
|
|
88
|
-
#last-refresh { font-size: 12px; white-space: nowrap; }
|
|
119
|
+
#last-refresh { font-size: 12px; white-space: nowrap; opacity: .7; }
|
|
89
120
|
|
|
90
121
|
/* ── Buttons ──────────────────────────────────────────────── */
|
|
91
122
|
|
|
92
123
|
.btn {
|
|
93
124
|
display: inline-flex;
|
|
94
125
|
align-items: center;
|
|
95
|
-
gap:
|
|
96
|
-
padding:
|
|
126
|
+
gap: 6px;
|
|
127
|
+
padding: 7px 14px;
|
|
97
128
|
border: 1px solid var(--border);
|
|
98
|
-
border-radius:
|
|
129
|
+
border-radius: var(--radius-sm);
|
|
99
130
|
background: var(--bg-tertiary);
|
|
100
131
|
color: var(--text-primary);
|
|
101
132
|
font-size: 13px;
|
|
133
|
+
font-weight: 500;
|
|
102
134
|
cursor: pointer;
|
|
103
|
-
transition:
|
|
135
|
+
transition: all var(--transition);
|
|
104
136
|
white-space: nowrap;
|
|
105
137
|
}
|
|
106
|
-
.btn:hover {
|
|
138
|
+
.btn:hover {
|
|
139
|
+
background: rgba(59,130,246,.1);
|
|
140
|
+
border-color: rgba(59,130,246,.3);
|
|
141
|
+
color: var(--text-heading);
|
|
142
|
+
}
|
|
143
|
+
.btn:active { transform: scale(.97); }
|
|
107
144
|
|
|
108
145
|
.btn-ghost {
|
|
109
146
|
background: transparent;
|
|
110
147
|
border-color: transparent;
|
|
111
148
|
}
|
|
112
|
-
.btn-ghost:hover {
|
|
149
|
+
.btn-ghost:hover {
|
|
150
|
+
background: var(--bg-hover);
|
|
151
|
+
border-color: transparent;
|
|
152
|
+
}
|
|
113
153
|
|
|
114
|
-
.btn-sm { padding:
|
|
154
|
+
.btn-sm { padding: 4px 10px; font-size: 12px; }
|
|
115
155
|
|
|
116
156
|
/* ── Main ─────────────────────────────────────────────────── */
|
|
117
157
|
|
|
118
158
|
main {
|
|
119
159
|
max-width: 1200px;
|
|
120
160
|
margin: 0 auto;
|
|
121
|
-
padding: 24px
|
|
161
|
+
padding: 28px 24px 60px;
|
|
122
162
|
}
|
|
123
163
|
|
|
124
164
|
/* ── Sections ─────────────────────────────────────────────── */
|
|
125
165
|
|
|
126
|
-
.screen { margin-bottom:
|
|
166
|
+
.screen { margin-bottom: 44px; }
|
|
127
167
|
|
|
128
168
|
.section-title {
|
|
129
|
-
font-size:
|
|
130
|
-
font-weight:
|
|
131
|
-
color: var(--text-
|
|
132
|
-
margin-bottom:
|
|
133
|
-
letter-spacing: .
|
|
169
|
+
font-size: 13px;
|
|
170
|
+
font-weight: 700;
|
|
171
|
+
color: var(--text-tertiary);
|
|
172
|
+
margin-bottom: 18px;
|
|
173
|
+
letter-spacing: .08em;
|
|
174
|
+
text-transform: uppercase;
|
|
134
175
|
}
|
|
135
176
|
|
|
136
177
|
/* ── Card Grid ────────────────────────────────────────────── */
|
|
@@ -138,45 +179,66 @@ main {
|
|
|
138
179
|
.card-grid {
|
|
139
180
|
display: grid;
|
|
140
181
|
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
|
141
|
-
gap:
|
|
182
|
+
gap: 16px;
|
|
142
183
|
}
|
|
143
184
|
|
|
144
185
|
.card {
|
|
145
186
|
background: var(--bg-secondary);
|
|
146
187
|
border: 1px solid var(--border);
|
|
147
188
|
border-radius: var(--radius);
|
|
148
|
-
padding:
|
|
149
|
-
transition:
|
|
189
|
+
padding: 18px 22px;
|
|
190
|
+
transition: all var(--transition);
|
|
191
|
+
position: relative;
|
|
192
|
+
overflow: hidden;
|
|
193
|
+
}
|
|
194
|
+
.card::before {
|
|
195
|
+
content: '';
|
|
196
|
+
position: absolute;
|
|
197
|
+
inset: 0;
|
|
198
|
+
border-radius: var(--radius);
|
|
199
|
+
padding: 1px;
|
|
200
|
+
background: linear-gradient(135deg, rgba(59,130,246,.1), transparent 50%);
|
|
201
|
+
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
|
202
|
+
-webkit-mask-composite: xor;
|
|
203
|
+
mask-composite: exclude;
|
|
204
|
+
pointer-events: none;
|
|
205
|
+
opacity: 0;
|
|
206
|
+
transition: opacity var(--transition);
|
|
150
207
|
}
|
|
151
208
|
.card:hover {
|
|
152
|
-
transform: translateY(-
|
|
153
|
-
box-shadow:
|
|
209
|
+
transform: translateY(-2px);
|
|
210
|
+
box-shadow: var(--shadow-md);
|
|
211
|
+
border-color: var(--border-glow);
|
|
154
212
|
}
|
|
213
|
+
.card:hover::before { opacity: 1; }
|
|
155
214
|
|
|
156
215
|
.card-health {
|
|
157
216
|
grid-column: 1 / -1;
|
|
158
217
|
display: flex;
|
|
159
218
|
align-items: center;
|
|
160
|
-
gap:
|
|
219
|
+
gap: 18px;
|
|
161
220
|
flex-wrap: wrap;
|
|
221
|
+
background: linear-gradient(135deg, var(--bg-secondary), var(--bg-elevated));
|
|
162
222
|
}
|
|
163
223
|
|
|
164
224
|
.card-label {
|
|
165
|
-
font-size:
|
|
166
|
-
font-weight:
|
|
225
|
+
font-size: 10px;
|
|
226
|
+
font-weight: 700;
|
|
167
227
|
text-transform: uppercase;
|
|
168
|
-
letter-spacing: .
|
|
228
|
+
letter-spacing: .1em;
|
|
169
229
|
color: var(--text-tertiary);
|
|
170
|
-
margin-bottom:
|
|
230
|
+
margin-bottom: 10px;
|
|
171
231
|
}
|
|
172
232
|
|
|
173
233
|
.card-value {
|
|
174
|
-
font-size:
|
|
175
|
-
font-weight:
|
|
176
|
-
line-height: 1.
|
|
234
|
+
font-size: 24px;
|
|
235
|
+
font-weight: 800;
|
|
236
|
+
line-height: 1.15;
|
|
237
|
+
letter-spacing: -.02em;
|
|
238
|
+
color: var(--text-heading);
|
|
177
239
|
}
|
|
178
240
|
|
|
179
|
-
.card-detail { margin-top:
|
|
241
|
+
.card-detail { margin-top: 8px; }
|
|
180
242
|
|
|
181
243
|
.card-empty {
|
|
182
244
|
color: var(--text-tertiary);
|
|
@@ -192,22 +254,27 @@ main {
|
|
|
192
254
|
display: inline-block;
|
|
193
255
|
width: 10px; height: 10px;
|
|
194
256
|
border-radius: 50%;
|
|
195
|
-
margin-right:
|
|
257
|
+
margin-right: 8px;
|
|
196
258
|
flex-shrink: 0;
|
|
197
259
|
}
|
|
198
|
-
.status-dot.status-healthy { background: var(--green); box-shadow: 0 0
|
|
199
|
-
.status-dot.status-warning { background: var(--yellow); box-shadow: 0 0
|
|
200
|
-
.status-dot.status-critical { background: var(--red); box-shadow: 0 0
|
|
201
|
-
.status-dot.status-unknown { background: var(--gray); }
|
|
202
|
-
.status-dot.status-running { background: var(--green); box-shadow: 0 0
|
|
203
|
-
.status-dot.status-stopped { background: var(--gray); }
|
|
204
|
-
.status-dot.status-stale { background: var(--yellow); box-shadow: 0 0
|
|
260
|
+
.status-dot.status-healthy { background: var(--green); box-shadow: 0 0 8px var(--green-glow), 0 0 2px var(--green); animation: pulse-green 2s ease-in-out infinite; }
|
|
261
|
+
.status-dot.status-warning { background: var(--yellow); box-shadow: 0 0 8px var(--yellow-glow), 0 0 2px var(--yellow); animation: pulse-yellow 2s ease-in-out infinite; }
|
|
262
|
+
.status-dot.status-critical { background: var(--red); box-shadow: 0 0 8px var(--red-glow), 0 0 2px var(--red); animation: pulse-red 1.5s ease-in-out infinite; }
|
|
263
|
+
.status-dot.status-unknown { background: var(--gray); opacity: .6; }
|
|
264
|
+
.status-dot.status-running { background: var(--green); box-shadow: 0 0 8px var(--green-glow), 0 0 2px var(--green); animation: pulse-green 2s ease-in-out infinite; }
|
|
265
|
+
.status-dot.status-stopped { background: var(--gray); opacity: .6; }
|
|
266
|
+
.status-dot.status-stale { background: var(--yellow); box-shadow: 0 0 8px var(--yellow-glow); }
|
|
267
|
+
|
|
268
|
+
@keyframes pulse-green { 0%,100%{ box-shadow: 0 0 4px var(--green-glow); } 50%{ box-shadow: 0 0 12px var(--green-glow), 0 0 4px var(--green); } }
|
|
269
|
+
@keyframes pulse-yellow { 0%,100%{ box-shadow: 0 0 4px var(--yellow-glow); } 50%{ box-shadow: 0 0 12px var(--yellow-glow), 0 0 4px var(--yellow); } }
|
|
270
|
+
@keyframes pulse-red { 0%,100%{ box-shadow: 0 0 4px var(--red-glow); } 50%{ box-shadow: 0 0 14px var(--red-glow), 0 0 6px var(--red); } }
|
|
205
271
|
|
|
206
272
|
.card-status {
|
|
207
273
|
display: flex;
|
|
208
274
|
align-items: center;
|
|
209
|
-
font-size:
|
|
210
|
-
font-weight:
|
|
275
|
+
font-size: 20px;
|
|
276
|
+
font-weight: 800;
|
|
277
|
+
letter-spacing: -.01em;
|
|
211
278
|
}
|
|
212
279
|
|
|
213
280
|
.status-text.status-healthy { color: var(--green); }
|
|
@@ -217,13 +284,21 @@ main {
|
|
|
217
284
|
|
|
218
285
|
.issue-list {
|
|
219
286
|
list-style: none;
|
|
220
|
-
margin-top:
|
|
221
|
-
padding-left:
|
|
287
|
+
margin-top: 10px;
|
|
288
|
+
padding-left: 18px;
|
|
289
|
+
}
|
|
290
|
+
.issue-list li {
|
|
291
|
+
position: relative;
|
|
292
|
+
padding: 2px 0;
|
|
293
|
+
font-size: 13px;
|
|
222
294
|
}
|
|
223
295
|
.issue-list li::before {
|
|
224
|
-
content: '
|
|
225
|
-
|
|
226
|
-
|
|
296
|
+
content: '';
|
|
297
|
+
position: absolute;
|
|
298
|
+
left: -14px; top: 10px;
|
|
299
|
+
width: 5px; height: 5px;
|
|
300
|
+
border-radius: 50%;
|
|
301
|
+
background: var(--yellow);
|
|
227
302
|
}
|
|
228
303
|
|
|
229
304
|
/* ── Badges ───────────────────────────────────────────────── */
|
|
@@ -231,57 +306,92 @@ main {
|
|
|
231
306
|
.badge {
|
|
232
307
|
display: inline-flex;
|
|
233
308
|
align-items: center;
|
|
234
|
-
padding:
|
|
235
|
-
border-radius:
|
|
309
|
+
padding: 3px 10px;
|
|
310
|
+
border-radius: 12px;
|
|
236
311
|
font-size: 11px;
|
|
237
312
|
font-weight: 600;
|
|
238
|
-
letter-spacing: .
|
|
313
|
+
letter-spacing: .02em;
|
|
314
|
+
border: 1px solid transparent;
|
|
239
315
|
}
|
|
240
316
|
|
|
241
|
-
.badge-strategy { background: var(--blue-bg); color: var(--blue); }
|
|
317
|
+
.badge-strategy { background: var(--blue-bg); color: var(--blue); border-color: rgba(59,130,246,.18); }
|
|
242
318
|
|
|
243
|
-
.badge-pass { background: var(--green-bg); color: var(--green); }
|
|
244
|
-
.badge-warn { background: var(--yellow-bg); color: var(--yellow); }
|
|
245
|
-
.badge-fail { background: var(--red-bg); color: var(--red); }
|
|
319
|
+
.badge-pass { background: var(--green-bg); color: var(--green); border-color: rgba(74,222,128,.18); }
|
|
320
|
+
.badge-warn { background: var(--yellow-bg); color: var(--yellow); border-color: rgba(245,158,11,.18); }
|
|
321
|
+
.badge-fail { background: var(--red-bg); color: var(--red); border-color: rgba(239,68,68,.18); }
|
|
246
322
|
|
|
247
|
-
.badge-git { background: var(--blue-bg); color: var(--blue); }
|
|
248
|
-
.badge-shadow { background: var(--amber-bg); color: var(--amber); }
|
|
249
|
-
.badge-pre { background: var(--purple-bg); color: var(--purple); }
|
|
250
|
-
.badge-trigger { background: var(--bg-tertiary); color: var(--text-secondary); font-size: 0.7rem; }
|
|
251
|
-
.badge-intent { background: var(--blue-bg); color: var(--blue); font-size: 0.7rem; max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: middle; }
|
|
323
|
+
.badge-git { background: var(--blue-bg); color: var(--blue); border-color: rgba(59,130,246,.18); }
|
|
324
|
+
.badge-shadow { background: var(--amber-bg); color: var(--amber); border-color: rgba(245,158,11,.18); }
|
|
325
|
+
.badge-pre { background: var(--purple-bg); color: var(--purple); border-color: rgba(167,139,250,.18); }
|
|
326
|
+
.badge-trigger { background: var(--bg-tertiary); color: var(--text-secondary); font-size: 0.7rem; border-color: var(--border-subtle); }
|
|
327
|
+
.badge-intent { background: var(--blue-bg); color: var(--blue); font-size: 0.7rem; border-color: rgba(59,130,246,.18); max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: middle; }
|
|
252
328
|
|
|
253
|
-
.backup-summary-cell { max-width:
|
|
254
|
-
|
|
255
|
-
.
|
|
256
|
-
.
|
|
329
|
+
.backup-summary-cell { max-width: 400px; }
|
|
330
|
+
|
|
331
|
+
.summary-stack { display: flex; flex-direction: column; gap: 4px; }
|
|
332
|
+
.summary-meta { display: flex; align-items: center; gap: 6px; }
|
|
333
|
+
.summary-files { font-size: 13px; font-weight: 600; color: var(--text-heading); }
|
|
334
|
+
.summary-intent {
|
|
335
|
+
font-size: 12px;
|
|
336
|
+
color: var(--blue);
|
|
337
|
+
background: var(--blue-bg);
|
|
338
|
+
padding: 2px 8px;
|
|
339
|
+
border-radius: 3px;
|
|
340
|
+
border-left: 2px solid var(--blue);
|
|
341
|
+
overflow: hidden;
|
|
342
|
+
text-overflow: ellipsis;
|
|
343
|
+
white-space: nowrap;
|
|
344
|
+
max-width: 380px;
|
|
345
|
+
}
|
|
346
|
+
.summary-message {
|
|
347
|
+
font-size: 12px;
|
|
348
|
+
color: var(--text-secondary);
|
|
349
|
+
overflow: hidden;
|
|
350
|
+
text-overflow: ellipsis;
|
|
351
|
+
white-space: nowrap;
|
|
352
|
+
max-width: 380px;
|
|
353
|
+
}
|
|
354
|
+
.summary-detail {
|
|
355
|
+
font-size: 11px;
|
|
356
|
+
color: var(--text-tertiary);
|
|
357
|
+
overflow: hidden;
|
|
358
|
+
text-overflow: ellipsis;
|
|
359
|
+
white-space: nowrap;
|
|
360
|
+
max-width: 380px;
|
|
361
|
+
}
|
|
257
362
|
|
|
258
363
|
/* ── Stats Row ────────────────────────────────────────────── */
|
|
259
364
|
|
|
260
365
|
.stats-row {
|
|
261
366
|
display: flex;
|
|
262
367
|
flex-wrap: wrap;
|
|
263
|
-
gap:
|
|
264
|
-
margin-bottom:
|
|
368
|
+
gap: 14px;
|
|
369
|
+
margin-bottom: 18px;
|
|
265
370
|
}
|
|
266
371
|
|
|
267
372
|
.stat-card {
|
|
268
373
|
background: var(--bg-secondary);
|
|
269
374
|
border: 1px solid var(--border);
|
|
270
375
|
border-radius: var(--radius);
|
|
271
|
-
padding:
|
|
272
|
-
min-width:
|
|
376
|
+
padding: 14px 18px;
|
|
377
|
+
min-width: 150px;
|
|
273
378
|
flex: 1;
|
|
379
|
+
transition: border-color var(--transition);
|
|
274
380
|
}
|
|
381
|
+
.stat-card:hover { border-color: var(--border-glow); }
|
|
275
382
|
.stat-label {
|
|
276
|
-
font-size:
|
|
383
|
+
font-size: 10px;
|
|
384
|
+
font-weight: 700;
|
|
277
385
|
text-transform: uppercase;
|
|
278
|
-
letter-spacing: .
|
|
386
|
+
letter-spacing: .08em;
|
|
279
387
|
color: var(--text-tertiary);
|
|
280
|
-
margin-bottom:
|
|
388
|
+
margin-bottom: 6px;
|
|
281
389
|
}
|
|
282
390
|
.stat-value {
|
|
283
|
-
font-size:
|
|
284
|
-
font-weight:
|
|
391
|
+
font-size: 22px;
|
|
392
|
+
font-weight: 800;
|
|
393
|
+
letter-spacing: -.02em;
|
|
394
|
+
color: var(--text-heading);
|
|
285
395
|
}
|
|
286
396
|
|
|
287
397
|
/* ── Filter Bar ───────────────────────────────────────────── */
|
|
@@ -289,25 +399,31 @@ main {
|
|
|
289
399
|
.filter-bar {
|
|
290
400
|
display: flex;
|
|
291
401
|
flex-wrap: wrap;
|
|
292
|
-
gap:
|
|
293
|
-
margin-bottom:
|
|
402
|
+
gap: 8px;
|
|
403
|
+
margin-bottom: 14px;
|
|
294
404
|
}
|
|
295
405
|
|
|
296
406
|
.filter-btn {
|
|
297
|
-
padding:
|
|
407
|
+
padding: 5px 14px;
|
|
298
408
|
border: 1px solid var(--border);
|
|
299
|
-
border-radius:
|
|
409
|
+
border-radius: 16px;
|
|
300
410
|
background: transparent;
|
|
301
411
|
color: var(--text-secondary);
|
|
302
412
|
font-size: 12px;
|
|
413
|
+
font-weight: 500;
|
|
303
414
|
cursor: pointer;
|
|
304
|
-
transition: all
|
|
415
|
+
transition: all var(--transition);
|
|
416
|
+
}
|
|
417
|
+
.filter-btn:hover {
|
|
418
|
+
background: var(--bg-hover);
|
|
419
|
+
color: var(--text-primary);
|
|
420
|
+
border-color: rgba(100,116,139,.3);
|
|
305
421
|
}
|
|
306
|
-
.filter-btn:hover { background: var(--bg-hover); color: var(--text-primary); }
|
|
307
422
|
.filter-btn.active {
|
|
308
423
|
background: var(--blue-bg);
|
|
309
424
|
color: var(--blue);
|
|
310
|
-
border-color:
|
|
425
|
+
border-color: rgba(59,130,246,.35);
|
|
426
|
+
font-weight: 600;
|
|
311
427
|
}
|
|
312
428
|
|
|
313
429
|
/* ── Table ────────────────────────────────────────────────── */
|
|
@@ -317,7 +433,7 @@ main {
|
|
|
317
433
|
border: 1px solid var(--border);
|
|
318
434
|
border-radius: var(--radius);
|
|
319
435
|
overflow: auto;
|
|
320
|
-
max-height:
|
|
436
|
+
max-height: 440px;
|
|
321
437
|
}
|
|
322
438
|
|
|
323
439
|
.data-table {
|
|
@@ -327,115 +443,140 @@ main {
|
|
|
327
443
|
}
|
|
328
444
|
.data-table th {
|
|
329
445
|
text-align: left;
|
|
330
|
-
padding:
|
|
331
|
-
font-weight:
|
|
332
|
-
font-size:
|
|
446
|
+
padding: 12px 16px;
|
|
447
|
+
font-weight: 700;
|
|
448
|
+
font-size: 10px;
|
|
333
449
|
text-transform: uppercase;
|
|
334
|
-
letter-spacing: .
|
|
450
|
+
letter-spacing: .08em;
|
|
335
451
|
color: var(--text-tertiary);
|
|
336
452
|
border-bottom: 1px solid var(--border);
|
|
337
453
|
position: sticky;
|
|
338
454
|
top: 0;
|
|
339
|
-
background: var(--bg-
|
|
455
|
+
background: var(--bg-elevated);
|
|
456
|
+
z-index: 2;
|
|
340
457
|
}
|
|
341
458
|
.data-table td {
|
|
342
|
-
padding:
|
|
343
|
-
border-bottom: 1px solid
|
|
459
|
+
padding: 10px 16px;
|
|
460
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
344
461
|
vertical-align: top;
|
|
345
462
|
}
|
|
346
|
-
.data-table tbody tr {
|
|
347
|
-
|
|
463
|
+
.data-table tbody tr {
|
|
464
|
+
cursor: pointer;
|
|
465
|
+
transition: background var(--transition);
|
|
466
|
+
}
|
|
467
|
+
.data-table tbody tr:hover {
|
|
468
|
+
background: rgba(59,130,246,.05);
|
|
469
|
+
}
|
|
470
|
+
.data-table tbody tr:nth-child(even) {
|
|
471
|
+
background: rgba(255,255,255,.015);
|
|
472
|
+
}
|
|
473
|
+
.data-table tbody tr:nth-child(even):hover {
|
|
474
|
+
background: rgba(59,130,246,.05);
|
|
475
|
+
}
|
|
348
476
|
|
|
349
477
|
/* ── Protection ───────────────────────────────────────────── */
|
|
350
478
|
|
|
351
479
|
.protection-grid {
|
|
352
480
|
display: grid;
|
|
353
481
|
grid-template-columns: 1fr 1fr;
|
|
354
|
-
gap:
|
|
482
|
+
gap: 16px;
|
|
355
483
|
}
|
|
356
484
|
|
|
357
485
|
.pattern-card {
|
|
358
486
|
background: var(--bg-secondary);
|
|
359
487
|
border: 1px solid var(--border);
|
|
360
488
|
border-radius: var(--radius);
|
|
361
|
-
padding:
|
|
489
|
+
padding: 18px;
|
|
490
|
+
transition: border-color var(--transition);
|
|
362
491
|
}
|
|
492
|
+
.pattern-card:hover { border-color: var(--border-glow); }
|
|
363
493
|
.pattern-card h4 {
|
|
364
|
-
font-size:
|
|
494
|
+
font-size: 10px;
|
|
495
|
+
font-weight: 700;
|
|
365
496
|
text-transform: uppercase;
|
|
366
|
-
letter-spacing: .
|
|
497
|
+
letter-spacing: .08em;
|
|
367
498
|
color: var(--text-tertiary);
|
|
368
|
-
margin-bottom:
|
|
499
|
+
margin-bottom: 12px;
|
|
369
500
|
}
|
|
370
501
|
|
|
371
502
|
.pattern-list {
|
|
372
503
|
list-style: none;
|
|
373
504
|
display: flex;
|
|
374
505
|
flex-wrap: wrap;
|
|
375
|
-
gap:
|
|
506
|
+
gap: 8px;
|
|
376
507
|
}
|
|
377
508
|
.pattern-item {
|
|
378
|
-
font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;
|
|
509
|
+
font-family: 'JetBrains Mono', 'Cascadia Code', 'Fira Code', 'Consolas', monospace;
|
|
379
510
|
font-size: 12px;
|
|
380
511
|
background: var(--bg-tertiary);
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
512
|
+
border: 1px solid var(--border-subtle);
|
|
513
|
+
padding: 3px 10px;
|
|
514
|
+
border-radius: 4px;
|
|
515
|
+
color: var(--blue);
|
|
516
|
+
transition: border-color var(--transition);
|
|
384
517
|
}
|
|
518
|
+
.pattern-item:hover { border-color: var(--blue); }
|
|
385
519
|
|
|
386
520
|
.protection-note {
|
|
387
|
-
margin-top:
|
|
521
|
+
margin-top: 16px;
|
|
388
522
|
font-size: 12px;
|
|
389
523
|
color: var(--text-tertiary);
|
|
390
524
|
font-style: italic;
|
|
525
|
+
line-height: 1.5;
|
|
391
526
|
}
|
|
392
527
|
|
|
393
528
|
.protection-count {
|
|
394
|
-
margin-top:
|
|
529
|
+
margin-top: 14px;
|
|
395
530
|
font-size: 14px;
|
|
396
|
-
font-weight:
|
|
531
|
+
font-weight: 700;
|
|
532
|
+
color: var(--text-heading);
|
|
397
533
|
}
|
|
398
534
|
|
|
399
535
|
/* ── Diagnostics Summary ──────────────────────────────────── */
|
|
400
536
|
|
|
401
537
|
.diag-summary {
|
|
402
|
-
background: var(--bg-secondary);
|
|
538
|
+
background: linear-gradient(135deg, var(--bg-secondary), var(--bg-elevated));
|
|
403
539
|
border: 1px solid var(--border);
|
|
404
540
|
border-radius: var(--radius);
|
|
405
|
-
padding:
|
|
541
|
+
padding: 20px 24px;
|
|
406
542
|
display: flex;
|
|
407
543
|
align-items: center;
|
|
408
|
-
gap:
|
|
544
|
+
gap: 24px;
|
|
409
545
|
cursor: pointer;
|
|
410
|
-
transition:
|
|
546
|
+
transition: all var(--transition);
|
|
411
547
|
}
|
|
412
548
|
.diag-summary:hover {
|
|
413
|
-
transform: translateY(-
|
|
414
|
-
box-shadow:
|
|
549
|
+
transform: translateY(-2px);
|
|
550
|
+
box-shadow: var(--shadow-md);
|
|
551
|
+
border-color: var(--border-glow);
|
|
415
552
|
}
|
|
416
553
|
|
|
417
554
|
.diag-counts {
|
|
418
555
|
display: flex;
|
|
419
|
-
gap:
|
|
556
|
+
gap: 16px;
|
|
420
557
|
}
|
|
421
558
|
.diag-count {
|
|
422
559
|
text-align: center;
|
|
560
|
+
min-width: 48px;
|
|
423
561
|
}
|
|
424
562
|
.diag-count .num {
|
|
425
|
-
font-size:
|
|
426
|
-
font-weight:
|
|
563
|
+
font-size: 26px;
|
|
564
|
+
font-weight: 800;
|
|
427
565
|
display: block;
|
|
566
|
+
letter-spacing: -.02em;
|
|
428
567
|
}
|
|
429
568
|
.diag-count .label {
|
|
430
|
-
font-size:
|
|
569
|
+
font-size: 10px;
|
|
570
|
+
font-weight: 600;
|
|
431
571
|
text-transform: uppercase;
|
|
432
|
-
letter-spacing: .
|
|
572
|
+
letter-spacing: .06em;
|
|
433
573
|
}
|
|
434
574
|
|
|
435
575
|
.diag-hint {
|
|
436
576
|
margin-left: auto;
|
|
437
577
|
font-size: 12px;
|
|
438
578
|
color: var(--text-tertiary);
|
|
579
|
+
opacity: .7;
|
|
439
580
|
}
|
|
440
581
|
|
|
441
582
|
/* ── Drawer ───────────────────────────────────────────────── */
|
|
@@ -443,11 +584,12 @@ main {
|
|
|
443
584
|
.drawer-overlay {
|
|
444
585
|
position: fixed;
|
|
445
586
|
inset: 0;
|
|
446
|
-
background: rgba(0,0,0,.
|
|
587
|
+
background: rgba(0,0,0,.6);
|
|
588
|
+
backdrop-filter: blur(4px);
|
|
447
589
|
z-index: 100;
|
|
448
590
|
opacity: 0;
|
|
449
591
|
visibility: hidden;
|
|
450
|
-
transition: opacity .
|
|
592
|
+
transition: opacity .25s, visibility .25s;
|
|
451
593
|
}
|
|
452
594
|
.drawer-overlay.active {
|
|
453
595
|
opacity: 1;
|
|
@@ -461,12 +603,13 @@ main {
|
|
|
461
603
|
max-width: 92vw;
|
|
462
604
|
background: var(--bg-secondary);
|
|
463
605
|
border-left: 1px solid var(--border);
|
|
606
|
+
box-shadow: -8px 0 32px rgba(0,0,0,.4);
|
|
464
607
|
z-index: 101;
|
|
465
608
|
display: flex;
|
|
466
609
|
flex-direction: column;
|
|
467
610
|
transform: translateX(100%);
|
|
468
611
|
visibility: hidden;
|
|
469
|
-
transition: transform .
|
|
612
|
+
transition: transform .3s cubic-bezier(.32,.72,0,1), visibility .3s;
|
|
470
613
|
}
|
|
471
614
|
.drawer.active {
|
|
472
615
|
transform: translateX(0);
|
|
@@ -477,99 +620,113 @@ main {
|
|
|
477
620
|
display: flex;
|
|
478
621
|
align-items: center;
|
|
479
622
|
justify-content: space-between;
|
|
480
|
-
padding:
|
|
623
|
+
padding: 18px 24px;
|
|
481
624
|
border-bottom: 1px solid var(--border);
|
|
482
625
|
}
|
|
483
|
-
.drawer-header h3 {
|
|
626
|
+
.drawer-header h3 {
|
|
627
|
+
font-size: 15px;
|
|
628
|
+
font-weight: 700;
|
|
629
|
+
color: var(--text-heading);
|
|
630
|
+
}
|
|
484
631
|
|
|
485
632
|
.drawer-close {
|
|
486
633
|
background: none;
|
|
487
634
|
border: none;
|
|
488
|
-
color: var(--text-
|
|
489
|
-
font-size:
|
|
635
|
+
color: var(--text-tertiary);
|
|
636
|
+
font-size: 24px;
|
|
490
637
|
cursor: pointer;
|
|
491
638
|
padding: 0 4px;
|
|
492
639
|
line-height: 1;
|
|
640
|
+
transition: color var(--transition);
|
|
641
|
+
border-radius: 4px;
|
|
493
642
|
}
|
|
494
|
-
.drawer-close:hover { color: var(--text-primary); }
|
|
643
|
+
.drawer-close:hover { color: var(--text-primary); background: var(--bg-hover); }
|
|
495
644
|
|
|
496
645
|
.drawer-body {
|
|
497
646
|
flex: 1;
|
|
498
647
|
overflow-y: auto;
|
|
499
|
-
padding:
|
|
648
|
+
padding: 24px;
|
|
500
649
|
}
|
|
501
650
|
|
|
502
651
|
/* ── Drawer: Restore Point ────────────────────────────────── */
|
|
503
652
|
|
|
504
653
|
.restore-field {
|
|
505
|
-
margin-bottom:
|
|
654
|
+
margin-bottom: 16px;
|
|
506
655
|
}
|
|
507
656
|
.restore-field-label {
|
|
508
|
-
font-size:
|
|
657
|
+
font-size: 10px;
|
|
658
|
+
font-weight: 700;
|
|
509
659
|
text-transform: uppercase;
|
|
510
|
-
letter-spacing: .
|
|
660
|
+
letter-spacing: .08em;
|
|
511
661
|
color: var(--text-tertiary);
|
|
512
|
-
margin-bottom:
|
|
662
|
+
margin-bottom: 4px;
|
|
513
663
|
}
|
|
514
664
|
.restore-field-value {
|
|
515
665
|
font-size: 14px;
|
|
516
666
|
word-break: break-all;
|
|
667
|
+
line-height: 1.5;
|
|
517
668
|
}
|
|
518
669
|
|
|
519
670
|
.restore-actions {
|
|
520
671
|
display: flex;
|
|
521
672
|
gap: 8px;
|
|
522
|
-
margin-top:
|
|
523
|
-
padding-top:
|
|
673
|
+
margin-top: 24px;
|
|
674
|
+
padding-top: 18px;
|
|
524
675
|
border-top: 1px solid var(--border);
|
|
525
676
|
}
|
|
526
677
|
|
|
527
678
|
.json-preview {
|
|
528
679
|
margin-top: 16px;
|
|
529
|
-
padding:
|
|
680
|
+
padding: 14px;
|
|
530
681
|
background: var(--bg-primary);
|
|
531
|
-
border
|
|
532
|
-
|
|
682
|
+
border: 1px solid var(--border-subtle);
|
|
683
|
+
border-radius: var(--radius-sm);
|
|
684
|
+
font-family: 'JetBrains Mono', 'Cascadia Code', 'Fira Code', 'Consolas', monospace;
|
|
533
685
|
font-size: 12px;
|
|
534
686
|
white-space: pre-wrap;
|
|
535
687
|
word-break: break-all;
|
|
536
688
|
max-height: 300px;
|
|
537
689
|
overflow: auto;
|
|
538
690
|
color: var(--text-secondary);
|
|
691
|
+
line-height: 1.6;
|
|
539
692
|
}
|
|
540
693
|
|
|
541
694
|
/* ── Drawer: Doctor Checks ────────────────────────────────── */
|
|
542
695
|
|
|
543
696
|
.check-item {
|
|
544
|
-
border-bottom: 1px solid
|
|
697
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
545
698
|
}
|
|
546
699
|
.check-item:last-child { border-bottom: none; }
|
|
547
700
|
|
|
548
701
|
.check-item summary {
|
|
549
702
|
display: flex;
|
|
550
703
|
align-items: center;
|
|
551
|
-
gap:
|
|
552
|
-
padding:
|
|
704
|
+
gap: 10px;
|
|
705
|
+
padding: 12px 0;
|
|
553
706
|
cursor: pointer;
|
|
554
707
|
list-style: none;
|
|
555
708
|
font-size: 13px;
|
|
709
|
+
transition: color var(--transition);
|
|
556
710
|
}
|
|
711
|
+
.check-item summary:hover { color: var(--text-heading); }
|
|
557
712
|
.check-item summary::-webkit-details-marker { display: none; }
|
|
558
713
|
.check-item summary::before {
|
|
559
714
|
content: '▸';
|
|
560
|
-
font-size:
|
|
715
|
+
font-size: 10px;
|
|
561
716
|
color: var(--text-tertiary);
|
|
562
|
-
transition: transform .
|
|
563
|
-
width:
|
|
717
|
+
transition: transform .2s ease;
|
|
718
|
+
width: 14px;
|
|
564
719
|
text-align: center;
|
|
720
|
+
flex-shrink: 0;
|
|
565
721
|
}
|
|
566
722
|
.check-item[open] summary::before { transform: rotate(90deg); }
|
|
567
723
|
|
|
568
|
-
.check-name { flex: 1; }
|
|
724
|
+
.check-name { flex: 1; font-weight: 500; }
|
|
569
725
|
.check-detail {
|
|
570
|
-
padding: 0 0
|
|
726
|
+
padding: 0 0 12px 24px;
|
|
571
727
|
font-size: 12px;
|
|
572
728
|
color: var(--text-secondary);
|
|
729
|
+
line-height: 1.6;
|
|
573
730
|
}
|
|
574
731
|
|
|
575
732
|
/* ── State panels ─────────────────────────────────────────── */
|
|
@@ -579,27 +736,28 @@ main {
|
|
|
579
736
|
flex-direction: column;
|
|
580
737
|
align-items: center;
|
|
581
738
|
justify-content: center;
|
|
582
|
-
min-height:
|
|
739
|
+
min-height: 320px;
|
|
583
740
|
text-align: center;
|
|
584
741
|
color: var(--text-secondary);
|
|
585
742
|
}
|
|
586
743
|
|
|
587
744
|
.error-icon {
|
|
588
|
-
font-size:
|
|
589
|
-
margin-bottom:
|
|
745
|
+
font-size: 40px;
|
|
746
|
+
margin-bottom: 14px;
|
|
590
747
|
color: var(--yellow);
|
|
748
|
+
filter: drop-shadow(0 0 8px var(--yellow-glow));
|
|
591
749
|
}
|
|
592
750
|
|
|
593
751
|
.error-panel {
|
|
594
752
|
text-align: center;
|
|
595
|
-
padding:
|
|
753
|
+
padding: 48px 24px;
|
|
596
754
|
color: var(--text-tertiary);
|
|
597
755
|
}
|
|
598
|
-
.error-panel .error-icon { font-size:
|
|
756
|
+
.error-panel .error-icon { font-size: 32px; margin-bottom: 10px; }
|
|
599
757
|
|
|
600
758
|
.empty-state {
|
|
601
759
|
text-align: center;
|
|
602
|
-
padding:
|
|
760
|
+
padding: 36px 20px;
|
|
603
761
|
color: var(--text-tertiary);
|
|
604
762
|
font-style: italic;
|
|
605
763
|
}
|
|
@@ -607,12 +765,12 @@ main {
|
|
|
607
765
|
/* ── Spinner ──────────────────────────────────────────────── */
|
|
608
766
|
|
|
609
767
|
.spinner {
|
|
610
|
-
width:
|
|
768
|
+
width: 36px; height: 36px;
|
|
611
769
|
border: 3px solid var(--border);
|
|
612
770
|
border-top-color: var(--blue);
|
|
613
771
|
border-radius: 50%;
|
|
614
|
-
animation: spin .
|
|
615
|
-
margin-bottom:
|
|
772
|
+
animation: spin .7s linear infinite;
|
|
773
|
+
margin-bottom: 14px;
|
|
616
774
|
}
|
|
617
775
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
618
776
|
|
|
@@ -624,7 +782,7 @@ main {
|
|
|
624
782
|
|
|
625
783
|
.text-muted { color: var(--text-secondary); }
|
|
626
784
|
.text-sm { font-size: 12px; }
|
|
627
|
-
.text-mono { font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace; font-size: 12px; }
|
|
785
|
+
.text-mono { font-family: 'JetBrains Mono', 'Cascadia Code', 'Fira Code', 'Consolas', monospace; font-size: 12px; }
|
|
628
786
|
|
|
629
787
|
.flex-between { display: flex; align-items: center; justify-content: space-between; }
|
|
630
788
|
.gap-4 { gap: 4px; }
|
|
@@ -632,19 +790,20 @@ main {
|
|
|
632
790
|
|
|
633
791
|
.copy-toast {
|
|
634
792
|
position: fixed;
|
|
635
|
-
bottom:
|
|
793
|
+
bottom: 28px;
|
|
636
794
|
left: 50%;
|
|
637
795
|
transform: translateX(-50%) translateY(20px);
|
|
638
796
|
background: var(--green);
|
|
639
797
|
color: #000;
|
|
640
|
-
padding:
|
|
641
|
-
border-radius:
|
|
798
|
+
padding: 8px 20px;
|
|
799
|
+
border-radius: var(--radius-sm);
|
|
642
800
|
font-size: 13px;
|
|
643
|
-
font-weight:
|
|
801
|
+
font-weight: 700;
|
|
644
802
|
opacity: 0;
|
|
645
|
-
transition: opacity .
|
|
803
|
+
transition: opacity .25s, transform .25s;
|
|
646
804
|
z-index: 200;
|
|
647
805
|
pointer-events: none;
|
|
806
|
+
box-shadow: 0 4px 16px rgba(74,222,128,.3);
|
|
648
807
|
}
|
|
649
808
|
.copy-toast.show {
|
|
650
809
|
opacity: 1;
|
|
@@ -658,12 +817,19 @@ main {
|
|
|
658
817
|
::-webkit-scrollbar-thumb { background: var(--bg-tertiary); border-radius: 3px; }
|
|
659
818
|
::-webkit-scrollbar-thumb:hover { background: var(--text-tertiary); }
|
|
660
819
|
|
|
820
|
+
/* ── Selection ───────────────────────────────────────────── */
|
|
821
|
+
|
|
822
|
+
::selection {
|
|
823
|
+
background: rgba(59,130,246,.3);
|
|
824
|
+
color: var(--text-heading);
|
|
825
|
+
}
|
|
826
|
+
|
|
661
827
|
/* ── Responsive ───────────────────────────────────────────── */
|
|
662
828
|
|
|
663
829
|
@media (max-width: 768px) {
|
|
664
|
-
#topbar { padding: 0
|
|
830
|
+
#topbar { padding: 0 14px; gap: 8px; }
|
|
665
831
|
.logo-text { display: none; }
|
|
666
|
-
main { padding:
|
|
832
|
+
main { padding: 18px 14px 40px; }
|
|
667
833
|
.card-grid { grid-template-columns: 1fr; }
|
|
668
834
|
.card-health { grid-column: auto; }
|
|
669
835
|
.protection-grid { grid-template-columns: 1fr; }
|
|
@@ -276,39 +276,6 @@ async function runBackup(projectDir, intervalOverride) {
|
|
|
276
276
|
// Git snapshot via Core
|
|
277
277
|
if ((cfg.backup_strategy === 'git' || cfg.backup_strategy === 'both') && repo) {
|
|
278
278
|
const context = { trigger: 'auto', changedFileCount };
|
|
279
|
-
if (porcelain) {
|
|
280
|
-
let pLines = porcelain.split('\n').filter(Boolean);
|
|
281
|
-
if (cfg.protect.length > 0 || cfg.ignore.length > 0) {
|
|
282
|
-
pLines = pLines.filter(line => {
|
|
283
|
-
const filePart = line.substring(3);
|
|
284
|
-
const arrowIdx = filePart.indexOf(' -> ');
|
|
285
|
-
const raw = arrowIdx >= 0 ? filePart.substring(arrowIdx + 4) : filePart;
|
|
286
|
-
const rel = unquoteGitPath(raw);
|
|
287
|
-
const fakeFile = { rel, full: path.join(projectDir, rel) };
|
|
288
|
-
return filterFiles([fakeFile], cfg).length > 0;
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
if (pLines.length > 0) {
|
|
292
|
-
const groups = { M: [], A: [], D: [], R: [] };
|
|
293
|
-
for (const l of pLines) {
|
|
294
|
-
const code = l.substring(0, 2).trim();
|
|
295
|
-
const filePart = l.substring(3);
|
|
296
|
-
const arrowIdx = filePart.indexOf(' -> ');
|
|
297
|
-
const file = arrowIdx >= 0 ? filePart.substring(arrowIdx + 4) : filePart;
|
|
298
|
-
const key = code.startsWith('R') ? 'R'
|
|
299
|
-
: code.includes('D') ? 'D'
|
|
300
|
-
: code.includes('A') || code === '??' ? 'A'
|
|
301
|
-
: 'M';
|
|
302
|
-
groups[key].push(file);
|
|
303
|
-
}
|
|
304
|
-
const parts = [];
|
|
305
|
-
if (groups.M.length) parts.push(`Modified ${groups.M.length}: ${groups.M.slice(0, 5).join(', ')}`);
|
|
306
|
-
if (groups.A.length) parts.push(`Added ${groups.A.length}: ${groups.A.slice(0, 5).join(', ')}`);
|
|
307
|
-
if (groups.D.length) parts.push(`Deleted ${groups.D.length}: ${groups.D.slice(0, 5).join(', ')}`);
|
|
308
|
-
if (groups.R.length) parts.push(`Renamed ${groups.R.length}: ${groups.R.slice(0, 5).join(', ')}`);
|
|
309
|
-
context.summary = parts.join('; ');
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
279
|
const snapResult = createGitSnapshot(projectDir, cfg, { branchRef, context });
|
|
313
280
|
if (snapResult.status === 'created') {
|
|
314
281
|
let msg = `Git snapshot ${snapResult.shortHash} (${snapResult.fileCount} files)`;
|
|
@@ -138,6 +138,46 @@ function createGitSnapshot(projectDir, cfg, opts = {}) {
|
|
|
138
138
|
return { status: 'skipped', reason: 'tree unchanged' };
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
// Build incremental summary from actual tree diff (not working-dir status)
|
|
142
|
+
let changedCount;
|
|
143
|
+
let incrementalSummary;
|
|
144
|
+
if (parentTree) {
|
|
145
|
+
const diffOut = git(['diff-tree', '--no-commit-id', '--name-status', '-r', parentTree, newTree], { cwd, allowFail: true });
|
|
146
|
+
if (diffOut) {
|
|
147
|
+
const diffLines = diffOut.split('\n').filter(Boolean);
|
|
148
|
+
changedCount = diffLines.length;
|
|
149
|
+
const groups = { M: [], A: [], D: [], R: [] };
|
|
150
|
+
for (const line of diffLines) {
|
|
151
|
+
const tab = line.indexOf('\t');
|
|
152
|
+
if (tab < 0) continue;
|
|
153
|
+
const code = line.substring(0, tab).trim();
|
|
154
|
+
const filePart = line.substring(tab + 1);
|
|
155
|
+
const key = code.startsWith('R') ? 'R'
|
|
156
|
+
: code === 'D' ? 'D'
|
|
157
|
+
: code === 'A' ? 'A'
|
|
158
|
+
: 'M';
|
|
159
|
+
const fileName = filePart.split('\t').pop();
|
|
160
|
+
groups[key].push(fileName);
|
|
161
|
+
}
|
|
162
|
+
const parts = [];
|
|
163
|
+
if (groups.M.length) parts.push(`Modified ${groups.M.length}: ${groups.M.slice(0, 5).join(', ')}`);
|
|
164
|
+
if (groups.A.length) parts.push(`Added ${groups.A.length}: ${groups.A.slice(0, 5).join(', ')}`);
|
|
165
|
+
if (groups.D.length) parts.push(`Deleted ${groups.D.length}: ${groups.D.slice(0, 5).join(', ')}`);
|
|
166
|
+
if (groups.R.length) parts.push(`Renamed ${groups.R.length}: ${groups.R.slice(0, 5).join(', ')}`);
|
|
167
|
+
if (parts.length) incrementalSummary = parts.join('; ');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Override context summary with the accurate incremental one
|
|
172
|
+
if (incrementalSummary && opts.context) {
|
|
173
|
+
opts.context.summary = incrementalSummary;
|
|
174
|
+
} else if (incrementalSummary && !opts.context) {
|
|
175
|
+
opts.context = { summary: incrementalSummary };
|
|
176
|
+
}
|
|
177
|
+
if (changedCount != null && opts.context) {
|
|
178
|
+
opts.context.changedFileCount = changedCount;
|
|
179
|
+
}
|
|
180
|
+
|
|
141
181
|
const ts = formatTimestamp(new Date());
|
|
142
182
|
const msg = buildCommitMessage(ts, opts);
|
|
143
183
|
const commitArgs = parentHash
|
|
@@ -154,18 +194,13 @@ function createGitSnapshot(projectDir, cfg, opts = {}) {
|
|
|
154
194
|
const lsOut = git(['ls-tree', '--name-only', '-r', newTree], { cwd, allowFail: true });
|
|
155
195
|
const fileCount = lsOut ? lsOut.split('\n').filter(Boolean).length : 0;
|
|
156
196
|
|
|
157
|
-
let changedCount;
|
|
158
|
-
if (parentTree) {
|
|
159
|
-
const diff = git(['diff-tree', '--no-commit-id', '--name-only', '-r', parentTree, newTree], { cwd, allowFail: true });
|
|
160
|
-
changedCount = diff ? diff.split('\n').filter(Boolean).length : 0;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
197
|
return {
|
|
164
198
|
status: 'created',
|
|
165
199
|
commitHash,
|
|
166
200
|
shortHash: commitHash.substring(0, 7),
|
|
167
201
|
fileCount,
|
|
168
202
|
changedCount,
|
|
203
|
+
incrementalSummary,
|
|
169
204
|
secretsExcluded: secretsExcluded.length > 0 ? secretsExcluded : undefined,
|
|
170
205
|
};
|
|
171
206
|
} catch (e) {
|