@netnodeag/kraftwerk 0.2.0 → 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.
@@ -0,0 +1,460 @@
1
+ /* kraftwerk inspector — control-room industrial. Warm graphite, hairlines,
2
+ indicator lamps. Dark is the native mode; light is the day shift. */
3
+
4
+ :root {
5
+ --bg: #141512;
6
+ --bg-deep: #0e0f0d;
7
+ --panel: #1b1d19;
8
+ --panel-2: #22241f;
9
+ --line: #2e312a;
10
+ --line-soft: #262822;
11
+ --ink: #ecebe3;
12
+ --ink-2: #b3b1a4;
13
+ --muted: #7c7a6e;
14
+ --accent: #f5a623;
15
+ --accent-dim: rgba(245, 166, 35, 0.14);
16
+ --ok: #56c15a;
17
+ --ok-dim: rgba(86, 193, 90, 0.14);
18
+ --fail: #e5484d;
19
+ --fail-dim: rgba(229, 72, 77, 0.14);
20
+ --run: #f5a623;
21
+ --pending: #4c4e46;
22
+ --shadow: 0 1px 0 rgba(255, 255, 255, 0.03) inset, 0 8px 24px rgba(0, 0, 0, 0.35);
23
+ color-scheme: dark;
24
+ }
25
+
26
+ :root[data-theme="light"] {
27
+ --bg: #f1f0ea;
28
+ --bg-deep: #e8e7df;
29
+ --panel: #fbfaf6;
30
+ --panel-2: #f3f2ec;
31
+ --line: #d9d7cc;
32
+ --line-soft: #e3e1d6;
33
+ --ink: #1d1e19;
34
+ --ink-2: #55564c;
35
+ --muted: #8a887b;
36
+ --accent: #c77c11;
37
+ --accent-dim: rgba(199, 124, 17, 0.12);
38
+ --ok: #2e8f38;
39
+ --ok-dim: rgba(46, 143, 56, 0.12);
40
+ --fail: #c73538;
41
+ --fail-dim: rgba(199, 53, 56, 0.1);
42
+ --run: #c77c11;
43
+ --pending: #c9c7bb;
44
+ --shadow: 0 1px 2px rgba(29, 30, 25, 0.06);
45
+ color-scheme: light;
46
+ }
47
+
48
+ * { box-sizing: border-box; margin: 0; padding: 0; }
49
+
50
+ html { height: 100%; }
51
+
52
+ body {
53
+ min-height: 100%;
54
+ background:
55
+ radial-gradient(1200px 400px at 50% -200px, rgba(245, 166, 35, 0.05), transparent 70%),
56
+ var(--bg);
57
+ color: var(--ink);
58
+ font-family: var(--font-sans), sans-serif;
59
+ font-size: 14px;
60
+ line-height: 1.5;
61
+ -webkit-font-smoothing: antialiased;
62
+ }
63
+
64
+ a { color: inherit; text-decoration: none; }
65
+ button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
66
+ code, pre, .mono { font-family: var(--font-mono), monospace; }
67
+
68
+ /* ---------- chrome ---------- */
69
+
70
+ .topbar {
71
+ position: sticky; top: 0; z-index: 10;
72
+ display: flex; align-items: center; gap: 14px;
73
+ padding: 10px 20px;
74
+ background: color-mix(in srgb, var(--bg) 86%, transparent);
75
+ backdrop-filter: blur(10px);
76
+ border-bottom: 1px solid var(--line);
77
+ }
78
+ .wordmark { display: flex; align-items: center; gap: 10px; }
79
+ .wordmark .lamp-block {
80
+ width: 22px; height: 22px; border-radius: 5px;
81
+ background: linear-gradient(135deg, var(--accent), #d2820b);
82
+ display: grid; place-items: center;
83
+ box-shadow: 0 0 14px rgba(245, 166, 35, 0.35);
84
+ }
85
+ .wordmark .lamp-block span { width: 7px; height: 7px; border-radius: 2px; background: #141512; }
86
+ .wordmark b {
87
+ font-family: var(--font-mono), monospace;
88
+ font-size: 13.5px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase;
89
+ }
90
+ .wordmark small {
91
+ font-size: 11px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--muted);
92
+ }
93
+ .topbar .spacer { flex: 1; }
94
+ .topbar nav { display: flex; gap: 4px; margin-left: 10px; }
95
+ .topbar nav a {
96
+ font-size: 11px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase;
97
+ color: var(--muted); padding: 4px 10px; border-radius: 6px;
98
+ }
99
+ .topbar nav a:hover { color: var(--ink); background: var(--panel-2); }
100
+ .topbar .outdir {
101
+ font-family: var(--font-mono), monospace;
102
+ font-size: 11px; color: var(--muted);
103
+ max-width: 44vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
104
+ }
105
+ .theme-btn {
106
+ border: 1px solid var(--line); border-radius: 6px; padding: 4px 10px;
107
+ font-size: 11.5px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-2);
108
+ }
109
+ .theme-btn:hover { border-color: var(--accent); color: var(--ink); }
110
+
111
+ .shell { max-width: 1240px; margin: 0 auto; padding: 26px 20px 80px; }
112
+
113
+ /* ---------- micro type ---------- */
114
+
115
+ .microlabel {
116
+ font-size: 10.5px; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase;
117
+ color: var(--muted);
118
+ }
119
+ .num { font-variant-numeric: tabular-nums; }
120
+
121
+ /* ---------- lamps ---------- */
122
+
123
+ .lamp { width: 9px; height: 9px; border-radius: 50%; flex: none; position: relative; }
124
+ .lamp.ok { background: var(--ok); box-shadow: 0 0 6px color-mix(in srgb, var(--ok) 60%, transparent); }
125
+ .lamp.failed, .lamp.blocked { background: var(--fail); box-shadow: 0 0 6px color-mix(in srgb, var(--fail) 60%, transparent); }
126
+ .lamp.aborted { background: var(--pending); }
127
+ .lamp.pending { background: transparent; border: 1.5px solid var(--pending); }
128
+ .lamp.running { background: var(--run); animation: lamp-pulse 1.4s ease-in-out infinite; }
129
+ @keyframes lamp-pulse {
130
+ 0%, 100% { box-shadow: 0 0 4px color-mix(in srgb, var(--run) 55%, transparent); opacity: 1; }
131
+ 50% { box-shadow: 0 0 14px var(--run); opacity: 0.75; }
132
+ }
133
+
134
+ .status-word { font-size: 11px; font-weight: 650; letter-spacing: 0.14em; text-transform: uppercase; }
135
+ .status-word.ok { color: var(--ok); }
136
+ .status-word.failed, .status-word.blocked { color: var(--fail); }
137
+ .status-word.running { color: var(--run); }
138
+ .status-word.aborted, .status-word.pending { color: var(--muted); }
139
+
140
+ /* ---------- run index ---------- */
141
+
142
+ .page-head { display: flex; align-items: baseline; gap: 14px; margin: 6px 2px 18px; }
143
+ .page-head h1 { font-size: 19px; font-weight: 650; letter-spacing: -0.01em; }
144
+ .page-head .count { color: var(--muted); font-size: 13px; }
145
+
146
+ .run-list { display: grid; gap: 10px; }
147
+ .run-card {
148
+ display: grid;
149
+ grid-template-columns: 12px 200px 1fr auto;
150
+ gap: 8px 16px; align-items: center;
151
+ background: var(--panel);
152
+ border: 1px solid var(--line);
153
+ border-radius: 8px;
154
+ padding: 13px 18px;
155
+ box-shadow: var(--shadow);
156
+ transition: border-color 120ms ease, transform 120ms ease;
157
+ }
158
+ .run-card:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--line)); transform: translateY(-1px); }
159
+ .run-card .rid { font-family: var(--font-mono), monospace; font-size: 12.5px; color: var(--ink-2); }
160
+ .run-card .wf { font-weight: 650; font-size: 14px; }
161
+ .run-card .req {
162
+ color: var(--muted); font-size: 12.5px;
163
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 52ch;
164
+ }
165
+ .run-card .meta {
166
+ display: flex; gap: 18px; align-items: center;
167
+ font-family: var(--font-mono), monospace; font-size: 12px; color: var(--ink-2);
168
+ }
169
+ .progress-track {
170
+ width: 90px; height: 4px; border-radius: 2px; background: var(--line-soft); overflow: hidden;
171
+ }
172
+ .progress-track i { display: block; height: 100%; background: var(--accent); border-radius: 2px; }
173
+ .run-card.is-ok .progress-track i { background: var(--ok); }
174
+ .run-card.is-failed .progress-track i { background: var(--fail); }
175
+
176
+ .empty {
177
+ border: 1px dashed var(--line); border-radius: 8px; padding: 40px;
178
+ text-align: center; color: var(--muted);
179
+ }
180
+
181
+ /* ---------- run detail ---------- */
182
+
183
+ .crumbs { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--muted); margin-bottom: 14px; }
184
+ .crumbs a:hover { color: var(--accent); }
185
+ .crumbs .sep { color: var(--line); }
186
+
187
+ .detail-head {
188
+ display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; margin-bottom: 6px;
189
+ }
190
+ .detail-head h1 { font-size: 18px; font-weight: 650; }
191
+ .detail-head .rid { font-family: var(--font-mono), monospace; font-size: 12px; color: var(--muted); }
192
+ .detail-req { color: var(--ink-2); font-size: 13px; margin-bottom: 18px; max-width: 90ch; }
193
+
194
+ .statgrid { display: flex; flex-wrap: wrap; gap: 26px; margin-bottom: 22px; }
195
+ .stat .microlabel { margin-bottom: 1px; }
196
+ .stat .v { font-family: var(--font-mono), monospace; font-size: 15px; font-weight: 600; }
197
+
198
+ .columns { display: grid; grid-template-columns: minmax(430px, 5fr) minmax(360px, 4fr); gap: 18px; align-items: start; }
199
+ @media (max-width: 1020px) { .columns { grid-template-columns: 1fr; } }
200
+
201
+ .panel {
202
+ background: var(--panel); border: 1px solid var(--line); border-radius: 8px;
203
+ box-shadow: var(--shadow); overflow: hidden;
204
+ }
205
+ .panel-head {
206
+ display: flex; align-items: center; gap: 10px;
207
+ padding: 10px 16px; border-bottom: 1px solid var(--line-soft);
208
+ background: var(--panel-2);
209
+ }
210
+ .panel-head .microlabel { color: var(--ink-2); }
211
+ .panel-head .spacer { flex: 1; }
212
+
213
+ /* phase timeline */
214
+
215
+ .phases { padding: 6px 0; }
216
+ .phase-row { position: relative; padding: 10px 16px 10px 40px; }
217
+ .phase-row + .phase-row { border-top: 1px solid var(--line-soft); }
218
+ .phase-row .lamp { position: absolute; left: 17px; top: 16px; }
219
+ .phase-row::before {
220
+ content: ""; position: absolute; left: 21px; top: 0; bottom: 0; width: 1px;
221
+ background: var(--line-soft);
222
+ }
223
+ .phase-row:first-child::before { top: 16px; }
224
+ .phase-row:last-child::before { bottom: auto; height: 16px; }
225
+ .phase-row .lamp { z-index: 1; }
226
+
227
+ .phase-top { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
228
+ .phase-name { font-weight: 650; font-size: 13.5px; }
229
+ .chip {
230
+ font-family: var(--font-mono), monospace; font-size: 10.5px;
231
+ border: 1px solid var(--line); border-radius: 4px; padding: 1px 7px; color: var(--ink-2);
232
+ }
233
+ .chip.agent { border-color: color-mix(in srgb, var(--accent) 40%, var(--line)); color: var(--accent); }
234
+ .phase-top .right { margin-left: auto; display: flex; gap: 14px; font-family: var(--font-mono), monospace; font-size: 11.5px; color: var(--muted); }
235
+ .phase-sub { margin-top: 3px; font-size: 12.5px; color: var(--ink-2); max-width: 72ch; }
236
+ .phase-activity {
237
+ margin-top: 3px; font-family: var(--font-mono), monospace; font-size: 11.5px; color: var(--run);
238
+ }
239
+ .phase-activity::before { content: "▸ "; }
240
+ .gate-line { margin-top: 5px; display: flex; flex-wrap: wrap; gap: 6px; }
241
+ .gate {
242
+ display: inline-flex; align-items: center; gap: 5px;
243
+ font-family: var(--font-mono), monospace; font-size: 10.5px;
244
+ border-radius: 4px; padding: 1.5px 7px;
245
+ background: var(--ok-dim); color: var(--ok);
246
+ }
247
+ .gate.failed { background: var(--fail-dim); color: var(--fail); }
248
+ .gate-fail-msg { margin-top: 4px; font-size: 12px; color: var(--fail); }
249
+
250
+ /* files */
251
+
252
+ .file-list { max-height: 300px; overflow: auto; }
253
+ .file-row {
254
+ display: flex; align-items: center; gap: 10px;
255
+ width: 100%; text-align: left;
256
+ padding: 7px 16px; font-family: var(--font-mono), monospace; font-size: 12.5px;
257
+ border-bottom: 1px solid var(--line-soft);
258
+ color: var(--ink-2);
259
+ }
260
+ .file-row:last-child { border-bottom: 0; }
261
+ .file-row:hover { background: var(--panel-2); color: var(--ink); }
262
+ .file-row.active { background: var(--accent-dim); color: var(--ink); }
263
+ .file-row .fname { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
264
+ .file-row .fsize { color: var(--muted); font-size: 11px; }
265
+
266
+ .viewer { border-top: 1px solid var(--line); }
267
+ .viewer-body pre {
268
+ padding: 14px 16px; overflow: auto; max-height: 560px;
269
+ font-size: 11.8px; line-height: 1.55; white-space: pre-wrap; word-break: break-word;
270
+ color: var(--ink-2);
271
+ }
272
+ .viewer-body iframe {
273
+ width: 100%; height: 620px; border: 0; background: #fff; display: block;
274
+ }
275
+ .viewer-body img { max-width: 100%; display: block; padding: 14px 16px; }
276
+ .viewer-note { padding: 6px 16px; font-size: 11px; color: var(--muted); border-bottom: 1px solid var(--line-soft); }
277
+ .open-raw {
278
+ font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase;
279
+ color: var(--muted); border: 1px solid var(--line); border-radius: 5px; padding: 2px 8px;
280
+ }
281
+ .open-raw:hover { color: var(--accent); border-color: var(--accent); }
282
+
283
+ .live-note { display: inline-flex; align-items: center; gap: 7px; font-size: 11px; color: var(--muted); }
284
+
285
+ /* ---------- workflows ---------- */
286
+
287
+ .wf-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 12px; }
288
+ .wf-card {
289
+ display: flex; flex-direction: column; gap: 6px;
290
+ background: var(--panel); border: 1px solid var(--line); border-radius: 8px;
291
+ padding: 16px 18px; box-shadow: var(--shadow);
292
+ transition: border-color 120ms ease, transform 120ms ease;
293
+ }
294
+ .wf-card:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--line)); transform: translateY(-1px); }
295
+ .wf-card-head { display: flex; align-items: baseline; gap: 10px; }
296
+ .wf-name { font-weight: 650; font-size: 15px; font-family: var(--font-mono), monospace; }
297
+ .wf-desc { color: var(--ink-2); font-size: 12.5px; flex: 1; }
298
+ .wf-meta { display: flex; gap: 8px; font-family: var(--font-mono), monospace; font-size: 11.5px; color: var(--muted); }
299
+ .wf-workspace { font-size: 11.5px; color: var(--ink-2); white-space: pre-wrap; }
300
+
301
+ /* agent identity hues */
302
+ .aid-0 { --aid: var(--accent); }
303
+ .aid-1 { --aid: #4fb8a8; }
304
+ .aid-2 { --aid: #a78bfa; }
305
+ .aid-3 { --aid: #e5793b; }
306
+
307
+ .agent-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 0; }
308
+ .agent-card { padding: 14px 18px; border-right: 1px solid var(--line-soft); border-bottom: 1px solid var(--line-soft); }
309
+ .agent-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
310
+ .agent-head b { font-size: 13.5px; }
311
+ .agent-head .spacer { flex: 1; }
312
+ .agent-id { font-size: 11px; color: var(--muted); }
313
+ .agent-dot { width: 10px; height: 10px; border-radius: 3px; background: var(--aid); box-shadow: 0 0 8px color-mix(in srgb, var(--aid) 45%, transparent); }
314
+ .agent-tools { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 8px; }
315
+ .tool-chip {
316
+ font-family: var(--font-mono), monospace; font-size: 10.5px;
317
+ border: 1px solid var(--line); border-radius: 4px; padding: 1px 6px; color: var(--ink-2);
318
+ }
319
+ .agent-persona { margin-top: 8px; font-size: 12px; color: var(--muted); line-height: 1.55; white-space: pre-wrap; }
320
+
321
+ .pipeline { padding: 8px 0; }
322
+ .step-node { position: relative; display: flex; gap: 14px; padding: 12px 18px; }
323
+ .step-node + .step-node { border-top: 1px solid var(--line-soft); }
324
+ .step-node::before {
325
+ content: ""; position: absolute; left: 30px; top: 0; bottom: 0; width: 1px; background: var(--line-soft);
326
+ }
327
+ .step-node:first-child::before { top: 18px; }
328
+ .step-node:last-child::before { bottom: auto; height: 18px; }
329
+ .step-index {
330
+ position: relative; z-index: 1; flex: none;
331
+ width: 25px; height: 25px; border-radius: 6px; margin-top: 1px;
332
+ display: grid; place-items: center;
333
+ font-family: var(--font-mono), monospace; font-size: 11.5px; font-weight: 700;
334
+ background: color-mix(in srgb, var(--aid) 16%, var(--panel));
335
+ border: 1px solid color-mix(in srgb, var(--aid) 55%, var(--line));
336
+ color: var(--aid);
337
+ }
338
+ .step-index.is-script { --aid: var(--ink-2); }
339
+ .step-body { flex: 1; min-width: 0; }
340
+ .agent-chip { border-color: color-mix(in srgb, var(--aid) 50%, var(--line)); color: var(--aid); }
341
+ .step-source { margin-top: 6px; }
342
+ .step-source summary {
343
+ cursor: pointer; font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
344
+ color: var(--muted); user-select: none;
345
+ }
346
+ .step-source summary:hover { color: var(--accent); }
347
+ .step-source pre {
348
+ margin-top: 6px; padding: 10px 12px; border: 1px solid var(--line-soft); border-radius: 6px;
349
+ background: var(--bg-deep); font-size: 11.5px; line-height: 1.55;
350
+ white-space: pre-wrap; word-break: break-word; color: var(--ink-2);
351
+ max-height: 360px; overflow: auto;
352
+ }
353
+ .gate.neutral { background: var(--panel-2); color: var(--ink-2); border: 1px solid var(--line-soft); }
354
+
355
+ ::-webkit-scrollbar { width: 10px; height: 10px; }
356
+ ::-webkit-scrollbar-thumb { background: var(--line); border-radius: 5px; border: 2px solid transparent; background-clip: content-box; }
357
+ ::-webkit-scrollbar-track { background: transparent; }
358
+
359
+ /* ---- run trigger + sandbox controls ---- */
360
+ .run-form { display: flex; gap: 10px; }
361
+ .run-form input[type="text"] {
362
+ flex: 1; font: 13px/1.4 var(--font-mono), monospace; color: var(--ink);
363
+ background: var(--panel-2); border: 1px solid var(--line);
364
+ border-radius: 7px; padding: 9px 12px; outline: none;
365
+ }
366
+ .run-form input[type="text"]:focus { border-color: var(--accent); }
367
+ .run-btn {
368
+ font: 600 12.5px/1 var(--font-sans), sans-serif; white-space: nowrap;
369
+ color: #141512; background: var(--accent);
370
+ border-radius: 7px; padding: 10px 16px;
371
+ }
372
+ .run-btn:hover { filter: brightness(1.08); }
373
+ .run-btn:disabled { opacity: 0.45; cursor: not-allowed; filter: none; }
374
+ .run-opts { display: flex; flex-wrap: wrap; gap: 6px 22px; margin-top: 10px; }
375
+ .run-opts label {
376
+ display: inline-flex; align-items: center; gap: 7px;
377
+ font-size: 12.5px; color: var(--ink-2); cursor: pointer;
378
+ }
379
+ .run-opts input[type="checkbox"] { accent-color: var(--accent); }
380
+ .run-opts .opt-hint { color: var(--muted); }
381
+ .sandbox-chip { border: 1px solid var(--line); color: var(--ink-2); }
382
+ .stop-btn {
383
+ font: 600 12px/1 var(--font-sans), sans-serif;
384
+ color: var(--fail); background: var(--fail-dim);
385
+ border: 1px solid color-mix(in srgb, var(--fail) 40%, transparent);
386
+ border-radius: 7px; padding: 7px 13px;
387
+ }
388
+ .stop-btn:hover { border-color: var(--fail); }
389
+ .stop-btn:disabled { opacity: 0.5; cursor: default; }
390
+
391
+ /* ---- runs screen: sidebar + tabbed detail, full width ---- */
392
+ .shell:has(.runs-screen) { max-width: none; padding: 0; }
393
+ .runs-screen { display: grid; grid-template-columns: 300px minmax(0, 1fr); align-items: start; }
394
+
395
+ .runs-side {
396
+ position: sticky; top: 45px; height: calc(100vh - 45px);
397
+ display: flex; flex-direction: column;
398
+ border-right: 1px solid var(--line); background: var(--panel);
399
+ }
400
+ .side-head {
401
+ display: flex; align-items: center; gap: 10px;
402
+ padding: 10px 14px; border-bottom: 1px solid var(--line-soft);
403
+ background: var(--panel-2);
404
+ }
405
+ .side-head .spacer { flex: 1; }
406
+ .side-list { flex: 1; overflow-y: auto; }
407
+ .side-row {
408
+ display: flex; align-items: flex-start; gap: 9px;
409
+ padding: 9px 12px 9px 14px; border-bottom: 1px solid var(--line-soft);
410
+ border-left: 2px solid transparent;
411
+ }
412
+ .side-row .lamp { margin-top: 4px; }
413
+ .side-row:hover { background: var(--panel-2); }
414
+ .side-row.active { background: var(--accent-dim); border-left-color: var(--accent); }
415
+ .side-row-body { flex: 1; min-width: 0; }
416
+ .side-row-top { display: flex; align-items: baseline; gap: 8px; }
417
+ .side-wf { font-weight: 650; font-size: 12.5px; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
418
+ .side-when { font-family: var(--font-mono), monospace; font-size: 10.5px; color: var(--muted); }
419
+ .side-row-sub { display: flex; align-items: baseline; gap: 8px; margin-top: 1px; }
420
+ .side-req {
421
+ flex: 1; font-size: 11.5px; color: var(--muted);
422
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
423
+ }
424
+ .side-row.active .side-req { color: var(--ink-2); }
425
+ .side-meta { font-family: var(--font-mono), monospace; font-size: 10.5px; color: var(--muted); }
426
+
427
+ .runs-main { padding: 16px 20px 40px; min-width: 0; }
428
+ .runs-main .detail-head .spacer { flex: 1; }
429
+
430
+ .tabs { display: flex; gap: 2px; background: var(--panel-2); border: 1px solid var(--line); border-radius: 7px; padding: 2px; }
431
+ .tabs button {
432
+ font-size: 11px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase;
433
+ color: var(--muted); padding: 5px 13px; border-radius: 5px;
434
+ }
435
+ .tabs button:hover { color: var(--ink); }
436
+ .tabs button.active { background: var(--panel); color: var(--ink); box-shadow: var(--shadow); }
437
+
438
+ .run-tab { max-width: 1080px; }
439
+
440
+ .art-layout {
441
+ display: grid; grid-template-columns: 280px minmax(0, 1fr); gap: 14px;
442
+ height: calc(100vh - 168px); min-height: 420px;
443
+ }
444
+ .art-files { display: flex; flex-direction: column; min-height: 0; }
445
+ .art-files .file-list { flex: 1; max-height: none; }
446
+ .art-viewer { display: flex; flex-direction: column; min-height: 0; }
447
+ .art-viewer .viewer { flex: 1; display: flex; flex-direction: column; min-height: 0; border-top: 0; }
448
+ .art-viewer .viewer-body { flex: 1; min-height: 0; }
449
+ .art-viewer .viewer-body iframe { height: 100%; }
450
+ .art-viewer .viewer-body pre { max-height: none; height: 100%; box-sizing: border-box; }
451
+ .art-viewer .viewer-body img { max-height: 100%; object-fit: contain; }
452
+ .art-viewer .empty { flex: 1; display: grid; place-items: center; }
453
+
454
+ @media (max-width: 940px) {
455
+ .runs-screen { grid-template-columns: 1fr; }
456
+ .runs-side { display: none; }
457
+ .art-layout { grid-template-columns: 1fr; height: auto; }
458
+ .art-files .file-list { max-height: 240px; }
459
+ .art-viewer .viewer-body iframe { height: 560px; }
460
+ }
@@ -0,0 +1,54 @@
1
+ import type { Metadata } from "next";
2
+ import { IBM_Plex_Sans, IBM_Plex_Mono } from "next/font/google";
3
+ import "./globals.css";
4
+ import { ThemeToggle } from "./theme-toggle";
5
+
6
+ const sans = IBM_Plex_Sans({
7
+ subsets: ["latin"],
8
+ weight: ["400", "500", "600", "700"],
9
+ variable: "--font-sans",
10
+ });
11
+ const mono = IBM_Plex_Mono({
12
+ subsets: ["latin"],
13
+ weight: ["400", "500", "600", "700"],
14
+ variable: "--font-mono",
15
+ });
16
+
17
+ export const metadata: Metadata = {
18
+ title: "kraftwerk inspector",
19
+ description: "Inspect kraftwerk workflow runs: live executions, phase traces, artifacts",
20
+ };
21
+
22
+ const themeInit = `(function(){try{var t=localStorage.getItem("kw-theme");if(t){document.documentElement.dataset.theme=t}else if(matchMedia("(prefers-color-scheme: light)").matches){document.documentElement.dataset.theme="light"}}catch(e){}})()`;
23
+
24
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
25
+ return (
26
+ <html lang="en" suppressHydrationWarning>
27
+ <body className={`${sans.variable} ${mono.variable}`}>
28
+ <script dangerouslySetInnerHTML={{ __html: themeInit }} />
29
+ <header className="topbar">
30
+ <a href="/" className="wordmark">
31
+ <span className="lamp-block">
32
+ <span />
33
+ </span>
34
+ <b>kraftwerk</b>
35
+ <small>inspector</small>
36
+ </a>
37
+ <nav>
38
+ <a href="/">runs</a>
39
+ <a href="/workflows">workflows</a>
40
+ </nav>
41
+ <span className="spacer" />
42
+ <OutDir />
43
+ <ThemeToggle />
44
+ </header>
45
+ <main className="shell">{children}</main>
46
+ </body>
47
+ </html>
48
+ );
49
+ }
50
+
51
+ async function OutDir() {
52
+ const { OUTPUT_DIR } = await import("@/lib/runs");
53
+ return <span className="outdir" title={OUTPUT_DIR}>{OUTPUT_DIR}</span>;
54
+ }
@@ -0,0 +1,16 @@
1
+ import { redirect } from "next/navigation";
2
+ import { listRuns, OUTPUT_DIR } from "@/lib/runs";
3
+
4
+ export const dynamic = "force-dynamic";
5
+
6
+ /** The runs screen lives at /runs/<id>; land on the latest run. */
7
+ export default async function Home() {
8
+ const runs = await listRuns();
9
+ if (runs.length > 0) redirect(`/runs/${runs[0].id}`);
10
+ return (
11
+ <div className="empty">
12
+ No runs found in <code>{OUTPUT_DIR}</code>. Start one with{" "}
13
+ <code>kraftwerk run &lt;workflow&gt; &lt;request&gt;</code> or from a workflow page.
14
+ </div>
15
+ );
16
+ }
@@ -0,0 +1,6 @@
1
+ import { RunsScreen } from "./run-detail";
2
+
3
+ export default async function RunPage({ params }: { params: Promise<{ id: string }> }) {
4
+ const { id } = await params;
5
+ return <RunsScreen id={id} />;
6
+ }