pygitweb 0.1.0__py3-none-any.whl

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 (95) hide show
  1. pygitweb/__init__.py +22 -0
  2. pygitweb/__meta__.py +9 -0
  3. pygitweb/actions.py +1303 -0
  4. pygitweb/addproject_permissions_test.py +114 -0
  5. pygitweb/api/__init__.py +0 -0
  6. pygitweb/api/action.py +20 -0
  7. pygitweb/api/plugins/__init__.py +0 -0
  8. pygitweb/api/plugins/project_zip.py +71 -0
  9. pygitweb/api/project.py +67 -0
  10. pygitweb/api/subpage.py +22 -0
  11. pygitweb/auth.py +387 -0
  12. pygitweb/auth.schema.json +106 -0
  13. pygitweb/auth_config.py +243 -0
  14. pygitweb/auth_oauth.py +343 -0
  15. pygitweb/auth_test.py +390 -0
  16. pygitweb/boards_permissions_test.py +113 -0
  17. pygitweb/change_queue.py +110 -0
  18. pygitweb/change_queue_routes_test.py +146 -0
  19. pygitweb/change_queue_test.py +91 -0
  20. pygitweb/comments_permissions_test.py +140 -0
  21. pygitweb/config.py +185 -0
  22. pygitweb/conftest.py +43 -0
  23. pygitweb/dependencies.py +87 -0
  24. pygitweb/formatting.py +182 -0
  25. pygitweb/formatting_test.py +68 -0
  26. pygitweb/git_helpers.py +454 -0
  27. pygitweb/git_helpers_test.py +223 -0
  28. pygitweb/gravatar.py +16 -0
  29. pygitweb/gravatar_test.py +10 -0
  30. pygitweb/hooks_install.py +278 -0
  31. pygitweb/hooks_install_test.py +363 -0
  32. pygitweb/hooks_permissions_test.py +116 -0
  33. pygitweb/main.py +887 -0
  34. pygitweb/merge_requests.py +230 -0
  35. pygitweb/merge_requests_test.py +85 -0
  36. pygitweb/mr_permissions_test.py +148 -0
  37. pygitweb/password_hash.py +74 -0
  38. pygitweb/password_hash_test.py +20 -0
  39. pygitweb/permissions.py +209 -0
  40. pygitweb/permissions_test.py +86 -0
  41. pygitweb/plugin_loader.py +83 -0
  42. pygitweb/projects.py +213 -0
  43. pygitweb/projects_test.py +186 -0
  44. pygitweb/sessions.py +73 -0
  45. pygitweb/settings.py +412 -0
  46. pygitweb/settings.schema.json +73 -0
  47. pygitweb/settings_config.py +122 -0
  48. pygitweb/settings_config_test.py +101 -0
  49. pygitweb/settings_permissions_test.py +76 -0
  50. pygitweb/static/blob-view.js +133 -0
  51. pygitweb/static/board.js +839 -0
  52. pygitweb/static/hook-install.js +53 -0
  53. pygitweb/static/icons/cloud-down.svg +1 -0
  54. pygitweb/static/icons/cloud-exclamation.svg +1 -0
  55. pygitweb/static/icons/cloud-filled.svg +1 -0
  56. pygitweb/static/icons/cloud-storm.svg +1 -0
  57. pygitweb/static/icons/cloud-up.svg +1 -0
  58. pygitweb/static/icons/cloud.svg +1 -0
  59. pygitweb/static/logo.svg +44 -0
  60. pygitweb/static/main.css +1210 -0
  61. pygitweb/static/navbar-auth.js +82 -0
  62. pygitweb/static/readme-render.js +174 -0
  63. pygitweb/static/search-page.js +139 -0
  64. pygitweb/static/summary-ref-switcher.js +130 -0
  65. pygitweb/static/themes/solarized.css +34 -0
  66. pygitweb/tasks.py +595 -0
  67. pygitweb/tasks_permissions_test.py +137 -0
  68. pygitweb/tasks_routes_test.py +430 -0
  69. pygitweb/templates/addproject.html +174 -0
  70. pygitweb/templates/blobdiff.html +57 -0
  71. pygitweb/templates/board.html +31 -0
  72. pygitweb/templates/board_card.html +44 -0
  73. pygitweb/templates/commitdiff.html +57 -0
  74. pygitweb/templates/hooks_summary_cell.html +37 -0
  75. pygitweb/templates/login.html +37 -0
  76. pygitweb/templates/merge_request_summary_cell.html +18 -0
  77. pygitweb/templates/merge_request_tag.html +90 -0
  78. pygitweb/templates/pagination.html +44 -0
  79. pygitweb/templates/preamble.html +89 -0
  80. pygitweb/templates/project_settings.html +13 -0
  81. pygitweb/templates/pygit2_settings.html +12 -0
  82. pygitweb/templates/pygitweb_settings.html +12 -0
  83. pygitweb/templates/search.html +77 -0
  84. pygitweb/templates/settings_form_field.html +27 -0
  85. pygitweb/templates/table.html +15 -0
  86. pygitweb/templates/user_profile.html +10 -0
  87. pygitweb/templates_env.py +21 -0
  88. pygitweb/timeline_cache.py +243 -0
  89. pygitweb/validation.py +75 -0
  90. pygitweb/validation_test.py +167 -0
  91. pygitweb-0.1.0.dist-info/METADATA +165 -0
  92. pygitweb-0.1.0.dist-info/RECORD +95 -0
  93. pygitweb-0.1.0.dist-info/WHEEL +5 -0
  94. pygitweb-0.1.0.dist-info/licenses/LICENSE +21 -0
  95. pygitweb-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1210 @@
