@monster-orange/den 0.2.8 → 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 (127) hide show
  1. package/CHANGELOG.md +1547 -0
  2. package/README.md +4 -2
  3. package/bin/den +2 -1
  4. package/docs/distributions.md +507 -4
  5. package/make/den.mk +19 -4
  6. package/make/journeys.mk +2 -1
  7. package/make/local_dev.mk +6 -13
  8. package/make/secrets-1password.mk +18 -0
  9. package/make/test.mk +1 -1
  10. package/package.json +9 -2
  11. package/schemas/den.plugin.v1.json +156 -0
  12. package/schemas/den.theme.v1.json +269 -0
  13. package/scripts/auth-configure.ts +16 -8
  14. package/scripts/heal-config.ts +0 -5
  15. package/scripts/setup-1password.ts +41 -19
  16. package/scripts/setup-secrets.ts +67 -101
  17. package/src/pillars/api/global-setup.ts +1 -1
  18. package/src/pillars/journeys/sandbox.ts +23 -18
  19. package/src/pillars/journeys/tools/browser-session.ts +2 -2
  20. package/src/scripts/setup-default-workspace.ts +2 -2
  21. package/src/shared/auth/headless-login.ts +1 -1
  22. package/src/shared/auth/sudo-vault.ts +93 -31
  23. package/src/shared/auth/token-providers/index.ts +7 -3
  24. package/src/shared/brand.ts +7 -0
  25. package/src/shared/config-error.ts +33 -3
  26. package/src/shared/constants.ts +3 -24
  27. package/src/shared/den-config.ts +141 -5
  28. package/src/shared/edge/config.ts +147 -21
  29. package/src/shared/edge/exec.ts +31 -0
  30. package/src/shared/edge/included.ts +169 -0
  31. package/src/shared/edge/render.ts +69 -14
  32. package/src/shared/edge/runtime.ts +128 -121
  33. package/src/shared/edge/status.ts +68 -8
  34. package/src/shared/edge/tls.ts +328 -0
  35. package/src/shared/edge/types.ts +25 -9
  36. package/src/shared/environment-manager/default-workspace.ts +16 -1
  37. package/src/shared/environment-manager/health.ts +73 -38
  38. package/src/shared/environment-manager/scope.ts +168 -0
  39. package/src/shared/environment-manager/service-runner.ts +789 -43
  40. package/src/shared/environment-manager/up-targets.ts +132 -0
  41. package/src/shared/health/rollup.ts +48 -0
  42. package/src/shared/open-url.ts +21 -2
  43. package/src/shared/org-sources.ts +78 -59
  44. package/src/shared/org.ts +53 -5
  45. package/src/shared/plugins/manifest.ts +406 -0
  46. package/src/shared/plugins/store.ts +358 -0
  47. package/src/shared/process/legacy-adopt.ts +285 -0
  48. package/src/shared/process/process-group.ts +202 -0
  49. package/src/shared/process/spawn-streaming.ts +7 -0
  50. package/src/shared/secrets/doppler.ts +1 -1
  51. package/src/shared/secrets/index.ts +279 -50
  52. package/src/shared/secrets/none.ts +56 -0
  53. package/src/shared/secrets/strategies.ts +202 -0
  54. package/src/shared/secrets/types.ts +1 -1
  55. package/src/shared/smoke/git-remote.ts +40 -0
  56. package/src/shared/stacks/manifest-commands.ts +1 -1
  57. package/src/shared/stacks/namespace.ts +178 -0
  58. package/src/shared/stacks/owners.ts +303 -0
  59. package/src/shared/stacks/render.ts +8 -3
  60. package/src/shared/stacks/runtime.ts +100 -16
  61. package/src/shared/stacks/status.ts +63 -12
  62. package/src/shared/strategies/registry.ts +89 -0
  63. package/src/shared/theme/contract.ts +538 -0
  64. package/src/shared/types/index.ts +12 -3
  65. package/src/ui/cli/create.ts +24 -7
  66. package/src/ui/cli/doctor-command.ts +131 -0
  67. package/src/ui/cli/edge-command.ts +56 -10
  68. package/src/ui/cli/flag-parser.ts +7 -2
  69. package/src/ui/cli/health-display.ts +25 -0
  70. package/src/ui/cli/index.ts +548 -60
  71. package/src/ui/cli/notice.ts +20 -0
  72. package/src/ui/cli/plugin-command.ts +849 -0
  73. package/src/ui/cli/stack-command.ts +63 -2
  74. package/src/ui/cli/test-runner.ts +11 -9
  75. package/src/ui/cli/theme-command.ts +136 -0
  76. package/src/ui/cli/web-command.ts +47 -23
  77. package/src/ui/mcp/api.ts +28 -2
  78. package/src/ui/mcp/scope.ts +22 -1
  79. package/src/ui/mcp/tools.ts +50 -7
  80. package/src/ui/server/appearance-prefs.ts +95 -0
  81. package/src/ui/server/daemon-reaper.ts +29 -1
  82. package/src/ui/server/engine-stamp.ts +83 -0
  83. package/src/ui/server/github-service.ts +23 -44
  84. package/src/ui/server/graph-page.ts +20 -0
  85. package/src/ui/server/graph-prefs.ts +44 -1
  86. package/src/ui/server/http-errors.ts +293 -0
  87. package/src/ui/server/http-helpers.ts +22 -3
  88. package/src/ui/server/index.ts +532 -51
  89. package/src/ui/server/public/appearance.js +531 -0
  90. package/src/ui/server/public/edge-page.js +248 -0
  91. package/src/ui/server/public/edge.html +251 -0
  92. package/src/ui/server/public/environments.html +65 -83
  93. package/src/ui/server/public/error-page.js +247 -0
  94. package/src/ui/server/public/error.html +260 -0
  95. package/src/ui/server/public/graph-model.js +332 -14
  96. package/src/ui/server/public/graph.css +141 -47
  97. package/src/ui/server/public/graph.js +223 -44
  98. package/src/ui/server/public/header.js +396 -600
  99. package/src/ui/server/public/health-fit.js +248 -0
  100. package/src/ui/server/public/health-rollup.js +128 -0
  101. package/src/ui/server/public/i18n/es.js +287 -0
  102. package/src/ui/server/public/i18n/ja.js +276 -0
  103. package/src/ui/server/public/index.html +139 -438
  104. package/src/ui/server/public/local-dev.html +3468 -4044
  105. package/src/ui/server/public/logs.html +71 -208
  106. package/src/ui/server/public/panel.js +14 -2
  107. package/src/ui/server/public/release.html +175 -389
  108. package/src/ui/server/public/services.html +596 -770
  109. package/src/ui/server/public/settings-page.js +759 -0
  110. package/src/ui/server/public/settings-registry.js +521 -0
  111. package/src/ui/server/public/settings.html +590 -0
  112. package/src/ui/server/public/shell.js +3869 -0
  113. package/src/ui/server/public/topbar.js +1413 -0
  114. package/src/ui/server/public/version-label.js +12 -0
  115. package/src/ui/server/public/workspace-picker.js +74 -34
  116. package/src/ui/server/readonly-routes.ts +12 -1
  117. package/src/ui/server/services-list.ts +94 -17
  118. package/src/ui/server/settings-distribution.ts +74 -0
  119. package/src/ui/server/shell-prefs.ts +192 -0
  120. package/src/ui/server/theme-routes.ts +292 -0
  121. package/src/ui/server/theme-runtime.ts +407 -0
  122. package/src/ui/server/ui-assets.ts +16 -1
  123. package/src/ui/server/ui-language.ts +50 -0
  124. package/src/ui/server/workspace-routes.ts +45 -3
  125. package/themes/den/den-theme.json +72 -0
  126. package/themes/den/favicon.ico +0 -0
  127. package/themes/den/mark.svg +6 -0
