beads-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/.beads/issues.jsonl +107 -0
  2. package/.editorconfig +10 -0
  3. package/.eslintrc.json +36 -0
  4. package/.github/workflows/ci.yml +38 -0
  5. package/.prettierignore +5 -0
  6. package/AGENTS.md +85 -0
  7. package/CHANGES.md +5 -0
  8. package/LICENSE +22 -0
  9. package/README.md +75 -0
  10. package/app/data/providers.js +178 -0
  11. package/app/data/providers.test.js +126 -0
  12. package/app/index.html +29 -0
  13. package/app/main.board-switch.test.js +94 -0
  14. package/app/main.deep-link.test.js +64 -0
  15. package/app/main.js +280 -0
  16. package/app/main.live-updates.test.js +229 -0
  17. package/app/main.test.js +17 -0
  18. package/app/main.theme.test.js +41 -0
  19. package/app/main.view-sync.test.js +54 -0
  20. package/app/protocol.js +200 -0
  21. package/app/protocol.md +64 -0
  22. package/app/protocol.test.js +57 -0
  23. package/app/router.js +78 -0
  24. package/app/router.test.js +34 -0
  25. package/app/state.js +87 -0
  26. package/app/state.test.js +21 -0
  27. package/app/styles.css +1343 -0
  28. package/app/utils/issue-id.js +10 -0
  29. package/app/utils/issue-type.js +27 -0
  30. package/app/utils/markdown.js +201 -0
  31. package/app/utils/markdown.test.js +103 -0
  32. package/app/utils/priority-badge.js +49 -0
  33. package/app/utils/priority.js +1 -0
  34. package/app/utils/status-badge.js +33 -0
  35. package/app/utils/status.js +23 -0
  36. package/app/utils/type-badge.js +36 -0
  37. package/app/utils/type-badge.test.js +30 -0
  38. package/app/views/board.js +183 -0
  39. package/app/views/board.test.js +184 -0
  40. package/app/views/detail.acceptance-notes.test.js +67 -0
  41. package/app/views/detail.assignee.test.js +161 -0
  42. package/app/views/detail.deps.test.js +97 -0
  43. package/app/views/detail.edits.test.js +146 -0
  44. package/app/views/detail.js +1039 -0
  45. package/app/views/detail.labels.test.js +73 -0
  46. package/app/views/detail.priority.test.js +86 -0
  47. package/app/views/detail.test.js +188 -0
  48. package/app/views/detail.ui47.test.js +78 -0
  49. package/app/views/epics.js +228 -0
  50. package/app/views/epics.test.js +283 -0
  51. package/app/views/issue-row.js +191 -0
  52. package/app/views/list.inline-edits.test.js +84 -0
  53. package/app/views/list.js +393 -0
  54. package/app/views/list.test.js +479 -0
  55. package/app/views/nav.js +67 -0
  56. package/app/views/nav.test.js +43 -0
  57. package/app/ws.js +252 -0
  58. package/app/ws.test.js +168 -0
  59. package/bin/bdui.js +18 -0
  60. package/docs/architecture.md +244 -0
  61. package/docs/db-watching.md +29 -0
  62. package/docs/quickstart.md +142 -0
  63. package/eslint.config.js +59 -0
  64. package/media/bdui-board.png +0 -0
  65. package/media/bdui-epics.png +0 -0
  66. package/media/bdui-issues.png +0 -0
  67. package/package.json +48 -0
  68. package/prettier.config.js +13 -0
  69. package/server/app.js +80 -0
  70. package/server/app.test.js +29 -0
  71. package/server/bd.js +125 -0
  72. package/server/bd.test.js +93 -0
  73. package/server/cli/cli.test.js +109 -0
  74. package/server/cli/commands.integration.test.js +155 -0
  75. package/server/cli/commands.js +91 -0
  76. package/server/cli/commands.unit.test.js +94 -0
  77. package/server/cli/daemon.js +239 -0
  78. package/server/cli/index.js +74 -0
  79. package/server/cli/open.js +96 -0
  80. package/server/cli/open.test.js +26 -0
  81. package/server/cli/usage.js +22 -0
  82. package/server/config.js +29 -0
  83. package/server/db.js +100 -0
  84. package/server/db.test.js +70 -0
  85. package/server/index.js +29 -0
  86. package/server/protocol.js +3 -0
  87. package/server/protocol.test.js +87 -0
  88. package/server/watcher.js +107 -0
  89. package/server/watcher.test.js +100 -0
  90. package/server/ws.handlers.test.js +174 -0
  91. package/server/ws.js +784 -0
  92. package/server/ws.labels.test.js +95 -0
  93. package/server/ws.mutations.test.js +261 -0
  94. package/server/ws.subscriptions.test.js +116 -0
  95. package/server/ws.test.js +52 -0
  96. package/test/setup-vitest.js +12 -0
  97. package/tsconfig.json +23 -0
  98. package/vitest.config.mjs +14 -0