1
+ /*
2
+ * Custom themes: load an extra stylesheet after this one that redefines
3
+ * the same --pgw-* (optional --pgw-hljs-*, --d2h-*) variables on :root.
4
+ * Example: <link rel="stylesheet" href="static/themes/solarized.css">
5
+ */
6
+
7
+ :root {
8
+ color-scheme: light;
9
+
10
+ /* Theme colors */
11
+ --pgw-theme-wheat: #d1caa1;
12
+ --pgw-theme-slate: #66717e;
13
+ --pgw-theme-mist: #d4d6b9;
14
+ --pgw-theme-ink: #0f1218;
15
+ --pgw-theme-ink-soft: #1a1f2e;
16
+
17
+ /* Surfaces */
18
+ --pgw-bg: #fafafa;
19
+ --pgw-bg-raised: #ffffff;
20
+ --pgw-bg-muted: #f4f4f5;
21
+ --pgw-border: #e4e4e7;
22
+ --pgw-border-muted: #d4d4d8;
23
+
24
+ --pgw-text: #18181b;
25
+ --pgw-text-muted: #52525b;
26
+ --pgw-text-dim: #71717a;
27
+
28
+ --pgw-link: #2563eb;
29
+ --pgw-link-visited: #7c3aed;
30
+ --pgw-link-hover: #2641dc;
31
+
32
+ --pgw-btn-bg: var(--pgw-theme-mist);
33
+ --pgw-btn-text: var(--pgw-theme-ink);
34
+ --pgw-btn-hover-bg: var(--pgw-theme-wheat);
35
+ --pgw-btn-hover-text: var(--pgw-theme-ink);
36
+
37
+ --pgw-accent-bg: #e4e4e7;
38
+ --pgw-accent-text: #18181b;
39
+ --pgw-title-bg: #f4f4f5;
40
+ --pgw-title-hover-bg: #e4e4e7;
41
+
42
+ --pgw-code-bg: #f4f4f5;
43
+ --pgw-code-border: #e4e4e7;
44
+
45
+ /* highlight.js (github-light palette via pgw / Tabler) */
46
+ --pgw-hljs-fg: var(--pgw-text);
47
+ --pgw-hljs-bg: transparent;
48
+ --pgw-hljs-comment: var(--pgw-text-muted);
49
+ --pgw-hljs-keyword: var(--tblr-pink, #a71d5d);
50
+ --pgw-hljs-type: var(--tblr-red, #cf222e);
51
+ --pgw-hljs-string: var(--pgw-link);
52
+ --pgw-hljs-title: var(--tblr-purple, #8250df);
53
+ --pgw-hljs-attr: var(--tblr-cyan, #0550ae);
54
+ --pgw-hljs-name: var(--tblr-orange, #953800);
55
+ --pgw-hljs-built_in: var(--pgw-link);
56
+ --pgw-hljs-tag: var(--pgw-add-fg);
57
+ --pgw-hljs-meta: var(--pgw-hljs-fg);
58
+ --pgw-hljs-addition-fg: var(--pgw-add-fg);
59
+ --pgw-hljs-addition-bg: var(--pgw-add-bg);
60
+ --pgw-hljs-deletion-fg: var(--pgw-rem-fg);
61
+ --pgw-hljs-deletion-bg: var(--pgw-rem-bg);
62
+
63
+ --pgw-table-stripe: #fafafa;
64
+ --pgw-table-hover: #f4f4f5;
65
+
66
+ --pgw-add-fg: #166534;
67
+ --pgw-add-bg: #dcfce7;
68
+ --pgw-rem-fg: #991b1b;
69
+ --pgw-rem-bg: #fee2e2;
70
+ --pgw-info-fg: #52525b;
71
+
72
+ --pgw-warn-fg: #92400e;
73
+ --pgw-warn-bg: #fffbeb;
74
+ --pgw-warn-border: #fcd34d;
75
+ --pgw-warn-accent: #d97706;
76
+
77
+ --pgw-blob-selection-bg: rgba(193, 173, 0, 0.35);
78
+
79
+ --pgw-navbar-bg: #e2e8f0;
80
+ --pgw-navbar-text: #1e293b;
81
+ --pgw-navbar-text-muted: #64748b;
82
+ --pgw-navbar-link-hover-bg: #cbd5e1;
83
+ --pgw-navbar-active-bg: #94a3b8;
84
+ --pgw-navbar-active-text: #ffffff;
85
+ --pgw-navbar-border: #cbd5e1;
86
+ --pgw-navbar-toggler-border: #94a3b8;
87
+ --pgw-navbar-toggler-icon: #475569;
88
+
89
+ /* diff2html (optional overrides) */
90
+ --d2h-info-bg-color: #f0f0f0;
91
+ --d2h-file-header-bg-color: rgb(220, 220, 255);
92
+ --d2h-empty-placeholder-bg-color: #f5f5f5;
93
+ --d2h-del-bg-color: rgb(255, 220, 220);
94
+ --d2h-ins-bg-color: rgb(220, 255, 220);
95
+ --d2h-dim-color: #666;
96
+ --d2h-bg-color: #fff;
97
+ --d2h-selected-color: rgba(255, 255, 255, 0.3);
98
+ }
99
+
100
+ @media (prefers-color-scheme: dark) {
101
+ :root {
102
+ color-scheme: dark;
103
+ --pgw-bg: #1a1a1e;
104
+ --pgw-bg-raised: #25252c;
105
+ --pgw-bg-muted: #2d2d36;
106
+ --pgw-border: #3d3d48;
107
+ --pgw-border-muted: #35353d;
108
+
109
+ --pgw-text: #e4e4e7;
110
+ --pgw-text-muted: #a1a1aa;
111
+ --pgw-text-dim: #71717a;
112
+
113
+ --pgw-link: #7dd3fc;
114
+ --pgw-link-visited: #c4b5fd;
115
+ --pgw-link-hover: #fda4af;
116
+
117
+ --pgw-btn-bg: #2d2d4a;
118
+ --pgw-btn-text: var(--pgw-text);
119
+ --pgw-btn-hover-bg: #4f4f7a;
120
+ --pgw-btn-hover-text: var(--pgw-text);
121
+
122
+ --pgw-accent-bg: #27272a;
123
+ --pgw-accent-text: #fafafa;
124
+ --pgw-title-bg: #2d2d36;
125
+ --pgw-title-hover-bg: #3d3d48;
126
+
127
+ --pgw-code-bg: #18181b;
128
+ --pgw-code-border: #27272a;
129
+
130
+ --pgw-hljs-fg: var(--pgw-text);
131
+ --pgw-hljs-comment: var(--pgw-text-dim);
132
+ --pgw-hljs-keyword: var(--tblr-red, #ff7b72);
133
+ --pgw-hljs-type: var(--pgw-hljs-keyword);
134
+ --pgw-hljs-string: var(--pgw-link);
135
+ --pgw-hljs-title: var(--tblr-purple, #d2a8ff);
136
+ --pgw-hljs-attr: var(--tblr-cyan, #79c0ff);
137
+ --pgw-hljs-name: var(--tblr-green, #7ee787);
138
+ --pgw-hljs-built_in: var(--tblr-orange, #ffa657);
139
+ --pgw-hljs-tag: var(--pgw-hljs-name);
140
+ --pgw-hljs-meta: var(--pgw-hljs-fg);
141
+
142
+ --pgw-table-stripe: #222226;
143
+ --pgw-table-hover: #2d2d36;
144
+
145
+ --pgw-add-fg: #86efac;
146
+ --pgw-add-bg: #14532d;
147
+ --pgw-rem-fg: #fca5a5;
148
+ --pgw-rem-bg: #7f1d1d;
149
+ --pgw-info-fg: #a1a1aa;
150
+
151
+ --pgw-warn-fg: #fde68a;
152
+ --pgw-warn-bg: #422006;
153
+ --pgw-warn-border: #92400e;
154
+ --pgw-warn-accent: #f59e0b;
155
+
156
+ --pgw-blob-selection-bg: rgba(193, 173, 0, 0.4);
157
+
158
+ --pgw-navbar-bg: #2d2d4a;
159
+ --pgw-navbar-text: #e2e8f0;
160
+ --pgw-navbar-text-muted: #94a3b8;
161
+ --pgw-navbar-link-hover-bg: #3d3d5c;
162
+ --pgw-navbar-active-bg: #4f4f7a;
163
+ --pgw-navbar-active-text: #f1f5f9;
164
+ --pgw-navbar-border: #3d3d48;
165
+ --pgw-navbar-toggler-border: #64748b;
166
+ --pgw-navbar-toggler-icon: #94a3b8;
167
+
168
+ --d2h-info-bg-color: #222;
169
+ --d2h-file-header-bg-color: rgb(38, 38, 98);
170
+ --d2h-empty-placeholder-bg-color: #222;
171
+ --d2h-del-bg-color: rgb(98, 38, 38);
172
+ --d2h-ins-bg-color: rgb(38, 98, 38);
173
+ --d2h-dim-color: #b0b0b0;
174
+ --d2h-bg-color: #222;
175
+ }
176
+ }
177
+
178
+ html {
179
+ box-sizing: border-box;
180
+ }
181
+
182
+ *, *::before, *::after {
183
+ box-sizing: inherit;
184
+ }
185
+
186
+ body {
187
+ font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
188
+ font-size: 0.875rem;
189
+ line-height: 1.5;
190
+ margin: 0;
191
+ padding: 0.75rem 1rem;
192
+ background-color: var(--pgw-bg);
193
+ color: var(--pgw-text);
194
+ border: 1px solid var(--pgw-border);
195
+ height: auto;
196
+
197
+ &:has(.page) {
198
+ padding: 0;
199
+ }
200
+ }
201
+
202
+ /* Card: override Tabler so cards use color-scheme variables */
203
+ .card {
204
+ background-color: var(--pgw-bg-raised);
205
+ border-color: var(--pgw-border);
206
+ color: var(--pgw-text);
207
+
208
+ .card-header {
209
+ background-color: var(--pgw-accent-bg);
210
+ border-bottom-color: var(--pgw-border);
211
+ color: var(--pgw-accent-text);
212
+ }
213
+
214
+ .card-title {
215
+ color: var(--pgw-accent-text);
216
+ }
217
+
218
+ .card-body {
219
+ background-color: var(--pgw-bg-raised);
220
+ color: var(--pgw-text);
221
+ }
222
+ }
223
+
224
+ .summary-section {
225
+ & > summary {
226
+ cursor: pointer;
227
+ list-style: none;
228
+
229
+ &::-webkit-details-marker {
230
+ display: none;
231
+ }
232
+
233
+ &::before {
234
+ content: "▸";
235
+ display: inline-block;
236
+ margin-right: 0.5rem;
237
+ transition: transform 0.15s ease;
238
+ }
239
+ }
240
+
241
+ &[open] > summary::before {
242
+ transform: rotate(90deg);
243
+ }
244
+
245
+ & .summary-tree-table:has(.table) {
246
+ padding: 0;
247
+ }
248
+ }
249
+
250
+ /* Table: override Tabler .table / .card-table so tables use color-scheme variables */
251
+ .table,
252
+ .table.card-table {
253
+ background-color: var(--pgw-bg-raised);
254
+ border-color: var(--pgw-border);
255
+ color: var(--pgw-text);
256
+
257
+ thead th,
258
+ th {
259
+ background-color: var(--pgw-accent-bg);
260
+ border-bottom-color: var(--pgw-border-muted);
261
+ color: var(--pgw-accent-text);
262
+ }
263
+
264
+ td {
265
+ border-top-color: var(--pgw-border-muted);
266
+ border-bottom-color: var(--pgw-border-muted);
267
+ color: var(--pgw-text);
268
+ }
269
+
270
+ tbody tr {
271
+ &:nth-child(even) {
272
+ background-color: var(--pgw-table-stripe);
273
+ }
274
+
275
+ &:hover {
276
+ background-color: var(--pgw-table-hover);
277
+
278
+ td {
279
+ background-color: var(--pgw-table-hover);
280
+ }
281
+ }
282
+ }
283
+ }
284
+
285
+ /* Form: override Tabler form controls so they use color-scheme variables */
286
+ .form-label,
287
+ .form-check-label {
288
+ color: var(--pgw-text);
289
+ }
290
+
291
+ .form-label.required::after {
292
+ color: var(--pgw-text-muted);
293
+ }
294
+
295
+ .form-hint {
296
+ color: var(--pgw-text-muted);
297
+
298
+ code {
299
+ background-color: var(--pgw-code-bg);
300
+ border-color: var(--pgw-code-border);
301
+ color: var(--pgw-text);
302
+ }
303
+ }
304
+
305
+ .form-control,
306
+ .form-select {
307
+ background-color: var(--pgw-bg-raised);
308
+ border-color: var(--pgw-border);
309
+ color: var(--pgw-text);
310
+
311
+ &::placeholder {
312
+ color: var(--pgw-text-muted);
313
+ }
314
+
315
+ &:focus {
316
+ background-color: var(--pgw-bg-raised);
317
+ border-color: var(--pgw-link);
318
+ color: var(--pgw-text);
319
+ }
320
+
321
+ &:disabled {
322
+ background-color: var(--pgw-bg-muted);
323
+ color: var(--pgw-text-muted);
324
+ }
325
+ }
326
+
327
+ .form-check-input {
328
+ background-color: var(--pgw-bg-raised);
329
+ border-color: var(--pgw-border);
330
+
331
+ &:checked {
332
+ background-color: var(--pgw-link);
333
+ border-color: var(--pgw-link);
334
+ }
335
+
336
+ &:focus {
337
+ border-color: var(--pgw-link);
338
+ }
339
+ }
340
+
341
+ .form-footer {
342
+ border-top-color: var(--pgw-border);
343
+ }
344
+
345
+ /* Buttons: primary and ghost variants */
346
+ .btn-primary {
347
+ background-color: var(--pgw-btn-bg);
348
+ border-color: var(--pgw-border-muted);
349
+ color: var(--pgw-btn-text);
350
+
351
+ &:hover {
352
+ background-color: var(--pgw-btn-hover-bg);
353
+ border-color: var(--pgw-border);
354
+ color: var(--pgw-btn-hover-text);
355
+ }
356
+ }
357
+
358
+ .btn-ghost-secondary {
359
+ background-color: transparent;
360
+ border-color: var(--pgw-border);
361
+ color: var(--pgw-text);
362
+
363
+ &:hover {
364
+ background-color: var(--pgw-bg-muted);
365
+ border-color: var(--pgw-border);
366
+ color: var(--pgw-text);
367
+ }
368
+ }
369
+
370
+ /* Dropzone (file upload area) */
371
+ .dropzone {
372
+ background-color: var(--pgw-bg-muted);
373
+ border-color: var(--pgw-border);
374
+ color: var(--pgw-text);
375
+
376
+ .dropzone-placeholder,
377
+ .text-muted {
378
+ color: var(--pgw-text-muted);
379
+ }
380
+
381
+ &.border-primary {
382
+ border-color: var(--pgw-link);
383
+ }
384
+ }
385
+
386
+ radio-picker {
387
+ display: flex;
388
+
389
+ label {
390
+ box-shadow: inset 0 0 1.2px 0 #000;
391
+ padding: 0.5rem;
392
+ cursor: pointer;
393
+ background: #0002;
394
+
395
+ &:has(input:checked) {
396
+ box-shadow: inset 0 0 0.2rem 0 #888;
397
+ }
398
+
399
+ &:has(input:focus-visible) {
400
+ outline: 2px solid #000;
401
+ }
402
+
403
+ &:hover {
404
+ background: #0004;
405
+ }
406
+
407
+ &:active {
408
+ background: #0006;
409
+ }
410
+ }
411
+
412
+ input {
413
+ opacity: 0;
414
+ position: absolute;
415
+ pointer-events: none;
416
+ }
417
+ }
418
+
419
+ /* Fluid vertical layout: fixed sidebar + main content offset */
420
+ .page {
421
+ .navbar-vertical.position-fixed {
422
+ top: 0;
423
+ bottom: 0;
424
+ left: 0;
425
+ z-index: 1030;
426
+ background-color: var(--pgw-navbar-bg);
427
+ }
428
+
429
+ .navbar-vertical.position-fixed ~ .page-wrapper {
430
+ margin-left: var(--tblr-navbar-vertical-width, 15rem);
431
+ }
432
+ }
433
+
434
+ a {
435
+ color: var(--pgw-link);
436
+ text-decoration: none;
437
+
438
+ &:hover {
439
+ color: var(--pgw-link-hover);
440
+ }
441
+
442
+ &:visited {
443
+ color: var(--pgw-link-visited);
444
+ }
445
+ }
446
+
447
+ h1, h2, h3 {
448
+ margin: 0 0 0.5em;
449
+ color: var(--pgw-accent-text);
450
+ font-weight: 600;
451
+ }
452
+
453
+ /* Tables */
454
+ table {
455
+ border-collapse: collapse;
456
+ border-spacing: 0;
457
+ background-color: var(--pgw-bg-raised);
458
+ border: 1px solid var(--pgw-border);
459
+ }
460
+
461
+ th, td {
462
+ padding: 0.1rem 0.2rem;
463
+ text-align: left;
464
+ vertical-align: top;
465
+ border-bottom: 1px solid var(--pgw-border-muted);
466
+ }
467
+
468
+ p {
469
+ color: var(--pgw-text);
470
+ }
471
+
472
+ th {
473
+ background-color: var(--pgw-accent-bg);
474
+ color: var(--pgw-accent-text);
475
+ font-weight: 600;
476
+ }
477
+
478
+ #environment {
479
+ background-color: var(--pgw-bg-raised);
480
+ color: var(--pgw-text);
481
+ }
482
+
483
+ #environment tr:nth-child(2n+1) {
484
+ background-color: var(--pgw-table-stripe);
485
+ }
486
+
487
+ tbody tr {
488
+ &:nth-child(even) {
489
+ background-color: var(--pgw-table-stripe);
490
+ }
491
+
492
+ &:hover {
493
+ background-color: var(--pgw-table-hover);
494
+ }
495
+ }
496
+
497
+ /* Optional: page header / footer blocks for future use */
498
+ .page_header,
499
+ .page_footer {
500
+ padding: 0.5rem 0.75rem;
501
+ background-color: var(--pgw-accent-bg);
502
+ color: var(--pgw-accent-text);
503
+ border: 1px solid var(--pgw-border);
504
+ }
505
+
506
+ .page_footer_text {
507
+ color: var(--pgw-text-muted);
508
+ font-style: italic;
509
+ }
510
+
511
+ .page_body {
512
+ padding: 0.5rem;
513
+ font-family: ui-monospace, monospace;
514
+ background-color: var(--pgw-code-bg);
515
+ border: 1px solid var(--pgw-code-border);
516
+ }
517
+
518
+ .title, .title_text {
519
+ background-color: var(--pgw-title-bg);
520
+ padding: 0.375rem 0.5rem;
521
+ border-bottom: 1px solid var(--pgw-border);
522
+ }
523
+
524
+ .title:hover, a.title:hover {
525
+ background-color: var(--pgw-title-hover-bg);
526
+ }
527
+
528
+ /* Vertical navbar: variables and link states */
529
+ .navbar-vertical {
530
+ .nav-link {
531
+ color: var(--pgw-navbar-text);
532
+
533
+ &:hover {
534
+ background-color: var(--pgw-navbar-link-hover-bg);
535
+ color: var(--pgw-navbar-text);
536
+
537
+ .nav-link-title,
538
+ .text-reset {
539
+ color: var(--pgw-navbar-text);
540
+ }
541
+ }
542
+
543
+ &.active {
544
+ background-color: var(--pgw-navbar-active-bg);
545
+ color: var(--pgw-navbar-active-text);
546
+
547
+ .nav-link-title {
548
+ color: var(--pgw-navbar-active-text);
549
+ }
550
+ }
551
+ }
552
+
553
+ .navbar-brand .text-reset,
554
+ .nav-link-title {
555
+ color: var(--pgw-navbar-text);
556
+ }
557
+
558
+ .navbar-toggler {
559
+ border-color: var(--pgw-navbar-toggler-border);
560
+ }
561
+
562
+ .navbar-toggler-icon {
563
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='%23475569' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
564
+ }
565
+
566
+ .nav-link-icon {
567
+ display: inline-flex;
568
+ align-items: center;
569
+ justify-content: center;
570
+ width: 1.5rem;
571
+ height: 1.5rem;
572
+
573
+ svg {
574
+ width: 1.25rem;
575
+ height: 1.25rem;
576
+ }
577
+ }
578
+ }
579
+
580
+ [data-bs-theme="dark"] {
581
+ .navbar-vertical .navbar-toggler-icon {
582
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='%2394a3b8' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
583
+ }
584
+ }
585
+
586
+ .navbar-user {
587
+ border-top-color: var(--pgw-navbar-border) !important;
588
+
589
+ .navbar-user-name {
590
+ color: var(--pgw-navbar-text);
591
+ }
592
+
593
+ .navbar-user-meta,
594
+ .text-muted {
595
+ color: var(--pgw-navbar-text-muted);
596
+ }
597
+
598
+ .avatar svg {
599
+ width: 1.25rem;
600
+ height: 1.25rem;
601
+ }
602
+
603
+ .avatar img {
604
+ width: 100%;
605
+ height: 100%;
606
+ object-fit: cover;
607
+ }
608
+ }
609
+
610
+ /* Diff/status helpers (use var(--pgw-add-*) etc. in markup if needed) */
611
+ .diff-add { color: var(--pgw-add-fg); background-color: var(--pgw-add-bg); }
612
+ .diff-rem { color: var(--pgw-rem-fg); background-color: var(--pgw-rem-bg); }
613
+ .text-muted { color: var(--pgw-text-muted); }
614
+
615
+ /* Blob view: line numbers + code with syntax highlighting */
616
+ .blob-view {
617
+ display: flex;
618
+ margin: 0.5rem 0;
619
+ max-width: 100%;
620
+ overflow: auto;
621
+ background-color: var(--pgw-code-bg);
622
+ border: 1px solid var(--pgw-code-border);
623
+
624
+ .blob-line-nums {
625
+ flex-shrink: 0;
626
+ user-select: none;
627
+ padding: 0.75rem 1rem 0.75rem 0.5rem;
628
+ text-align: right;
629
+ color: var(--pgw-text-muted);
630
+ white-space: pre;
631
+ font-family: ui-monospace, "Fira Code", "Fira Mono", monospace;
632
+ font-size: 0.9rem;
633
+ line-height: 1.5;
634
+ border-right: 1px solid var(--pgw-border);
635
+ }
636
+
637
+ .blob-content {
638
+ flex: 1;
639
+ margin: 0;
640
+ padding: 0.75rem 1rem;
641
+ overflow: auto;
642
+ font-family: ui-monospace, "Fira Code", "Fira Mono", monospace;
643
+ font-size: 0.9rem;
644
+ line-height: 1.5;
645
+ min-width: 0;
646
+
647
+ code {
648
+ background: none;
649
+ padding: 0;
650
+ font-size: inherit;
651
+ line-height: inherit;
652
+ }
653
+ }
654
+
655
+ .blob-line-selected,
656
+ .blob-selection {
657
+ background-color: var(--pgw-blob-selection-bg);
658
+ }
659
+ }
660
+
661
+ /* highlight.js: token colors from --pgw-hljs-* (replaces CDN theme; tracks prefers-color-scheme) */
662
+ .hljs,
663
+ .hljs-subst {
664
+ color: var(--pgw-hljs-fg);
665
+ background: var(--pgw-hljs-bg);
666
+ }
667
+
668
+ .hljs-comment,
669
+ .hljs-formula {
670
+ color: var(--pgw-hljs-comment);
671
+ }
672
+
673
+ .hljs-operator,
674
+ .hljs-punctuation {
675
+ color: var(--pgw-hljs-fg);
676
+ }
677
+
678
+ .hljs-doctag,
679
+ .hljs-keyword,
680
+ .hljs-meta .hljs-keyword,
681
+ .hljs-template-tag,
682
+ .hljs-template-variable,
683
+ .hljs-type,
684
+ .hljs-variable.language_ {
685
+ color: var(--pgw-hljs-keyword);
686
+ }
687
+
688
+ .hljs-title,
689
+ .hljs-title.class_,
690
+ .hljs-title.class_.inherited__,
691
+ .hljs-title.function_,
692
+ .hljs-function .hljs-title,
693
+ .hljs-section,
694
+ .hljs-attribute,
695
+ .ruby .hljs-property {
696
+ color: var(--pgw-hljs-title);
697
+ }
698
+
699
+ .hljs-attr,
700
+ .hljs-literal,
701
+ .hljs-meta,
702
+ .hljs-number,
703
+ .hljs-selector-attr,
704
+ .hljs-selector-class,
705
+ .hljs-selector-id,
706
+ .hljs-variable,
707
+ .hljs-variable.constant_ {
708
+ color: var(--pgw-hljs-attr);
709
+ }
710
+
711
+ .hljs-built_in,
712
+ .hljs-symbol {
713
+ color: var(--pgw-hljs-built_in);
714
+ }
715
+
716
+ .hljs-meta .hljs-string,
717
+ .hljs-regexp,
718
+ .hljs-string {
719
+ color: var(--pgw-hljs-string);
720
+ }
721
+
722
+ .hljs-addition,
723
+ .hljs-code,
724
+ .hljs-keyword.hljs-atrule,
725
+ .hljs-title.class_.inherited__ {
726
+ color: var(--pgw-hljs-string);
727
+ }
728
+
729
+ .hljs-name,
730
+ .hljs-quote,
731
+ .hljs-selector-pseudo,
732
+ .hljs-selector-tag {
733
+ color: var(--pgw-hljs-name);
734
+ }
735
+
736
+ .hljs-tag {
737
+ color: var(--pgw-hljs-tag);
738
+ }
739
+
740
+ .hljs-bullet {
741
+ color: var(--pgw-hljs-built_in);
742
+ }
743
+
744
+ .hljs-emphasis {
745
+ color: var(--pgw-hljs-keyword);
746
+ font-style: italic;
747
+ }
748
+
749
+ .hljs-strong {
750
+ color: var(--pgw-hljs-title);
751
+ font-weight: 700;
752
+ }
753
+
754
+ .hljs-addition {
755
+ color: var(--pgw-hljs-addition-fg);
756
+ background-color: var(--pgw-hljs-addition-bg);
757
+ }
758
+
759
+ .hljs-deletion {
760
+ color: var(--pgw-hljs-deletion-fg);
761
+ background-color: var(--pgw-hljs-deletion-bg);
762
+ }
763
+
764
+ .blob-view.blame-view {
765
+ .blame-commits {
766
+ flex-shrink: 0;
767
+ user-select: none;
768
+ padding: 0.75rem 0.5rem;
769
+ text-align: right;
770
+ color: var(--pgw-text-muted);
771
+ white-space: pre;
772
+ font-family: ui-monospace, "Fira Code", "Fira Mono", monospace;
773
+ font-size: 0.9rem;
774
+ line-height: 1.5;
775
+ border-right: 1px solid var(--pgw-border);
776
+
777
+ .blame-commit-link {
778
+ color: var(--pgw-link);
779
+ text-decoration: none;
780
+ font-size: 0.85rem;
781
+
782
+ &:hover {
783
+ text-decoration: underline;
784
+ }
785
+ }
786
+ }
787
+ }
788
+
789
+ /* README markdown: base and all elements use color-scheme variables */
790
+ .markdown-body {
791
+ color: var(--pgw-text);
792
+ background-color: transparent;
793
+
794
+ h1, h2, h3, h4, h5, h6 {
795
+ color: var(--pgw-accent-text);
796
+ border-bottom-color: var(--pgw-border-muted);
797
+ }
798
+
799
+ a {
800
+ color: var(--pgw-link);
801
+
802
+ &:hover {
803
+ color: var(--pgw-link-hover);
804
+ }
805
+
806
+ &:visited {
807
+ color: var(--pgw-link-visited);
808
+ }
809
+ }
810
+
811
+ blockquote {
812
+ border-left: 0.25rem solid var(--pgw-border);
813
+ color: var(--pgw-text-muted);
814
+ padding-left: 1rem;
815
+ margin-left: 0;
816
+ }
817
+
818
+ hr {
819
+ border-color: var(--pgw-border-muted);
820
+ background-color: var(--pgw-border-muted);
821
+ }
822
+
823
+ table {
824
+ background-color: var(--pgw-bg-raised);
825
+ border-color: var(--pgw-border);
826
+ }
827
+
828
+ th, td {
829
+ border-color: var(--pgw-border-muted);
830
+ }
831
+
832
+ th {
833
+ background-color: var(--pgw-accent-bg);
834
+ color: var(--pgw-accent-text);
835
+ }
836
+
837
+ tbody tr {
838
+ &:nth-child(even) {
839
+ background-color: var(--pgw-table-stripe);
840
+ }
841
+
842
+ &:hover {
843
+ background-color: var(--pgw-table-hover);
844
+ }
845
+ }
846
+
847
+ img {
848
+ background-color: var(--pgw-bg-muted);
849
+ }
850
+
851
+ .readme-heading-wrap {
852
+ position: relative;
853
+ display: block;
854
+ }
855
+
856
+ .readme-section-anchor {
857
+ position: absolute;
858
+ right: 100%;
859
+ padding-right: 0.35rem;
860
+ opacity: 0;
861
+ color: var(--pgw-text-muted);
862
+ font-size: 1.3rem;
863
+ text-decoration: none;
864
+ transition: opacity 0.15s ease;
865
+
866
+ &:focus {
867
+ opacity: 1;
868
+ }
869
+ }
870
+
871
+ .readme-heading-wrap:hover .readme-section-anchor,
872
+ .readme-section-anchor:hover {
873
+ opacity: 1;
874
+ }
875
+ }
876
+
877
+ code {
878
+ background-color: var(--pgw-code-bg);
879
+ color: var(--pgw-text);
880
+ border: 1px solid var(--pgw-code-border);
881
+ }
882
+
883
+ pre {
884
+ background-color: var(--pgw-code-bg);
885
+ border: 1px solid var(--pgw-code-border);
886
+ color: var(--pgw-text);
887
+
888
+ code {
889
+ background: none;
890
+ border: none;
891
+ }
892
+ }
893
+
894
+ .readme-toc-mount {
895
+ position: fixed;
896
+ left: var(--tblr-navbar-vertical-width, 15rem);
897
+ top: 0;
898
+ bottom: 0;
899
+ width: 12rem;
900
+ z-index: 1040;
901
+ pointer-events: none;
902
+ overflow: hidden;
903
+
904
+ .readme-toc {
905
+ pointer-events: auto;
906
+ padding: 1rem 0.5rem 1rem 0;
907
+ max-height: 100%;
908
+ overflow-y: auto;
909
+ }
910
+ }
911
+
912
+ .readme-toc-list {
913
+ list-style: none;
914
+ margin: 0;
915
+ padding: 0;
916
+ font-size: 0.8rem;
917
+ }
918
+
919
+ .readme-toc-item {
920
+ margin: 0.2rem 0;
921
+ }
922
+
923
+ .readme-toc-link {
924
+ display: block;
925
+ padding: 0.2rem 0.4rem;
926
+ color: var(--pgw-text-muted);
927
+ text-decoration: none;
928
+ border-radius: 0.25rem;
929
+ white-space: nowrap;
930
+ overflow: hidden;
931
+ text-overflow: ellipsis;
932
+
933
+ &:hover {
934
+ color: var(--pgw-link-hover);
935
+ background-color: var(--pgw-bg-raised);
936
+ }
937
+ }
938
+
939
+ .readme-toc-item.readme-toc-active .readme-toc-link {
940
+ color: var(--pgw-link);
941
+ font-weight: 600;
942
+ background-color: var(--pgw-accent-bg);
943
+ }
944
+
945
+ .readme-toc-level-1 { padding-left: 0; }
946
+ .readme-toc-level-2 { padding-left: 0.5rem; }
947
+ .readme-toc-level-3 { padding-left: 1rem; }
948
+ .readme-toc-level-4 { padding-left: 1.5rem; }
949
+ .readme-toc-level-5 { padding-left: 2rem; }
950
+ .readme-toc-level-6 { padding-left: 2.5rem; }
951
+
952
+ /* Board view: lift container max-width so columns can use full width; keep centered */
953
+ .container-xl:has(.board-columns) {
954
+ max-width: none;
955
+ margin-left: auto;
956
+ margin-right: auto;
957
+ }
958
+
959
+ /* Board view: center the column group when there's space; still scroll when many columns */
960
+ .board-columns {
961
+ justify-content: center;
962
+ }
963
+
964
+ /* Board view columns: use theme variables so they respect light/dark */
965
+ .board-column {
966
+ background-color: var(--pgw-bg-muted);
967
+ color: var(--pgw-text);
968
+ border-color: var(--pgw-border) !important;
969
+
970
+ .h6,
971
+ .text-muted {
972
+ color: var(--pgw-text-muted) !important;
973
+ }
974
+ }
975
+
976
+ /* Empty columns still accept drags over the card stack area */
977
+ .board-column-cards {
978
+ min-height: 3rem;
979
+ }
980
+
981
+ /*
982
+ * Board task error banner: Tabler .alert layout + PGW tokens so it tracks
983
+ * prefers-color-scheme (and matches .card / .table overrides that use --pgw-*).
984
+ */
985
+ .board-task-error-hint.alert {
986
+ --bs-alert-color: var(--pgw-warn-fg);
987
+ --bs-alert-bg: var(--pgw-warn-bg);
988
+ --bs-alert-border-color: var(--pgw-warn-border);
989
+ background-color: var(--pgw-warn-bg);
990
+ color: var(--pgw-warn-fg);
991
+ border: 1px solid var(--pgw-warn-border);
992
+ border-left: 0.25rem solid var(--pgw-warn-accent);
993
+ }
994
+
995
+ /* Board: Sync status icons (mask so currentColor is respected when loaded) */
996
+ .board-card-icon-wrap {
997
+ opacity: 0.7;
998
+ color: var(--pgw-text-muted);
999
+ }
1000
+
1001
+ .board-card-icon {
1002
+ display: inline-block;
1003
+ width: 1.25rem;
1004
+ height: 1.25rem;
1005
+ background-color: currentColor;
1006
+ -webkit-mask-image: url(/static/icons/cloud.svg);
1007
+ mask-image: url(/static/icons/cloud.svg);
1008
+ -webkit-mask-size: contain;
1009
+ mask-size: contain;
1010
+ -webkit-mask-repeat: no-repeat;
1011
+ mask-repeat: no-repeat;
1012
+ -webkit-mask-position: center;
1013
+ mask-position: center;
1014
+ }
1015
+ .board-card-icon[data-icon="cloud-up"] {
1016
+ -webkit-mask-image: url(/static/icons/cloud-up.svg);
1017
+ mask-image: url(/static/icons/cloud-up.svg);
1018
+ }
1019
+ .board-card-icon[data-icon="cloud-filled"] {
1020
+ -webkit-mask-image: url(/static/icons/cloud-filled.svg);
1021
+ mask-image: url(/static/icons/cloud-filled.svg);
1022
+ }
1023
+ .board-card-icon[data-icon="cloud-exclamation"] {
1024
+ -webkit-mask-image: url(/static/icons/cloud-exclamation.svg);
1025
+ mask-image: url(/static/icons/cloud-exclamation.svg);
1026
+ }
1027
+ .board-card .board-card-title {
1028
+ padding-right: 1.75rem;
1029
+ }
1030
+
1031
+ /* Board card popout: overlay and card on top of the board (above navbar and Bootstrap modals) */
1032
+ .board-card-popout-overlay {
1033
+ position: fixed;
1034
+ inset: 0;
1035
+ z-index: 1060;
1036
+ background-color: rgba(0, 0, 0, 0.35);
1037
+ display: flex;
1038
+ align-items: center;
1039
+ justify-content: center;
1040
+ padding: 1.5rem;
1041
+ box-sizing: border-box;
1042
+
1043
+ .board-card-popout {
1044
+ position: relative;
1045
+ z-index: 1061;
1046
+ width: 70%;
1047
+ max-width: 70%;
1048
+ height: 80%;
1049
+ max-height: 80%;
1050
+ display: flex;
1051
+ flex-direction: column;
1052
+ pointer-events: auto;
1053
+ box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.2);
1054
+ }
1055
+ }
1056
+
1057
+ /* Popout card: editable fields (click to edit) */
1058
+ .board-card-popout {
1059
+ .card-body {
1060
+ flex: 1;
1061
+ min-height: 0;
1062
+ overflow-y: auto;
1063
+ }
1064
+
1065
+ .board-card-editable,
1066
+ .board-card-title.board-card-editable {
1067
+ cursor: pointer;
1068
+ border-radius: 0.25rem;
1069
+ min-height: 1.5em;
1070
+
1071
+ &:hover {
1072
+ background-color: var(--pgw-bg-muted);
1073
+ }
1074
+ }
1075
+
1076
+ .board-card-title-edit,
1077
+ .board-card-description-edit,
1078
+ .board-card-due-date-edit,
1079
+ .board-card-status-edit,
1080
+ .board-card-priority-edit {
1081
+ min-width: 10rem;
1082
+ width: 100%;
1083
+ }
1084
+
1085
+ .board-card-description-edit {
1086
+ min-height: 4rem;
1087
+ resize: vertical;
1088
+ }
1089
+
1090
+ .board-card-comment-item {
1091
+ display: flex;
1092
+ gap: 0.5rem;
1093
+ align-items: flex-start;
1094
+ }
1095
+
1096
+ .board-card-comment-item + .board-card-comment-item {
1097
+ margin-top: 0.75rem;
1098
+ }
1099
+
1100
+ .board-card-comment-avatar {
1101
+ flex-shrink: 0;
1102
+ width: 2rem;
1103
+ height: 2rem;
1104
+ border-radius: 50%;
1105
+ overflow: hidden;
1106
+ display: flex;
1107
+ align-items: center;
1108
+ justify-content: center;
1109
+ background-color: var(--pgw-bg-muted);
1110
+ }
1111
+
1112
+ .board-card-comment-avatar img {
1113
+ width: 100%;
1114
+ height: 100%;
1115
+ object-fit: cover;
1116
+ }
1117
+
1118
+ .board-card-comment-avatar-fallback {
1119
+ font-size: 0.65rem;
1120
+ font-weight: 600;
1121
+ color: var(--pgw-text-muted);
1122
+ text-transform: uppercase;
1123
+ }
1124
+
1125
+ .board-card-comment-body {
1126
+ min-width: 0;
1127
+ flex: 1;
1128
+ }
1129
+
1130
+ .board-card-comment-author {
1131
+ font-weight: 500;
1132
+ color: var(--pgw-text);
1133
+ }
1134
+
1135
+ .board-card-description-preview {
1136
+ color: var(--pgw-text);
1137
+ }
1138
+
1139
+ .board-card-description-full.board-card-markdown,
1140
+ .board-card-comment-content.board-card-markdown {
1141
+ color: var(--pgw-text);
1142
+ word-break: break-word;
1143
+ }
1144
+
1145
+ .board-card-markdown.markdown-body {
1146
+ font-size: inherit;
1147
+ line-height: 1.5;
1148
+
1149
+ > :first-child {
1150
+ margin-top: 0;
1151
+ }
1152
+
1153
+ > :last-child {
1154
+ margin-bottom: 0;
1155
+ }
1156
+
1157
+ p {
1158
+ margin-bottom: 0.5rem;
1159
+ }
1160
+
1161
+ pre {
1162
+ margin-bottom: 0.5rem;
1163
+ }
1164
+ }
1165
+ }
1166
+
1167
+ /* Search page: input, status, and result lines */
1168
+ .search-status {
1169
+ font-style: italic;
1170
+ }
1171
+
1172
+ .search-results {
1173
+ display: flex;
1174
+ flex-direction: column;
1175
+ gap: 0.25rem;
1176
+
1177
+ &.is-loading {
1178
+ opacity: 0.6;
1179
+ }
1180
+ }
1181
+
1182
+ .search-result {
1183
+ background-color: var(--pgw-bg-raised);
1184
+ border: 1px solid var(--pgw-border);
1185
+ border-radius: 0.25rem;
1186
+ padding: 0.25rem 0.5rem;
1187
+
1188
+ &:hover {
1189
+ background-color: var(--pgw-table-hover);
1190
+ }
1191
+ }
1192
+
1193
+ .search-result-line {
1194
+ margin: 0;
1195
+ font-family: ui-monospace, "Fira Code", "Fira Mono", monospace;
1196
+ font-size: 0.85rem;
1197
+ line-height: 1.4;
1198
+ color: var(--pgw-text);
1199
+ white-space: pre-wrap;
1200
+ word-break: break-word;
1201
+ }
1202
+
1203
+ .subpage-frame {
1204
+ display: block;
1205
+ width: 100%;
1206
+ height: 82vh;
1207
+ min-height: 42rem;
1208
+ border: 1px solid var(--pgw-border);
1209
+ background-color: var(--pgw-bg-raised);
1210
+ }