free-coding-models 0.5.15 → 0.5.16
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/changelog/v0.5.16.md +15 -0
- package/package.json +1 -1
- package/web/dist/assets/index-avN2GM3H.css +1 -0
- package/web/dist/assets/index-vsysCImz.js +41 -0
- package/web/dist/index.html +6 -2
- package/web/index.html +4 -0
- package/web/server.js +166 -1
- package/web/src/App.jsx +31 -14
- package/web/src/components/dashboard/ExpandedDetailRow.jsx +464 -0
- package/web/src/components/dashboard/ExpandedDetailRow.module.css +347 -0
- package/web/src/components/dashboard/ModelTable.jsx +49 -29
- package/web/src/components/dashboard/ModelTable.module.css +12 -0
- package/web/dist/assets/index-BRqzsHVw.css +0 -1
- package/web/dist/assets/index-CUYQh5_t.js +0 -39
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file web/src/components/dashboard/ExpandedDetailRow.module.css
|
|
3
|
+
* @description CSS module for the 3-column expandable detail row shown beneath a table row.
|
|
4
|
+
* 📖 Columns: Info (left), Mini Playground (center), AI Latency Test (right).
|
|
5
|
+
* 📖 Supports dark/light themes via CSS variables and [data-theme="light"] overrides.
|
|
6
|
+
* 📖 Responsive: collapses to single column on screens < 900px.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/* ─── Root container ─── */
|
|
10
|
+
.row {
|
|
11
|
+
grid-column: 1 / -1;
|
|
12
|
+
padding: 16px 20px;
|
|
13
|
+
background: var(--color-bg-elevated);
|
|
14
|
+
border-bottom: 2px solid var(--color-accent);
|
|
15
|
+
animation: slideDown 180ms ease-out;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@keyframes slideDown {
|
|
19
|
+
from {
|
|
20
|
+
opacity: 0;
|
|
21
|
+
max-height: 0;
|
|
22
|
+
}
|
|
23
|
+
to {
|
|
24
|
+
opacity: 1;
|
|
25
|
+
max-height: 600px;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.grid {
|
|
30
|
+
display: grid;
|
|
31
|
+
grid-template-columns: 280px 1fr 300px;
|
|
32
|
+
gap: 20px;
|
|
33
|
+
min-height: 220px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* ─── Column shared ─── */
|
|
37
|
+
.col {
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-direction: column;
|
|
40
|
+
gap: 10px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.colTitle {
|
|
44
|
+
font-size: 10px;
|
|
45
|
+
font-weight: 700;
|
|
46
|
+
text-transform: uppercase;
|
|
47
|
+
letter-spacing: 0.8px;
|
|
48
|
+
color: var(--color-text-muted);
|
|
49
|
+
margin-bottom: 2px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* ─── Column 1: Info ─── */
|
|
53
|
+
.statGrid {
|
|
54
|
+
display: grid;
|
|
55
|
+
grid-template-columns: 1fr 1fr;
|
|
56
|
+
gap: 8px 12px;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.statItem {
|
|
60
|
+
display: flex;
|
|
61
|
+
flex-direction: column;
|
|
62
|
+
gap: 2px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.statLabel {
|
|
66
|
+
font-size: 9px;
|
|
67
|
+
font-weight: 700;
|
|
68
|
+
text-transform: uppercase;
|
|
69
|
+
letter-spacing: 0.5px;
|
|
70
|
+
color: var(--color-text-dim);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.statValue {
|
|
74
|
+
font-family: var(--font-mono);
|
|
75
|
+
font-size: 12px;
|
|
76
|
+
font-weight: 600;
|
|
77
|
+
display: flex;
|
|
78
|
+
align-items: center;
|
|
79
|
+
gap: 5px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.infoActions {
|
|
83
|
+
display: flex;
|
|
84
|
+
align-items: center;
|
|
85
|
+
gap: 8px;
|
|
86
|
+
margin-top: auto;
|
|
87
|
+
padding-top: 8px;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.favBtn {
|
|
91
|
+
display: inline-flex;
|
|
92
|
+
align-items: center;
|
|
93
|
+
gap: 4px;
|
|
94
|
+
padding: 6px 10px;
|
|
95
|
+
background: var(--color-bg-card);
|
|
96
|
+
color: var(--color-text-muted);
|
|
97
|
+
border: 1px solid var(--color-border);
|
|
98
|
+
border-radius: 6px;
|
|
99
|
+
font-size: 11px;
|
|
100
|
+
font-weight: 600;
|
|
101
|
+
cursor: pointer;
|
|
102
|
+
transition: all 150ms;
|
|
103
|
+
}
|
|
104
|
+
.favBtn:hover {
|
|
105
|
+
border-color: #ffd700;
|
|
106
|
+
color: #ffd700;
|
|
107
|
+
background: rgba(255, 215, 0, 0.08);
|
|
108
|
+
}
|
|
109
|
+
.favBtnActive {
|
|
110
|
+
border-color: #ffd700;
|
|
111
|
+
color: #ffd700;
|
|
112
|
+
background: rgba(255, 215, 0, 0.12);
|
|
113
|
+
}
|
|
114
|
+
:global([data-theme="light"]) .favBtn { color: #555; }
|
|
115
|
+
:global([data-theme="light"]) .favBtn:hover { color: #b08800; border-color: #b08800; background: rgba(176, 136, 0, 0.06); }
|
|
116
|
+
:global([data-theme="light"]) .favBtnActive { color: #b08800; border-color: #b08800; background: rgba(176, 136, 0, 0.10); }
|
|
117
|
+
|
|
118
|
+
/* ─── Column 2: Mini Playground ─── */
|
|
119
|
+
.playgroundHeader {
|
|
120
|
+
font-size: 12px;
|
|
121
|
+
font-weight: 700;
|
|
122
|
+
color: var(--color-text);
|
|
123
|
+
margin-bottom: 2px;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.inputRow {
|
|
127
|
+
display: flex;
|
|
128
|
+
gap: 6px;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.inputField {
|
|
132
|
+
flex: 1;
|
|
133
|
+
padding: 8px 10px;
|
|
134
|
+
font-size: 12px;
|
|
135
|
+
font-family: var(--font-mono);
|
|
136
|
+
background: var(--color-bg-card);
|
|
137
|
+
color: var(--color-text);
|
|
138
|
+
border: 1px solid var(--color-border);
|
|
139
|
+
border-radius: 6px;
|
|
140
|
+
outline: none;
|
|
141
|
+
transition: border-color 150ms;
|
|
142
|
+
}
|
|
143
|
+
.inputField:focus {
|
|
144
|
+
border-color: var(--color-accent);
|
|
145
|
+
}
|
|
146
|
+
.inputField::placeholder {
|
|
147
|
+
color: var(--color-text-dim);
|
|
148
|
+
}
|
|
149
|
+
.inputField:disabled {
|
|
150
|
+
opacity: 0.5;
|
|
151
|
+
cursor: not-allowed;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.sendBtn {
|
|
155
|
+
display: inline-flex;
|
|
156
|
+
align-items: center;
|
|
157
|
+
justify-content: center;
|
|
158
|
+
gap: 4px;
|
|
159
|
+
padding: 8px 12px;
|
|
160
|
+
font-size: 12px;
|
|
161
|
+
font-weight: 700;
|
|
162
|
+
font-family: var(--font-mono);
|
|
163
|
+
background: var(--color-accent);
|
|
164
|
+
color: var(--color-bg);
|
|
165
|
+
border: 1px solid transparent;
|
|
166
|
+
border-radius: 6px;
|
|
167
|
+
cursor: pointer;
|
|
168
|
+
transition: all 150ms;
|
|
169
|
+
white-space: nowrap;
|
|
170
|
+
}
|
|
171
|
+
.sendBtn:hover:not(:disabled) {
|
|
172
|
+
opacity: 0.9;
|
|
173
|
+
}
|
|
174
|
+
.sendBtn:disabled {
|
|
175
|
+
opacity: 0.4;
|
|
176
|
+
cursor: not-allowed;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.responseArea {
|
|
180
|
+
flex: 1;
|
|
181
|
+
min-height: 60px;
|
|
182
|
+
max-height: 160px;
|
|
183
|
+
overflow-y: auto;
|
|
184
|
+
padding: 10px 12px;
|
|
185
|
+
font-family: var(--font-mono);
|
|
186
|
+
font-size: 11px;
|
|
187
|
+
line-height: 1.5;
|
|
188
|
+
color: var(--color-text);
|
|
189
|
+
background: var(--color-bg-card);
|
|
190
|
+
border: 1px solid var(--color-border);
|
|
191
|
+
border-radius: 6px;
|
|
192
|
+
white-space: pre-wrap;
|
|
193
|
+
word-break: break-word;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.responsePlaceholder {
|
|
197
|
+
color: var(--color-text-dim);
|
|
198
|
+
font-style: italic;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/* ─── Column 3: AI Latency ─── */
|
|
202
|
+
.benchBtn {
|
|
203
|
+
display: inline-flex;
|
|
204
|
+
align-items: center;
|
|
205
|
+
justify-content: center;
|
|
206
|
+
gap: 6px;
|
|
207
|
+
padding: 8px 14px;
|
|
208
|
+
font-size: 12px;
|
|
209
|
+
font-weight: 700;
|
|
210
|
+
font-family: var(--font-mono);
|
|
211
|
+
background: rgba(180, 0, 255, 0.12);
|
|
212
|
+
color: #b400ff;
|
|
213
|
+
border: 1px solid rgba(180, 0, 255, 0.4);
|
|
214
|
+
border-radius: 6px;
|
|
215
|
+
cursor: pointer;
|
|
216
|
+
transition: all 150ms;
|
|
217
|
+
}
|
|
218
|
+
.benchBtn:hover:not(:disabled) {
|
|
219
|
+
background: rgba(180, 0, 255, 0.20);
|
|
220
|
+
border-color: rgba(180, 0, 255, 0.7);
|
|
221
|
+
}
|
|
222
|
+
.benchBtn:disabled {
|
|
223
|
+
opacity: 0.4;
|
|
224
|
+
cursor: not-allowed;
|
|
225
|
+
}
|
|
226
|
+
:global([data-theme="light"]) .benchBtn {
|
|
227
|
+
background: rgba(140, 0, 200, 0.06);
|
|
228
|
+
color: #6a00a0;
|
|
229
|
+
border-color: rgba(140, 0, 200, 0.3);
|
|
230
|
+
}
|
|
231
|
+
:global([data-theme="light"]) .benchBtn:hover:not(:disabled) {
|
|
232
|
+
background: rgba(140, 0, 200, 0.12);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.metricsGrid {
|
|
236
|
+
display: grid;
|
|
237
|
+
grid-template-columns: 1fr 1fr;
|
|
238
|
+
gap: 6px;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.metricCard {
|
|
242
|
+
display: flex;
|
|
243
|
+
flex-direction: column;
|
|
244
|
+
gap: 2px;
|
|
245
|
+
padding: 8px 10px;
|
|
246
|
+
background: var(--color-bg-card);
|
|
247
|
+
border: 1px solid var(--color-border);
|
|
248
|
+
border-radius: 6px;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.metricLabel {
|
|
252
|
+
font-size: 9px;
|
|
253
|
+
font-weight: 700;
|
|
254
|
+
text-transform: uppercase;
|
|
255
|
+
letter-spacing: 0.5px;
|
|
256
|
+
color: var(--color-text-dim);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.metricValue {
|
|
260
|
+
font-family: var(--font-mono);
|
|
261
|
+
font-size: 14px;
|
|
262
|
+
font-weight: 700;
|
|
263
|
+
color: var(--color-accent);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.streamText {
|
|
267
|
+
min-height: 40px;
|
|
268
|
+
max-height: 80px;
|
|
269
|
+
overflow-y: auto;
|
|
270
|
+
padding: 8px 10px;
|
|
271
|
+
font-family: var(--font-mono);
|
|
272
|
+
font-size: 11px;
|
|
273
|
+
line-height: 1.4;
|
|
274
|
+
color: var(--color-text);
|
|
275
|
+
background: var(--color-bg-card);
|
|
276
|
+
border: 1px solid var(--color-border);
|
|
277
|
+
border-radius: 6px;
|
|
278
|
+
white-space: pre-wrap;
|
|
279
|
+
word-break: break-word;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.progressBar {
|
|
283
|
+
width: 100%;
|
|
284
|
+
height: 4px;
|
|
285
|
+
background: var(--color-bg-card);
|
|
286
|
+
border-radius: 2px;
|
|
287
|
+
overflow: hidden;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.progressFill {
|
|
291
|
+
height: 100%;
|
|
292
|
+
background: var(--color-accent);
|
|
293
|
+
border-radius: 2px;
|
|
294
|
+
transition: width 200ms ease-out;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/* ─── SWE score classes ─── */
|
|
298
|
+
.sweHigh { color: #ffd700; font-weight: 700; }
|
|
299
|
+
.sweMid { color: #3ddc84; font-weight: 600; }
|
|
300
|
+
.sweLow { color: var(--color-text-dim); }
|
|
301
|
+
:global([data-theme="light"]) .sweHigh { color: #b08800; }
|
|
302
|
+
:global([data-theme="light"]) .sweMid { color: #1a8f4e; }
|
|
303
|
+
|
|
304
|
+
/* ─── Ping classes ─── */
|
|
305
|
+
.pingFast { color: #00ff88; }
|
|
306
|
+
.pingMedium { color: #ffaa00; }
|
|
307
|
+
.pingSlow { color: #ff4444; }
|
|
308
|
+
.pingNone { color: var(--color-text-dim); }
|
|
309
|
+
:global([data-theme="light"]) .pingFast { color: #008f4d; }
|
|
310
|
+
:global([data-theme="light"]) .pingMedium { color: #b86b00; }
|
|
311
|
+
:global([data-theme="light"]) .pingSlow { color: #c8143a; }
|
|
312
|
+
|
|
313
|
+
/* ─── No API key banner ─── */
|
|
314
|
+
.noKeyBanner {
|
|
315
|
+
grid-column: 1 / -1;
|
|
316
|
+
display: flex;
|
|
317
|
+
align-items: center;
|
|
318
|
+
justify-content: center;
|
|
319
|
+
gap: 10px;
|
|
320
|
+
padding: 32px 20px;
|
|
321
|
+
color: var(--color-text-dim);
|
|
322
|
+
font-size: 13px;
|
|
323
|
+
font-weight: 600;
|
|
324
|
+
text-align: center;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.noKeyIcon {
|
|
328
|
+
font-size: 20px;
|
|
329
|
+
opacity: 0.5;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/* ─── Spinning animation ─── */
|
|
333
|
+
.spinning {
|
|
334
|
+
animation: spin 1s linear infinite;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
@keyframes spin {
|
|
338
|
+
from { transform: rotate(0deg); }
|
|
339
|
+
to { transform: rotate(360deg); }
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/* ─── Responsive: single column below 900px ─── */
|
|
343
|
+
@media (max-width: 900px) {
|
|
344
|
+
.grid {
|
|
345
|
+
grid-template-columns: 1fr;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* 📖 Custom widths persist in localStorage via the useColumnSizing hook and survive reloads.
|
|
14
14
|
* 📖 A "Reset columns" button appears in the toolbar only when the user has custom widths.
|
|
15
15
|
*/
|
|
16
|
-
import { useMemo, useEffect } from 'react'
|
|
16
|
+
import { useMemo, useEffect, useState, useCallback, Fragment } from 'react'
|
|
17
17
|
import {
|
|
18
18
|
useReactTable,
|
|
19
19
|
getCoreRowModel,
|
|
@@ -43,6 +43,7 @@ import { sweClass } from '../../utils/ranks.js'
|
|
|
43
43
|
// 📖 file the TUI uses; both surfaces share the source of truth for which
|
|
44
44
|
// 📖 providers are compatible with which tools.
|
|
45
45
|
import { getCompatibleTools } from '../../../../src/core/tool-metadata.js'
|
|
46
|
+
import ExpandedDetailRow from './ExpandedDetailRow.jsx'
|
|
46
47
|
import styles from './ModelTable.module.css'
|
|
47
48
|
|
|
48
49
|
const colHelper = createColumnHelper()
|
|
@@ -345,7 +346,15 @@ export default function ModelTable({
|
|
|
345
346
|
filtered, onSelectModel, onBenchmarkRow, onLaunch, favorites,
|
|
346
347
|
sortColumn, sortDirection, onSort,
|
|
347
348
|
toolMode = null,
|
|
349
|
+
onToast, onSetToolMode, onCycleToolMode, onOpenFallback,
|
|
348
350
|
}) {
|
|
351
|
+
// 📖 Expand row state — only one row expanded at a time (accordion)
|
|
352
|
+
const [expandedRowId, setExpandedRowId] = useState(null)
|
|
353
|
+
|
|
354
|
+
const toggleExpand = useCallback((model) => {
|
|
355
|
+
const key = `${model.providerKey}/${model.modelId}`
|
|
356
|
+
setExpandedRowId((prev) => prev === key ? null : key)
|
|
357
|
+
}, [])
|
|
349
358
|
// Compute top3 for medal rows
|
|
350
359
|
const top3Ids = useMemo(() => {
|
|
351
360
|
const online = filtered.filter(m => m.status === 'up' && m.avg != null && m.avg !== Infinity && m.avg < 99000)
|
|
@@ -501,49 +510,60 @@ export default function ModelTable({
|
|
|
501
510
|
<tbody>
|
|
502
511
|
{rows.map((row, i) => {
|
|
503
512
|
const m = row.original
|
|
513
|
+
const rowKey = `${m.providerKey}/${m.modelId}`
|
|
514
|
+
const isExpanded = expandedRowId === rowKey
|
|
504
515
|
const rankIdx = [...top3Ids].indexOf(m.modelId)
|
|
505
516
|
const rowClasses = []
|
|
506
517
|
if (rankIdx >= 0) rowClasses.push(styles[`rank${rankIdx + 1}`])
|
|
507
|
-
// 📖 Per-row benchmark state comes from the backend. The old global
|
|
508
|
-
// 📖 heuristic made every unbenchmarked row spin until the whole scan ended.
|
|
509
518
|
if (m.isBenchmarking) rowClasses.push(styles.benchRow)
|
|
510
|
-
|
|
511
|
-
// 📖 model isn't compatible with it. Mirrors the TUI render-table.js
|
|
512
|
-
// 📖 behavior. The picker itself ships in M3; for M1 the visual is wired.
|
|
519
|
+
if (isExpanded) rowClasses.push(styles.expandedRow)
|
|
513
520
|
if (toolMode) {
|
|
514
521
|
const compat = getCompatibleTools(m.providerKey)
|
|
515
522
|
if (!compat.includes(toolMode)) {
|
|
516
523
|
rowClasses.push(styles.incompatible)
|
|
517
524
|
}
|
|
518
525
|
}
|
|
519
|
-
// 📖 "Unusable" rows (no API key / bad API key) get the same 80% opacity
|
|
520
|
-
// 📖 fade the TUI uses, so the user can see at a glance which models
|
|
521
|
-
// 📖 they cannot actually use. The fade is applied as opacity on the
|
|
522
|
-
// 📖 whole <tr> so it composes cleanly with rank/bench/incompatible
|
|
523
|
-
// 📖 styles. Cursor hover is not implemented in the web, so the
|
|
524
|
-
// 📖 fade is never overridden by an active-selection highlight.
|
|
525
526
|
if (m.status === 'noauth' || m.status === 'auth_error') {
|
|
526
527
|
rowClasses.push(styles.unusable)
|
|
527
528
|
}
|
|
528
529
|
return (
|
|
529
|
-
<
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
530
|
+
<Fragment key={row.id}>
|
|
531
|
+
<tr
|
|
532
|
+
className={rowClasses.join(' ')}
|
|
533
|
+
onClick={() => toggleExpand(m)}
|
|
534
|
+
>
|
|
535
|
+
{row.getVisibleCells().map(cell => {
|
|
536
|
+
const sizePx = `${cell.column.getSize()}px`
|
|
537
|
+
return (
|
|
538
|
+
<td
|
|
539
|
+
key={cell.id}
|
|
540
|
+
className={styles.td}
|
|
541
|
+
style={{ width: sizePx, minWidth: sizePx, maxWidth: sizePx }}
|
|
542
|
+
>
|
|
543
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
544
|
+
</td>
|
|
545
|
+
)
|
|
546
|
+
})}
|
|
547
|
+
</tr>
|
|
548
|
+
{/* 📖 Expanded detail row — 3-column panel with info, playground, AI latency */}
|
|
549
|
+
{isExpanded && (
|
|
550
|
+
<tr className={styles.expandRowWrapper}>
|
|
551
|
+
<td colSpan={row.getVisibleCells().length} className={styles.expandRowCell}>
|
|
552
|
+
<ExpandedDetailRow
|
|
553
|
+
model={m}
|
|
554
|
+
favorites={favorites}
|
|
555
|
+
onBenchmark={onBenchmarkRow}
|
|
556
|
+
onLaunch={onLaunch}
|
|
557
|
+
onToast={onToast}
|
|
558
|
+
toolMode={toolMode}
|
|
559
|
+
onSetToolMode={onSetToolMode}
|
|
560
|
+
onCycleToolMode={onCycleToolMode}
|
|
561
|
+
onOpenFallback={onOpenFallback}
|
|
562
|
+
/>
|
|
543
563
|
</td>
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
</
|
|
564
|
+
</tr>
|
|
565
|
+
)}
|
|
566
|
+
</Fragment>
|
|
547
567
|
)
|
|
548
568
|
})}
|
|
549
569
|
</tbody>
|
|
@@ -356,4 +356,16 @@
|
|
|
356
356
|
.incompatible .launchCell,
|
|
357
357
|
.benchRow .launchCell {
|
|
358
358
|
opacity: 1;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/* ─── Expandable row styles ─── */
|
|
362
|
+
.expandedRow {
|
|
363
|
+
background: var(--color-bg-active) !important;
|
|
364
|
+
}
|
|
365
|
+
.expandRowWrapper {
|
|
366
|
+
border: none;
|
|
367
|
+
}
|
|
368
|
+
.expandRowCell {
|
|
369
|
+
padding: 0 !important;
|
|
370
|
+
border-bottom: 2px solid var(--color-accent) !important;
|
|
359
371
|
}
|