beads-ui 0.1.2 → 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.
Files changed (54) hide show
  1. package/CHANGES.md +29 -2
  2. package/README.md +39 -45
  3. package/app/data/list-selectors.js +98 -0
  4. package/app/data/providers.js +25 -127
  5. package/app/data/sort.js +45 -0
  6. package/app/data/subscription-issue-store.js +161 -0
  7. package/app/data/subscription-issue-stores.js +102 -0
  8. package/app/data/subscriptions-store.js +219 -0
  9. package/app/index.html +8 -0
  10. package/app/main.js +483 -61
  11. package/app/protocol.js +10 -14
  12. package/app/protocol.md +21 -19
  13. package/app/router.js +45 -9
  14. package/app/state.js +27 -11
  15. package/app/styles.css +373 -184
  16. package/app/utils/issue-id-renderer.js +71 -0
  17. package/app/utils/issue-url.js +9 -0
  18. package/app/utils/markdown.js +15 -194
  19. package/app/utils/priority-badge.js +0 -2
  20. package/app/utils/status-badge.js +0 -1
  21. package/app/utils/toast.js +34 -0
  22. package/app/utils/type-badge.js +0 -3
  23. package/app/views/board.js +439 -87
  24. package/app/views/detail.js +364 -154
  25. package/app/views/epics.js +128 -76
  26. package/app/views/issue-dialog.js +163 -0
  27. package/app/views/issue-row.js +10 -11
  28. package/app/views/list.js +164 -93
  29. package/app/views/new-issue-dialog.js +345 -0
  30. package/app/ws.js +36 -9
  31. package/bin/bdui.js +1 -1
  32. package/docs/adr/001-push-only-lists.md +134 -0
  33. package/docs/adr/002-per-subscription-stores-and-full-issue-push.md +200 -0
  34. package/docs/architecture.md +35 -85
  35. package/docs/data-exchange-subscription-plan.md +198 -0
  36. package/docs/db-watching.md +2 -1
  37. package/docs/migration-v2.md +54 -0
  38. package/docs/protocol/issues-push-v2.md +179 -0
  39. package/docs/subscription-issue-store.md +112 -0
  40. package/package.json +11 -3
  41. package/server/bd.js +0 -2
  42. package/server/cli/commands.js +12 -5
  43. package/server/cli/daemon.js +12 -5
  44. package/server/cli/index.js +34 -5
  45. package/server/cli/usage.js +2 -2
  46. package/server/config.js +12 -6
  47. package/server/db.js +0 -1
  48. package/server/index.js +9 -5
  49. package/server/list-adapters.js +218 -0
  50. package/server/subscriptions.js +277 -0
  51. package/server/validators.js +111 -0
  52. package/server/watcher.js +6 -9
  53. package/server/ws.js +466 -227
  54. package/docs/quickstart.md +0 -142
package/app/styles.css CHANGED
@@ -64,7 +64,22 @@
64
64
  --pre-fg: #e5e7eb;
65
65
 
66
66
  /* focus ring */
67
- --outline-offset: 6px;
67
+ --outline-offset-l: 6px;
68
+ --outline-offset-m: 2px;
69
+ --outline-offset-s: 0px;
70
+
71
+ /* Spacing scale (2px baseline) */
72
+ --space-0: 0;
73
+ --space-1: 2px;
74
+ --space-2: 4px;
75
+ --space-3: 6px;
76
+ --space-4: 8px;
77
+ --space-5: 10px;
78
+ --space-6: 12px;
79
+ --space-7: 14px;
80
+ --space-8: 16px;
81
+ --space-9: 18px;
82
+ --space-10: 20px;
68
83
  }
69
84
 
70
85
  html,