@@ -0,0 +1,590 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <script>
7
+ // Mode before first paint: yours (the server fills it in below),
8
+ // else the system's, else the theme's default, else dark.
9
+ (function () {
10
+ var m = "@@DEN_MODE@@";
11
+ if (m !== "dark" && m !== "light") {
12
+ var q = function (s) {
13
+ return (
14
+ window.matchMedia &&
15
+ matchMedia("(prefers-color-scheme: " + s + ")").matches
16
+ );
17
+ };
18
+ m = q("light")
19
+ ? "light"
20
+ : q("dark")
21
+ ? "dark"
22
+ : "@@DEN_DEFAULT_MODE@@";
23
+ }
24
+ document.documentElement.dataset.mode =
25
+ m === "light" ? "light" : "dark";
26
+ })();
27
+ </script>
28
+ <link rel="stylesheet" href="/theme.css" id="theme-css" />
29
+ <title>__DEN_TITLE__ · Settings</title>
30
+ <style>
31
+ /* The Settings page (R21): the prototype's (den-shell.html) page
32
+ header, sections, rows, segmented buttons, swatches and navigation
33
+ list, ported as they are; --r is --radius, --r-btn is
34
+ --radius-button. The category list on the left has no prototype
35
+ counterpart: it is drawn like the rail's items (.ni). */
36
+ *,
37
+ *::before,
38
+ *::after {
39
+ box-sizing: border-box;
40
+ }
41
+ html,
42
+ body {
43
+ height: 100%;
44
+ margin: 0;
45
+ background: var(--bg);
46
+ color: var(--text);
47
+ }
48
+ body {
49
+ display: flex;
50
+ flex-direction: column;
51
+ overflow: hidden;
52
+ }
53
+ button,
54
+ input,
55
+ select {
56
+ font: inherit;
57
+ color: inherit;
58
+ }
59
+ /* .main */
60
+ html body.den-has-shell > #den-content {
61
+ overflow: auto;
62
+ min-width: 0;
63
+ padding: 20px 24px 32px 24px;
64
+ font: 14px/1.45 var(--sans);
65
+ color: var(--text);
66
+ display: block;
67
+ }
68
+ #den-content :focus-visible {
69
+ outline: 2px solid var(--accent);
70
+ outline-offset: 2px;
71
+ }
72
+ /* .ph: the page header */
73
+ .ph {
74
+ display: flex;
75
+ align-items: flex-end;
76
+ gap: 16px;
77
+ flex-wrap: wrap;
78
+ margin-bottom: 16px;
79
+ }
80
+ .ph h1 {
81
+ font: 600 22px/1.2 var(--display);
82
+ margin: 0;
83
+ text-wrap: balance;
84
+ }
85
+ .ph .sub {
86
+ color: var(--muted);
87
+ font-size: 13px;
88
+ margin-top: 4px;
89
+ }
90
+ /* R21a (Ward QA 2026-11-05): the header with its search stays put
91
+ while the rows scroll under it; so does the category list, below
92
+ it (--set-top: the header's height, set by the page). */
93
+ .ph {
94
+ position: sticky;
95
+ top: 0;
96
+ z-index: 4;
97
+ background: var(--bg);
98
+ box-shadow: 0 -20px 0 0 var(--bg), 0 12px 0 0 var(--bg);
99
+ }
100
+ .ph .act {
101
+ margin-left: auto;
102
+ display: flex;
103
+ gap: 8px;
104
+ align-items: center;
105
+ }
106
+ .ph input[type="search"] {
107
+ border: 1px solid var(--border);
108
+ background: var(--surface);
109
+ border-radius: var(--radius-button);
110
+ padding: 6px 10px;
111
+ min-width: 0;
112
+ width: 220px;
113
+ color: var(--text);
114
+ }
115
+ .set-count {
116
+ margin: -8px 0 12px;
117
+ min-height: 0;
118
+ color: var(--muted);
119
+ font-size: 13px;
120
+ }
121
+ .set-count:empty {
122
+ display: none;
123
+ }
124
+ .den-sr {
125
+ position: absolute;
126
+ width: 1px;
127
+ height: 1px;
128
+ padding: 0;
129
+ overflow: hidden;
130
+ clip: rect(0 0 0 0);
131
+ white-space: nowrap;
132
+ border: 0;
133
+ }
134
+ kbd {
135
+ font: 11px var(--mono);
136
+ border: 1px solid var(--border);
137
+ border-bottom-width: 2px;
138
+ border-radius: 4px;
139
+ padding: 0 5px;
140
+ color: var(--muted);
141
+ background: var(--surface);
142
+ }
143
+ /* Categories | rows */
144
+ .set-layout {
145
+ display: grid;
146
+ grid-template-columns: 180px minmax(0, 760px);
147
+ gap: 24px;
148
+ align-items: start;
149
+ }
150
+ #set-body section[data-cat] {
151
+ scroll-margin-top: var(--set-top, 0px);
152
+ }
153
+ .set-nav {
154
+ position: sticky;
155
+ top: var(--set-top, 0px);
156
+ display: grid;
157
+ gap: 2px;
158
+ }
159
+ /* drawn like the rail's .ni */
160
+ .set-ni {
161
+ display: flex;
162
+ align-items: center;
163
+ gap: 8px;
164
+ padding: 6px 10px;
165
+ border-radius: var(--radius-button);
166
+ color: var(--muted);
167
+ text-decoration: none;
168
+ position: relative;
169
+ }
170
+ .set-ni:hover {
171
+ background: var(--sunken);
172
+ color: var(--text);
173
+ }
174
+ .set-ni[aria-current="true"] {
175
+ background: color-mix(in srgb, var(--accent) 13%, transparent);
176
+ color: var(--text);
177
+ font-weight: 500;
178
+ }
179
+ .set-ni-l {
180
+ flex: 1;
181
+ min-width: 0;
182
+ overflow: hidden;
183
+ text-overflow: ellipsis;
184
+ white-space: nowrap;
185
+ }
186
+ .den-bdg {
187
+ font: 11px var(--mono);
188
+ padding: 0 6px;
189
+ border-radius: 999px;
190
+ background: var(--sunken);
191
+ color: var(--muted);
192
+ font-variant-numeric: tabular-nums;
193
+ }
194
+ .den-tagx {
195
+ font: 10px var(--mono);
196
+ color: var(--faint);
197
+ border: 1px dashed var(--border-strong);
198
+ border-radius: 4px;
199
+ padding: 0 4px;
200
+ font-weight: 400;
201
+ vertical-align: middle;
202
+ }
203
+ /* .set: sections */
204
+ .set {
205
+ display: grid;
206
+ gap: 22px;
207
+ max-width: 760px;
208
+ min-width: 0;
209
+ }
210
+ .set section {
211
+ background: var(--surface);
212
+ border: 1px solid var(--border);
213
+ border-radius: var(--radius);
214
+ padding: 16px;
215
+ scroll-margin-top: 8px;
216
+ }
217
+ .set h3 {
218
+ margin: 0 0 4px;
219
+ font: 600 15px var(--display);
220
+ }
221
+ .set h3:focus {
222
+ outline: none;
223
+ }
224
+ .set p {
225
+ margin: 0 0 12px;
226
+ color: var(--muted);
227
+ font-size: 13px;
228
+ }
229
+ /* .seg: segmented buttons */
230
+ .seg {
231
+ display: inline-flex;
232
+ border: 1px solid var(--border);
233
+ border-radius: var(--radius-button);
234
+ overflow: hidden;
235
+ flex-wrap: wrap;
236
+ }
237
+ .seg button {
238
+ background: none;
239
+ border: 0;
240
+ padding: 6px 12px;
241
+ cursor: pointer;
242
+ color: var(--muted);
243
+ }
244
+ .seg button[aria-pressed="true"] {
245
+ background: color-mix(in srgb, var(--accent) 16%, transparent);
246
+ color: var(--text);
247
+ font-weight: 500;
248
+ }
249
+ /* .row: label | control */
250
+ .set-row + .set-row {
251
+ border-top: 1px solid var(--border);
252
+ margin-top: 6px;
253
+ padding-top: 6px;
254
+ }
255
+ .row {
256
+ display: flex;
257
+ align-items: center;
258
+ gap: 12px;
259
+ flex-wrap: wrap;
260
+ padding: 6px 0;
261
+ }
262
+ .row .l {
263
+ width: 120px;
264
+ color: var(--muted);
265
+ font-size: 13px;
266
+ align-self: flex-start;
267
+ padding-top: 6px;
268
+ }
269
+ .row .ctl {
270
+ flex: 1 1 300px;
271
+ min-width: 0;
272
+ display: flex;
273
+ flex-wrap: wrap;
274
+ align-items: center;
275
+ gap: 8px;
276
+ }
277
+ .set-scope {
278
+ font: 10px var(--mono);
279
+ color: var(--faint);
280
+ border: 1px dashed var(--border-strong);
281
+ border-radius: 4px;
282
+ padding: 0 4px;
283
+ align-self: flex-start;
284
+ margin-top: 8px;
285
+ white-space: nowrap;
286
+ }
287
+ .set-scope[data-scope="workspace"] {
288
+ color: var(--accent-text);
289
+ border-color: color-mix(in srgb, var(--accent) 40%, transparent);
290
+ }
291
+ .set-row .d {
292
+ color: var(--muted);
293
+ font-size: 12.5px;
294
+ padding: 0 0 4px 132px;
295
+ }
296
+ .set-note {
297
+ color: var(--muted);
298
+ font-size: 12.5px;
299
+ }
300
+ .set-warn {
301
+ flex-basis: 100%;
302
+ margin: 4px 0 0 !important;
303
+ color: var(--warn) !important;
304
+ }
305
+ .set-saved {
306
+ display: block;
307
+ padding-left: 132px;
308
+ font-size: 12.5px;
309
+ }
310
+ .set-saved:empty {
311
+ display: none;
312
+ }
313
+ .set-saved.ok {
314
+ color: var(--ok);
315
+ }
316
+ .set-saved.bad {
317
+ color: var(--bad);
318
+ }
319
+ .set-ro {
320
+ color: var(--text);
321
+ padding: 6px 0;
322
+ overflow-wrap: anywhere;
323
+ }
324
+ .set-radios {
325
+ display: grid;
326
+ gap: 2px;
327
+ flex: 1;
328
+ }
329
+ .set-radio {
330
+ display: flex;
331
+ align-items: center;
332
+ gap: 8px;
333
+ padding: 5px 8px;
334
+ border-radius: var(--radius-button);
335
+ cursor: pointer;
336
+ }
337
+ .set-radio:hover {
338
+ background: var(--sunken);
339
+ }
340
+ .set-radio.off {
341
+ opacity: 0.5;
342
+ cursor: default;
343
+ }
344
+ .set-radio input {
345
+ accent-color: var(--accent);
346
+ margin: 0;
347
+ }
348
+ .set-tag {
349
+ font: 11px var(--mono);
350
+ color: var(--muted);
351
+ border: 1px solid var(--border);
352
+ border-radius: 999px;
353
+ padding: 0 6px;
354
+ }
355
+ .set-langs {
356
+ display: grid;
357
+ gap: 4px;
358
+ flex: 1;
359
+ }
360
+ .set-lang {
361
+ display: flex;
362
+ align-items: center;
363
+ gap: 10px;
364
+ }
365
+ .set-lang label {
366
+ width: 110px;
367
+ font-size: 13px;
368
+ }
369
+ select,
370
+ input[type="number"],
371
+ input[type="text"] {
372
+ border: 1px solid var(--border);
373
+ background: var(--surface);
374
+ border-radius: var(--radius-button);
375
+ padding: 5px 8px;
376
+ color: var(--text);
377
+ }
378
+ input[type="number"] {
379
+ width: 100px;
380
+ }
381
+ .set-check {
382
+ width: 16px;
383
+ height: 16px;
384
+ accent-color: var(--accent);
385
+ margin: 6px 0;
386
+ }
387
+ .set-unit {
388
+ color: var(--muted);
389
+ font-size: 13px;
390
+ }
391
+ /* .btn */
392
+ .btn {
393
+ border: 1px solid var(--border);
394
+ background: var(--surface);
395
+ border-radius: var(--radius-button);
396
+ padding: 6px 12px;
397
+ cursor: pointer;
398
+ font-weight: 500;
399
+ white-space: nowrap;
400
+ }
401
+ .btn:hover {
402
+ border-color: var(--border-strong);
403
+ }
404
+ .btn:disabled {
405
+ opacity: 0.45;
406
+ cursor: default;
407
+ }
408
+ .btn.primary {
409
+ background: var(--accent-fill);
410
+ border-color: var(--accent-fill);
411
+ color: var(--on-accent);
412
+ }
413
+ .btn.sm {
414
+ padding: 2px 9px;
415
+ font-size: 12.5px;
416
+ }
417
+ .set-save {
418
+ margin-top: 6px;
419
+ }
420
+ /* .navlist: Navigation's pages and pins */
421
+ .navlist {
422
+ display: grid;
423
+ gap: 4px;
424
+ flex: 1;
425
+ }
426
+ .navlist .it {
427
+ display: flex;
428
+ align-items: center;
429
+ gap: 10px;
430
+ padding: 6px 8px;
431
+ border: 1px solid var(--border);
432
+ border-radius: var(--radius-button);
433
+ background: var(--bg);
434
+ }
435
+ .navlist .it .nm {
436
+ flex: 1;
437
+ min-width: 0;
438
+ }
439
+ .navlist .it.off .nm {
440
+ color: var(--faint);
441
+ text-decoration: line-through;
442
+ }
443
+ .set-grp {
444
+ color: var(--faint);
445
+ font-size: 12px;
446
+ }
447
+ .den-mono {
448
+ font-family: var(--mono);
449
+ font-size: 12.5px;
450
+ }
451
+ /* .swatches */
452
+ .swatches {
453
+ display: flex;
454
+ gap: 6px;
455
+ flex-wrap: wrap;
456
+ margin-top: 10px;
457
+ padding-bottom: 14px;
458
+ flex-basis: 100%;
459
+ }
460
+ .sw {
461
+ /* The prototype's swatch is content-box: 34px plus its border. */
462
+ box-sizing: content-box;
463
+ width: 34px;
464
+ height: 34px;
465
+ border-radius: 6px;
466
+ border: 1px solid var(--border);
467
+ position: relative;
468
+ }
469
+ .sw span {
470
+ position: absolute;
471
+ top: 38px;
472
+ left: 0;
473
+ font: 9px var(--mono);
474
+ color: var(--faint);
475
+ white-space: nowrap;
476
+ }
477
+ /* .empty */
478
+ .empty {
479
+ border: 1px dashed var(--border-strong);
480
+ border-radius: var(--radius);
481
+ padding: 28px;
482
+ color: var(--muted);
483
+ max-width: 640px;
484
+ }
485
+ .empty b {
486
+ color: var(--text);
487
+ }
488
+ @media (max-width: 860px) {
489
+ .set-layout {
490
+ grid-template-columns: minmax(0, 1fr);
491
+ }
492
+ .set-nav {
493
+ position: static;
494
+ display: flex;
495
+ overflow-x: auto;
496
+ }
497
+ .set-row .d,
498
+ .set-saved {
499
+ padding-left: 0;
500
+ }
501
+ }
502
+ @media (max-width: 720px) {
503
+ html body.den-has-shell > #den-content {
504
+ padding: 16px;
505
+ }
506
+ .ph input[type="search"] {
507
+ width: 100%;
508
+ }
509
+ .ph .act {
510
+ margin-left: 0;
511
+ flex: 1 1 100%;
512
+ }
513
+ }
514
+ </style>
515
+ </head>
516
+ <body>
517
+ <!-- The shell: top bar and rail, the same on every page (shell.js). -->
518
+ <div id="den-shell" data-page="settings"></div>
519
+ <script src="/shell.js"></script>
520
+
521
+ <!-- The page's one main landmark: everything after the shell. -->
522
+ <main id="den-content">
523
+ <div class="ph">
524
+ <div>
525
+ <h1>Settings</h1>
526
+ <div class="sub">
527
+ Saved to your __DEN_TITLE__ preferences and remembered on every page
528
+ </div>
529
+ </div>
530
+ <div class="act" role="search">
531
+ <label class="den-sr" for="set-q">Search settings</label>
532
+ <input
533
+ type="search"
534
+ id="set-q"
535
+ placeholder="Search settings"
536
+ autocomplete="off"
537
+ aria-keyshortcuts="/"
538
+ />
539
+ <kbd aria-hidden="true" title="Press / to search">/</kbd>
540
+ </div>
541
+ </div>
542
+ <p class="set-count" id="set-count" aria-live="polite"></p>
543
+ <div class="set-layout">
544
+ <nav
545
+ class="set-nav"
546
+ id="set-nav"
547
+ aria-label="Settings categories"
548
+ ></nav>
549
+ <div class="set" id="set-body"></div>
550
+ </div>
551
+
552
+ <script type="module">
553
+ import {
554
+ applyDenConfig,
555
+ initChangelog,
556
+ initHealth,
557
+ initLogBadge,
558
+ initWorkspacePill,
559
+ } from "/header.js";
560
+ import {
561
+ appearanceState,
562
+ ensureAppearance,
563
+ setAppearance,
564
+ } from "/appearance.js";
565
+ import { registerDenSettings } from "/settings-registry.js";
566
+ import { mountSettings } from "/settings-page.js";
567
+ void Promise.all([
568
+ initHealth(),
569
+ initWorkspacePill(),
570
+ applyDenConfig(),
571
+ initChangelog(),
572
+ ]).then(initLogBadge);
573
+ // R21a: the category list sticks just below the sticky header.
574
+ const ph = document.querySelector("#den-content > .ph");
575
+ const setTop = () =>
576
+ document
577
+ .getElementById("den-content")
578
+ .style.setProperty("--set-top", `${ph.offsetHeight + 16}px`);
579
+ setTop();
580
+ if (typeof ResizeObserver === "function")
581
+ new ResizeObserver(setTop).observe(ph);
582
+ registerDenSettings(window.denShell);
583
+ void mountSettings({
584
+ shell: window.denShell,
585
+ appearance: { appearanceState, ensureAppearance, setAppearance },
586
+ });
587
+ </script>
588
+ </main>
589
+ </body>
590
+ </html>