@kirrosh/zond 0.7.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.
Files changed (102) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/LICENSE +21 -0
  3. package/README.md +130 -0
  4. package/package.json +53 -0
  5. package/src/bun-types.d.ts +5 -0
  6. package/src/cli/commands/add-api.ts +51 -0
  7. package/src/cli/commands/ai-generate.ts +106 -0
  8. package/src/cli/commands/chat.ts +43 -0
  9. package/src/cli/commands/ci-init.ts +163 -0
  10. package/src/cli/commands/collections.ts +41 -0
  11. package/src/cli/commands/compare.ts +129 -0
  12. package/src/cli/commands/coverage.ts +156 -0
  13. package/src/cli/commands/doctor.ts +127 -0
  14. package/src/cli/commands/init.ts +84 -0
  15. package/src/cli/commands/mcp.ts +16 -0
  16. package/src/cli/commands/run.ts +156 -0
  17. package/src/cli/commands/runs.ts +108 -0
  18. package/src/cli/commands/serve.ts +22 -0
  19. package/src/cli/commands/update.ts +142 -0
  20. package/src/cli/commands/validate.ts +18 -0
  21. package/src/cli/index.ts +529 -0
  22. package/src/cli/output.ts +24 -0
  23. package/src/cli/runtime.ts +7 -0
  24. package/src/core/agent/agent-loop.ts +116 -0
  25. package/src/core/agent/context-manager.ts +41 -0
  26. package/src/core/agent/system-prompt.ts +28 -0
  27. package/src/core/agent/tools/diagnose-failure.ts +51 -0
  28. package/src/core/agent/tools/explore-api.ts +40 -0
  29. package/src/core/agent/tools/index.ts +46 -0
  30. package/src/core/agent/tools/query-results.ts +40 -0
  31. package/src/core/agent/tools/run-tests.ts +38 -0
  32. package/src/core/agent/tools/send-request.ts +44 -0
  33. package/src/core/agent/tools/validate-tests.ts +23 -0
  34. package/src/core/agent/types.ts +22 -0
  35. package/src/core/diagnostics/failure-hints.ts +63 -0
  36. package/src/core/generator/ai/ai-generator.ts +61 -0
  37. package/src/core/generator/ai/llm-client.ts +159 -0
  38. package/src/core/generator/ai/output-parser.ts +307 -0
  39. package/src/core/generator/ai/prompt-builder.ts +153 -0
  40. package/src/core/generator/ai/types.ts +56 -0
  41. package/src/core/generator/chunker.ts +47 -0
  42. package/src/core/generator/coverage-scanner.ts +87 -0
  43. package/src/core/generator/data-factory.ts +115 -0
  44. package/src/core/generator/endpoint-warnings.ts +43 -0
  45. package/src/core/generator/index.ts +12 -0
  46. package/src/core/generator/openapi-reader.ts +143 -0
  47. package/src/core/generator/schema-utils.ts +52 -0
  48. package/src/core/generator/serializer.ts +189 -0
  49. package/src/core/generator/types.ts +48 -0
  50. package/src/core/parser/filter.ts +14 -0
  51. package/src/core/parser/index.ts +21 -0
  52. package/src/core/parser/schema.ts +175 -0
  53. package/src/core/parser/types.ts +52 -0
  54. package/src/core/parser/variables.ts +154 -0
  55. package/src/core/parser/yaml-parser.ts +85 -0
  56. package/src/core/reporter/console.ts +175 -0
  57. package/src/core/reporter/index.ts +23 -0
  58. package/src/core/reporter/json.ts +9 -0
  59. package/src/core/reporter/junit.ts +78 -0
  60. package/src/core/reporter/types.ts +12 -0
  61. package/src/core/runner/assertions.ts +173 -0
  62. package/src/core/runner/execute-run.ts +97 -0
  63. package/src/core/runner/executor.ts +183 -0
  64. package/src/core/runner/http-client.ts +69 -0
  65. package/src/core/runner/index.ts +12 -0
  66. package/src/core/runner/types.ts +48 -0
  67. package/src/core/setup-api.ts +113 -0
  68. package/src/core/utils.ts +9 -0
  69. package/src/db/queries.ts +774 -0
  70. package/src/db/schema.ts +159 -0
  71. package/src/mcp/descriptions.ts +88 -0
  72. package/src/mcp/server.ts +52 -0
  73. package/src/mcp/tools/ci-init.ts +54 -0
  74. package/src/mcp/tools/coverage-analysis.ts +141 -0
  75. package/src/mcp/tools/describe-endpoint.ts +241 -0
  76. package/src/mcp/tools/explore-api.ts +84 -0
  77. package/src/mcp/tools/generate-and-save.ts +129 -0
  78. package/src/mcp/tools/generate-missing-tests.ts +91 -0
  79. package/src/mcp/tools/generate-tests-guide.ts +391 -0
  80. package/src/mcp/tools/manage-server.ts +86 -0
  81. package/src/mcp/tools/query-db.ts +255 -0
  82. package/src/mcp/tools/run-tests.ts +71 -0
  83. package/src/mcp/tools/save-test-suite.ts +218 -0
  84. package/src/mcp/tools/send-request.ts +63 -0
  85. package/src/mcp/tools/set-work-dir.ts +35 -0
  86. package/src/mcp/tools/setup-api.ts +84 -0
  87. package/src/mcp/tools/validate-tests.ts +43 -0
  88. package/src/tui/chat-ui.ts +150 -0
  89. package/src/web/data/collection-state.ts +360 -0
  90. package/src/web/routes/api.ts +234 -0
  91. package/src/web/routes/dashboard.ts +313 -0
  92. package/src/web/routes/runs.ts +64 -0
  93. package/src/web/schemas.ts +121 -0
  94. package/src/web/server.ts +134 -0
  95. package/src/web/static/htmx.min.js +1 -0
  96. package/src/web/static/style.css +827 -0
  97. package/src/web/views/endpoints-tab.ts +170 -0
  98. package/src/web/views/health-strip.ts +92 -0
  99. package/src/web/views/layout.ts +48 -0
  100. package/src/web/views/results.ts +209 -0
  101. package/src/web/views/runs-tab.ts +126 -0
  102. package/src/web/views/suites-tab.ts +153 -0