@@ -98,20 +113,21 @@ body {
98
113
 
99
114
  a {
100
115
  color: var(--link);
101
- }
102
- a:visited {
103
- color: var(--link-visited);
104
- }
105
- a:hover,
106
- a:focus {
107
- color: var(--link-hover);
116
+
117
+ &:visited {
118
+ color: var(--link-visited);
119
+ }
120
+ &:hover,
121
+ &:focus {
122
+ color: var(--link-hover);
123
+ }
108
124
  }
109
125
 
110
126
  .app-header {
111
127
  position: sticky;
112
128
  top: 0;
113
129
  z-index: 50;
114
- padding: 12px 18px 0 18px;
130
+ padding: var(--space-6) var(--space-9) 0 var(--space-9);
115
131
  border-bottom: 1px solid var(--border);
116
132
  display: flex;
117
133
  align-items: start;
@@ -133,87 +149,94 @@ a:focus {
133
149
  .header-left {
134
150
  display: flex;
135
151
  align-items: baseline;
136
- gap: 12px;
152
+ gap: var(--space-6);
137
153
  }
138
154
 
139
155
  /* Header navigation tabs (sit next to title) */
140
156
  .header-nav {
141
157
  display: flex;
142
158
  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;
159
+ gap: var(--space-3);
160
+
161
+ .tab {
162
+ display: inline-block;
163
+ padding: var(--space-4) var(--space-6) 9px;
164
+ color: var(--muted);
165
+ text-decoration: none;
166
+ font-weight: 600;
167
+ border: 1px solid transparent;
168
+ border-top-left-radius: 8px;
169
+ border-top-right-radius: 8px;
170
+ border-bottom-left-radius: 0;
171
+ border-bottom-right-radius: 0;
172
+ line-height: 1.2;
173
+
174
+ &:hover,
175
+ &:focus {
176
+ color: var(--fg);
177
+ outline-offset: var(--outline-offset-s);
178
+ }
179
+
180
+ &.active {
181
+ color: var(--fg);
182
+ background: var(--bg);
183
+ border-color: var(--border);
184
+ /* Blend into the page content below the header by covering the header border */
185
+ border-bottom-color: var(--bg);
186
+ margin-bottom: -1px; /* overlap header bottom border */
187
+ padding-bottom: 10px;
188
+ position: relative;
189
+ z-index: 1;
190
+ }
191
+ }
172
192
  }
173
193
 
174
194
  .header-actions {
175
195
  display: flex;
176
196
  align-items: center;
177
- gap: 10px;
197
+ gap: var(--space-5);
178
198
  }
179
199
  .theme-toggle {
180
200
  display: inline-flex;
181
201
  align-items: center;
182
- gap: 6px;
202
+ gap: var(--space-3);
183
203
  font-size: 12px;
184
204
  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%);
205
+
206
+ input[type='checkbox'] {
207
+ --switch-h: 22px;
208
+ appearance: none;
209
+ position: relative;
210
+ width: 44px;
211
+ height: var(--switch-h);
212
+ border-radius: var(--switch-h);
213
+ border: 1px solid var(--control-border);
214
+ background: var(--control-bg);
215
+ transition:
216
+ background 160ms ease,
217
+ border-color 160ms ease;
218
+ cursor: pointer;
219
+
220
+ &::after {
221
+ content: '';
222
+ position: absolute;
223
+ top: 50%;
224
+ left: 2px;
225
+ width: calc(var(--switch-h) - 6px);
226
+ height: calc(var(--switch-h) - 6px);
227
+ border-radius: 999px;
228
+ background: var(--button-bg);
229
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
230
+ transform: translate(0, -50%);
231
+ transition: transform 180ms ease;
232
+ }
233
+
234
+ &:checked::after {
235
+ left: auto;
236
+ right: 2px;
237
+ transform: translate(0, -50%);
238
+ }
239
+ }
217
240
  }
218
241
 
219
242
  .app-shell {
@@ -227,16 +250,20 @@ a:focus {
227
250
  }
228
251
 
229
252
  /* 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 */
253
+ .route {
254
+ &.board {
255
+ display: flex;
256
+ flex-direction: column;
257
+ min-height: 0;
258
+ height: 100%;
259
+ max-height: 100%;
260
+
261
+ .panel__body {
262
+ flex: 1;
263
+ min-height: 0;
264
+ overflow-x: auto; /* scroll columns horizontal */
265
+ }
266
+ }
240
267
  }
241
268
 
242
269
  .panel {
@@ -253,13 +280,13 @@ a:focus {
253
280
  background: inherit;
254
281
  display: flex;
255
282
  align-items: center;
256
- gap: 10px;
283
+ gap: var(--space-5);
257
284
  min-height: 44px;
258
- padding: 6px 18px;
285
+ padding: var(--space-3) var(--space-9);
259
286
  }
260
287
 
261
288
  #detail-panel .panel__header {
262
- padding-left: 18px;
289
+ padding-left: var(--space-9);
263
290
  }
264
291
 
265
292
  .muted {
@@ -279,61 +306,63 @@ a:focus {
279
306
  }
280
307
  .editable:hover {
281
308
  outline: 2px solid var(--control-border, var(--border));
282
- outline-offset: var(--outline-offset);
309
+ outline-offset: var(--outline-offset-l);
283
310
  }
284
311
  .editable:focus-within {
285
- outline: 2px solid #93c5fd;
286
- outline-offset: var(--outline-offset);
312
+ outline: 2px solid color-mix(in srgb, var(--link) 60%, transparent);
313
+ outline-offset: var(--outline-offset-l);
287
314
  cursor: text;
288
315
  }
289
316
  .editable-actions {
290
- margin-top: 6px;
317
+ margin-top: var(--space-3);
291
318
  display: flex;
292
- gap: 8px;
319
+ gap: var(--space-4);
293
320
  }
294
321
 
295
322
  /* 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;
323
+ .md {
324
+ h1 {
325
+ font-size: 20px;
326
+ margin: var(--space-6) 0 var(--space-3);
327
+ }
328
+ h2 {
329
+ font-size: 18px;
330
+ margin: var(--space-6) 0 var(--space-3);
331
+ }
332
+ h3 {
333
+ font-size: 16px;
334
+ margin: var(--space-5) 0 var(--space-3);
335
+ }
336
+ h4 {
337
+ font-size: 15px;
338
+ margin: var(--space-4) 0 var(--space-2);
339
+ }
340
+ h5,
341
+ h6 {
342
+ font-size: 14px;
343
+ margin: var(--space-3) 0 var(--space-2);
344
+ }
345
+ p {
346
+ margin: var(--space-3) 0;
347
+ }
348
+ ul,
349
+ ol {
350
+ margin: var(--space-3) 0 var(--space-3) var(--space-7);
351
+ padding-left: var(--space-7);
352
+ }
353
+ code {
354
+ background: var(--code-bg);
355
+ color: var(--code-fg);
356
+ padding: 3px var(--space-2);
357
+ border-radius: 3px;
358
+ }
359
+ pre {
360
+ background: var(--pre-bg);
361
+ color: var(--pre-fg);
362
+ padding: var(--space-5);
363
+ overflow: auto;
364
+ border-radius: 6px;
365
+ }
337
366
  }
338
367
 
339
368
  /* Form controls */
@@ -346,7 +375,7 @@ textarea {
346
375
  color: var(--control-fg, var(--fg));
347
376
  border: 1px solid var(--control-border, var(--border));
348
377
  border-radius: 4px;
349
- padding: 4px 6px;
378
+ padding: var(--space-2) var(--space-3);
350
379
  line-height: 2;
351
380
  transition:
352
381
  border-color 160ms ease,
@@ -376,7 +405,7 @@ button {
376
405
  background: var(--button-bg, #f3f4f6);
377
406
  color: var(--button-fg, var(--fg));
378
407
  border: 1px solid var(--button-border, var(--border));
379
- padding: 4px 8px;
408
+ padding: var(--space-2) var(--space-4);
380
409
  border-radius: 4px;
381
410
  cursor: pointer;
382
411
  transition:
@@ -385,6 +414,19 @@ button {
385
414
  border-color 140ms ease,
386
415
  transform 60ms ease;
387
416
  }
417
+ /* Inline issue ID renderer: looks like plain text */
418
+ button.id-copy {
419
+ background: transparent;
420
+ color: inherit;
421
+ border: none;
422
+ padding: 0;
423
+ margin: 0;
424
+ line-height: inherit;
425
+ font: inherit;
426
+ }
427
+ button.id-copy:hover {
428
+ text-decoration: underline;
429
+ }
388
430
  button:hover {
389
431
  filter: brightness(1.02);
390
432
  }
@@ -413,7 +455,7 @@ button:disabled {
413
455
  cursor: default;
414
456
  }
415
457
  .type-badge + .type-badge {
416
- margin-left: 4px;
458
+ margin-left: var(--space-2);
417
459
  }
418
460
  .type-badge--neutral {
419
461
  background: var(--badge-bg-neutral);
@@ -460,7 +502,7 @@ button:disabled {
460
502
  .status-badge + .status-badge,
461
503
  .priority-badge + .priority-badge,
462
504
  .badge-select + .badge-select {
463
- margin-left: 4px;
505
+ margin-left: var(--space-2);
464
506
  }
465
507
 
466
508
  /* Status + Priority palette (light) derived from base */
@@ -671,7 +713,7 @@ html[data-theme='dark'] {
671
713
  }
672
714
  .badge-select:focus {
673
715
  outline: 2px solid color-mix(in srgb, var(--link) 50%, transparent);
674
- outline-offset: 2px;
716
+ outline-offset: var(--outline-offset-m);
675
717
  box-shadow: none;
676
718
  }
677
719
 
@@ -687,8 +729,8 @@ input.inline-edit {
687
729
  outline-offset: var(--outline-offset);
688
730
  }
689
731
  input.inline-edit:focus {
690
- outline: 2px solid #93c5fd;
691
- outline-offset: var(--outline-offset);
732
+ outline: 2px solid color-mix(in srgb, var(--link) 50%, transparent);
733
+ outline-offset: var(--outline-offset-l);
692
734
  box-shadow: none;
693
735
  }
694
736
 
@@ -739,7 +781,7 @@ input.inline-edit:focus {
739
781
  /* Keyboard focus ring helper */
740
782
  :focus-visible {
741
783
  outline: 2px solid color-mix(in srgb, var(--link) 60%, transparent);
742
- outline-offset: var(--outline-offset);
784
+ outline-offset: var(--outline-offset-m);
743
785
  }
744
786
 
745
787
  /* Subtle scrollbars (WebKit/Blink) */
@@ -779,6 +821,7 @@ input.inline-edit:focus {
779
821
 
780
822
  /* Spacing for acceptance and notes sections in details */
781
823
  #detail-root .acceptance,
824
+ #detail-root .design,
782
825
  #detail-root .notes {
783
826
  margin-top: 32px;
784
827
  }
@@ -791,10 +834,17 @@ input.inline-edit:focus {
791
834
  line-height: inherit;
792
835
  }
793
836
  /* Title input fills available width and stays inline with buttons */
794
- #detail-root .detail-title input[type='text'] {
795
- flex: 1 1 auto;
837
+ #detail-root .detail-title h2 {
838
+ display: flex;
839
+ align-items: center;
840
+ }
841
+ #detail-root .detail-title h2 input[type='text'] {
842
+ flex: 1;
796
843
  padding: 0;
797
844
  min-width: 10px;
845
+ font-size: inherit;
846
+ line-height: inherit;
847
+ margin-right: 8px;
798
848
  }
799
849
  /* Remove borders in detail view; rely on global :focus-visible outline */
800
850
  #detail-root input[type='text'],
@@ -802,7 +852,7 @@ input.inline-edit:focus {
802
852
  border: none;
803
853
  background: transparent;
804
854
  outline: 2px solid var(--control-border, var(--border));
805
- outline-offset: var(--outline-offset);
855
+ outline-offset: var(--outline-offset-l);
806
856
  }
807
857
  #detail-root input[type='text']:focus,
808
858
  #detail-root textarea:focus {
@@ -830,49 +880,54 @@ input.inline-edit:focus {
830
880
  .epic-header {
831
881
  display: flex;
832
882
  align-items: center;
833
- gap: 8px;
834
- padding: 10px 12px;
883
+ gap: var(--space-4);
884
+ padding: var(--space-5) var(--space-6);
835
885
  cursor: pointer;
836
886
  background: color-mix(in srgb, var(--panel-bg) 90%, transparent);
837
887
  border-bottom: 1px solid var(--border);
838
- }
839
- .epic-header:hover {
840
- background: color-mix(in srgb, var(--control-bg) 60%, transparent);
888
+
889
+ &:hover {
890
+ background: color-mix(in srgb, var(--control-bg) 60%, transparent);
891
+ }
892
+
893
+ /* Compact native progress for epic status */
894
+ progress {
895
+ width: 140px;
896
+ height: 8px;
897
+ accent-color: var(--status-closed-base);
898
+ }
841
899
  }
842
900
  .epic-children {
843
901
  padding: 8px 12px 12px;
844
902
  }
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
903
  .table {
852
904
  width: 100%;
853
905
  border-collapse: collapse;
854
906
  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;
907
+
908
+ th,
909
+ td {
910
+ text-align: left;
911
+ padding: var(--space-6) var(--space-8);
912
+ border-bottom: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
913
+ }
914
+ /* Ensure form controls fill the cell */
915
+ td {
916
+ input[type='text'],
917
+ select {
918
+ width: 100%;
919
+ box-sizing: border-box;
920
+ }
921
+ .editable {
922
+ display: block;
923
+ width: 100%;
924
+ }
925
+ }
926
+ th {
927
+ font-size: 12px;
928
+ color: var(--muted);
929
+ font-weight: 600;
930
+ }
876
931
  }
877
932
  .epic-row:hover {
878
933
  background: color-mix(in srgb, var(--control-bg) 55%, transparent);
@@ -882,8 +937,8 @@ input.inline-edit:focus {
882
937
  .board-root {
883
938
  display: grid;
884
939
  grid-template-columns: repeat(4, 1fr);
885
- gap: 16px;
886
- padding: 12px;
940
+ gap: var(--space-8);
941
+ padding: var(--space-6);
887
942
  }
888
943
  .board-column {
889
944
  display: flex;
@@ -896,6 +951,10 @@ input.inline-edit:focus {
896
951
  background: color-mix(in srgb, var(--panel-bg) 92%, transparent);
897
952
  }
898
953
  .board-column__header {
954
+ display: flex;
955
+ align-items: center;
956
+ justify-content: space-between;
957
+ gap: 8px;
899
958
  position: sticky;
900
959
  top: 0;
901
960
  z-index: 1;
@@ -905,20 +964,31 @@ input.inline-edit:focus {
905
964
  background: inherit;
906
965
  backdrop-filter: saturate(140%) blur(4px);
907
966
  }
967
+ /* Small, unobtrusive select for closed filter */
968
+ .board-closed-filter {
969
+ margin: calc(var(--space-4) * -1) 0;
970
+
971
+ select {
972
+ font-size: 13px;
973
+ border-radius: 999px;
974
+ border-color: var(--border);
975
+ padding: 2px 18px 2px 12px;
976
+ }
977
+ }
908
978
  .board-column__body {
909
- padding: 10px;
979
+ padding: var(--space-5);
910
980
  overflow: visible;
911
981
  display: flex;
912
982
  flex-direction: column;
913
983
  /* UI-77: increase vertical spacing between card rows */
914
- gap: 14px;
984
+ gap: var(--space-7);
915
985
  }
916
986
  .board-card {
917
987
  /* UI-76: remove borders and match page body background */
918
988
  border: none;
919
989
  border-radius: 6px;
920
990
  /* UI-77: increase internal padding for readability */
921
- padding: 12px 14px;
991
+ padding: var(--space-6) var(--space-7);
922
992
  /* Match the body background so cards feel integrated */
923
993
  background: var(--bg);
924
994
  cursor: pointer;
@@ -936,17 +1006,21 @@ input.inline-edit:focus {
936
1006
  0 2px 8px color-mix(in srgb, #000 16%, transparent),
937
1007
  0 4px 16px color-mix(in srgb, #000 12%, transparent);
938
1008
  }
1009
+ .board-card:focus {
1010
+ outline: 2px solid color-mix(in srgb, var(--link) 50%, transparent);
1011
+ outline-offset: var(--outline-offset-s);
1012
+ }
939
1013
  .board-card__title {
940
1014
  font-weight: 600;
941
- margin-bottom: 4px;
1015
+ margin-bottom: var(--space-2);
942
1016
  }
943
1017
  .board-card__meta {
944
- margin-top: 8px;
1018
+ margin-top: var(--space-4);
945
1019
  font-size: 12px;
946
1020
  color: var(--muted);
947
1021
  display: flex;
948
1022
  align-items: center;
949
- gap: 8px;
1023
+ gap: var(--space-4);
950
1024
  }
951
1025
 
952
1026
  @media (max-width: 1100px) {
@@ -955,6 +1029,16 @@ input.inline-edit:focus {
955
1029
  }
956
1030
  }
957
1031
 
1032
+ /* a11y helper */
1033
+ .visually-hidden {
1034
+ position: absolute !important;
1035
+ height: 1px;
1036
+ width: 1px;
1037
+ overflow: hidden;
1038
+ clip: rect(1px, 1px, 1px, 1px);
1039
+ white-space: nowrap;
1040
+ }
1041
+
958
1042
  @media (prefers-color-scheme: dark) {
959
1043
  :root {
960
1044
  --fg: #e5e7eb;
@@ -1152,7 +1236,7 @@ html[data-theme='dark'] {
1152
1236
  }
1153
1237
  .detail-side {
1154
1238
  position: sticky;
1155
- top: 70px;
1239
+ top: 18px;
1156
1240
  }
1157
1241
  .detail-main h2 {
1158
1242
  font-size: 28px;
@@ -1220,6 +1304,14 @@ html[data-theme='dark'] {
1220
1304
  padding: 0;
1221
1305
  margin: 0 0 8px;
1222
1306
  }
1307
+ .props-card ul li {
1308
+ display: grid;
1309
+ grid-template-columns: auto auto 1fr auto;
1310
+ gap: 6px;
1311
+ align-items: center;
1312
+ padding: 2px 0;
1313
+ cursor: pointer;
1314
+ }
1223
1315
  .props-card__title {
1224
1316
  font-weight: 700;
1225
1317
  font-size: 13px;
@@ -1248,6 +1340,103 @@ html[data-theme='dark'] {
1248
1340
  gap: 8px;
1249
1341
  }
1250
1342
 
1343
+ /* --- Issue Details Dialog (UI-104) --- */
1344
+ #issue-dialog {
1345
+ padding: 0;
1346
+ border: 1px solid var(--border);
1347
+ border-radius: 8px;
1348
+ background: var(--bg);
1349
+ color: var(--fg);
1350
+ width: min(96vw, 1200px);
1351
+ max-height: 96vh;
1352
+ margin: 2vh auto;
1353
+ overflow: hidden;
1354
+ }
1355
+ #issue-dialog::backdrop {
1356
+ background: color-mix(in srgb, #000 45%, transparent);
1357
+ backdrop-filter: blur(1px);
1358
+ }
1359
+ .issue-dialog__header {
1360
+ display: flex;
1361
+ align-items: center;
1362
+ justify-content: space-between;
1363
+ gap: 10px;
1364
+ padding: 8px 12px;
1365
+ border-bottom: 1px solid var(--border);
1366
+ }
1367
+ .issue-dialog__title {
1368
+ font-weight: 700;
1369
+ }
1370
+ .issue-dialog__close {
1371
+ border-radius: 4px;
1372
+ padding: 4px 8px;
1373
+ min-width: 28px;
1374
+ line-height: 1.2;
1375
+ }
1376
+ .issue-dialog__body {
1377
+ min-height: 200px;
1378
+ max-height: calc(96vh - 44px);
1379
+ overflow: auto;
1380
+ }
1381
+
1382
+ .new-issue__body {
1383
+ padding: 12px;
1384
+ }
1385
+ .new-issue__form {
1386
+ display: grid;
1387
+ grid-template-columns: 120px 1fr;
1388
+ gap: 10px 12px;
1389
+ align-items: center;
1390
+ }
1391
+ .new-issue__form label {
1392
+ color: var(--muted);
1393
+ font-size: 12px;
1394
+ }
1395
+ .new-issue__actions {
1396
+ display: flex;
1397
+ justify-content: flex-end;
1398
+ gap: 8px;
1399
+ margin-top: 8px;
1400
+ }
1401
+ .new-issue__error {
1402
+ color: var(--danger, #b00020);
1403
+ font-size: 12px;
1404
+ }
1405
+
1406
+ /* --- New Issue Dialog (UI-106) --- */
1407
+ #new-issue-dialog {
1408
+ padding: 0;
1409
+ border: 1px solid var(--border);
1410
+ border-radius: 8px;
1411
+ background: var(--bg);
1412
+ color: var(--fg);
1413
+ width: min(640px, 96vw);
1414
+ max-height: 96vh;
1415
+ margin: 2vh auto;
1416
+ overflow: hidden;
1417
+ }
1418
+ #new-issue-dialog::backdrop {
1419
+ background: color-mix(in srgb, #000 45%, transparent);
1420
+ backdrop-filter: blur(1px);
1421
+ }
1422
+ .new-issue__header {
1423
+ display: flex;
1424
+ align-items: center;
1425
+ justify-content: space-between;
1426
+ gap: 10px;
1427
+ padding: 8px 12px;
1428
+ border-bottom: 1px solid var(--border);
1429
+ }
1430
+ .new-issue__title {
1431
+ font-weight: 700;
1432
+ }
1433
+ .new-issue__close {
1434
+ border-radius: 4px;
1435
+ padding: 4px 8px;
1436
+ min-width: 28px;
1437
+ line-height: 1.2;
1438
+ }
1439
+
1251
1440
  .prop.labels {
1252
1441
  align-items: start;
1253
1442
  }