athar 0.2.1 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -1
- data/README.md +60 -0
- data/app/assets/images/athar/logo.png +0 -0
- data/app/assets/javascripts/athar/dashboard.js +290 -0
- data/app/assets/stylesheets/athar/dashboard.css +841 -0
- data/app/controllers/athar/application_controller.rb +10 -0
- data/app/controllers/athar/dashboard_controller.rb +57 -0
- data/app/controllers/athar/deletions_controller.rb +14 -0
- data/app/controllers/athar/table_events_controller.rb +11 -0
- data/app/controllers/athar/themes_controller.rb +16 -0
- data/app/helpers/athar/asset_helper.rb +28 -0
- data/app/helpers/athar/dashboard/cell_helper.rb +88 -0
- data/app/helpers/athar/dashboard/detail_helper.rb +50 -0
- data/app/helpers/athar/dashboard/filter_link_helper.rb +22 -0
- data/app/helpers/athar/dashboard/formatting_helper.rb +47 -0
- data/app/helpers/athar/dashboard/icon_helper.rb +40 -0
- data/app/helpers/athar/dashboard_helper.rb +11 -0
- data/app/views/athar/dashboard/_filter_bar.html.erb +95 -0
- data/app/views/athar/dashboard/_kpi_strip.html.erb +46 -0
- data/app/views/athar/dashboard/_pager.html.erb +32 -0
- data/app/views/athar/dashboard/_row.html.erb +72 -0
- data/app/views/athar/dashboard/_sidebar.html.erb +106 -0
- data/app/views/athar/dashboard/_table.html.erb +30 -0
- data/app/views/athar/dashboard/_topbar.html.erb +30 -0
- data/app/views/athar/dashboard/index.html.erb +31 -0
- data/app/views/athar/deletions/_detail.html.erb +115 -0
- data/app/views/athar/deletions/show.html.erb +3 -0
- data/app/views/athar/table_events/_detail.html.erb +80 -0
- data/app/views/athar/table_events/show.html.erb +3 -0
- data/app/views/layouts/athar/application.html.erb +29 -0
- data/config/routes.rb +8 -0
- data/lib/athar/dashboard/actor_labels.rb +31 -0
- data/lib/athar/dashboard/actor_options.rb +71 -0
- data/lib/athar/dashboard/connection_info.rb +25 -0
- data/lib/athar/dashboard/feed_query.rb +222 -0
- data/lib/athar/dashboard/filter_set.rb +63 -0
- data/lib/athar/dashboard/kpi_calculator.rb +102 -0
- data/lib/athar/dashboard/model_registry.rb +141 -0
- data/lib/athar/dashboard/sparkline.rb +42 -0
- data/lib/athar/dashboard/trigger_args_parser.rb +42 -0
- data/lib/athar/dashboard.rb +16 -0
- data/lib/athar/engine.rb +12 -0
- data/lib/athar/middleware/asset_server.rb +78 -0
- data/lib/athar/version.rb +1 -1
- data/lib/athar.rb +1 -0
- metadata +41 -1
|
@@ -0,0 +1,841 @@
|
|
|
1
|
+
/* ===== tokens ===== */
|
|
2
|
+
:root[data-theme="dark"] {
|
|
3
|
+
--bg: #0c0d10;
|
|
4
|
+
--bg-1: #111317;
|
|
5
|
+
--bg-2: #15181d;
|
|
6
|
+
--bg-3: #1a1e24;
|
|
7
|
+
--bg-row-hover: #16191f;
|
|
8
|
+
--bg-row-open: #14171c;
|
|
9
|
+
--bg-detail: #0e1014;
|
|
10
|
+
--border: #20242c;
|
|
11
|
+
--border-strong: #2a2f38;
|
|
12
|
+
--fg: #e6e9ef;
|
|
13
|
+
--fg-1: #c2c8d2;
|
|
14
|
+
--fg-2: #8a92a0;
|
|
15
|
+
--fg-3: #5d6573;
|
|
16
|
+
--fg-4: #3f4651;
|
|
17
|
+
--accent: #d6deea;
|
|
18
|
+
--selected: #1d2530;
|
|
19
|
+
--selected-bar: #2c3a52;
|
|
20
|
+
--mode-identity: #6b7384;
|
|
21
|
+
--mode-only: #6694e8;
|
|
22
|
+
--mode-snapshot: #c98a3f;
|
|
23
|
+
--mask: #c98a3f;
|
|
24
|
+
--truncate: #d96565;
|
|
25
|
+
--kind-delete: #6b7384;
|
|
26
|
+
--kind-truncate: #d96565;
|
|
27
|
+
--conn: #4ea36b;
|
|
28
|
+
--shadow: 0 1px 0 0 rgba(255,255,255,0.02), 0 0 0 1px rgba(0,0,0,0.6);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
:root[data-theme="light"] {
|
|
32
|
+
--bg: #f5f6f8;
|
|
33
|
+
--bg-1: #ffffff;
|
|
34
|
+
--bg-2: #fafbfc;
|
|
35
|
+
--bg-3: #eef0f4;
|
|
36
|
+
--bg-row-hover: #f3f5f8;
|
|
37
|
+
--bg-row-open: #f3f5f8;
|
|
38
|
+
--bg-detail: #f9fafc;
|
|
39
|
+
--border: #e3e6eb;
|
|
40
|
+
--border-strong: #cdd2da;
|
|
41
|
+
--fg: #0f1216;
|
|
42
|
+
--fg-1: #2a2f38;
|
|
43
|
+
--fg-2: #5b6471;
|
|
44
|
+
--fg-3: #8a92a0;
|
|
45
|
+
--fg-4: #b6bcc7;
|
|
46
|
+
--accent: #1a1d22;
|
|
47
|
+
--selected: #e7eef9;
|
|
48
|
+
--selected-bar: #c5d4ee;
|
|
49
|
+
--mode-identity: #6b7384;
|
|
50
|
+
--mode-only: #2563d4;
|
|
51
|
+
--mode-snapshot: #b16a1f;
|
|
52
|
+
--mask: #b16a1f;
|
|
53
|
+
--truncate: #c43a3a;
|
|
54
|
+
--kind-delete: #6b7384;
|
|
55
|
+
--kind-truncate: #c43a3a;
|
|
56
|
+
--conn: #2f8a52;
|
|
57
|
+
--shadow: 0 1px 0 0 rgba(0,0,0,0.02);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
* { box-sizing: border-box; }
|
|
61
|
+
html, body { height: 100%; margin: 0; }
|
|
62
|
+
body {
|
|
63
|
+
font-family: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
64
|
+
font-size: 12.5px;
|
|
65
|
+
line-height: 1.4;
|
|
66
|
+
color: var(--fg);
|
|
67
|
+
background: var(--bg);
|
|
68
|
+
-webkit-font-smoothing: antialiased;
|
|
69
|
+
font-feature-settings: "cv11", "ss01";
|
|
70
|
+
}
|
|
71
|
+
.mono, code, pre, input, select, .kbd { font-family: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace; }
|
|
72
|
+
button { font-family: inherit; cursor: pointer; }
|
|
73
|
+
|
|
74
|
+
/* ===== layout ===== */
|
|
75
|
+
.app {
|
|
76
|
+
display: grid;
|
|
77
|
+
grid-template-columns: 264px 1fr;
|
|
78
|
+
height: 100vh;
|
|
79
|
+
overflow: hidden;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* ===== sidebar ===== */
|
|
83
|
+
.sidebar {
|
|
84
|
+
background: var(--bg-1);
|
|
85
|
+
border-right: 1px solid var(--border);
|
|
86
|
+
display: flex;
|
|
87
|
+
flex-direction: column;
|
|
88
|
+
min-width: 0;
|
|
89
|
+
}
|
|
90
|
+
.sidebar-head {
|
|
91
|
+
padding: 12px 12px 10px;
|
|
92
|
+
border-bottom: 1px solid var(--border);
|
|
93
|
+
display: flex;
|
|
94
|
+
flex-direction: column;
|
|
95
|
+
gap: 10px;
|
|
96
|
+
}
|
|
97
|
+
.kbd-row { display: flex; align-items: center; justify-content: space-between; }
|
|
98
|
+
.brand { display: flex; align-items: center; gap: 6px; }
|
|
99
|
+
.brand-name { line-height: 1; }
|
|
100
|
+
.brand-version { line-height: 1; }
|
|
101
|
+
.brand-logo {
|
|
102
|
+
width: 18px;
|
|
103
|
+
height: 18px;
|
|
104
|
+
border-radius: 4px;
|
|
105
|
+
margin-right: 4px;
|
|
106
|
+
display: block;
|
|
107
|
+
flex-shrink: 0;
|
|
108
|
+
}
|
|
109
|
+
:root[data-theme="light"] .brand-logo {
|
|
110
|
+
background: #0c0d10;
|
|
111
|
+
padding: 1px;
|
|
112
|
+
}
|
|
113
|
+
.brand-name { font-weight: 600; letter-spacing: -0.01em; color: var(--fg); }
|
|
114
|
+
.brand-version { font-family: "JetBrains Mono", monospace; font-size: 10px; color: var(--fg-3); }
|
|
115
|
+
|
|
116
|
+
.sidebar-search {
|
|
117
|
+
display: flex; align-items: center; gap: 6px;
|
|
118
|
+
background: var(--bg-2);
|
|
119
|
+
border: 1px solid var(--border);
|
|
120
|
+
border-radius: 4px;
|
|
121
|
+
padding: 4px 6px;
|
|
122
|
+
color: var(--fg-3);
|
|
123
|
+
}
|
|
124
|
+
.sidebar-search input {
|
|
125
|
+
flex: 1; min-width: 0;
|
|
126
|
+
background: transparent;
|
|
127
|
+
border: 0; outline: 0;
|
|
128
|
+
color: var(--fg);
|
|
129
|
+
font-size: 11.5px;
|
|
130
|
+
}
|
|
131
|
+
.kbd {
|
|
132
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
133
|
+
height: 16px; min-width: 14px; padding: 0 3px;
|
|
134
|
+
border: 1px solid var(--border);
|
|
135
|
+
border-radius: 3px;
|
|
136
|
+
font-size: 10px;
|
|
137
|
+
color: var(--fg-3);
|
|
138
|
+
background: var(--bg-3);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.sidebar-all {
|
|
142
|
+
margin: 6px 6px 0;
|
|
143
|
+
border-radius: 4px;
|
|
144
|
+
}
|
|
145
|
+
.sidebar-scroll { flex: 1; overflow-y: auto; padding: 4px 0 8px; }
|
|
146
|
+
.sidebar-scroll::-webkit-scrollbar { width: 8px; }
|
|
147
|
+
.sidebar-scroll::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 4px; }
|
|
148
|
+
.sidebar-scroll::-webkit-scrollbar-track { background: transparent; }
|
|
149
|
+
|
|
150
|
+
.sidebar-group { margin-top: 10px; }
|
|
151
|
+
.sidebar-group-head {
|
|
152
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
153
|
+
padding: 4px 14px 4px;
|
|
154
|
+
font-size: 10px;
|
|
155
|
+
text-transform: uppercase;
|
|
156
|
+
letter-spacing: 0.06em;
|
|
157
|
+
color: var(--fg-3);
|
|
158
|
+
}
|
|
159
|
+
.sidebar-group-count { color: var(--fg-4); font-family: "JetBrains Mono", monospace; }
|
|
160
|
+
|
|
161
|
+
.sidebar-row {
|
|
162
|
+
position: relative;
|
|
163
|
+
display: flex;
|
|
164
|
+
align-items: center;
|
|
165
|
+
justify-content: space-between;
|
|
166
|
+
width: calc(100% - 12px);
|
|
167
|
+
margin: 0 6px;
|
|
168
|
+
padding: 5px 8px;
|
|
169
|
+
border: 0;
|
|
170
|
+
background: transparent;
|
|
171
|
+
color: var(--fg-1);
|
|
172
|
+
border-radius: 3px;
|
|
173
|
+
text-align: left;
|
|
174
|
+
font-size: 12px;
|
|
175
|
+
overflow: hidden;
|
|
176
|
+
}
|
|
177
|
+
.sidebar-row:hover { background: var(--bg-row-hover); }
|
|
178
|
+
.sidebar-row.is-active { background: var(--selected); color: var(--fg); }
|
|
179
|
+
.sidebar-row.is-active::before {
|
|
180
|
+
content: "";
|
|
181
|
+
position: absolute;
|
|
182
|
+
left: 0; top: 4px; bottom: 4px;
|
|
183
|
+
width: 2px;
|
|
184
|
+
background: var(--accent);
|
|
185
|
+
border-radius: 1px;
|
|
186
|
+
}
|
|
187
|
+
.sidebar-bar {
|
|
188
|
+
position: absolute;
|
|
189
|
+
left: 0; top: 0; bottom: 0;
|
|
190
|
+
background: var(--bg-2);
|
|
191
|
+
z-index: 0;
|
|
192
|
+
pointer-events: none;
|
|
193
|
+
opacity: 0.6;
|
|
194
|
+
}
|
|
195
|
+
.sidebar-row.is-active .sidebar-bar { background: var(--selected-bar); opacity: 0.7; }
|
|
196
|
+
.sidebar-row-label, .sidebar-count { position: relative; z-index: 1; }
|
|
197
|
+
.sidebar-row-label {
|
|
198
|
+
display: flex;
|
|
199
|
+
align-items: center;
|
|
200
|
+
gap: 6px;
|
|
201
|
+
min-width: 0;
|
|
202
|
+
overflow: hidden;
|
|
203
|
+
white-space: nowrap;
|
|
204
|
+
}
|
|
205
|
+
.sidebar-model { overflow: hidden; text-overflow: ellipsis; }
|
|
206
|
+
.sidebar-count {
|
|
207
|
+
font-family: "JetBrains Mono", monospace;
|
|
208
|
+
font-size: 11px;
|
|
209
|
+
color: var(--fg-3);
|
|
210
|
+
font-variant-numeric: tabular-nums;
|
|
211
|
+
}
|
|
212
|
+
.sidebar-row.is-active .sidebar-count { color: var(--fg-1); }
|
|
213
|
+
.sidebar-all .sidebar-row-label { font-weight: 500; }
|
|
214
|
+
.sidebar-all .dot {
|
|
215
|
+
width: 6px; height: 6px; border-radius: 50%;
|
|
216
|
+
background: var(--accent);
|
|
217
|
+
box-shadow: 0 0 0 2px color-mix(in oklab, var(--accent) 25%, transparent);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.sidebar-tag {
|
|
221
|
+
display: inline-flex;
|
|
222
|
+
align-items: center;
|
|
223
|
+
height: 14px;
|
|
224
|
+
padding: 0 4px;
|
|
225
|
+
border: 1px solid var(--border-strong);
|
|
226
|
+
border-radius: 2px;
|
|
227
|
+
font-size: 9px;
|
|
228
|
+
color: var(--fg-3);
|
|
229
|
+
background: var(--bg-2);
|
|
230
|
+
font-family: "JetBrains Mono", monospace;
|
|
231
|
+
}
|
|
232
|
+
.sidebar-tag.mask { color: var(--mask); border-color: color-mix(in oklab, var(--mask) 30%, var(--border)); }
|
|
233
|
+
|
|
234
|
+
.mode-dot {
|
|
235
|
+
width: 6px; height: 6px;
|
|
236
|
+
border-radius: 50%;
|
|
237
|
+
background: var(--mode-identity);
|
|
238
|
+
flex-shrink: 0;
|
|
239
|
+
}
|
|
240
|
+
.mode-dot.mode-identity { background: var(--mode-identity); }
|
|
241
|
+
.mode-dot.mode-only { background: var(--mode-only); }
|
|
242
|
+
.mode-dot.mode-snapshot { background: var(--mode-snapshot); }
|
|
243
|
+
|
|
244
|
+
.sidebar-foot {
|
|
245
|
+
border-top: 1px solid var(--border);
|
|
246
|
+
padding: 8px 12px;
|
|
247
|
+
display: flex;
|
|
248
|
+
flex-direction: column;
|
|
249
|
+
gap: 6px;
|
|
250
|
+
background: var(--bg-1);
|
|
251
|
+
}
|
|
252
|
+
.legend {
|
|
253
|
+
display: flex;
|
|
254
|
+
gap: 10px;
|
|
255
|
+
font-size: 10px;
|
|
256
|
+
color: var(--fg-3);
|
|
257
|
+
font-family: "JetBrains Mono", monospace;
|
|
258
|
+
}
|
|
259
|
+
.legend span { display: inline-flex; align-items: center; gap: 4px; }
|
|
260
|
+
.legend i { width: 6px; height: 6px; border-radius: 50%; display: inline-block; }
|
|
261
|
+
.retention {
|
|
262
|
+
display: flex;
|
|
263
|
+
justify-content: space-between;
|
|
264
|
+
align-items: baseline;
|
|
265
|
+
font-size: 10.5px;
|
|
266
|
+
}
|
|
267
|
+
.retention-label { text-transform: uppercase; letter-spacing: 0.06em; color: var(--fg-3); font-size: 9.5px; }
|
|
268
|
+
.retention-val { font-family: "JetBrains Mono", monospace; color: var(--fg-2); }
|
|
269
|
+
|
|
270
|
+
/* ===== main ===== */
|
|
271
|
+
.main {
|
|
272
|
+
display: flex;
|
|
273
|
+
flex-direction: column;
|
|
274
|
+
min-width: 0;
|
|
275
|
+
overflow: hidden;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/* topbar */
|
|
279
|
+
.topbar {
|
|
280
|
+
height: 38px;
|
|
281
|
+
flex-shrink: 0;
|
|
282
|
+
display: flex;
|
|
283
|
+
align-items: center;
|
|
284
|
+
justify-content: space-between;
|
|
285
|
+
padding: 0 14px;
|
|
286
|
+
border-bottom: 1px solid var(--border);
|
|
287
|
+
background: var(--bg-1);
|
|
288
|
+
}
|
|
289
|
+
.crumbs {
|
|
290
|
+
display: flex;
|
|
291
|
+
align-items: center;
|
|
292
|
+
gap: 8px;
|
|
293
|
+
min-width: 0;
|
|
294
|
+
overflow: hidden;
|
|
295
|
+
}
|
|
296
|
+
.crumb { color: var(--fg-2); font-size: 12px; }
|
|
297
|
+
.crumb-active { color: var(--fg); font-weight: 500; }
|
|
298
|
+
.crumb-sep { color: var(--fg-4); }
|
|
299
|
+
.crumb-meta { color: var(--fg-3); font-size: 11px; }
|
|
300
|
+
.topbar-right { display: flex; gap: 8px; align-items: center; }
|
|
301
|
+
.conn-pill, .time-pill {
|
|
302
|
+
display: inline-flex; align-items: center; gap: 6px;
|
|
303
|
+
height: 22px;
|
|
304
|
+
padding: 0 8px;
|
|
305
|
+
border: 1px solid var(--border);
|
|
306
|
+
border-radius: 3px;
|
|
307
|
+
background: var(--bg-2);
|
|
308
|
+
font-size: 11px;
|
|
309
|
+
}
|
|
310
|
+
.conn-dot {
|
|
311
|
+
width: 6px; height: 6px;
|
|
312
|
+
background: var(--conn);
|
|
313
|
+
border-radius: 50%;
|
|
314
|
+
box-shadow: 0 0 0 2px color-mix(in oklab, var(--conn) 22%, transparent);
|
|
315
|
+
}
|
|
316
|
+
.time-pill { color: var(--fg-2); }
|
|
317
|
+
|
|
318
|
+
/* kpi strip */
|
|
319
|
+
.kpi-strip {
|
|
320
|
+
display: grid;
|
|
321
|
+
grid-template-columns: repeat(5, 1fr) 1.4fr;
|
|
322
|
+
gap: 0;
|
|
323
|
+
border-bottom: 1px solid var(--border);
|
|
324
|
+
background: var(--bg-1);
|
|
325
|
+
}
|
|
326
|
+
.kpi {
|
|
327
|
+
padding: 10px 14px;
|
|
328
|
+
border-right: 1px solid var(--border);
|
|
329
|
+
display: flex;
|
|
330
|
+
flex-direction: column;
|
|
331
|
+
gap: 2px;
|
|
332
|
+
}
|
|
333
|
+
.kpi:last-child { border-right: 0; }
|
|
334
|
+
.kpi-label {
|
|
335
|
+
font-size: 9.5px;
|
|
336
|
+
text-transform: uppercase;
|
|
337
|
+
letter-spacing: 0.07em;
|
|
338
|
+
color: var(--fg-3);
|
|
339
|
+
}
|
|
340
|
+
.kpi-value {
|
|
341
|
+
font-family: "JetBrains Mono", monospace;
|
|
342
|
+
font-size: 18px;
|
|
343
|
+
font-weight: 500;
|
|
344
|
+
color: var(--fg);
|
|
345
|
+
font-variant-numeric: tabular-nums;
|
|
346
|
+
letter-spacing: -0.01em;
|
|
347
|
+
}
|
|
348
|
+
.kpi-sub {
|
|
349
|
+
font-size: 11px;
|
|
350
|
+
color: var(--fg-3);
|
|
351
|
+
font-family: "JetBrains Mono", monospace;
|
|
352
|
+
}
|
|
353
|
+
.spark-kpi { padding-bottom: 8px; }
|
|
354
|
+
.spark {
|
|
355
|
+
display: flex;
|
|
356
|
+
align-items: flex-end;
|
|
357
|
+
gap: 2px;
|
|
358
|
+
height: 28px;
|
|
359
|
+
margin-top: 4px;
|
|
360
|
+
}
|
|
361
|
+
.spark-bar {
|
|
362
|
+
flex: 1;
|
|
363
|
+
min-height: 1px;
|
|
364
|
+
background: var(--fg-3);
|
|
365
|
+
border-radius: 1px;
|
|
366
|
+
opacity: 0.7;
|
|
367
|
+
}
|
|
368
|
+
.spark-bar:nth-last-child(-n+3) { background: var(--accent); opacity: 1; }
|
|
369
|
+
|
|
370
|
+
/* filter bar */
|
|
371
|
+
.filter-bar {
|
|
372
|
+
display: flex;
|
|
373
|
+
align-items: center;
|
|
374
|
+
gap: 10px;
|
|
375
|
+
padding: 8px 14px;
|
|
376
|
+
border-bottom: 1px solid var(--border);
|
|
377
|
+
background: var(--bg-1);
|
|
378
|
+
flex-wrap: wrap;
|
|
379
|
+
}
|
|
380
|
+
.filter-search {
|
|
381
|
+
flex: 1;
|
|
382
|
+
min-width: 220px;
|
|
383
|
+
display: flex;
|
|
384
|
+
align-items: center;
|
|
385
|
+
gap: 6px;
|
|
386
|
+
background: var(--bg-2);
|
|
387
|
+
border: 1px solid var(--border);
|
|
388
|
+
border-radius: 4px;
|
|
389
|
+
padding: 4px 8px;
|
|
390
|
+
height: 26px;
|
|
391
|
+
color: var(--fg-3);
|
|
392
|
+
}
|
|
393
|
+
.filter-search input {
|
|
394
|
+
flex: 1; min-width: 0;
|
|
395
|
+
background: transparent; border: 0; outline: 0;
|
|
396
|
+
color: var(--fg);
|
|
397
|
+
font-size: 11.5px;
|
|
398
|
+
}
|
|
399
|
+
.seg, .filter-select {
|
|
400
|
+
display: inline-flex;
|
|
401
|
+
align-items: center;
|
|
402
|
+
height: 26px;
|
|
403
|
+
border: 1px solid var(--border);
|
|
404
|
+
border-radius: 4px;
|
|
405
|
+
background: var(--bg-2);
|
|
406
|
+
overflow: hidden;
|
|
407
|
+
}
|
|
408
|
+
.seg-label {
|
|
409
|
+
padding: 0 8px;
|
|
410
|
+
font-size: 10px;
|
|
411
|
+
text-transform: uppercase;
|
|
412
|
+
letter-spacing: 0.06em;
|
|
413
|
+
color: var(--fg-3);
|
|
414
|
+
border-right: 1px solid var(--border);
|
|
415
|
+
height: 100%;
|
|
416
|
+
display: inline-flex;
|
|
417
|
+
align-items: center;
|
|
418
|
+
background: var(--bg-3);
|
|
419
|
+
}
|
|
420
|
+
.seg-btn {
|
|
421
|
+
height: 100%;
|
|
422
|
+
padding: 0 9px;
|
|
423
|
+
background: transparent;
|
|
424
|
+
border: 0;
|
|
425
|
+
color: var(--fg-2);
|
|
426
|
+
font-size: 11.5px;
|
|
427
|
+
border-right: 1px solid var(--border);
|
|
428
|
+
}
|
|
429
|
+
.seg-btn:last-child { border-right: 0; }
|
|
430
|
+
.seg-btn:hover { background: var(--bg-row-hover); color: var(--fg); }
|
|
431
|
+
.seg-btn.is-active { background: var(--selected); color: var(--fg); }
|
|
432
|
+
.filter-select select {
|
|
433
|
+
border: 0; background: transparent;
|
|
434
|
+
color: var(--fg);
|
|
435
|
+
height: 100%;
|
|
436
|
+
padding: 0 8px;
|
|
437
|
+
font-size: 11.5px;
|
|
438
|
+
outline: 0;
|
|
439
|
+
cursor: pointer;
|
|
440
|
+
}
|
|
441
|
+
.filter-clear {
|
|
442
|
+
height: 26px;
|
|
443
|
+
padding: 0 10px;
|
|
444
|
+
border: 1px solid var(--border);
|
|
445
|
+
background: transparent;
|
|
446
|
+
color: var(--fg-2);
|
|
447
|
+
border-radius: 4px;
|
|
448
|
+
font-size: 11.5px;
|
|
449
|
+
}
|
|
450
|
+
.filter-clear:hover { color: var(--fg); border-color: var(--border-strong); }
|
|
451
|
+
|
|
452
|
+
/* table */
|
|
453
|
+
.table-wrap {
|
|
454
|
+
flex: 1;
|
|
455
|
+
overflow: auto;
|
|
456
|
+
background: var(--bg);
|
|
457
|
+
}
|
|
458
|
+
.table-wrap::-webkit-scrollbar { width: 10px; height: 10px; }
|
|
459
|
+
.table-wrap::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 5px; }
|
|
460
|
+
.dtable {
|
|
461
|
+
width: 100%;
|
|
462
|
+
border-collapse: collapse;
|
|
463
|
+
font-size: 12px;
|
|
464
|
+
}
|
|
465
|
+
.dtable thead th {
|
|
466
|
+
position: sticky; top: 0; z-index: 2;
|
|
467
|
+
background: var(--bg-1);
|
|
468
|
+
text-align: left;
|
|
469
|
+
padding: 7px 10px;
|
|
470
|
+
font-size: 10px;
|
|
471
|
+
text-transform: uppercase;
|
|
472
|
+
letter-spacing: 0.07em;
|
|
473
|
+
color: var(--fg-3);
|
|
474
|
+
font-weight: 500;
|
|
475
|
+
border-bottom: 1px solid var(--border);
|
|
476
|
+
}
|
|
477
|
+
.th-expand { width: 24px; padding-right: 0 !important; }
|
|
478
|
+
.th-when { width: 138px; }
|
|
479
|
+
.th-id { width: 84px; text-align: right; }
|
|
480
|
+
.th-summary { width: auto; }
|
|
481
|
+
|
|
482
|
+
.drow { cursor: pointer; }
|
|
483
|
+
.drow > td {
|
|
484
|
+
padding: 6px 10px;
|
|
485
|
+
border-bottom: 1px solid var(--border);
|
|
486
|
+
vertical-align: middle;
|
|
487
|
+
white-space: nowrap;
|
|
488
|
+
overflow: hidden;
|
|
489
|
+
text-overflow: ellipsis;
|
|
490
|
+
}
|
|
491
|
+
.drow:hover > td { background: var(--bg-row-hover); }
|
|
492
|
+
.drow.is-open > td { background: var(--bg-row-open); border-bottom-color: transparent; }
|
|
493
|
+
.drow.is-trunc .kind-icon { color: var(--truncate); }
|
|
494
|
+
.drow.is-trunc .record-type { color: var(--truncate); }
|
|
495
|
+
|
|
496
|
+
.td-expand { color: var(--fg-3); padding-right: 0 !important; }
|
|
497
|
+
.drow.is-open .td-expand { color: var(--fg); }
|
|
498
|
+
|
|
499
|
+
.td-when { font-family: "JetBrains Mono", monospace; }
|
|
500
|
+
.when-rel { color: var(--fg); font-size: 11.5px; font-variant-numeric: tabular-nums; }
|
|
501
|
+
.when-abs { color: var(--fg-3); font-size: 10.5px; font-variant-numeric: tabular-nums; }
|
|
502
|
+
|
|
503
|
+
.kind-icon { display: inline-block; margin-right: 6px; color: var(--kind-delete); vertical-align: -1px; }
|
|
504
|
+
.record-type { font-weight: 500; color: var(--fg); }
|
|
505
|
+
.record-id-cell { display: inline-flex; align-items: center; gap: 2px; margin-left: 6px; }
|
|
506
|
+
.record-id { font-family: "JetBrains Mono", monospace; color: var(--fg-2); font-size: 11.5px; }
|
|
507
|
+
|
|
508
|
+
.actor { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
|
|
509
|
+
.actor-name { color: var(--fg-1); }
|
|
510
|
+
.actor-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--fg-3); flex-shrink: 0; }
|
|
511
|
+
.actor-admin .actor-dot, .actor-engineer .actor-dot { background: var(--mode-only); }
|
|
512
|
+
.actor-job .actor-dot { background: var(--mode-snapshot); }
|
|
513
|
+
.actor-db .actor-dot { background: var(--truncate); }
|
|
514
|
+
.actor-anonymous .actor-dot { background: var(--fg-4); }
|
|
515
|
+
.actor-job .actor-name, .actor-db .actor-name { font-family: "JetBrains Mono", monospace; font-size: 11.5px; color: var(--fg-2); }
|
|
516
|
+
|
|
517
|
+
.td-summary { color: var(--fg-2); max-width: 0; }
|
|
518
|
+
.summary-bits { display: inline-flex; gap: 6px; align-items: center; }
|
|
519
|
+
.dot-sep { color: var(--fg-4); }
|
|
520
|
+
.reason { font-style: italic; color: var(--fg-2); }
|
|
521
|
+
.meta-kv { display: inline-flex; gap: 4px; align-items: baseline; font-family: "JetBrains Mono", monospace; font-size: 11px; }
|
|
522
|
+
.meta-k { color: var(--fg-3); }
|
|
523
|
+
.meta-v { color: var(--fg-1); max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
524
|
+
|
|
525
|
+
.td-id { text-align: right; color: var(--fg-3); font-size: 11px; font-variant-numeric: tabular-nums; }
|
|
526
|
+
|
|
527
|
+
.dim { color: var(--fg-3); }
|
|
528
|
+
.muted { color: var(--fg-4); }
|
|
529
|
+
|
|
530
|
+
/* pills */
|
|
531
|
+
.pill {
|
|
532
|
+
display: inline-flex; align-items: center;
|
|
533
|
+
height: 16px;
|
|
534
|
+
padding: 0 5px;
|
|
535
|
+
border-radius: 2px;
|
|
536
|
+
font-size: 10px;
|
|
537
|
+
border: 1px solid var(--border-strong);
|
|
538
|
+
background: var(--bg-2);
|
|
539
|
+
color: var(--fg-2);
|
|
540
|
+
font-family: "JetBrains Mono", monospace;
|
|
541
|
+
letter-spacing: 0.02em;
|
|
542
|
+
margin-right: 4px;
|
|
543
|
+
}
|
|
544
|
+
.pill:last-child { margin-right: 0; }
|
|
545
|
+
.pill.subtle { color: var(--fg-3); }
|
|
546
|
+
.mode-pill.mode-identity { color: var(--mode-identity); border-color: color-mix(in oklab, var(--mode-identity) 35%, var(--border)); }
|
|
547
|
+
.mode-pill.mode-only { color: var(--mode-only); border-color: color-mix(in oklab, var(--mode-only) 35%, var(--border)); background: color-mix(in oklab, var(--mode-only) 8%, var(--bg-2)); }
|
|
548
|
+
.mode-pill.mode-snapshot { color: var(--mode-snapshot); border-color: color-mix(in oklab, var(--mode-snapshot) 35%, var(--border)); background: color-mix(in oklab, var(--mode-snapshot) 8%, var(--bg-2)); }
|
|
549
|
+
.mask-pill { color: var(--mask); border-color: color-mix(in oklab, var(--mask) 35%, var(--border)); }
|
|
550
|
+
|
|
551
|
+
/* copy button */
|
|
552
|
+
.copy-btn {
|
|
553
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
554
|
+
width: 16px; height: 16px;
|
|
555
|
+
border: 0; background: transparent;
|
|
556
|
+
color: var(--fg-3);
|
|
557
|
+
border-radius: 2px;
|
|
558
|
+
margin-left: 2px;
|
|
559
|
+
vertical-align: -2px;
|
|
560
|
+
}
|
|
561
|
+
.copy-btn:hover { background: var(--bg-3); color: var(--fg); }
|
|
562
|
+
|
|
563
|
+
/* detail */
|
|
564
|
+
.drow-detail > td {
|
|
565
|
+
padding: 0;
|
|
566
|
+
background: var(--bg-detail);
|
|
567
|
+
border-bottom: 1px solid var(--border);
|
|
568
|
+
}
|
|
569
|
+
.detail {
|
|
570
|
+
padding: 12px 18px 14px;
|
|
571
|
+
border-top: 1px dashed var(--border-strong);
|
|
572
|
+
}
|
|
573
|
+
.detail-grid {
|
|
574
|
+
display: grid;
|
|
575
|
+
grid-template-columns: 1fr 1fr;
|
|
576
|
+
gap: 14px 24px;
|
|
577
|
+
}
|
|
578
|
+
.detail-section { min-width: 0; }
|
|
579
|
+
.detail-wide { grid-column: span 2; }
|
|
580
|
+
.detail-h {
|
|
581
|
+
display: flex;
|
|
582
|
+
align-items: center;
|
|
583
|
+
gap: 6px;
|
|
584
|
+
font-size: 10px;
|
|
585
|
+
text-transform: uppercase;
|
|
586
|
+
letter-spacing: 0.08em;
|
|
587
|
+
color: var(--fg-3);
|
|
588
|
+
padding-bottom: 6px;
|
|
589
|
+
margin-bottom: 6px;
|
|
590
|
+
border-bottom: 1px solid var(--border);
|
|
591
|
+
}
|
|
592
|
+
.detail-h .pill { margin-left: auto; margin-right: 0; }
|
|
593
|
+
.detail-h .pill + .pill { margin-left: 6px; }
|
|
594
|
+
|
|
595
|
+
.kv-table { width: 100%; font-size: 11.5px; }
|
|
596
|
+
.kv-table td { padding: 2px 0; vertical-align: top; }
|
|
597
|
+
.kv-key {
|
|
598
|
+
font-family: "JetBrains Mono", monospace;
|
|
599
|
+
color: var(--fg-3);
|
|
600
|
+
width: 140px;
|
|
601
|
+
white-space: nowrap;
|
|
602
|
+
}
|
|
603
|
+
.kv-val {
|
|
604
|
+
font-family: "JetBrains Mono", monospace;
|
|
605
|
+
color: var(--fg);
|
|
606
|
+
word-break: break-all;
|
|
607
|
+
}
|
|
608
|
+
.kv-val .masked { color: var(--mask); }
|
|
609
|
+
.kv-val .num { color: var(--mode-only); }
|
|
610
|
+
.kv-val .bool { color: var(--mode-snapshot); }
|
|
611
|
+
|
|
612
|
+
.json-empty {
|
|
613
|
+
font-family: "JetBrains Mono", monospace;
|
|
614
|
+
color: var(--fg-3);
|
|
615
|
+
font-size: 11.5px;
|
|
616
|
+
padding: 4px 0;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
.code {
|
|
620
|
+
background: var(--bg-2);
|
|
621
|
+
border: 1px solid var(--border);
|
|
622
|
+
border-radius: 3px;
|
|
623
|
+
padding: 8px 10px;
|
|
624
|
+
font-size: 11.5px;
|
|
625
|
+
color: var(--fg-1);
|
|
626
|
+
margin: 4px 0 0;
|
|
627
|
+
white-space: pre;
|
|
628
|
+
overflow-x: auto;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/* pager */
|
|
632
|
+
.pager {
|
|
633
|
+
flex-shrink: 0;
|
|
634
|
+
display: flex;
|
|
635
|
+
justify-content: space-between;
|
|
636
|
+
align-items: center;
|
|
637
|
+
padding: 8px 14px;
|
|
638
|
+
border-top: 1px solid var(--border);
|
|
639
|
+
background: var(--bg-1);
|
|
640
|
+
height: 36px;
|
|
641
|
+
font-size: 11.5px;
|
|
642
|
+
}
|
|
643
|
+
.pager-info { color: var(--fg-2); }
|
|
644
|
+
.pager-info .mono { color: var(--fg); font-variant-numeric: tabular-nums; }
|
|
645
|
+
.pager-btns { display: flex; align-items: center; gap: 4px; }
|
|
646
|
+
.pager-btns button {
|
|
647
|
+
width: 24px; height: 22px;
|
|
648
|
+
border: 1px solid var(--border);
|
|
649
|
+
background: var(--bg-2);
|
|
650
|
+
color: var(--fg-1);
|
|
651
|
+
border-radius: 3px;
|
|
652
|
+
font-size: 11px;
|
|
653
|
+
}
|
|
654
|
+
.pager-btns button:hover:not(:disabled) { background: var(--bg-3); }
|
|
655
|
+
.pager-btns button:disabled { color: var(--fg-4); cursor: not-allowed; }
|
|
656
|
+
.pager-page { color: var(--fg-2); padding: 0 8px; font-variant-numeric: tabular-nums; }
|
|
657
|
+
|
|
658
|
+
/* empty */
|
|
659
|
+
.table-empty {
|
|
660
|
+
display: flex;
|
|
661
|
+
flex-direction: column;
|
|
662
|
+
align-items: center;
|
|
663
|
+
justify-content: center;
|
|
664
|
+
padding: 80px 20px;
|
|
665
|
+
color: var(--fg-3);
|
|
666
|
+
gap: 6px;
|
|
667
|
+
}
|
|
668
|
+
.empty-mark { font-size: 28px; color: var(--fg-4); font-family: "JetBrains Mono", monospace; }
|
|
669
|
+
.empty-title { color: var(--fg-1); font-weight: 500; }
|
|
670
|
+
.empty-sub { font-size: 11px; color: var(--fg-3); font-family: "JetBrains Mono", monospace; }
|
|
671
|
+
|
|
672
|
+
/* Theme toggle — these rules live in an inline <style> block in the
|
|
673
|
+
prototype's index.html, so the verbatim styles.css port missed them. */
|
|
674
|
+
.theme-btn {
|
|
675
|
+
width: 22px; height: 22px;
|
|
676
|
+
border: 1px solid var(--border);
|
|
677
|
+
background: var(--bg-2);
|
|
678
|
+
color: var(--fg-1);
|
|
679
|
+
border-radius: 3px;
|
|
680
|
+
font-size: 13px;
|
|
681
|
+
line-height: 1;
|
|
682
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
683
|
+
cursor: pointer;
|
|
684
|
+
}
|
|
685
|
+
.theme-btn:hover { background: var(--bg-3); }
|
|
686
|
+
|
|
687
|
+
/* Copy button — match the trash/kind icon visually: 11×11 SVG with a fuller
|
|
688
|
+
body (defined in dashboard_helper#icon_copy) instead of the prototype's
|
|
689
|
+
sparse path. Wrapper is tight to the icon, no extra box. */
|
|
690
|
+
.copy-btn {
|
|
691
|
+
display: inline-flex;
|
|
692
|
+
align-items: center;
|
|
693
|
+
padding: 0;
|
|
694
|
+
margin-left: 4px;
|
|
695
|
+
vertical-align: -1px;
|
|
696
|
+
color: var(--fg-2);
|
|
697
|
+
}
|
|
698
|
+
.copy-btn:hover { color: var(--fg); }
|
|
699
|
+
|
|
700
|
+
/* ===== engine adapters =====
|
|
701
|
+
The prototype's segmented controls and sidebar rows are <button> elements
|
|
702
|
+
driven by JS state. Our Hotwire build uses <a> for Turbo navigation, so
|
|
703
|
+
these rules teach links to behave like the buttons they replaced. */
|
|
704
|
+
|
|
705
|
+
/* Strip the browser's default <a> underline for any link the dashboard styles. */
|
|
706
|
+
.app a { text-decoration: none; color: inherit; }
|
|
707
|
+
|
|
708
|
+
/* Segment buttons (time / mode / kind) need explicit flex/height/box-sizing
|
|
709
|
+
when rendered as <a> rather than <button>. */
|
|
710
|
+
.seg-btn {
|
|
711
|
+
display: inline-flex;
|
|
712
|
+
align-items: center;
|
|
713
|
+
box-sizing: border-box;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/* Pager nav buttons rendered as <a> need to match the prototype's <button>
|
|
717
|
+
box styling (the prototype CSS targets `.pager-btns button` exclusively). */
|
|
718
|
+
.pager-btns a {
|
|
719
|
+
width: 24px;
|
|
720
|
+
height: 22px;
|
|
721
|
+
border: 1px solid var(--border);
|
|
722
|
+
background: var(--bg-2);
|
|
723
|
+
color: var(--fg-1);
|
|
724
|
+
border-radius: 3px;
|
|
725
|
+
font-size: 11px;
|
|
726
|
+
display: inline-flex;
|
|
727
|
+
align-items: center;
|
|
728
|
+
justify-content: center;
|
|
729
|
+
}
|
|
730
|
+
.pager-btns a:hover { background: var(--bg-3); }
|
|
731
|
+
|
|
732
|
+
/* `clear` is an <a> in our build; match the prototype's <button> sizing. */
|
|
733
|
+
a.filter-clear {
|
|
734
|
+
display: inline-flex;
|
|
735
|
+
align-items: center;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/* Form holds search + actor side-by-side. `flex-shrink: 0` keeps the form at
|
|
739
|
+
its content width so the actor select never overlaps the segments; when
|
|
740
|
+
the row can't fit everything, `.filter-bar`'s `flex-wrap: wrap` pushes
|
|
741
|
+
segs to a second row instead. */
|
|
742
|
+
.filter-form {
|
|
743
|
+
display: flex;
|
|
744
|
+
align-items: center;
|
|
745
|
+
gap: 10px;
|
|
746
|
+
flex: 1 0 auto;
|
|
747
|
+
}
|
|
748
|
+
.filter-form .filter-search { flex: 1 1 220px; min-width: 0; }
|
|
749
|
+
.filter-form .filter-select { flex: 0 0 auto; }
|
|
750
|
+
|
|
751
|
+
/* Visually-hidden text for screen readers (labels, status announcements). */
|
|
752
|
+
.sr-only {
|
|
753
|
+
position: absolute;
|
|
754
|
+
width: 1px;
|
|
755
|
+
height: 1px;
|
|
756
|
+
padding: 0;
|
|
757
|
+
margin: -1px;
|
|
758
|
+
overflow: hidden;
|
|
759
|
+
clip: rect(0, 0, 0, 0);
|
|
760
|
+
white-space: nowrap;
|
|
761
|
+
border: 0;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/* The dashboard wrapper and its inner regions sit between .main (flex column,
|
|
765
|
+
owns scroll boundary) and .table-wrap (needs flex: 1 + overflow: auto to
|
|
766
|
+
scroll the table independently). `display: contents` makes them transparent
|
|
767
|
+
to layout so their children become direct flex children of .main, restoring
|
|
768
|
+
the prototype's vertical-fill / table-scroll behavior. */
|
|
769
|
+
#athar-dashboard,
|
|
770
|
+
#athar-pre,
|
|
771
|
+
#athar-post { display: contents; }
|
|
772
|
+
|
|
773
|
+
/* Loading indicator: while a partial fetch is in flight (the partial-link
|
|
774
|
+
delegated handler toggles `is-loading` on the swap regions), dim the
|
|
775
|
+
affected sections and disable pointer events to prevent double-submission.
|
|
776
|
+
The 200ms delay before fade-in avoids flicker on fast loads; the un-loading
|
|
777
|
+
state has a short symmetric fade-in so the snap-back isn't abrupt.
|
|
778
|
+
|
|
779
|
+
The filter bar is intentionally never dimmed — it lives outside both swap
|
|
780
|
+
regions and its descendants are not selected here. That keeps every search
|
|
781
|
+
keystroke flash-free. */
|
|
782
|
+
#athar-pre .kpi-strip,
|
|
783
|
+
#athar-post .table-wrap,
|
|
784
|
+
#athar-post .pager {
|
|
785
|
+
transition: opacity 120ms ease-in;
|
|
786
|
+
}
|
|
787
|
+
#athar-pre.is-loading .kpi-strip,
|
|
788
|
+
#athar-post.is-loading .table-wrap,
|
|
789
|
+
#athar-post.is-loading .pager {
|
|
790
|
+
opacity: 0.6;
|
|
791
|
+
transition: opacity 120ms 200ms ease-out;
|
|
792
|
+
pointer-events: none;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
/* "All deletions" sits directly under the brand row; without a gap it
|
|
796
|
+
visually collides with the head section. Specificity raised because
|
|
797
|
+
.sidebar-row's later `margin: 0 6px` would otherwise win the tie. */
|
|
798
|
+
.sidebar-row.sidebar-all { margin-top: 14px; }
|
|
799
|
+
|
|
800
|
+
/* Native <select> arrows are browser-positioned and can't be styled, so
|
|
801
|
+
suppress the native arrow and render our own chevron as an absolutely
|
|
802
|
+
positioned SVG (rendered inline next to the <select>). */
|
|
803
|
+
/* The actor select shows long emails as options. Without explicit sizing,
|
|
804
|
+
appearance:none lets the select grow to its widest option (~250px),
|
|
805
|
+
crowding out the time/mode/kind segments. Cap to a sensible width and
|
|
806
|
+
render our own chevron next to it. The native dropdown popup ignores
|
|
807
|
+
width, so long labels remain fully visible when opened. */
|
|
808
|
+
.filter-select { position: relative; }
|
|
809
|
+
.filter-select select {
|
|
810
|
+
appearance: none;
|
|
811
|
+
-webkit-appearance: none;
|
|
812
|
+
-moz-appearance: none;
|
|
813
|
+
width: 220px;
|
|
814
|
+
padding-right: 24px;
|
|
815
|
+
text-overflow: ellipsis;
|
|
816
|
+
}
|
|
817
|
+
.filter-select > svg {
|
|
818
|
+
position: absolute;
|
|
819
|
+
right: 8px;
|
|
820
|
+
top: 50%;
|
|
821
|
+
transform: translateY(-50%);
|
|
822
|
+
color: var(--fg-2);
|
|
823
|
+
pointer-events: none;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/* Copy button on requery code blocks: positioned top-right, with a small
|
|
827
|
+
backdrop so the icon stays legible over the code text. */
|
|
828
|
+
.code-wrap { position: relative; }
|
|
829
|
+
.code-wrap .code { padding-right: 36px; }
|
|
830
|
+
.code-wrap > .copy-btn {
|
|
831
|
+
position: absolute;
|
|
832
|
+
top: 6px;
|
|
833
|
+
right: 6px;
|
|
834
|
+
width: 22px;
|
|
835
|
+
height: 22px;
|
|
836
|
+
background: var(--bg-3);
|
|
837
|
+
border: 1px solid var(--border);
|
|
838
|
+
border-radius: 3px;
|
|
839
|
+
margin: 0;
|
|
840
|
+
}
|
|
841
|
+
.code-wrap > .copy-btn:hover { background: var(--bg-2); color: var(--fg); }
|