package/app/styles.css ADDED
@@ -0,0 +1,1343 @@
1
+ :root {
2
+ --fg: #222;
3
+ --bg: #fff;
4
+ --muted: #666;
5
+ --border: #e5e7eb;
6
+ --panel-bg: #fafafa;
7
+
8
+ /* Badges: base + derived via color-mix */
9
+ --badge-radius: 999px;
10
+
11
+ /* Status base colors */
12
+ --status-open-base: #51cb4e; /* open */
13
+ --status-in-progress-base: #666666; /* in_progress */
14
+ --status-closed-base: #5f26c9; /* closed */
15
+
16
+ /* Type base colors */
17
+ --type-bug-base: #9f2011;
18
+ --type-task-base: #cd9e33;
19
+ --type-epic-base: #f69842;
20
+ --type-feature-base: #51cb43;
21
+ --type-chore-base: #666666;
22
+
23
+ /* Neutral badge */
24
+ --badge-bg-neutral: color-mix(in srgb, var(--panel-bg) 85%, #6b7280);
25
+ --badge-fg-neutral: color-mix(in srgb, #6b7280 85%, #000);
26
+
27
+ /* Derived Type badge colors (light) */
28
+ --badge-bg-bug: color-mix(in srgb, var(--panel-bg) 85%, var(--type-bug-base));
29
+ --badge-fg-bug: color-mix(in srgb, var(--type-bug-base) 90%, #000);
30
+ --badge-bg-feature: color-mix(
31
+ in srgb,
32
+ var(--panel-bg) 85%,
33
+ var(--type-feature-base)
34
+ );
35
+ --badge-fg-feature: color-mix(in srgb, var(--type-feature-base) 90%, #000);
36
+ --badge-bg-task: color-mix(
37
+ in srgb,
38
+ var(--panel-bg) 85%,
39
+ var(--type-task-base)
40
+ );
41
+ --badge-fg-task: color-mix(in srgb, var(--type-task-base) 90%, #000);
42
+ --badge-bg-epic: color-mix(
43
+ in srgb,
44
+ var(--panel-bg) 85%,
45
+ var(--type-epic-base)
46
+ );
47
+ --badge-fg-epic: color-mix(in srgb, var(--type-epic-base) 90%, #000);
48
+ --badge-bg-chore: color-mix(
49
+ in srgb,
50
+ var(--panel-bg) 85%,
51
+ var(--type-chore-base)
52
+ );
53
+ --badge-fg-chore: color-mix(in srgb, var(--type-chore-base) 90%, #000);
54
+
55
+ /* Links */
56
+ --link: #1d4ed8;
57
+ --link-hover: #1e40af;
58
+ --link-visited: #6d28d9;
59
+
60
+ /* Markdown code/pre */
61
+ --code-bg: #f3f4f6;
62
+ --code-fg: #111827;
63
+ --pre-bg: #0b1021;
64
+ --pre-fg: #e5e7eb;
65
+
66
+ /* focus ring */
67
+ --outline-offset: 6px;
68
+ }
69
+
70
+ html,
71
+ body {
72
+ height: 100%;
73
+ margin: 0;
74
+ }
75
+
76
+ /* Ensure the HTML hidden attribute wins over any display rules we set on routes */
77
+ [hidden] {
78
+ display: none !important;
79
+ }
80
+
81
+ body {
82
+ color: var(--fg);
83
+ background: var(--bg);
84
+ font-family:
85
+ -apple-system,
86
+ system-ui,
87
+ Segoe UI,
88
+ Roboto,
89
+ Ubuntu,
90
+ Cantarell,
91
+ Noto Sans,
92
+ sans-serif;
93
+ -webkit-font-smoothing: antialiased;
94
+ -moz-osx-font-smoothing: grayscale;
95
+ text-rendering: optimizeLegibility;
96
+ letter-spacing: 0.01em;
97
+ }
98
+
99
+ a {
100
+ color: var(--link);
101
+ }
102
+ a:visited {
103
+ color: var(--link-visited);
104
+ }
105
+ a:hover,
106
+ a:focus {
107
+ color: var(--link-hover);
108
+ }
109
+
110
+ .app-header {
111
+ position: sticky;
112
+ top: 0;
113
+ z-index: 50;
114
+ padding: 12px 18px 0 18px;
115
+ border-bottom: 1px solid var(--border);
116
+ display: flex;
117
+ align-items: start;
118
+ justify-content: space-between;
119
+ background: linear-gradient(
120
+ 180deg,
121
+ color-mix(in srgb, var(--panel-bg) 80%, transparent),
122
+ var(--panel-bg)
123
+ );
124
+ backdrop-filter: saturate(150%) blur(6px);
125
+ }
126
+ .app-title {
127
+ font-size: 18px;
128
+ margin: 0;
129
+ letter-spacing: 0.02em;
130
+ }
131
+
132
+ /* Header layout: group title + tabs on the left */
133
+ .header-left {
134
+ display: flex;
135
+ align-items: baseline;
136
+ gap: 12px;
137
+ }
138
+
139
+ /* Header navigation tabs (sit next to title) */
140
+ .header-nav {
141
+ display: flex;
142
+ align-items: flex-end;
143
+ gap: 6px;
144
+ }
145
+ .header-nav .tab {
146
+ display: inline-block;
147
+ padding: 8px 12px 9px;
148
+ color: var(--muted);
149
+ text-decoration: none;
150
+ font-weight: 600;
151
+ border: 1px solid transparent;
152
+ border-top-left-radius: 8px;
153
+ border-top-right-radius: 8px;
154
+ border-bottom-left-radius: 0;
155
+ border-bottom-right-radius: 0;
156
+ line-height: 1.2;
157
+ }
158
+ .header-nav .tab:hover,
159
+ .header-nav .tab:focus {
160
+ color: var(--fg);
161
+ }
162
+ .header-nav .tab.active {
163
+ color: var(--fg);
164
+ background: var(--bg);
165
+ border-color: var(--border);
166
+ /* Blend into the page content below the header by covering the header border */
167
+ border-bottom-color: var(--bg);
168
+ margin-bottom: -1px; /* overlap header bottom border */
169
+ padding-bottom: 10px;
170
+ position: relative;
171
+ z-index: 1;
172
+ }
173
+
174
+ .header-actions {
175
+ display: flex;
176
+ align-items: center;
177
+ gap: 10px;
178
+ }
179
+ .theme-toggle {
180
+ display: inline-flex;
181
+ align-items: center;
182
+ gap: 6px;
183
+ font-size: 12px;
184
+ color: var(--muted);
185
+ }
186
+ .theme-toggle input[type='checkbox'] {
187
+ --switch-h: 22px;
188
+ appearance: none;
189
+ position: relative;
190
+ width: 44px;
191
+ height: var(--switch-h);
192
+ border-radius: var(--switch-h);
193
+ border: 1px solid var(--control-border);
194
+ background: var(--control-bg);
195
+ transition:
196
+ background 160ms ease,
197
+ border-color 160ms ease;
198
+ cursor: pointer;
199
+ }
200
+ .theme-toggle input[type='checkbox']::after {
201
+ content: '';
202
+ position: absolute;
203
+ top: 50%;
204
+ left: 2px;
205
+ width: calc(var(--switch-h) - 6px);
206
+ height: calc(var(--switch-h) - 6px);
207
+ border-radius: 999px;
208
+ background: var(--button-bg);
209
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
210
+ transform: translate(0, -50%);
211
+ transition: transform 180ms ease;
212
+ }
213
+ .theme-toggle input[type='checkbox']:checked::after {
214
+ left: auto;
215
+ right: 2px;
216
+ transform: translate(0, -50%);
217
+ }
218
+
219
+ .app-shell {
220
+ /* Fill remaining viewport height below sticky header */
221
+ height: calc(100% - 53px);
222
+ }
223
+
224
+ /* Route shells */
225
+ .route {
226
+ min-height: 0; /* allow children to scroll */
227
+ }
228
+
229
+ /* Board route: fill height and let columns scroll */
230
+ .route.board {
231
+ display: flex;
232
+ flex-direction: column;
233
+ min-height: 0;
234
+ max-height: 100%;
235
+ }
236
+ .route.board .panel__body {
237
+ flex: 1;
238
+ min-height: 0;
239
+ overflow-x: auto; /* scroll columns horizontal */
240
+ }
241
+
242
+ .panel {
243
+ min-height: 0;
244
+ overflow: auto;
245
+ }
246
+ .panel + .panel {
247
+ border-left: 1px solid var(--border);
248
+ }
249
+
250
+ .panel__header {
251
+ position: sticky;
252
+ top: 0;
253
+ background: inherit;
254
+ display: flex;
255
+ align-items: center;
256
+ gap: 10px;
257
+ min-height: 44px;
258
+ padding: 6px 18px;
259
+ }
260
+
261
+ #detail-panel .panel__header {
262
+ padding-left: 18px;
263
+ }
264
+
265
+ .muted {
266
+ color: var(--muted);
267
+ }
268
+
269
+ .text-truncate {
270
+ overflow: hidden;
271
+ white-space: nowrap;
272
+ text-overflow: ellipsis;
273
+ }
274
+
275
+ /* Details view */
276
+ .editable {
277
+ position: relative;
278
+ border-radius: 4px;
279
+ }
280
+ .editable:hover {
281
+ outline: 2px solid var(--control-border, var(--border));
282
+ outline-offset: var(--outline-offset);
283
+ }
284
+ .editable:focus-within {
285
+ outline: 2px solid #93c5fd;
286
+ outline-offset: var(--outline-offset);
287
+ cursor: text;
288
+ }
289
+ .editable-actions {
290
+ margin-top: 6px;
291
+ display: flex;
292
+ gap: 8px;
293
+ }
294
+
295
+ /* Minimal markdown styles */
296
+ .md h1 {
297
+ font-size: 20px;
298
+ margin: 12px 0 6px;
299
+ }
300
+ .md h2 {
301
+ font-size: 18px;
302
+ margin: 12px 0 6px;
303
+ }
304
+ .md h3 {
305
+ font-size: 16px;
306
+ margin: 10px 0 6px;
307
+ }
308
+ .md h4 {
309
+ font-size: 15px;
310
+ margin: 8px 0 4px;
311
+ }
312
+ .md h5,
313
+ .md h6 {
314
+ font-size: 14px;
315
+ margin: 6px 0 4px;
316
+ }
317
+ .md p {
318
+ margin: 6px 0;
319
+ }
320
+ .md ul,
321
+ .md ol {
322
+ margin: 6px 0 6px 14px;
323
+ padding-left: 14px;
324
+ }
325
+ .md code {
326
+ background: var(--code-bg);
327
+ color: var(--code-fg);
328
+ padding: 3px 4px;
329
+ border-radius: 3px;
330
+ }
331
+ .md pre {
332
+ background: var(--pre-bg);
333
+ color: var(--pre-fg);
334
+ padding: 10px;
335
+ overflow: auto;
336
+ border-radius: 6px;
337
+ }
338
+
339
+ /* Form controls */
340
+ input[type='text'],
341
+ input[type='search'],
342
+ input[type='number'],
343
+ select,
344
+ textarea {
345
+ background: var(--control-bg, #fff);
346
+ color: var(--control-fg, var(--fg));
347
+ border: 1px solid var(--control-border, var(--border));
348
+ border-radius: 4px;
349
+ padding: 4px 6px;
350
+ line-height: 2;
351
+ transition:
352
+ border-color 160ms ease,
353
+ box-shadow 160ms ease,
354
+ background 160ms ease;
355
+ }
356
+ input::placeholder,
357
+ textarea::placeholder {
358
+ color: var(--control-placeholder);
359
+ }
360
+ input:focus,
361
+ select:focus,
362
+ textarea:focus {
363
+ outline: none;
364
+ border-color: color-mix(in srgb, var(--link) 60%, var(--control-border));
365
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--link) 24%, transparent);
366
+ }
367
+ select {
368
+ appearance: none;
369
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'%3E%3Cpath d='M5 7l5 5 5-5H5z' fill='%23666'/%3E%3C/svg%3E");
370
+ background-repeat: no-repeat;
371
+ background-position: right 4px center;
372
+ background-size: 12px 12px;
373
+ padding-right: 24px;
374
+ }
375
+ button {
376
+ background: var(--button-bg, #f3f4f6);
377
+ color: var(--button-fg, var(--fg));
378
+ border: 1px solid var(--button-border, var(--border));
379
+ padding: 4px 8px;
380
+ border-radius: 4px;
381
+ cursor: pointer;
382
+ transition:
383
+ background 140ms ease,
384
+ color 140ms ease,
385
+ border-color 140ms ease,
386
+ transform 60ms ease;
387
+ }
388
+ button:hover {
389
+ filter: brightness(1.02);
390
+ }
391
+ button:active {
392
+ transform: translateY(1px);
393
+ }
394
+ button:disabled {
395
+ opacity: 0.6;
396
+ cursor: default;
397
+ }
398
+
399
+ /* Type badge */
400
+ .type-badge {
401
+ display: inline-block;
402
+ padding: 0 6px;
403
+ line-height: 18px;
404
+ height: 18px;
405
+ border-radius: var(--badge-radius);
406
+ font-size: 11px;
407
+ font-weight: 600;
408
+ vertical-align: middle;
409
+ user-select: none;
410
+ border: 1px solid color-mix(in srgb, currentColor 35%, transparent);
411
+ }
412
+ .type-badge[title] {
413
+ cursor: default;
414
+ }
415
+ .type-badge + .type-badge {
416
+ margin-left: 4px;
417
+ }
418
+ .type-badge--neutral {
419
+ background: var(--badge-bg-neutral);
420
+ color: var(--badge-fg-neutral);
421
+ }
422
+ .type-badge--bug {
423
+ background: var(--badge-bg-bug);
424
+ color: var(--badge-fg-bug);
425
+ }
426
+ .type-badge--feature {
427
+ background: var(--badge-bg-feature);
428
+ color: var(--badge-fg-feature);
429
+ }
430
+ .type-badge--task {
431
+ background: var(--badge-bg-task);
432
+ color: var(--badge-fg-task);
433
+ }
434
+ .type-badge--epic {
435
+ background: var(--badge-bg-epic);
436
+ color: var(--badge-fg-epic);
437
+ }
438
+ .type-badge--chore {
439
+ background: var(--badge-bg-chore);
440
+ color: var(--badge-fg-chore);
441
+ }
442
+
443
+ /* Generic badge base (shared look) */
444
+ .badge,
445
+ .status-badge,
446
+ .priority-badge,
447
+ .badge-select {
448
+ display: inline-block;
449
+ padding: 0 8px;
450
+ line-height: 18px;
451
+ height: 18px;
452
+ border-radius: var(--badge-radius);
453
+ font-size: 11px;
454
+ font-weight: 600;
455
+ vertical-align: middle;
456
+ user-select: none;
457
+ border: 1px solid color-mix(in srgb, currentColor 35%, transparent);
458
+ }
459
+ .badge + .badge,
460
+ .status-badge + .status-badge,
461
+ .priority-badge + .priority-badge,
462
+ .badge-select + .badge-select {
463
+ margin-left: 4px;
464
+ }
465
+
466
+ /* Status + Priority palette (light) derived from base */
467
+ :root {
468
+ /* Status badges */
469
+ --badge-bg-open: color-mix(
470
+ in srgb,
471
+ var(--panel-bg) 72%,
472
+ var(--status-open-base)
473
+ );
474
+ --badge-fg-open: color-mix(in srgb, var(--status-open-base) 90%, #000);
475
+ --badge-bg-in-progress: color-mix(
476
+ in srgb,
477
+ var(--panel-bg) 85%,
478
+ var(--status-in-progress-base)
479
+ );
480
+ --badge-fg-in-progress: color-mix(
481
+ in srgb,
482
+ var(--status-in-progress-base) 90%,
483
+ #000
484
+ );
485
+ --badge-bg-closed: color-mix(
486
+ in srgb,
487
+ var(--panel-bg) 85%,
488
+ var(--status-closed-base)
489
+ );
490
+ --badge-fg-closed: color-mix(in srgb, var(--status-closed-base) 90%, #000);
491
+
492
+ /* Priority palette mapped to provided colors */
493
+ /* p0 -> bug, p1 -> epic, p2 -> task, p3 -> feature, p4 -> chore */
494
+ --badge-bg-p0: color-mix(in srgb, var(--panel-bg) 85%, var(--type-bug-base));
495
+ --badge-fg-p0: color-mix(in srgb, var(--type-bug-base) 90%, #000);
496
+ --badge-bg-p1: color-mix(in srgb, var(--panel-bg) 72%, var(--type-epic-base));
497
+ --badge-fg-p1: color-mix(in srgb, var(--type-epic-base) 90%, #000);
498
+ --badge-bg-p2: color-mix(in srgb, var(--panel-bg) 72%, var(--type-task-base));
499
+ --badge-fg-p2: color-mix(in srgb, var(--type-task-base) 90%, #000);
500
+ --badge-bg-p3: color-mix(
501
+ in srgb,
502
+ var(--panel-bg) 72%,
503
+ var(--type-feature-base)
504
+ );
505
+ --badge-fg-p3: color-mix(in srgb, var(--type-feature-base) 90%, #000);
506
+ --badge-bg-p4: color-mix(
507
+ in srgb,
508
+ var(--panel-bg) 85%,
509
+ var(--type-chore-base)
510
+ );
511
+ --badge-fg-p4: color-mix(in srgb, var(--type-chore-base) 90%, #000);
512
+ }
513
+
514
+ @media (prefers-color-scheme: dark) {
515
+ :root {
516
+ --badge-bg-open: color-mix(
517
+ in srgb,
518
+ var(--panel-bg) 80%,
519
+ var(--status-open-base)
520
+ );
521
+ --badge-fg-open: color-mix(in srgb, var(--status-open-base) 75%, #fff);
522
+ --badge-bg-in-progress: color-mix(
523
+ in srgb,
524
+ var(--panel-bg) 80%,
525
+ var(--status-in-progress-base)
526
+ );
527
+ --badge-fg-in-progress: color-mix(
528
+ in srgb,
529
+ var(--status-in-progress-base) 75%,
530
+ #fff
531
+ );
532
+ --badge-bg-closed: color-mix(
533
+ in srgb,
534
+ var(--panel-bg) 80%,
535
+ var(--status-closed-base)
536
+ );
537
+ --badge-fg-closed: color-mix(in srgb, var(--status-closed-base) 75%, #fff);
538
+
539
+ --badge-bg-p0: color-mix(
540
+ in srgb,
541
+ var(--panel-bg) 80%,
542
+ var(--type-bug-base)
543
+ );
544
+ --badge-fg-p0: color-mix(in srgb, var(--type-bug-base) 75%, #fff);
545
+ --badge-bg-p1: color-mix(
546
+ in srgb,
547
+ var(--panel-bg) 80%,
548
+ var(--type-epic-base)
549
+ );
550
+ --badge-fg-p1: color-mix(in srgb, var(--type-epic-base) 75%, #fff);
551
+ --badge-bg-p2: color-mix(
552
+ in srgb,
553
+ var(--panel-bg) 80%,
554
+ var(--type-task-base)
555
+ );
556
+ --badge-fg-p2: color-mix(in srgb, var(--type-task-base) 75%, #fff);
557
+ --badge-bg-p3: color-mix(
558
+ in srgb,
559
+ var(--panel-bg) 80%,
560
+ var(--type-feature-base)
561
+ );
562
+ --badge-fg-p3: color-mix(in srgb, var(--type-feature-base) 75%, #fff);
563
+ --badge-bg-p4: color-mix(
564
+ in srgb,
565
+ var(--panel-bg) 80%,
566
+ var(--type-chore-base)
567
+ );
568
+ --badge-fg-p4: color-mix(in srgb, var(--type-chore-base) 75%, #fff);
569
+ }
570
+ }
571
+
572
+ html[data-theme='dark'] {
573
+ --badge-bg-open: color-mix(
574
+ in srgb,
575
+ var(--panel-bg) 80%,
576
+ var(--status-open-base)
577
+ );
578
+ --badge-fg-open: color-mix(in srgb, var(--status-open-base) 75%, #fff);
579
+ --badge-bg-in-progress: color-mix(
580
+ in srgb,
581
+ var(--panel-bg) 80%,
582
+ var(--status-in-progress-base)
583
+ );
584
+ --badge-fg-in-progress: color-mix(
585
+ in srgb,
586
+ var(--status-in-progress-base) 75%,
587
+ #fff
588
+ );
589
+ --badge-bg-closed: color-mix(
590
+ in srgb,
591
+ var(--panel-bg) 80%,
592
+ var(--status-closed-base)
593
+ );
594
+ --badge-fg-closed: color-mix(in srgb, var(--status-closed-base) 75%, #fff);
595
+
596
+ --badge-bg-p0: color-mix(in srgb, var(--panel-bg) 80%, var(--type-bug-base));
597
+ --badge-fg-p0: color-mix(in srgb, var(--type-bug-base) 75%, #fff);
598
+ --badge-bg-p1: color-mix(in srgb, var(--panel-bg) 80%, var(--type-epic-base));
599
+ --badge-fg-p1: color-mix(in srgb, var(--type-epic-base) 75%, #fff);
600
+ --badge-bg-p2: color-mix(in srgb, var(--panel-bg) 80%, var(--type-task-base));
601
+ --badge-fg-p2: color-mix(in srgb, var(--type-task-base) 75%, #fff);
602
+ --badge-bg-p3: color-mix(
603
+ in srgb,
604
+ var(--panel-bg) 80%,
605
+ var(--type-feature-base)
606
+ );
607
+ --badge-fg-p3: color-mix(in srgb, var(--type-feature-base) 75%, #fff);
608
+ --badge-bg-p4: color-mix(
609
+ in srgb,
610
+ var(--panel-bg) 80%,
611
+ var(--type-chore-base)
612
+ );
613
+ --badge-fg-p4: color-mix(in srgb, var(--type-chore-base) 75%, #fff);
614
+ }
615
+
616
+ .status-badge.is-open,
617
+ .badge-select.badge--status.is-open {
618
+ background: var(--badge-bg-open);
619
+ color: var(--badge-fg-open);
620
+ }
621
+ .status-badge.is-in_progress,
622
+ .badge-select.badge--status.is-in_progress {
623
+ background: var(--badge-bg-in-progress);
624
+ color: var(--badge-fg-in-progress);
625
+ }
626
+ .status-badge.is-closed,
627
+ .badge-select.badge--status.is-closed {
628
+ background: var(--badge-bg-closed);
629
+ color: var(--badge-fg-closed);
630
+ }
631
+
632
+ .priority-badge.is-p0,
633
+ .badge-select.badge--priority.is-p0 {
634
+ background: var(--badge-bg-p0);
635
+ color: var(--badge-fg-p0);
636
+ }
637
+ .priority-badge.is-p1,
638
+ .badge-select.badge--priority.is-p1 {
639
+ background: var(--badge-bg-p1);
640
+ color: var(--badge-fg-p1);
641
+ }
642
+ .priority-badge.is-p2,
643
+ .badge-select.badge--priority.is-p2 {
644
+ background: var(--badge-bg-p2);
645
+ color: var(--badge-fg-p2);
646
+ }
647
+ .priority-badge.is-p3,
648
+ .badge-select.badge--priority.is-p3 {
649
+ background: var(--badge-bg-p3);
650
+ color: var(--badge-fg-p3);
651
+ }
652
+ .priority-badge.is-p4,
653
+ .badge-select.badge--priority.is-p4 {
654
+ background: var(--badge-bg-p4);
655
+ color: var(--badge-fg-p4);
656
+ }
657
+
658
+ /* Make selects look like badges while keeping the native arrow */
659
+ .badge-select {
660
+ appearance: none;
661
+ border: none;
662
+ padding: 0 22px 0 8px; /* room for arrow */
663
+ height: 20px;
664
+ line-height: 18px;
665
+ background-clip: padding-box;
666
+ cursor: pointer;
667
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'%3E%3Cpath d='M5 7l5 5 5-5H5z' fill='%23000' opacity='0.45'/%3E%3C/svg%3E");
668
+ background-repeat: no-repeat;
669
+ background-position: right 6px center;
670
+ background-size: 12px 12px;
671
+ }
672
+ .badge-select:focus {
673
+ outline: 2px solid color-mix(in srgb, var(--link) 50%, transparent);
674
+ outline-offset: 2px;
675
+ box-shadow: none;
676
+ }
677
+
678
+ /* Inline, minimal text input for table editing */
679
+ input.inline-edit {
680
+ border: none;
681
+ background: transparent;
682
+ padding: 0;
683
+ margin: 0;
684
+ line-height: inherit;
685
+ font: inherit;
686
+ outline: 2px solid var(--control-border, var(--border));
687
+ outline-offset: var(--outline-offset);
688
+ }
689
+ input.inline-edit:focus {
690
+ outline: 2px solid #93c5fd;
691
+ outline-offset: var(--outline-offset);
692
+ box-shadow: none;
693
+ }
694
+
695
+ #list-panel .panel__header input {
696
+ flex: 1;
697
+ }
698
+
699
+ /* Issues list polish */
700
+ #list-panel .panel__body ul {
701
+ list-style: none;
702
+ padding: 0;
703
+ margin: 0;
704
+ }
705
+ #list-panel .panel__body li {
706
+ display: grid;
707
+ grid-template-columns: auto 1fr auto;
708
+ align-items: center;
709
+ gap: 8px 10px;
710
+ padding: 10px 18px;
711
+ margin: 0;
712
+ border-radius: 8px;
713
+ transition:
714
+ background 140ms ease,
715
+ transform 80ms ease,
716
+ box-shadow 140ms ease;
717
+ }
718
+ #list-panel .panel__body li:hover {
719
+ background: color-mix(in srgb, var(--control-bg) 60%, transparent);
720
+ }
721
+ #list-panel .panel__body li.selected {
722
+ background: color-mix(in srgb, var(--link) 10%, transparent);
723
+ box-shadow: 0 0 0 1px color-mix(in srgb, var(--link) 35%, var(--border)) inset;
724
+ }
725
+ #list-panel .panel__body li .muted {
726
+ font-size: 12px;
727
+ }
728
+
729
+ /* Details typography */
730
+ #detail-panel .panel__body {
731
+ line-height: 1.5;
732
+ }
733
+ #detail-panel .panel__body h1,
734
+ #detail-panel .panel__body h2,
735
+ #detail-panel .panel__body h3 {
736
+ letter-spacing: 0.01em;
737
+ }
738
+
739
+ /* Keyboard focus ring helper */
740
+ :focus-visible {
741
+ outline: 2px solid color-mix(in srgb, var(--link) 60%, transparent);
742
+ outline-offset: var(--outline-offset);
743
+ }
744
+
745
+ /* Subtle scrollbars (WebKit/Blink) */
746
+ * {
747
+ scrollbar-color: color-mix(in srgb, var(--muted) 35%, transparent) transparent;
748
+ scrollbar-width: thin;
749
+ }
750
+ *::-webkit-scrollbar {
751
+ width: 10px;
752
+ height: 10px;
753
+ }
754
+ *::-webkit-scrollbar-thumb {
755
+ background: color-mix(in srgb, var(--muted) 35%, transparent);
756
+ border-radius: 10px;
757
+ border: 2px solid transparent;
758
+ background-clip: padding-box;
759
+ }
760
+ *::-webkit-scrollbar-track {
761
+ background: transparent;
762
+ }
763
+
764
+ /* Responsive: stack panels on narrow screens */
765
+ @media (max-width: 900px) {
766
+ .app-shell {
767
+ height: auto;
768
+ }
769
+ .panel + .panel {
770
+ border-left: none;
771
+ border-top: 1px solid var(--border);
772
+ }
773
+ }
774
+
775
+ /* Detail content paddings */
776
+ #detail-root {
777
+ padding: 18px;
778
+ }
779
+
780
+ /* Spacing for acceptance and notes sections in details */
781
+ #detail-root .acceptance,
782
+ #detail-root .notes {
783
+ margin-top: 32px;
784
+ }
785
+ /* Keep editor font sizes consistent with read mode */
786
+ #detail-root input[type='text'],
787
+ #detail-root textarea {
788
+ font-size: inherit;
789
+ font-family: inherit;
790
+ font-weight: inherit;
791
+ line-height: inherit;
792
+ }
793
+ /* Title input fills available width and stays inline with buttons */
794
+ #detail-root .detail-title input[type='text'] {
795
+ flex: 1 1 auto;
796
+ padding: 0;
797
+ min-width: 10px;
798
+ }
799
+ /* Remove borders in detail view; rely on global :focus-visible outline */
800
+ #detail-root input[type='text'],
801
+ #detail-root textarea {
802
+ border: none;
803
+ background: transparent;
804
+ outline: 2px solid var(--control-border, var(--border));
805
+ outline-offset: var(--outline-offset);
806
+ }
807
+ #detail-root input[type='text']:focus,
808
+ #detail-root textarea:focus {
809
+ border: none;
810
+ box-shadow: none;
811
+ outline: 2px solid color-mix(in srgb, var(--link) 60%, transparent);
812
+ }
813
+
814
+ /* Epics view styles */
815
+ .epic-group {
816
+ border: 1px solid var(--border);
817
+ border-radius: 6px;
818
+ margin: 10px 12px;
819
+ overflow: hidden;
820
+ }
821
+
822
+ /* UI-83: Issues table container aligns with epics/cards */
823
+ .issues-block {
824
+ border: 1px solid var(--border);
825
+ border-radius: 6px;
826
+ margin: 0px 12px;
827
+ overflow: hidden;
828
+ background: var(--bg);
829
+ }
830
+ .epic-header {
831
+ display: flex;
832
+ align-items: center;
833
+ gap: 8px;
834
+ padding: 10px 12px;
835
+ cursor: pointer;
836
+ background: color-mix(in srgb, var(--panel-bg) 90%, transparent);
837
+ border-bottom: 1px solid var(--border);
838
+ }
839
+ .epic-header:hover {
840
+ background: color-mix(in srgb, var(--control-bg) 60%, transparent);
841
+ }
842
+ .epic-children {
843
+ padding: 8px 12px 12px;
844
+ }
845
+ /* Compact native progress for epic status */
846
+ .epic-header progress {
847
+ width: 140px;
848
+ height: 8px;
849
+ accent-color: var(--status-closed-base);
850
+ }
851
+ .table {
852
+ width: 100%;
853
+ border-collapse: collapse;
854
+ table-layout: fixed; /* keep column proportions stable while editing */
855
+ }
856
+ .table th,
857
+ .table td {
858
+ text-align: left;
859
+ padding: 12px 16px;
860
+ border-bottom: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
861
+ }
862
+ /* Ensure form controls fill the cell */
863
+ .table td input[type='text'],
864
+ .table td select {
865
+ width: 100%;
866
+ box-sizing: border-box;
867
+ }
868
+ .table td .editable {
869
+ display: block;
870
+ width: 100%;
871
+ }
872
+ .table th {
873
+ font-size: 12px;
874
+ color: var(--muted);
875
+ font-weight: 600;
876
+ }
877
+ .epic-row:hover {
878
+ background: color-mix(in srgb, var(--control-bg) 55%, transparent);
879
+ }
880
+
881
+ /* Board view styles */
882
+ .board-root {
883
+ display: grid;
884
+ grid-template-columns: repeat(4, 1fr);
885
+ gap: 16px;
886
+ padding: 12px;
887
+ }
888
+ .board-column {
889
+ display: flex;
890
+ flex-direction: column;
891
+ min-height: 0;
892
+ min-width: 380px;
893
+ overflow: auto;
894
+ border: 1px solid var(--border);
895
+ border-radius: 6px;
896
+ background: color-mix(in srgb, var(--panel-bg) 92%, transparent);
897
+ }
898
+ .board-column__header {
899
+ position: sticky;
900
+ top: 0;
901
+ z-index: 1;
902
+ padding: 10px 12px;
903
+ font-weight: 700;
904
+ border-bottom: 1px solid var(--border);
905
+ background: inherit;
906
+ backdrop-filter: saturate(140%) blur(4px);
907
+ }
908
+ .board-column__body {
909
+ padding: 10px;
910
+ overflow: visible;
911
+ display: flex;
912
+ flex-direction: column;
913
+ /* UI-77: increase vertical spacing between card rows */
914
+ gap: 14px;
915
+ }
916
+ .board-card {
917
+ /* UI-76: remove borders and match page body background */
918
+ border: none;
919
+ border-radius: 6px;
920
+ /* UI-77: increase internal padding for readability */
921
+ padding: 12px 14px;
922
+ /* Match the body background so cards feel integrated */
923
+ background: var(--bg);
924
+ cursor: pointer;
925
+ transition:
926
+ box-shadow 140ms ease,
927
+ transform 140ms ease;
928
+ /* UI-76: subtle elevation without borders */
929
+ box-shadow:
930
+ 0 1px 2px color-mix(in srgb, #000 10%, transparent),
931
+ 0 2px 6px color-mix(in srgb, #000 8%, transparent);
932
+ }
933
+ .board-card:hover {
934
+ /* Keep background; emphasize elevation slightly on hover */
935
+ box-shadow:
936
+ 0 2px 8px color-mix(in srgb, #000 16%, transparent),
937
+ 0 4px 16px color-mix(in srgb, #000 12%, transparent);
938
+ }
939
+ .board-card__title {
940
+ font-weight: 600;
941
+ margin-bottom: 4px;
942
+ }
943
+ .board-card__meta {
944
+ margin-top: 8px;
945
+ font-size: 12px;
946
+ color: var(--muted);
947
+ display: flex;
948
+ align-items: center;
949
+ gap: 8px;
950
+ }
951
+
952
+ @media (max-width: 1100px) {
953
+ .board-root {
954
+ grid-template-columns: 1fr;
955
+ }
956
+ }
957
+
958
+ @media (prefers-color-scheme: dark) {
959
+ :root {
960
+ --fg: #e5e7eb;
961
+ --bg: #0b1021;
962
+ --muted: #9ca3af;
963
+ --panel-bg: #0f172a;
964
+ --border: #1f2937;
965
+
966
+ /* Dark variants derived via color-mix for contrast */
967
+ --badge-bg-neutral: color-mix(in srgb, var(--panel-bg) 80%, #9ca3af);
968
+ --badge-fg-neutral: color-mix(in srgb, #9ca3af 75%, #fff);
969
+ --badge-bg-bug: color-mix(
970
+ in srgb,
971
+ var(--panel-bg) 80%,
972
+ var(--type-bug-base)
973
+ );
974
+ --badge-fg-bug: color-mix(in srgb, var(--type-bug-base) 75%, #fff);
975
+ --badge-bg-feature: color-mix(
976
+ in srgb,
977
+ var(--panel-bg) 80%,
978
+ var(--type-feature-base)
979
+ );
980
+ --badge-fg-feature: color-mix(in srgb, var(--type-feature-base) 75%, #fff);
981
+ --badge-bg-task: color-mix(
982
+ in srgb,
983
+ var(--panel-bg) 80%,
984
+ var(--type-task-base)
985
+ );
986
+ --badge-fg-task: color-mix(in srgb, var(--type-task-base) 75%, #fff);
987
+ --badge-bg-epic: color-mix(
988
+ in srgb,
989
+ var(--panel-bg) 80%,
990
+ var(--type-epic-base)
991
+ );
992
+ --badge-fg-epic: color-mix(in srgb, var(--type-epic-base) 75%, #fff);
993
+ --badge-bg-chore: color-mix(
994
+ in srgb,
995
+ var(--panel-bg) 80%,
996
+ var(--type-chore-base)
997
+ );
998
+ --badge-fg-chore: color-mix(in srgb, var(--type-chore-base) 75%, #fff);
999
+
1000
+ --link: #93c5fd;
1001
+ --link-hover: #bfdbfe;
1002
+ --link-visited: #c4b5fd;
1003
+
1004
+ --code-bg: #1f2937;
1005
+ --code-fg: #e5e7eb;
1006
+ --pre-bg: #0a0f1f;
1007
+ --pre-fg: #e5e7eb;
1008
+ }
1009
+ }
1010
+
1011
+ /* Manual theme override */
1012
+ html[data-theme='dark'] {
1013
+ --fg: #e5e7eb;
1014
+ --bg: #0b1021;
1015
+ --muted: #9ca3af;
1016
+ --panel-bg: #0f172a;
1017
+ --border: #1f2937;
1018
+
1019
+ --badge-bg-neutral: #374151;
1020
+ --badge-fg-neutral: #f3f4f6;
1021
+ --badge-bg-bug: color-mix(in srgb, var(--panel-bg) 80%, var(--type-bug-base));
1022
+ --badge-fg-bug: color-mix(in srgb, var(--type-bug-base) 75%, #fff);
1023
+ --badge-bg-feature: color-mix(
1024
+ in srgb,
1025
+ var(--panel-bg) 80%,
1026
+ var(--type-feature-base)
1027
+ );
1028
+ --badge-fg-feature: color-mix(in srgb, var(--type-feature-base) 75%, #fff);
1029
+ --badge-bg-task: color-mix(
1030
+ in srgb,
1031
+ var(--panel-bg) 80%,
1032
+ var(--type-task-base)
1033
+ );
1034
+ --badge-fg-task: color-mix(in srgb, var(--type-task-base) 75%, #fff);
1035
+ --badge-bg-epic: color-mix(
1036
+ in srgb,
1037
+ var(--panel-bg) 80%,
1038
+ var(--type-epic-base)
1039
+ );
1040
+ --badge-fg-epic: color-mix(in srgb, var(--type-epic-base) 75%, #fff);
1041
+ --badge-bg-chore: color-mix(
1042
+ in srgb,
1043
+ var(--panel-bg) 80%,
1044
+ var(--type-chore-base)
1045
+ );
1046
+ --badge-fg-chore: color-mix(in srgb, var(--type-chore-base) 75%, #fff);
1047
+
1048
+ --link: #93c5fd;
1049
+ --link-hover: #bfdbfe;
1050
+ --link-visited: #c4b5fd;
1051
+
1052
+ --code-bg: #1f2937;
1053
+ --code-fg: #e5e7eb;
1054
+ --pre-bg: #0a0f1f;
1055
+ --pre-fg: #e5e7eb;
1056
+ }
1057
+
1058
+ /* Force light theme when OS prefers dark */
1059
+ html[data-theme='light'] {
1060
+ --fg: #222;
1061
+ --bg: #fff;
1062
+ --muted: #666;
1063
+ --panel-bg: #fafafa;
1064
+ --border: #e5e7eb;
1065
+
1066
+ --badge-bg-neutral: #e5e7eb;
1067
+ --badge-fg-neutral: #374151;
1068
+ --badge-bg-bug: color-mix(in srgb, var(--panel-bg) 85%, var(--type-bug-base));
1069
+ --badge-fg-bug: color-mix(in srgb, var(--type-bug-base) 90%, #000);
1070
+ --badge-bg-feature: color-mix(
1071
+ in srgb,
1072
+ var(--panel-bg) 85%,
1073
+ var(--type-feature-base)
1074
+ );
1075
+ --badge-fg-feature: color-mix(in srgb, var(--type-feature-base) 90%, #000);
1076
+ --badge-bg-task: color-mix(
1077
+ in srgb,
1078
+ var(--panel-bg) 85%,
1079
+ var(--type-task-base)
1080
+ );
1081
+ --badge-fg-task: color-mix(in srgb, var(--type-task-base) 90%, #000);
1082
+ --badge-bg-epic: color-mix(
1083
+ in srgb,
1084
+ var(--panel-bg) 85%,
1085
+ var(--type-epic-base)
1086
+ );
1087
+ --badge-fg-epic: color-mix(in srgb, var(--type-epic-base) 90%, #000);
1088
+ --badge-bg-chore: color-mix(
1089
+ in srgb,
1090
+ var(--panel-bg) 85%,
1091
+ var(--type-chore-base)
1092
+ );
1093
+ --badge-fg-chore: color-mix(in srgb, var(--type-chore-base) 90%, #000);
1094
+
1095
+ --link: #1d4ed8;
1096
+ --link-hover: #1e40af;
1097
+ --link-visited: #6d28d9;
1098
+
1099
+ --code-bg: #f3f4f6;
1100
+ --code-fg: #1f2937;
1101
+ --pre-bg: #0b1021;
1102
+ --pre-fg: #e5e7eb;
1103
+
1104
+ /* Light controls override to prevent dark OS bleed-through */
1105
+ --control-bg: #fff;
1106
+ --control-fg: #111827;
1107
+ --control-border: #d1d5db;
1108
+ --control-placeholder: #9ca3af;
1109
+ --button-bg: #f3f4f6;
1110
+ --button-fg: #111827;
1111
+ --button-border: #d1d5db;
1112
+ }
1113
+
1114
+ /* Controls palette (light defaults) */
1115
+ :root {
1116
+ --control-bg: #fff;
1117
+ --control-fg: #111827;
1118
+ --control-border: #d1d5db;
1119
+ --control-placeholder: #9ca3af;
1120
+ --button-bg: #f3f4f6;
1121
+ --button-fg: #111827;
1122
+ --button-border: #d1d5db;
1123
+ }
1124
+ @media (prefers-color-scheme: dark) {
1125
+ :root {
1126
+ --control-bg: #111827;
1127
+ --control-fg: #e5e7eb;
1128
+ --control-border: #374151;
1129
+ --control-placeholder: #9ca3af;
1130
+ --button-bg: #1f2937;
1131
+ --button-fg: #e5e7eb;
1132
+ --button-border: #374151;
1133
+ }
1134
+ }
1135
+ html[data-theme='dark'] {
1136
+ --control-bg: #111827;
1137
+ --control-fg: #e5e7eb;
1138
+ --control-border: #374151;
1139
+ --control-placeholder: #9ca3af;
1140
+ --button-bg: #1f2937;
1141
+ --button-fg: #e5e7eb;
1142
+ --button-border: #374151;
1143
+ }
1144
+
1145
+ /* --- Design polish additions --- */
1146
+ /* Detail two-column layout with a properties card */
1147
+ .detail-layout {
1148
+ display: grid;
1149
+ grid-template-columns: 1fr 320px;
1150
+ gap: 32px;
1151
+ align-items: start;
1152
+ }
1153
+ .detail-side {
1154
+ position: sticky;
1155
+ top: 70px;
1156
+ }
1157
+ .detail-main h2 {
1158
+ font-size: 28px;
1159
+ line-height: 1.25;
1160
+ margin: 0 0 32px;
1161
+ }
1162
+ /* Title line: title left (flex:1), ID right-aligned */
1163
+ .detail-title {
1164
+ display: flex;
1165
+ align-items: baseline;
1166
+ justify-content: space-between;
1167
+ gap: 16px;
1168
+ }
1169
+ .detail-title h2 {
1170
+ flex: 1 1 auto;
1171
+ display: flex;
1172
+ align-items: baseline;
1173
+ flex-wrap: nowrap;
1174
+ gap: 8px;
1175
+ }
1176
+ .detail-title .detail-id {
1177
+ color: var(--muted);
1178
+ }
1179
+ .detail-toolbar {
1180
+ display: flex;
1181
+ align-items: center;
1182
+ justify-content: space-between;
1183
+ gap: 12px;
1184
+ padding: 6px 0 10px;
1185
+ border-bottom: 1px solid var(--border);
1186
+ margin-bottom: 14px;
1187
+ }
1188
+ .detail-breadcrumb {
1189
+ color: var(--muted);
1190
+ font-size: 13px;
1191
+ }
1192
+ .btn-bar {
1193
+ display: flex;
1194
+ gap: 8px;
1195
+ flex-wrap: wrap;
1196
+ }
1197
+ .btn {
1198
+ background: var(--button-bg);
1199
+ color: var(--button-fg);
1200
+ border: 1px solid var(--button-border);
1201
+ border-radius: 6px;
1202
+ padding: 6px 10px;
1203
+ font-size: 12px;
1204
+ }
1205
+ .btn.primary {
1206
+ background: color-mix(in srgb, var(--link) 18%, var(--button-bg));
1207
+ border-color: color-mix(in srgb, var(--link) 32%, var(--button-border));
1208
+ }
1209
+ .props-card {
1210
+ background: color-mix(in srgb, var(--panel-bg) 92%, transparent);
1211
+ border: 1px solid var(--border);
1212
+ border-radius: 0;
1213
+ padding: 10px;
1214
+ }
1215
+ .props-card:not(:last-child) {
1216
+ border-bottom: none;
1217
+ }
1218
+ .props-card ul {
1219
+ list-style: none;
1220
+ padding: 0;
1221
+ margin: 0 0 8px;
1222
+ }
1223
+ .props-card__title {
1224
+ font-weight: 700;
1225
+ font-size: 13px;
1226
+ margin: 0 0 8px;
1227
+ }
1228
+ .prop {
1229
+ display: grid;
1230
+ grid-template-columns: 1fr 1fr;
1231
+ align-items: center;
1232
+ gap: 10px;
1233
+ padding: 8px 0;
1234
+ border-top: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
1235
+ }
1236
+ .prop:first-of-type {
1237
+ border-top: none;
1238
+ }
1239
+ .prop .label {
1240
+ color: var(--muted);
1241
+ font-size: 12px;
1242
+ min-width: 80px;
1243
+ }
1244
+ .prop .value {
1245
+ font-size: 13px;
1246
+ display: flex;
1247
+ align-items: center;
1248
+ gap: 8px;
1249
+ }
1250
+
1251
+ .prop.labels {
1252
+ align-items: start;
1253
+ }
1254
+ .prop.labels .value > div {
1255
+ display: flex;
1256
+ flex-wrap: wrap;
1257
+ gap: 6px;
1258
+ }
1259
+ .prop.labels .value > div input {
1260
+ flex: 1;
1261
+ }
1262
+
1263
+ /* Sidebar control aesthetics */
1264
+ #detail-root .props-card input[type='text'] {
1265
+ border: 1px solid var(--control-border);
1266
+ outline: none;
1267
+ border-radius: 4px;
1268
+ padding: 2px 8px;
1269
+ }
1270
+
1271
+ /* Compact icon button (used in label chips) */
1272
+ #detail-root .props-card .icon-button {
1273
+ text-align: center;
1274
+ border-radius: var(--badge-radius);
1275
+ border: 1px solid color-mix(in srgb, currentColor 35%, transparent);
1276
+ padding: 0 6px;
1277
+ height: 18px;
1278
+ margin-right: -8px;
1279
+ background: transparent;
1280
+ color: inherit;
1281
+ }
1282
+
1283
+ .props-card__footer {
1284
+ display: flex;
1285
+ justify-content: stretch;
1286
+ gap: 8px;
1287
+ padding: 8px 0;
1288
+ }
1289
+ .props-card__footer input {
1290
+ flex: 1;
1291
+ }
1292
+
1293
+ /* List row layout closer to mock */
1294
+ #list-panel .panel__body li.issue-item {
1295
+ display: grid;
1296
+ grid-template-columns: 1fr auto;
1297
+ align-items: center;
1298
+ gap: 6px 12px;
1299
+ padding: 12px 18px;
1300
+ border-bottom: 1px solid color-mix(in srgb, var(--border) 65%, transparent);
1301
+ border-radius: 0;
1302
+ cursor: pointer;
1303
+ transition:
1304
+ background 140ms ease,
1305
+ box-shadow 140ms ease;
1306
+ }
1307
+ #list-panel .panel__body li.issue-item:hover {
1308
+ background: color-mix(in srgb, var(--control-bg) 55%, transparent);
1309
+ }
1310
+ #list-panel .panel__body li.issue-item.selected {
1311
+ background: color-mix(in srgb, var(--link) 10%, transparent);
1312
+ box-shadow: 0 0 0 1px color-mix(in srgb, var(--link) 35%, var(--border)) inset;
1313
+ }
1314
+ #list-panel .issue-title {
1315
+ font-weight: 600;
1316
+ }
1317
+ #list-panel .issue-meta {
1318
+ color: var(--muted);
1319
+ font-size: 12px;
1320
+ margin-top: 2px;
1321
+ }
1322
+ #list-panel .issue-right {
1323
+ display: grid;
1324
+ grid-template-rows: auto auto;
1325
+ justify-items: end;
1326
+ gap: 4px;
1327
+ }
1328
+ #list-panel .issue-id {
1329
+ color: var(--muted);
1330
+ font-weight: 600;
1331
+ }
1332
+ .mono {
1333
+ font-family:
1334
+ ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
1335
+ 'Courier New', monospace;
1336
+ font-variant-ligatures: none;
1337
+ }
1338
+
1339
+ @media (max-width: 1100px) {
1340
+ .detail-layout {
1341
+ grid-template-columns: 1fr;
1342
+ }
1343
+ }