clawkeep 0.1.0

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/ui/index.html ADDED
@@ -0,0 +1,78 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>ClawKeep</title>
7
+ <link rel="stylesheet" href="/style.css">
8
+ </head>
9
+ <body>
10
+ <div class="app">
11
+ <aside class="sidebar">
12
+ <div class="sidebar-brand">
13
+ <span class="brand-icon">🐾</span>
14
+ <span class="brand-text">ClawKeep</span>
15
+ </div>
16
+ <nav class="sidebar-nav">
17
+ <button class="nav-item active" data-tab="dashboard"><span class="nav-icon">◉</span> Dashboard</button>
18
+ <button class="nav-item" data-tab="history"><span class="nav-icon">↻</span> History</button>
19
+ <button class="nav-item" data-tab="backup"><span class="nav-icon">☁</span> Backup</button>
20
+ <button class="nav-item" data-tab="browse"><span class="nav-icon">≡</span> Browse</button>
21
+ </nav>
22
+ <div class="sidebar-footer">
23
+ <div id="sidebar-info" class="sidebar-info"></div>
24
+ <a class="sidebar-link" href="https://clawkeep.com" target="_blank">clawkeep.com</a>
25
+ </div>
26
+ </aside>
27
+
28
+ <main class="main">
29
+ <header class="topbar">
30
+ <div><h1 id="page-title">Dashboard</h1></div>
31
+ <div class="topbar-right">
32
+ <span id="last-updated" class="topbar-meta"></span>
33
+ <button class="btn btn-ghost" onclick="refresh()">↻ Refresh</button>
34
+ <button class="btn btn-primary" onclick="takeSnap()">✚ Backup now</button>
35
+ </div>
36
+ </header>
37
+
38
+ <div class="content">
39
+ <!-- DASHBOARD -->
40
+ <section id="tab-dashboard">
41
+ <div id="stats-grid" class="stats-grid"></div>
42
+ <div id="protection-status"></div>
43
+ <div id="pending-banner"></div>
44
+ <div class="box"><div class="box-header">Recent changes <span id="recent-count" class="count"></span></div><div id="recent-body" class="box-body"></div></div>
45
+ </section>
46
+
47
+ <!-- HISTORY -->
48
+ <section id="tab-history" class="hidden">
49
+ <div id="history-list">
50
+ <div id="compare-bar" class="compare-bar hidden"></div>
51
+ <div class="box"><div class="box-header"><span>Backup history <span id="history-count" class="count"></span></span><button id="compare-toggle" class="btn btn-ghost btn-sm" onclick="toggleCompareMode()">Compare</button></div><div id="history-body" class="box-body"></div></div>
52
+ </div>
53
+ <div id="compare-result" class="hidden"></div>
54
+ </section>
55
+
56
+ <!-- BACKUP -->
57
+ <section id="tab-backup" class="hidden">
58
+ <div id="backup-content"></div>
59
+ </section>
60
+
61
+ <!-- BROWSE -->
62
+ <section id="tab-browse" class="hidden">
63
+ <div class="box">
64
+ <div class="box-header"><div id="fb-breadcrumb" class="breadcrumb"></div></div>
65
+ <div id="fb-body" class="box-body"></div>
66
+ </div>
67
+ <div id="file-viewer" class="file-viewer"></div>
68
+ <div id="readme-render"></div>
69
+ </section>
70
+ </div>
71
+ </main>
72
+ </div>
73
+ <div id="modal-overlay" class="modal-overlay hidden" onclick="closeModal()"></div>
74
+ <div id="modal" class="modal hidden"></div>
75
+ <div id="toast-wrap" class="toast-wrap"></div>
76
+ <script src="/app.js"></script>
77
+ </body>
78
+ </html>
package/ui/style.css ADDED
@@ -0,0 +1,386 @@
1
+ /* ClawKeep — Backup Dashboard */
2
+ :root {
3
+ --bg: #0d1117;
4
+ --bg2: #010409;
5
+ --surface: #161b22;
6
+ --overlay: #1c2129;
7
+ --border: #30363d;
8
+ --border2: #3d444d;
9
+ --t1: #f0f6fc;
10
+ --t2: #e6edf3;
11
+ --t3: #8b949e;
12
+ --t4: #484f58;
13
+ --link: #58a6ff;
14
+ --green: #3fb950;
15
+ --green-bg: rgba(63,185,80,0.1);
16
+ --red: #f85149;
17
+ --red-bg: rgba(248,81,73,0.1);
18
+ --yellow: #d29922;
19
+ --yellow-bg: rgba(210,153,34,0.1);
20
+ --purple: #bc8cff;
21
+ --blue-bg: rgba(88,166,255,0.1);
22
+ --orange: #d18616;
23
+ --mono: ui-monospace, 'SFMono-Regular', 'SF Mono', Menlo, Consolas, monospace;
24
+ --sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
25
+ --r: 6px;
26
+ --sidebar: 240px;
27
+ }
28
+
29
+ * { margin: 0; padding: 0; box-sizing: border-box; }
30
+ body { font-family: var(--sans); background: var(--bg); color: var(--t2); font-size: 14px; line-height: 1.5; -webkit-font-smoothing: antialiased; }
31
+ a { color: var(--link); text-decoration: none; }
32
+ a:hover { text-decoration: underline; }
33
+
34
+ /* Layout */
35
+ .app { display: flex; min-height: 100vh; }
36
+
37
+ /* Sidebar */
38
+ .sidebar {
39
+ width: var(--sidebar); background: var(--bg2);
40
+ border-right: 1px solid var(--border);
41
+ display: flex; flex-direction: column;
42
+ position: fixed; top: 0; bottom: 0; left: 0; z-index: 20;
43
+ }
44
+ .sidebar-brand { padding: 16px; display: flex; align-items: center; gap: 10px; border-bottom: 1px solid var(--border); }
45
+ .brand-icon { font-size: 22px; }
46
+ .brand-text { font-size: 18px; font-weight: 600; color: var(--t1); letter-spacing: -0.02em; }
47
+ .brand-ver { font-size: 10px; color: var(--t4); margin-left: auto; font-family: var(--mono); }
48
+
49
+ .sidebar-nav { flex: 1; padding: 8px; }
50
+ .nav-item {
51
+ display: flex; align-items: center; gap: 10px;
52
+ padding: 7px 10px; border-radius: var(--r);
53
+ font-size: 14px; color: var(--t3); background: none;
54
+ border: none; cursor: pointer; font-family: var(--sans);
55
+ transition: background 0.08s; width: 100%; text-align: left;
56
+ }
57
+ .nav-item:hover { color: var(--t2); background: var(--overlay); }
58
+ .nav-item.active { color: var(--t1); background: var(--overlay); font-weight: 500; }
59
+ .nav-icon { font-size: 16px; width: 20px; text-align: center; }
60
+
61
+ .sidebar-footer { padding: 12px 16px; border-top: 1px solid var(--border); }
62
+ .sidebar-info { font-size: 12px; color: var(--t3); line-height: 1.4; }
63
+ .sidebar-info strong { font-size: 13px; color: var(--t2); font-weight: 600; display: block; margin-bottom: 2px; }
64
+ .sidebar-link { font-size: 11px; color: var(--t4); text-decoration: none; display: block; margin-top: 6px; }
65
+
66
+ /* Main */
67
+ .main { flex: 1; margin-left: var(--sidebar); }
68
+
69
+ /* Topbar */
70
+ .topbar {
71
+ display: flex; align-items: center; justify-content: space-between;
72
+ padding: 12px 24px; border-bottom: 1px solid var(--border);
73
+ background: var(--surface);
74
+ }
75
+ .topbar h1 { font-size: 16px; font-weight: 600; }
76
+ .topbar-right { display: flex; align-items: center; gap: 8px; }
77
+ .topbar-meta { font-size: 12px; color: var(--t4); margin-right: 4px; }
78
+
79
+ /* Buttons */
80
+ .btn {
81
+ display: inline-flex; align-items: center; gap: 6px;
82
+ padding: 5px 12px; border-radius: var(--r); font-size: 12px;
83
+ font-weight: 500; cursor: pointer; border: 1px solid var(--border);
84
+ font-family: var(--sans); transition: all 0.1s;
85
+ }
86
+ .btn-ghost { background: var(--overlay); color: var(--t2); }
87
+ .btn-ghost:hover { background: var(--border); }
88
+ .btn-primary { background: #238636; color: #fff; border-color: rgba(240,246,252,0.1); font-weight: 600; }
89
+ .btn-primary:hover { background: #2ea043; }
90
+
91
+ /* Content */
92
+ .content { padding: 24px; }
93
+
94
+ /* Stats */
95
+ .stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px; margin-bottom: 16px; background: var(--border); border: 1px solid var(--border); border-radius: var(--r); overflow: hidden; }
96
+ .stat-card { background: var(--surface); padding: 14px 16px; }
97
+ .stat-label { font-size: 11px; color: var(--t4); margin-bottom: 2px; text-transform: uppercase; letter-spacing: 0.05em; }
98
+ .stat-value { font-size: 22px; font-weight: 600; color: var(--t1); }
99
+ .stat-sub { font-size: 12px; color: var(--t4); margin-top: 2px; }
100
+
101
+ /* Pending banner — compact unsaved changes indicator */
102
+ .pending-banner {
103
+ display: flex; align-items: center; gap: 12px;
104
+ padding: 10px 16px; margin-bottom: 16px;
105
+ background: var(--yellow-bg); border: 1px solid rgba(210,153,34,0.25);
106
+ border-radius: var(--r); font-size: 13px;
107
+ }
108
+ .pending-left { display: flex; align-items: center; gap: 8px; flex-shrink: 0; color: var(--yellow); }
109
+ .pending-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--yellow); flex-shrink: 0; animation: pulse 2s infinite; }
110
+ @keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
111
+ .pending-files { flex: 1; display: flex; align-items: center; gap: 6px; overflow: hidden; font-family: var(--mono); font-size: 11px; color: var(--t3); flex-wrap: wrap; }
112
+ .pending-path { color: var(--t3); }
113
+ .pending-sep { color: var(--t4); }
114
+ .pending-more { color: var(--t4); font-style: italic; }
115
+ .btn-sm { padding: 4px 10px; font-size: 11px; }
116
+
117
+ /* Grid */
118
+ .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px; }
119
+
120
+ /* Box — GitHub style */
121
+ .box { border: 1px solid var(--border); border-radius: var(--r); overflow: hidden; background: var(--surface); }
122
+ .box + .box { margin-top: 16px; }
123
+ .box-header {
124
+ display: flex; align-items: center; justify-content: space-between;
125
+ padding: 12px 16px; background: var(--overlay); border-bottom: 1px solid var(--border);
126
+ font-size: 14px; font-weight: 600; color: var(--t1);
127
+ }
128
+ .box-header .count { font-weight: 400; color: var(--t3); font-size: 12px; margin-left: 6px; }
129
+ .box-header .file-icon { margin-right: 6px; }
130
+ .box-body { }
131
+ .box-body-pad { padding: 16px; }
132
+
133
+ /* File list — GitHub style */
134
+ .file-row {
135
+ display: flex; align-items: center; gap: 8px;
136
+ padding: 8px 16px; border-bottom: 1px solid var(--border);
137
+ font-size: 14px; cursor: pointer; transition: background 0.06s;
138
+ }
139
+ .file-row:last-child { border-bottom: none; }
140
+ .file-row:hover { background: var(--overlay); }
141
+ .file-icon { width: 20px; text-align: center; font-size: 14px; flex-shrink: 0; color: var(--t3); }
142
+ .file-name { color: var(--link); flex-shrink: 0; }
143
+ .file-name.is-dir { font-weight: 500; }
144
+ .file-msg { flex: 1; color: var(--t4); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: right; padding-left: 16px; }
145
+ .file-time { color: var(--t4); font-size: 12px; flex-shrink: 0; min-width: 80px; text-align: right; }
146
+
147
+ /* Breadcrumb */
148
+ .breadcrumb { display: flex; align-items: center; gap: 2px; font-size: 14px; }
149
+ .bc-seg { color: var(--link); cursor: pointer; }
150
+ .bc-seg:hover { text-decoration: underline; }
151
+ .bc-sep { color: var(--t4); margin: 0 2px; }
152
+ .bc-current { color: var(--t1); font-weight: 600; }
153
+
154
+ /* File viewer */
155
+ .file-viewer { margin-top: 16px; }
156
+ .fv-header {
157
+ display: flex; align-items: center; justify-content: space-between;
158
+ padding: 8px 16px; background: var(--overlay); border-bottom: 1px solid var(--border);
159
+ }
160
+ .fv-path { font-family: var(--mono); font-size: 12px; color: var(--t2); }
161
+ .fv-meta { font-size: 12px; color: var(--t3); display: flex; align-items: center; gap: 8px; }
162
+ .fv-close { background: none; border: none; color: var(--t3); cursor: pointer; font-size: 18px; padding: 0 4px; }
163
+ .fv-close:hover { color: var(--t1); }
164
+
165
+ /* Code table (line numbers + syntax) */
166
+ .code-scroll { overflow: auto; max-height: 700px; }
167
+ .code-table { border-collapse: collapse; width: 100%; font-family: var(--mono); font-size: 12px; line-height: 1.6; }
168
+ .code-row { }
169
+ .code-row.highlighted { background: rgba(88,166,255,0.1); }
170
+ .code-row.highlighted .ln { color: var(--link); }
171
+ .ln {
172
+ width: 1%; min-width: 44px; padding: 0 12px 0 16px;
173
+ text-align: right; color: var(--t4); user-select: none;
174
+ cursor: pointer; vertical-align: top; white-space: nowrap;
175
+ border-right: 1px solid var(--border);
176
+ }
177
+ .ln:hover { color: var(--t2); }
178
+ .code-line {
179
+ padding: 0 16px; white-space: pre; color: var(--t2);
180
+ word-break: break-all;
181
+ }
182
+
183
+ /* Syntax highlighting — GitHub dark palette */
184
+ .hl-kw { color: #ff7b72; }
185
+ .hl-str { color: #a5d6ff; }
186
+ .hl-cm { color: #8b949e; font-style: italic; }
187
+ .hl-num { color: #79c0ff; }
188
+ .hl-const { color: #79c0ff; }
189
+ .hl-fn { color: #d2a8ff; }
190
+ .hl-prop { color: #7ee787; }
191
+
192
+ /* README render below file list */
193
+ .readme-box { margin-top: 16px; }
194
+ .readme-box .md-render { max-height: none; }
195
+
196
+ /* Markdown */
197
+ .md-render { font-family: var(--sans); font-size: 14px; line-height: 1.6; color: var(--t2); padding: 16px 24px; max-height: 600px; overflow: auto; }
198
+ .md-render h1 { font-size: 24px; font-weight: 600; color: var(--t1); padding-bottom: 8px; border-bottom: 1px solid var(--border); margin: 0 0 16px; }
199
+ .md-render h2 { font-size: 18px; font-weight: 600; color: var(--t1); padding-bottom: 6px; border-bottom: 1px solid var(--border); margin: 24px 0 12px; }
200
+ .md-render h3 { font-size: 16px; font-weight: 600; color: var(--t1); margin: 16px 0 8px; }
201
+ .md-render p { margin: 8px 0; }
202
+ .md-render code { font-family: var(--mono); font-size: 12px; background: rgba(110,118,129,0.12); padding: 2px 6px; border-radius: 4px; }
203
+ .md-render pre { background: var(--overlay); border: 1px solid var(--border); border-radius: var(--r); padding: 16px; margin: 12px 0; overflow-x: auto; }
204
+ .md-render pre code { background: none; padding: 0; }
205
+ .md-render ul, .md-render ol { padding-left: 24px; margin: 8px 0; }
206
+ .md-render li { margin: 4px 0; }
207
+ .md-render li::marker { color: var(--t3); }
208
+ .md-render strong { color: var(--t1); font-weight: 600; }
209
+ .md-render blockquote { border-left: 3px solid var(--border); padding: 4px 16px; margin: 8px 0; color: var(--t3); }
210
+ .md-render hr { border: none; border-top: 1px solid var(--border); margin: 20px 0; }
211
+ .md-render table { border-collapse: collapse; width: 100%; margin: 12px 0; }
212
+ .md-render th, .md-render td { border: 1px solid var(--border); padding: 8px 12px; font-size: 13px; text-align: left; }
213
+ .md-render th { background: var(--overlay); font-weight: 600; color: var(--t1); }
214
+
215
+ /* Commit list — timeline style */
216
+ .commit-row {
217
+ display: flex; align-items: stretch; gap: 12px;
218
+ padding: 12px 16px; border-bottom: 1px solid var(--border);
219
+ transition: background 0.06s; cursor: pointer;
220
+ }
221
+ .commit-row:last-child { border-bottom: none; }
222
+ .commit-row:last-child .commit-line { display: none; }
223
+ .commit-row:hover { background: var(--overlay); }
224
+ .commit-timeline { display: flex; flex-direction: column; align-items: center; width: 16px; flex-shrink: 0; padding-top: 6px; }
225
+ .commit-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; border: 2px solid var(--bg); }
226
+ .commit-dot.latest { background: var(--green); box-shadow: 0 0 0 2px rgba(63,185,80,0.3); }
227
+ .commit-dot.old { background: var(--t4); }
228
+ .commit-line { width: 2px; flex: 1; background: var(--border); margin-top: 4px; }
229
+ .commit-body { flex: 1; min-width: 0; }
230
+ .commit-msg { font-size: 14px; color: var(--t1); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
231
+ .commit-row:hover .commit-msg { color: var(--link); }
232
+ .commit-meta { font-size: 12px; color: var(--t4); margin-top: 4px; display: flex; align-items: center; gap: 8px; }
233
+ .commit-hash { font-family: var(--mono); font-size: 11px; color: var(--t3); background: var(--overlay); padding: 1px 6px; border-radius: 4px; border: 1px solid var(--border); flex-shrink: 0; }
234
+ .commit-row:hover .commit-hash { color: var(--link); border-color: var(--link); }
235
+ .commit-fulldate { color: var(--t4); margin-left: auto; }
236
+
237
+ /* Commit detail page */
238
+ .commit-detail-page { }
239
+ .commit-back { margin-bottom: 16px; }
240
+ .cdp-header {
241
+ border: 1px solid var(--border); border-radius: var(--r);
242
+ background: var(--surface); padding: 20px; margin-bottom: 16px;
243
+ }
244
+ .cdp-message { font-size: 20px; font-weight: 600; color: var(--t1); margin-bottom: 8px; line-height: 1.3; }
245
+ .cdp-meta { display: flex; align-items: center; gap: 10px; font-size: 13px; color: var(--t3); margin-bottom: 6px; }
246
+ .cdp-summary { font-size: 12px; color: var(--t4); }
247
+
248
+ /* Per-file diff sections */
249
+ .diff-section { border: 1px solid var(--border); border-radius: var(--r); margin-bottom: 12px; overflow: hidden; background: var(--surface); }
250
+ .diff-file-header {
251
+ display: flex; align-items: center; gap: 8px;
252
+ padding: 8px 12px; background: var(--overlay);
253
+ border-bottom: 1px solid var(--border);
254
+ cursor: pointer; font-size: 13px; user-select: none;
255
+ }
256
+ .diff-file-header:hover { background: var(--border); }
257
+ .diff-chevron { font-size: 10px; color: var(--t4); width: 16px; text-align: center; flex-shrink: 0; }
258
+ .diff-file-name { font-family: var(--mono); font-size: 12px; color: var(--t2); flex: 1; }
259
+ .diff-stat { display: flex; gap: 6px; flex-shrink: 0; }
260
+ .diff-stat-add { font-family: var(--mono); font-size: 11px; color: var(--green); font-weight: 600; }
261
+ .diff-stat-del { font-family: var(--mono); font-size: 11px; color: var(--red); font-weight: 600; }
262
+
263
+ .diff-file-body { overflow: auto; max-height: 600px; }
264
+ .diff-file-body.collapsed { display: none; }
265
+
266
+ .diff-table { border-collapse: collapse; width: 100%; font-family: var(--mono); font-size: 12px; line-height: 1.5; }
267
+ .diff-table td { padding: 0; vertical-align: top; }
268
+ .diff-ln { width: 1%; min-width: 40px; padding: 0 8px; text-align: right; color: var(--t4); user-select: none; white-space: nowrap; }
269
+ .diff-ln-code { padding: 0 12px; white-space: pre; }
270
+
271
+ .diff-hunk td { background: rgba(88,166,255,0.06); }
272
+ .diff-line-add td { background: var(--green-bg); }
273
+ .diff-ln-del td { background: var(--red-bg); }
274
+
275
+ .d-hunk { color: var(--purple); font-style: italic; }
276
+ .d-add { color: var(--green); }
277
+ .d-del { color: var(--red); }
278
+
279
+ /* Browse at commit button */
280
+ .cdp-actions { margin-top: 12px; display: flex; gap: 8px; }
281
+ .time-travel-bar {
282
+ display: flex; align-items: center; gap: 10px;
283
+ padding: 8px 16px; margin-bottom: 16px;
284
+ background: var(--yellow-bg); border: 1px solid rgba(210,153,34,0.3);
285
+ border-radius: var(--r); font-size: 13px; color: var(--yellow);
286
+ }
287
+ .time-travel-bar .tt-hash { font-family: var(--mono); font-size: 12px; }
288
+ .time-travel-bar .btn { margin-left: auto; }
289
+
290
+ /* Protection status */
291
+ .prot-box { margin-bottom: 16px; }
292
+ .prot-item { display: flex; align-items: center; gap: 8px; padding: 8px 0; font-size: 14px; }
293
+ .prot-item + .prot-item { border-top: 1px solid var(--border); }
294
+ .prot-icon { width: 20px; text-align: center; font-size: 15px; flex-shrink: 0; }
295
+ .prot-ok { color: var(--green); }
296
+ .prot-warn { color: var(--yellow); }
297
+
298
+ /* View all link */
299
+ .view-all { padding: 10px 16px; text-align: right; border-top: 1px solid var(--border); }
300
+ .view-all a { font-size: 13px; color: var(--link); text-decoration: none; }
301
+ .view-all a:hover { text-decoration: underline; }
302
+
303
+ /* Backup target cards */
304
+ .target-prompt { font-size: 14px; color: var(--t3); margin-bottom: 16px; }
305
+ .target-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
306
+ .target-card {
307
+ border: 1px solid var(--border); border-radius: var(--r);
308
+ padding: 20px 16px; text-align: center; cursor: pointer;
309
+ transition: all 0.1s; background: var(--bg);
310
+ }
311
+ .target-card:hover { border-color: var(--link); background: var(--overlay); }
312
+ .target-card.disabled { opacity: 0.4; cursor: not-allowed; pointer-events: none; }
313
+ .target-card-icon { font-size: 28px; margin-bottom: 8px; }
314
+ .target-card-title { font-size: 14px; font-weight: 600; color: var(--t1); margin-bottom: 4px; }
315
+ .target-card-desc { font-size: 12px; color: var(--t4); }
316
+
317
+ /* Backup target status */
318
+ .target-status { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
319
+ .target-type { font-size: 15px; font-weight: 600; color: var(--t1); margin-bottom: 4px; }
320
+ .target-detail { font-size: 13px; color: var(--t3); }
321
+ .target-actions { display: flex; gap: 8px; flex-shrink: 0; }
322
+
323
+ /* Export card */
324
+ .export-desc { font-size: 13px; color: var(--t3); margin-bottom: 12px; }
325
+ .export-form { display: flex; gap: 8px; align-items: center; }
326
+ .export-form .modal-input { margin-bottom: 0; flex: 1; }
327
+ .export-size { font-size: 12px; color: var(--t4); margin-top: 8px; }
328
+
329
+ /* Changes list */
330
+ .change-row { display: flex; align-items: center; gap: 8px; padding: 6px 16px; font-size: 13px; border-bottom: 1px solid var(--border); }
331
+ .change-row:last-child { border-bottom: none; }
332
+ .change-badge { font-family: var(--mono); font-size: 10px; font-weight: 700; padding: 1px 6px; border-radius: 3px; }
333
+ .cb-m { background: var(--yellow-bg); color: var(--yellow); }
334
+ .cb-a { background: var(--green-bg); color: var(--green); }
335
+ .cb-d { background: var(--red-bg); color: var(--red); }
336
+ .change-path { font-family: var(--mono); font-size: 12px; color: var(--t2); }
337
+
338
+ /* Toast */
339
+ .toast-wrap { position: fixed; bottom: 16px; right: 16px; z-index: 100; }
340
+ .toast { background: var(--surface); border: 1px solid var(--border); padding: 8px 16px; border-radius: var(--r); font-size: 13px; color: var(--t2); box-shadow: 0 4px 12px rgba(0,0,0,0.4); animation: toastIn 0.15s ease; margin-top: 6px; }
341
+ .toast-ok { border-left: 3px solid var(--green); }
342
+ @keyframes toastIn { from { opacity:0; transform:translateY(8px); } to { opacity:1; transform:translateY(0); } }
343
+
344
+ /* Modal */
345
+ .modal-overlay {
346
+ position: fixed; inset: 0; background: rgba(0,0,0,0.6); z-index: 200;
347
+ animation: fadeIn 0.1s ease;
348
+ }
349
+ .modal {
350
+ position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
351
+ background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
352
+ padding: 24px; width: 420px; max-width: 90vw; z-index: 201;
353
+ animation: fadeIn 0.15s ease;
354
+ }
355
+ @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
356
+ .modal h3 { font-size: 16px; font-weight: 600; color: var(--t1); margin-bottom: 8px; }
357
+ .modal p { font-size: 13px; color: var(--t3); line-height: 1.5; margin-bottom: 16px; }
358
+ .modal .modal-hash { font-family: var(--mono); background: var(--overlay); padding: 1px 6px; border-radius: 4px; color: var(--t2); }
359
+ .modal-actions { display: flex; gap: 8px; justify-content: flex-end; }
360
+ .btn-danger { background: #da3633; color: #fff; border-color: rgba(240,246,252,0.1); font-weight: 600; }
361
+ .btn-danger:hover { background: #f85149; }
362
+ .modal-input { width: 100%; background: var(--bg); border: 1px solid var(--border); color: var(--t2); padding: 8px 12px; border-radius: var(--r); font-size: 13px; font-family: var(--sans); margin-bottom: 12px; }
363
+ .modal-input:focus { outline: none; border-color: var(--link); }
364
+ .modal-input::placeholder { color: var(--t4); }
365
+
366
+ /* Compare bar */
367
+ .compare-bar {
368
+ display: flex; align-items: center; gap: 10px;
369
+ padding: 10px 16px; margin-bottom: 12px;
370
+ background: var(--blue-bg); border: 1px solid rgba(88,166,255,0.25);
371
+ border-radius: var(--r); font-size: 13px; color: var(--link);
372
+ }
373
+ .compare-bar .commit-hash { font-size: 11px; }
374
+ .compare-bar .btn { margin-left: auto; }
375
+ .compare-mode .commit-row { position: relative; }
376
+ .compare-mode .commit-row::before {
377
+ content: ''; position: absolute; left: 8px; top: 50%; transform: translateY(-50%);
378
+ width: 14px; height: 14px; border-radius: 3px; border: 2px solid var(--t4);
379
+ }
380
+ .compare-mode .commit-row.selected::before { background: var(--link); border-color: var(--link); }
381
+
382
+ .empty { text-align: center; padding: 40px; color: var(--t4); font-size: 14px; }
383
+ .hidden { display: none !important; }
384
+
385
+ @media (max-width: 900px) { .stats-grid { grid-template-columns: repeat(2, 1fr); } .grid-2 { grid-template-columns: 1fr; } .target-cards { grid-template-columns: repeat(2, 1fr); } .target-status { flex-direction: column; align-items: flex-start; } .export-form { flex-direction: column; } }
386
+ @media (max-width: 640px) { .sidebar { display: none; } .main { margin-left: 0; } .target-cards { grid-template-columns: 1fr 1fr; } }