@@ -0,0 +1,827 @@
1
+ /* ═══════════════════════════════════════════════════
2
+ zond Design System
3
+ Mission Control — dark-first developer dashboard
4
+ ═══════════════════════════════════════════════════ */
5
+
6
+ :root {
7
+ /* Surface */
8
+ --bg: #0c0e12;
9
+ --bg-secondary: #12151b;
10
+ --bg-raised: #1a1e27;
11
+ --bg-hover: #1f2430;
12
+ --bg-inset: #0a0c10;
13
+
14
+ /* Text */
15
+ --text: #e2e6ef;
16
+ --text-dim: #8690a5;
17
+ --text-muted: #545d72;
18
+
19
+ /* Borders */
20
+ --border: #1e2330;
21
+ --border-subtle: #171b24;
22
+
23
+ /* Status */
24
+ --pass: #34d399;
25
+ --pass-dim: #064e3b;
26
+ --fail: #f87171;
27
+ --fail-dim: #7f1d1d;
28
+ --warn: #fbbf24;
29
+ --warn-dim: #78350f;
30
+ --skip: #64748b;
31
+ --skip-dim: #1e293b;
32
+ --error: #ea580c;
33
+
34
+ /* Accent */
35
+ --accent: #3d8bfd;
36
+ --accent-hover: #5b9ffd;
37
+ --accent-dim: #172554;
38
+
39
+ /* Method colors */
40
+ --method-get: #34d399;
41
+ --method-post: #60a5fa;
42
+ --method-put: #fbbf24;
43
+ --method-patch: #c084fc;
44
+ --method-delete: #f87171;
45
+
46
+ /* Info */
47
+ --info: #60a5fa;
48
+ --info-dim: #1e3a5f;
49
+
50
+ /* Misc */
51
+ --text-inverse: #0c0e12;
52
+ --card-shadow: 0 1px 3px rgba(0,0,0,0.3);
53
+ --radius: 6px;
54
+ --radius-sm: 3px;
55
+ --radius-pill: 100px;
56
+ --font-ui: 'DM Sans', system-ui, sans-serif;
57
+ --font-mono: 'JetBrains Mono', 'Cascadia Code', 'Fira Code', monospace;
58
+ }
59
+
60
+ /* ── Light mode ── */
61
+ @media (prefers-color-scheme: light) {
62
+ :root {
63
+ --bg: #f0f2f5;
64
+ --bg-secondary: #ffffff;
65
+ --bg-raised: #f7f8fa;
66
+ --bg-hover: #eef0f4;
67
+ --bg-inset: #e8eaef;
68
+ --text: #1a1e27;
69
+ --text-dim: #545d72;
70
+ --text-muted: #8690a5;
71
+ --border: #dce0e8;
72
+ --border-subtle: #e8eaef;
73
+ --pass: #16a34a;
74
+ --pass-dim: #d1fae5;
75
+ --fail: #dc2626;
76
+ --fail-dim: #fee2e2;
77
+ --warn: #d97706;
78
+ --warn-dim: #fef3c7;
79
+ --skip: #9ca3af;
80
+ --skip-dim: #e2e8f0;
81
+ --accent: #2563eb;
82
+ --accent-hover: #1d4ed8;
83
+ --accent-dim: #dbeafe;
84
+ --method-get: #16a34a;
85
+ --method-post: #2563eb;
86
+ --method-put: #d97706;
87
+ --method-patch: #9333ea;
88
+ --method-delete: #dc2626;
89
+ --info: #2563eb;
90
+ --info-dim: #dbeafe;
91
+ --text-inverse: #ffffff;
92
+ --card-shadow: 0 1px 3px rgba(0,0,0,0.08);
93
+ }
94
+ }
95
+
96
+ /* ── Reset & Base ── */
97
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
98
+
99
+ html { font-size: 15px; }
100
+
101
+ body {
102
+ font-family: 'DM Sans', system-ui, -apple-system, sans-serif;
103
+ background: var(--bg);
104
+ color: var(--text);
105
+ line-height: 1.5;
106
+ -webkit-font-smoothing: antialiased;
107
+ }
108
+
109
+ /* Subtle grid background */
110
+ body::before {
111
+ content: '';
112
+ position: fixed;
113
+ inset: 0;
114
+ background-image:
115
+ linear-gradient(var(--border-subtle) 1px, transparent 1px),
116
+ linear-gradient(90deg, var(--border-subtle) 1px, transparent 1px);
117
+ background-size: 48px 48px;
118
+ opacity: 0.3;
119
+ pointer-events: none;
120
+ z-index: 0;
121
+ }
122
+
123
+ .main-container { max-width: 1280px; margin: 0 auto; padding: 0 1.25rem; position: relative; z-index: 1; }
124
+
125
+ /* ── Navbar ── */
126
+ .navbar {
127
+ display: flex;
128
+ align-items: center;
129
+ gap: 1rem;
130
+ height: 48px;
131
+ padding: 0 1.25rem;
132
+ background: rgba(18,21,27,0.85);
133
+ border-bottom: 1px solid var(--border);
134
+ position: sticky;
135
+ top: 0;
136
+ z-index: 100;
137
+ backdrop-filter: blur(12px);
138
+ }
139
+ @media (prefers-color-scheme: light) {
140
+ .navbar { background: rgba(255,255,255,0.85); }
141
+ }
142
+ .nav-brand {
143
+ font-weight: 700;
144
+ font-size: 0.95rem;
145
+ letter-spacing: -0.5px;
146
+ font-family: var(--font-mono);
147
+ display: flex;
148
+ align-items: center;
149
+ gap: 0.5rem;
150
+ }
151
+ .logo-dot {
152
+ width: 8px;
153
+ height: 8px;
154
+ border-radius: 50%;
155
+ background: var(--accent);
156
+ box-shadow: 0 0 8px var(--accent);
157
+ animation: pulse-dot 2s ease-in-out infinite;
158
+ }
159
+ @keyframes pulse-dot {
160
+ 0%, 100% { opacity: 1; }
161
+ 50% { opacity: 0.5; }
162
+ }
163
+ .nav-separator {
164
+ width: 1px;
165
+ height: 20px;
166
+ background: var(--border);
167
+ margin: 0 0.25rem;
168
+ }
169
+ .collection-selector {
170
+ font-family: var(--font-mono);
171
+ font-size: 0.85rem;
172
+ font-weight: 500;
173
+ padding: 0.3rem 0.75rem;
174
+ background: var(--bg-raised);
175
+ border: 1px solid var(--border);
176
+ border-radius: 3px;
177
+ color: var(--text);
178
+ cursor: pointer;
179
+ outline: none;
180
+ }
181
+ .collection-selector:focus { border-color: var(--accent); }
182
+ .nav-actions { display: flex; align-items: center; gap: 0.5rem; margin-left: auto; }
183
+
184
+ /* ── Footer ── */
185
+ .footer {
186
+ margin-top: 3rem;
187
+ padding: 1rem 0;
188
+ border-top: 1px solid var(--border);
189
+ color: var(--text-muted);
190
+ font-size: 0.75rem;
191
+ font-family: var(--font-mono);
192
+ text-align: center;
193
+ position: relative;
194
+ z-index: 1;
195
+ }
196
+
197
+ /* ── Health Strip ── */
198
+ .health-strip {
199
+ display: grid;
200
+ grid-template-columns: auto 1fr auto;
201
+ align-items: center;
202
+ gap: 1.5rem;
203
+ padding: 1rem 1.25rem;
204
+ margin: 1rem 0;
205
+ background: var(--bg-secondary);
206
+ border: 1px solid var(--border);
207
+ border-radius: var(--radius);
208
+ }
209
+ .health-donut-zone { display: flex; align-items: center; gap: 1rem; flex-shrink: 0; }
210
+ .health-info-zone { min-width: 200px; }
211
+
212
+ .coverage-donut { position: relative; text-align: center; }
213
+ .coverage-donut svg circle:last-of-type { transition: stroke-dashoffset 0.6s ease; }
214
+ .donut-label {
215
+ font-size: 0.7rem;
216
+ color: var(--text-muted);
217
+ margin-top: 0.25rem;
218
+ font-family: var(--font-mono);
219
+ }
220
+ .coverage-label { display: flex; flex-direction: column; }
221
+ .coverage-label .label-title {
222
+ font-size: 0.75rem;
223
+ font-weight: 600;
224
+ text-transform: uppercase;
225
+ letter-spacing: 0.5px;
226
+ color: var(--text-dim);
227
+ }
228
+ .coverage-label .label-value {
229
+ font-family: var(--font-mono);
230
+ font-size: 0.9rem;
231
+ font-weight: 500;
232
+ color: var(--text-dim);
233
+ }
234
+
235
+ .health-stats { display: flex; gap: 1.5rem; justify-content: center; flex-wrap: wrap; }
236
+ .stat-block { display: flex; flex-direction: column; align-items: center; min-width: 56px; }
237
+ .stat-value { display: block; font-size: 1.75rem; font-weight: 700; font-family: var(--font-mono); line-height: 1.1; }
238
+ .stat-label { font-size: 0.7rem; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-top: 2px; }
239
+ .stat-pass .stat-value { color: var(--pass); }
240
+ .stat-fail .stat-value { color: var(--fail); }
241
+ .stat-skip .stat-value { color: var(--skip); }
242
+
243
+ .health-progress { margin-top: 0.5rem; }
244
+ .health-progress-label { font-size: 0.7rem; color: var(--text-muted); margin-top: 0.2rem; display: block; font-family: var(--font-mono); }
245
+
246
+ /* ── Env Alert ── */
247
+ .env-alert {
248
+ grid-column: 1 / -1;
249
+ display: flex;
250
+ align-items: center;
251
+ gap: 0.5rem;
252
+ padding: 0.5rem 1rem;
253
+ background: var(--warn-dim);
254
+ border: 1px solid var(--warn);
255
+ border-radius: var(--radius);
256
+ color: var(--warn);
257
+ font-size: 0.8rem;
258
+ font-weight: 500;
259
+ }
260
+ .env-alert-icon { font-size: 1rem; }
261
+
262
+ /* ── Tab Bar ── */
263
+ .tab-bar {
264
+ display: flex;
265
+ gap: 0;
266
+ border-bottom: 1px solid var(--border);
267
+ margin: 0.75rem 0 0;
268
+ }
269
+ .tab-btn {
270
+ display: flex;
271
+ align-items: center;
272
+ gap: 0.4rem;
273
+ padding: 0.6rem 1.25rem;
274
+ font-size: 0.85rem;
275
+ font-weight: 600;
276
+ color: var(--text-dim);
277
+ background: none;
278
+ border: none;
279
+ border-bottom: 2px solid transparent;
280
+ margin-bottom: -1px;
281
+ cursor: pointer;
282
+ transition: color 0.15s, border-color 0.15s;
283
+ }
284
+ .tab-btn:hover { color: var(--text); }
285
+ .tab-btn.tab-active {
286
+ color: var(--text);
287
+ border-bottom-color: var(--accent);
288
+ }
289
+ .tab-count {
290
+ display: inline-block;
291
+ padding: 0.1rem 0.45rem;
292
+ border-radius: var(--radius-pill);
293
+ font-size: 0.7rem;
294
+ font-weight: 600;
295
+ font-family: var(--font-mono);
296
+ background: var(--bg-raised);
297
+ color: var(--text-dim);
298
+ }
299
+ .tab-btn.tab-active .tab-count {
300
+ background: var(--accent-dim);
301
+ color: var(--accent);
302
+ }
303
+
304
+ /* ── Tab Content ── */
305
+ #tab-content {
306
+ background: var(--bg-secondary);
307
+ border: 1px solid var(--border);
308
+ border-top: none;
309
+ border-radius: 0 0 var(--radius) var(--radius);
310
+ min-height: 300px;
311
+ animation: fade-in 0.2s ease;
312
+ }
313
+
314
+ @keyframes fade-in {
315
+ from { opacity: 0; transform: translateY(4px); }
316
+ to { opacity: 1; transform: translateY(0); }
317
+ }
318
+
319
+ .tab-empty {
320
+ padding: 3rem 2rem;
321
+ text-align: center;
322
+ color: var(--text-dim);
323
+ font-size: 0.9rem;
324
+ }
325
+ .tab-empty code { background: var(--bg-raised); padding: 0.15rem 0.4rem; border-radius: 3px; font-size: 0.85rem; }
326
+
327
+ /* ── Filter Bar ── */
328
+ .filter-bar {
329
+ display: flex;
330
+ gap: 0.4rem;
331
+ padding: 0.75rem 1rem;
332
+ flex-wrap: wrap;
333
+ }
334
+ .filter-chip {
335
+ padding: 0.25rem 0.7rem;
336
+ border: 1px solid var(--border);
337
+ border-radius: var(--radius-pill);
338
+ background: var(--bg-raised);
339
+ color: var(--text-dim);
340
+ font-size: 0.75rem;
341
+ font-weight: 500;
342
+ cursor: pointer;
343
+ transition: all 0.15s;
344
+ }
345
+ .filter-chip:hover { background: var(--bg-hover); color: var(--text); }
346
+ .filter-chip.filter-active { background: var(--accent-dim); color: var(--accent); border-color: var(--accent); }
347
+ .filter-chip-pass.filter-active { background: var(--pass-dim); color: var(--pass); border-color: var(--pass); }
348
+ .filter-chip-fail.filter-active { background: var(--fail-dim); color: var(--fail); border-color: var(--fail); }
349
+ .filter-chip-notrun.filter-active { background: var(--skip-dim); color: var(--skip); border-color: var(--skip); }
350
+ .filter-chip-notest.filter-active { background: var(--bg-raised); color: var(--text-dim); border-color: var(--text-dim); }
351
+
352
+ /* ── Endpoint List ── */
353
+ .endpoint-list { overflow: hidden; }
354
+
355
+ .endpoint-row {
356
+ display: grid;
357
+ grid-template-columns: 28px 64px 1fr auto;
358
+ align-items: center;
359
+ gap: 0.6rem;
360
+ padding: 0.55rem 1rem;
361
+ border-bottom: 1px solid var(--border-subtle);
362
+ cursor: pointer;
363
+ transition: background 0.1s;
364
+ }
365
+ .endpoint-row:last-of-type { border-bottom: none; }
366
+ .endpoint-row:hover { background: var(--bg-hover); }
367
+ .endpoint-row:has(.status-fail) { background: rgba(248,113,113,0.03); }
368
+
369
+ .endpoint-path { font-family: var(--font-mono); font-size: 0.85rem; font-weight: 500; }
370
+ .endpoint-summary { font-size: 0.7rem; color: var(--text-muted); margin-left: 0.5rem; }
371
+
372
+ /* Status dots */
373
+ .status-dot {
374
+ display: inline-block;
375
+ width: 10px;
376
+ height: 10px;
377
+ border-radius: 50%;
378
+ }
379
+ .status-pass { background: var(--pass); box-shadow: 0 0 6px var(--pass); }
380
+ .status-fail { background: var(--fail); box-shadow: 0 0 6px var(--fail); }
381
+ .status-notrun {
382
+ background: transparent;
383
+ border: 2px dashed var(--skip);
384
+ width: 10px;
385
+ height: 10px;
386
+ }
387
+ .status-notest { background: var(--skip); opacity: 0.3; }
388
+
389
+ /* Endpoint detail expand */
390
+ .endpoint-detail {
391
+ padding: 0.75rem 1rem 0.75rem 2.5rem;
392
+ background: var(--bg-inset);
393
+ border-bottom: 1px solid var(--border-subtle);
394
+ font-size: 0.85rem;
395
+ }
396
+ .ep-detail-section { margin: 0.25rem 0; }
397
+ .ep-detail-section ul { list-style: none; padding-left: 0; }
398
+ .ep-detail-section li { padding: 0.15rem 0; }
399
+ .ep-detail-section code { font-family: var(--font-mono); font-size: 0.75rem; color: var(--text-dim); }
400
+
401
+ /* ── Warning Badges ── */
402
+ .warning-badge {
403
+ display: inline-block;
404
+ padding: 0.08rem 0.4rem;
405
+ border-radius: 3px;
406
+ font-size: 0.6rem;
407
+ font-weight: 700;
408
+ text-transform: uppercase;
409
+ letter-spacing: 0.04em;
410
+ margin-left: 0.3rem;
411
+ }
412
+ .warning-deprecated { background: var(--warn-dim); color: var(--warn); border: 1px solid var(--warn); }
413
+ .warning-schema { background: var(--skip-dim); color: var(--skip); border: 1px solid var(--skip); }
414
+ .warning-params { background: var(--accent-dim); color: var(--accent); border: 1px solid var(--accent); }
415
+
416
+ /* ── Method Badges (ghost style) ── */
417
+ .badge-method {
418
+ display: inline-block;
419
+ padding: 0.12rem 0.45rem;
420
+ border-radius: 3px;
421
+ font-size: 0.7rem;
422
+ font-weight: 700;
423
+ font-family: var(--font-mono);
424
+ min-width: 48px;
425
+ text-align: center;
426
+ }
427
+ .method-get { background: rgba(52,211,153,0.1); color: var(--method-get); }
428
+ .method-post { background: rgba(96,165,250,0.1); color: var(--method-post); }
429
+ .method-put { background: rgba(251,191,36,0.1); color: var(--method-put); }
430
+ .method-patch { background: rgba(192,132,252,0.1); color: var(--method-patch); }
431
+ .method-delete { background: rgba(248,113,113,0.1); color: var(--method-delete); }
432
+
433
+ /* ── Suite List ── */
434
+ .suite-list { padding: 0; overflow: hidden; }
435
+
436
+ .suite-row {
437
+ display: grid;
438
+ grid-template-columns: 1fr auto auto auto;
439
+ align-items: center;
440
+ gap: 0.75rem;
441
+ padding: 0.65rem 1rem;
442
+ border-bottom: 1px solid var(--border-subtle);
443
+ cursor: pointer;
444
+ transition: background 0.1s;
445
+ }
446
+ .suite-row:last-of-type { border-bottom: none; }
447
+ .suite-row:hover { background: var(--bg-hover); }
448
+ .suite-error-row { cursor: default; }
449
+
450
+ .suite-info { display: flex; flex-direction: column; gap: 0.15rem; overflow: hidden; }
451
+ .suite-name { font-family: var(--font-mono); font-size: 0.85rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
452
+ .suite-desc { font-size: 0.75rem; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
453
+
454
+ .suite-tags { display: flex; gap: 0.25rem; }
455
+ .tag-pill {
456
+ font-family: var(--font-mono);
457
+ font-size: 0.65rem;
458
+ font-weight: 600;
459
+ padding: 0.15rem 0.5rem;
460
+ border-radius: var(--radius-pill);
461
+ background: var(--bg-raised);
462
+ border: 1px solid var(--border);
463
+ color: var(--text-dim);
464
+ }
465
+ .tag-pill.smoke { border-color: var(--pass); color: var(--pass); background: var(--pass-dim); }
466
+ .tag-pill.crud { border-color: var(--info); color: var(--info); background: var(--info-dim); }
467
+ .tag-pill.auth { border-color: var(--warn); color: var(--warn); background: var(--warn-dim); }
468
+ .tag-pill.destructive { border-color: var(--fail); color: var(--fail); background: var(--fail-dim); }
469
+
470
+ .suite-steps-count { font-family: var(--font-mono); font-size: 0.8rem; color: var(--text-dim); white-space: nowrap; }
471
+
472
+ .suite-result {
473
+ display: flex;
474
+ align-items: center;
475
+ gap: 0.35rem;
476
+ font-family: var(--font-mono);
477
+ font-size: 0.8rem;
478
+ font-weight: 600;
479
+ padding: 0.2rem 0.6rem;
480
+ border-radius: var(--radius-sm);
481
+ white-space: nowrap;
482
+ }
483
+ .suite-result.pass { color: var(--pass); background: var(--pass-dim); }
484
+ .suite-result.fail { color: var(--fail); background: var(--fail-dim); }
485
+ .suite-result.not-run { color: var(--text-dim); background: var(--bg-raised); border: 1px dashed var(--border); }
486
+
487
+ .suite-detail {
488
+ padding: 0.5rem 1rem;
489
+ background: var(--bg-inset);
490
+ border-bottom: 1px solid var(--border-subtle);
491
+ display: none;
492
+ }
493
+
494
+ /* ── Endpoint Detail: Covering Suites ── */
495
+ .covering-suite {
496
+ display: flex;
497
+ align-items: center;
498
+ gap: 0.5rem;
499
+ padding: 0.35rem 0;
500
+ font-size: 0.8rem;
501
+ }
502
+ .covering-suite .suite-ref {
503
+ font-family: var(--font-mono);
504
+ font-size: 0.8rem;
505
+ color: var(--accent);
506
+ }
507
+ .dim { color: var(--text-dim); }
508
+
509
+ .step-icon { font-family: var(--font-mono); font-weight: 700; font-size: 0.75rem; text-align: center; }
510
+ .step-icon.pass { color: var(--pass); }
511
+ .step-icon.fail { color: var(--fail); }
512
+ .step-icon.skip { color: var(--skip); }
513
+
514
+ .step-duration { font-family: var(--font-mono); font-size: 0.75rem; color: var(--text-dim); }
515
+
516
+ /* Assertion rows in endpoint detail */
517
+ .assertion-row {
518
+ display: flex;
519
+ align-items: center;
520
+ gap: 0.4rem;
521
+ padding: 0.2rem 0;
522
+ font-family: var(--font-mono);
523
+ font-size: 0.75rem;
524
+ }
525
+ .assertion-icon { font-weight: 700; }
526
+ .assertion-icon.pass { color: var(--pass); }
527
+ .assertion-icon.fail { color: var(--fail); }
528
+ .assertion-field { color: var(--text-dim); }
529
+ .assertion-rule { color: var(--text-muted); }
530
+ .assertion-actual { color: var(--fail); }
531
+
532
+ /* Failure hints */
533
+ .failure-hint {
534
+ display: flex;
535
+ align-items: flex-start;
536
+ gap: 0.5rem;
537
+ margin-top: 0.5rem;
538
+ padding: 0.5rem 0.65rem;
539
+ background: var(--warn-dim);
540
+ border: 1px solid var(--warn);
541
+ border-radius: var(--radius-sm);
542
+ font-size: 0.8rem;
543
+ color: var(--warn);
544
+ }
545
+ .failure-hint code {
546
+ font-family: var(--font-mono);
547
+ background: rgba(0,0,0,0.2);
548
+ padding: 0.05rem 0.3rem;
549
+ border-radius: 2px;
550
+ font-size: 0.75rem;
551
+ }
552
+
553
+ /* Server error badge */
554
+ .warning-badge.server-error { background: var(--fail-dim); color: var(--fail); border: 1px solid var(--fail); }
555
+
556
+ /* ── Runs List ── */
557
+ .runs-list { overflow: hidden; }
558
+ .runs-header {
559
+ display: grid;
560
+ grid-template-columns: 60px 100px 1fr 80px 60px;
561
+ gap: 0.5rem;
562
+ padding: 0.4rem 1rem;
563
+ font-size: 0.65rem;
564
+ font-weight: 600;
565
+ color: var(--text-muted);
566
+ text-transform: uppercase;
567
+ letter-spacing: 0.06em;
568
+ border-bottom: 1px solid var(--border);
569
+ background: var(--bg-raised);
570
+ }
571
+ .run-row {
572
+ display: grid;
573
+ grid-template-columns: 60px 100px 1fr 80px 60px;
574
+ gap: 0.5rem;
575
+ align-items: center;
576
+ padding: 0.5rem 1rem;
577
+ border-bottom: 1px solid var(--border-subtle);
578
+ cursor: pointer;
579
+ font-size: 0.85rem;
580
+ transition: background 0.1s;
581
+ }
582
+ .run-row:last-child { border-bottom: none; }
583
+ .run-row:hover { background: var(--bg-hover); }
584
+ .run-id { font-weight: 700; font-family: var(--font-mono); }
585
+ .run-time { color: var(--text-dim); font-size: 0.75rem; }
586
+ .run-results { display: flex; align-items: center; gap: 0.5rem; }
587
+ .run-progress { width: 60px; height: 4px; flex-shrink: 0; }
588
+ .run-counts { font-size: 0.75rem; font-family: var(--font-mono); }
589
+ .run-duration { color: var(--text-dim); font-size: 0.75rem; font-family: var(--font-mono); }
590
+
591
+ /* Run detail header */
592
+ .run-detail-header {
593
+ display: flex;
594
+ align-items: center;
595
+ gap: 0.75rem;
596
+ flex-wrap: wrap;
597
+ margin-bottom: 0.5rem;
598
+ padding: 0.75rem 1rem;
599
+ border-bottom: 1px solid var(--border);
600
+ }
601
+ .text-dim { color: var(--text-dim); font-size: 0.85rem; }
602
+
603
+ /* ── Badges ── */
604
+ .badge {
605
+ display: inline-block;
606
+ padding: 0.12rem 0.45rem;
607
+ border-radius: 3px;
608
+ font-size: 0.7rem;
609
+ font-weight: 700;
610
+ font-family: var(--font-mono);
611
+ }
612
+ .badge-pass { background: var(--pass-dim); color: var(--pass); }
613
+ .badge-fail { background: var(--fail-dim); color: var(--fail); }
614
+ .badge-skip { background: var(--skip-dim); color: var(--skip); }
615
+ .badge-error { background: rgba(234,88,12,0.15); color: var(--error); }
616
+
617
+ /* ── Progress Bar ── */
618
+ .progress-bar { display: flex; height: 4px; border-radius: 4px; overflow: hidden; background: var(--border); }
619
+ .progress-pass { background: var(--pass); }
620
+ .progress-fail { background: var(--fail); }
621
+ .progress-skip { background: var(--skip); }
622
+
623
+ /* ── Buttons ── */
624
+ .btn {
625
+ display: inline-flex;
626
+ align-items: center;
627
+ gap: 0.3rem;
628
+ padding: 0.35rem 0.85rem;
629
+ border: 1px solid var(--border);
630
+ border-radius: var(--radius);
631
+ background: var(--accent);
632
+ color: #fff;
633
+ font-size: 0.8rem;
634
+ font-weight: 600;
635
+ cursor: pointer;
636
+ text-decoration: none;
637
+ transition: background 0.15s;
638
+ }
639
+ .btn:hover { background: var(--accent-hover); }
640
+ .btn-sm { padding: 0.2rem 0.5rem; font-size: 0.75rem; }
641
+ .btn-outline { background: transparent; color: var(--accent); }
642
+ .btn-outline:hover { background: var(--accent); color: #fff; }
643
+ .btn-run { background: var(--pass); border-color: var(--pass); color: var(--text-inverse); font-family: var(--font-mono); }
644
+ .btn-run:hover { background: #2bc48a; border-color: #2bc48a; }
645
+ .btn-run[disabled] { opacity: 0.6; cursor: wait; }
646
+
647
+ /* ── Cards ── */
648
+ .cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin: 1.5rem 0; }
649
+ .card {
650
+ background: var(--bg-secondary);
651
+ border: 1px solid var(--border);
652
+ border-radius: var(--radius);
653
+ padding: 1rem 1.25rem;
654
+ box-shadow: var(--card-shadow);
655
+ }
656
+ .card-label { font-size: 0.85rem; color: var(--text-dim); margin-bottom: 0.25rem; }
657
+ .card-value { font-size: 1.75rem; font-weight: 700; }
658
+
659
+ /* ── Tables ── */
660
+ table { width: 100%; border-collapse: collapse; margin: 1rem 0; }
661
+ th, td { text-align: left; padding: 0.5rem 0.75rem; border-bottom: 1px solid var(--border); }
662
+ th { font-weight: 600; font-size: 0.85rem; color: var(--text-dim); }
663
+ tr:hover { background: var(--bg-secondary); }
664
+ a { color: var(--accent); }
665
+
666
+ /* ── Section Headings ── */
667
+ h1 { font-size: 1.5rem; margin: 1.5rem 0 0.5rem; }
668
+ h2 { font-size: 1.25rem; margin: 1.5rem 0 0.5rem; }
669
+ h3 { font-size: 1.05rem; margin: 1rem 0 0.25rem; }
670
+
671
+ /* ── Step Rows (Suite Results) ── */
672
+ .suite-section { margin: 1.5rem 0; padding: 0 1rem; }
673
+ .step-row {
674
+ display: grid;
675
+ grid-template-columns: 20px 1fr auto auto;
676
+ align-items: center;
677
+ gap: 0.5rem;
678
+ padding: 0.3rem 0.5rem;
679
+ border-radius: var(--radius-sm);
680
+ font-size: 0.8rem;
681
+ cursor: pointer;
682
+ transition: background 0.1s;
683
+ }
684
+ .step-row:hover { background: var(--bg-hover); }
685
+ .step-name { flex: 1; }
686
+ .step-label {
687
+ font-family: var(--font-mono);
688
+ font-size: 0.8rem;
689
+ overflow: hidden;
690
+ text-overflow: ellipsis;
691
+ white-space: nowrap;
692
+ }
693
+ .step-captures { display: flex; gap: 0.25rem; }
694
+ .capture-pill {
695
+ font-family: var(--font-mono);
696
+ font-size: 0.65rem;
697
+ padding: 0.1rem 0.4rem;
698
+ border-radius: var(--radius-pill);
699
+ background: var(--info-dim);
700
+ color: var(--info);
701
+ }
702
+
703
+ /* Step detail panel (assertions, request/response) */
704
+ .step-detail-panel {
705
+ margin: 0.25rem 0 0.5rem 1.5rem;
706
+ padding: 0.75rem;
707
+ background: var(--bg-secondary);
708
+ border: 1px solid var(--border);
709
+ border-radius: var(--radius-sm);
710
+ }
711
+ .req-res-toggle {
712
+ font-size: 0.75rem;
713
+ font-weight: 600;
714
+ color: var(--text-dim);
715
+ cursor: pointer;
716
+ margin-top: 0.5rem;
717
+ padding: 0.2rem 0;
718
+ }
719
+ .req-res-toggle:hover { color: var(--text); }
720
+ .req-res-body pre {
721
+ background: var(--bg-inset);
722
+ border: 1px solid var(--border);
723
+ border-radius: var(--radius-sm);
724
+ padding: 0.5rem;
725
+ overflow-x: auto;
726
+ white-space: pre-wrap;
727
+ word-break: break-all;
728
+ max-height: 200px;
729
+ overflow-y: auto;
730
+ }
731
+
732
+ /* ── Detail Panel ── */
733
+ .detail-panel {
734
+ background: var(--bg-raised);
735
+ border: 1px solid var(--border);
736
+ border-radius: var(--radius);
737
+ padding: 1rem;
738
+ margin: 0.5rem 0;
739
+ }
740
+ .detail-panel pre {
741
+ background: var(--bg);
742
+ border: 1px solid var(--border);
743
+ border-radius: 4px;
744
+ padding: 0.75rem;
745
+ overflow-x: auto;
746
+ font-size: 0.85rem;
747
+ font-family: var(--font-mono);
748
+ white-space: pre-wrap;
749
+ word-break: break-all;
750
+ }
751
+
752
+ /* ── Body Details ── */
753
+ .body-details { margin: 0.5rem 0; }
754
+ .body-details summary { cursor: pointer; font-weight: 600; font-size: 0.85rem; color: var(--text-dim); padding: 0.25rem 0; }
755
+ .body-details pre { max-height: 300px; overflow-y: auto; }
756
+
757
+ /* ── Assertions ── */
758
+ .assertion-list { list-style: none; padding: 0; }
759
+ .assertion-list li { padding: 0.25rem 0; font-size: 0.85rem; font-family: var(--font-mono); }
760
+ .assertion-pass::before { content: "\2713 "; color: var(--pass); }
761
+ .assertion-fail::before { content: "\2717 "; color: var(--fail); }
762
+
763
+ /* ── Capture Badges ── */
764
+ .capture-badge {
765
+ display: inline-block;
766
+ padding: 0.1rem 0.5rem;
767
+ border-radius: 10px;
768
+ font-size: 0.7rem;
769
+ font-family: var(--font-mono);
770
+ background: var(--accent-dim);
771
+ color: var(--accent);
772
+ margin-left: 0.25rem;
773
+ }
774
+
775
+ /* ── Chain Connector ── */
776
+ .chain-connector {
777
+ border-left: 2px solid var(--accent);
778
+ margin-left: 0.95rem;
779
+ padding-left: 0.75rem;
780
+ }
781
+ .step-row.chained { position: relative; }
782
+ .step-row.chained::before {
783
+ content: "";
784
+ position: absolute;
785
+ left: -0.85rem;
786
+ top: 50%;
787
+ transform: translateY(-50%);
788
+ width: 8px;
789
+ height: 8px;
790
+ border-radius: 50%;
791
+ background: var(--accent);
792
+ }
793
+
794
+ /* ── Suite Metadata (legacy, used by results.ts) ── */
795
+ .suite-description { color: var(--text-dim); font-size: 0.85rem; margin: 0.15rem 0 0.5rem; }
796
+ .tag-badge {
797
+ display: inline-block;
798
+ padding: 0.08rem 0.45rem;
799
+ border-radius: var(--radius-pill);
800
+ font-size: 0.65rem;
801
+ font-weight: 600;
802
+ background: var(--accent-dim);
803
+ color: var(--accent);
804
+ margin-right: 0.25rem;
805
+ }
806
+
807
+ /* ── Skip Reason ── */
808
+ .skip-reason { font-size: 0.85rem; color: var(--text-dim); padding: 0.25rem 0; }
809
+ .skip-reason code { background: var(--bg-raised); padding: 0.1rem 0.3rem; border-radius: 3px; font-size: 0.8rem; }
810
+
811
+ /* ── Htmx Indicator ── */
812
+ .htmx-indicator { display: none; }
813
+ .htmx-request .htmx-indicator { display: inline; }
814
+
815
+ /* ── Legacy compat ── */
816
+ .history-row:hover { background: var(--bg-hover); }
817
+ .pagination { display: flex; gap: 0.5rem; margin: 1.5rem 0; align-items: center; }
818
+
819
+ /* ── Responsive ── */
820
+ @media (max-width: 768px) {
821
+ .health-strip { grid-template-columns: 1fr; gap: 1rem; }
822
+ .endpoint-row { grid-template-columns: 20px 48px 1fr; }
823
+ .endpoint-badges { display: none; }
824
+ .run-row { grid-template-columns: 48px 1fr 60px; }
825
+ .run-time, .run-duration { display: none; }
826
+ .runs-header { display: none; }
827
+ }