@chrisdudek/yg 5.4.0 → 5.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,278 @@
1
+ /*
2
+ * Component styles — the count header, the tree, the node panel, the glossary tooltip,
3
+ * and the command-palette overlay.
4
+ *
5
+ * The reusable surface pieces the views compose. Tokens come from tokens.css; the shell
6
+ * frame from shell.css. Calm, dense, document-like; the only saturated color is a state
7
+ * (resolved through the state classes in tokens.css). This file stays focused on the
8
+ * in-stage components and the ⌘K overlay.
9
+ */
10
+
11
+ /* ── Count header + chips ───────────────────────────────────────────────── */
12
+ .stage-header {
13
+ border-bottom: 1px solid var(--border);
14
+ padding-bottom: 16px;
15
+ margin-bottom: 18px;
16
+ }
17
+ .stage-title {
18
+ font-size: 24px;
19
+ line-height: 32px;
20
+ font-weight: 600;
21
+ margin: 0 0 4px;
22
+ letter-spacing: -0.01em;
23
+ }
24
+ .stage-sub {
25
+ color: var(--text-secondary);
26
+ font-size: 12.5px;
27
+ margin: 0 0 16px;
28
+ }
29
+ .count-bar {
30
+ display: flex;
31
+ flex-wrap: wrap;
32
+ gap: 8px;
33
+ }
34
+ .count-chip {
35
+ display: inline-flex;
36
+ align-items: center;
37
+ gap: 6px;
38
+ padding: 4px 10px;
39
+ border-radius: 7px;
40
+ border: 1px solid var(--border-subtle);
41
+ background: var(--surface-1);
42
+ font-size: 13px;
43
+ }
44
+ .chip-count {
45
+ font-weight: 600;
46
+ }
47
+ .chip-label {
48
+ color: var(--text-secondary);
49
+ }
50
+
51
+ .stage-intro {
52
+ margin-bottom: 14px;
53
+ }
54
+ .stage-notfound {
55
+ margin: 10px 0 0;
56
+ padding: 8px 12px;
57
+ border: 1px solid var(--border);
58
+ border-left: 3px solid var(--unverified);
59
+ border-radius: 8px;
60
+ background: var(--surface-1);
61
+ color: var(--text-secondary);
62
+ font-size: 13px;
63
+ }
64
+ .stage-vtitle {
65
+ font-size: 16px;
66
+ margin: 0 0 2px;
67
+ }
68
+ .stage-vblurb {
69
+ margin: 0;
70
+ color: var(--text-secondary);
71
+ font-size: 13px;
72
+ }
73
+ .stage-note {
74
+ color: var(--text-muted);
75
+ font-size: 12.5px;
76
+ border: 1px dashed var(--border);
77
+ border-radius: 9px;
78
+ padding: 10px 12px;
79
+ margin: 4px 0 18px;
80
+ }
81
+
82
+ /* ── Tree (virtualized) ─────────────────────────────────────────────────── */
83
+ .tree-filter {
84
+ margin: 4px 0 8px;
85
+ }
86
+ .tree-filter-input {
87
+ width: 100%;
88
+ padding: 7px 11px;
89
+ border: 1px solid var(--border);
90
+ border-radius: 8px;
91
+ background: var(--bg);
92
+ color: var(--text-primary);
93
+ font: inherit;
94
+ font-size: 13px;
95
+ }
96
+ /* "You are here" marker for a row revealed by a palette pick / deep-link. */
97
+ .tree-row-revealed {
98
+ background: var(--surface-3);
99
+ box-shadow: inset 3px 0 0 var(--accent);
100
+ }
101
+ .tree-mount {
102
+ margin: 4px 0 18px;
103
+ }
104
+ .tree-scroller {
105
+ position: relative;
106
+ height: 60vh;
107
+ min-height: 280px;
108
+ overflow: auto;
109
+ border: 1px solid var(--border);
110
+ border-radius: 10px;
111
+ background: var(--surface-1);
112
+ }
113
+ .tree-spacer {
114
+ position: relative;
115
+ width: 100%;
116
+ }
117
+ .tree-window {
118
+ position: absolute;
119
+ top: 0;
120
+ left: 0;
121
+ right: 0;
122
+ will-change: transform;
123
+ }
124
+ .tree-row {
125
+ display: flex;
126
+ align-items: center;
127
+ gap: 9px;
128
+ height: 28px;
129
+ padding-right: 12px;
130
+ border-bottom: 1px solid var(--surface-2);
131
+ font-size: 13px;
132
+ cursor: pointer;
133
+ }
134
+ .tree-row:hover {
135
+ background: var(--surface-3);
136
+ }
137
+ .tree-name {
138
+ font-weight: 500;
139
+ }
140
+ .tree-type {
141
+ margin-left: auto;
142
+ color: var(--text-muted);
143
+ font-size: 11px;
144
+ }
145
+
146
+ /* ── Node panel ─────────────────────────────────────────────────────────── */
147
+ .panel-title {
148
+ font-size: 13px;
149
+ margin: 0 0 10px;
150
+ font-family: 'JetBrains Mono', ui-monospace, monospace;
151
+ word-break: break-all;
152
+ }
153
+ .panel-head {
154
+ display: flex;
155
+ align-items: center;
156
+ gap: 9px;
157
+ font-size: 13px;
158
+ margin-bottom: 8px;
159
+ }
160
+ .panel-sub {
161
+ color: var(--text-secondary);
162
+ font-size: 12.5px;
163
+ margin: 0;
164
+ }
165
+
166
+ /* ── Glossary term tooltip ──────────────────────────────────────────────── */
167
+ .term {
168
+ border-bottom: 1px dotted var(--border-strong);
169
+ cursor: help;
170
+ }
171
+
172
+ /* ── Command palette overlay ────────────────────────────────────────────── */
173
+ .palette-backdrop {
174
+ position: fixed;
175
+ inset: 0;
176
+ display: flex;
177
+ align-items: flex-start;
178
+ justify-content: center;
179
+ padding-top: 60px;
180
+ background: rgba(0, 0, 0, 0.32);
181
+ z-index: 50;
182
+ }
183
+ .palette-box {
184
+ width: 640px;
185
+ max-width: 92vw;
186
+ background: var(--surface-1);
187
+ border: 1px solid var(--border);
188
+ border-radius: 14px;
189
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04), 0 16px 40px -10px rgba(0, 0, 0, 0.45);
190
+ overflow: hidden;
191
+ }
192
+ .palette-input {
193
+ display: flex;
194
+ align-items: center;
195
+ gap: 11px;
196
+ padding: 13px 16px;
197
+ border-bottom: 1px solid var(--border-subtle);
198
+ }
199
+ .palette-search-ic {
200
+ color: var(--text-muted);
201
+ }
202
+ .palette-field {
203
+ flex: 1;
204
+ border: 0;
205
+ background: none;
206
+ color: var(--text-primary);
207
+ font: inherit;
208
+ font-size: 16px;
209
+ }
210
+ .palette-field:focus {
211
+ outline: none;
212
+ }
213
+ .palette-results {
214
+ max-height: 380px;
215
+ overflow: auto;
216
+ padding: 6px;
217
+ }
218
+ .palette-empty {
219
+ padding: 16px;
220
+ color: var(--text-muted);
221
+ font-size: 13px;
222
+ }
223
+ .palette-row {
224
+ display: flex;
225
+ align-items: center;
226
+ gap: 11px;
227
+ padding: 8px 12px;
228
+ border-radius: 8px;
229
+ font-size: 13.5px;
230
+ cursor: pointer;
231
+ }
232
+ .palette-row.on {
233
+ background: var(--surface-3);
234
+ }
235
+ .palette-ic {
236
+ width: 18px;
237
+ text-align: center;
238
+ color: var(--text-muted);
239
+ }
240
+ .palette-nm {
241
+ font-weight: 500;
242
+ }
243
+ .palette-sub {
244
+ color: var(--text-muted);
245
+ font-size: 11.5px;
246
+ }
247
+ .palette-kind {
248
+ margin-left: auto;
249
+ font-size: 10px;
250
+ color: var(--text-muted);
251
+ border: 1px solid var(--border-subtle);
252
+ border-radius: 5px;
253
+ padding: 0 6px;
254
+ }
255
+ .palette-foot {
256
+ display: flex;
257
+ gap: 16px;
258
+ padding: 9px 14px;
259
+ border-top: 1px solid var(--border-subtle);
260
+ font-size: 11px;
261
+ color: var(--text-muted);
262
+ }
263
+
264
+ .portal-error {
265
+ color: var(--refused);
266
+ padding: 24px;
267
+ font-size: 14px;
268
+ }
269
+
270
+ @media (prefers-reduced-motion: reduce) {
271
+ *,
272
+ *::before,
273
+ *::after {
274
+ transition: none !important;
275
+ animation: none !important;
276
+ scroll-behavior: auto !important;
277
+ }
278
+ }
@@ -0,0 +1,257 @@
1
+ /*
2
+ * The three-region shell layout — nav rail, top bar, center stage, node panel.
3
+ *
4
+ * The persistent chrome the portal lives in: a left navigation rail (brand, ⌘K trigger,
5
+ * grouped view links), a top bar (live status + Refresh/Approve cluster, breadcrumb, ⌘K
6
+ * search, theme toggle), the center stage the dispatcher renders into, and a right-hand
7
+ * node-panel slot that opens on selection. Border-first elevation; one accent voltage
8
+ * (the primary button + the active rail link). Tokens come from tokens.css.
9
+ */
10
+
11
+ .app-grid {
12
+ display: grid;
13
+ grid-template-columns: 248px 1fr 0;
14
+ min-height: 100vh;
15
+ }
16
+ .app-grid:has(.app-panel.open) {
17
+ grid-template-columns: 248px 1fr 420px;
18
+ }
19
+ .app-rail {
20
+ border-right: 1px solid var(--border-subtle);
21
+ background: var(--surface-1);
22
+ min-width: 0;
23
+ /* Pinned: the rail stays visible on scroll with its own overflow, so the tabs never
24
+ scroll away. The shell grid has min-height:100vh and the page/body scrolls. */
25
+ position: sticky;
26
+ top: 0;
27
+ align-self: start;
28
+ height: 100vh;
29
+ overflow-y: auto;
30
+ }
31
+ .rail-brand {
32
+ display: flex;
33
+ align-items: center;
34
+ gap: 9px;
35
+ width: 100%;
36
+ padding: 14px 16px;
37
+ border: 0;
38
+ border-bottom: 1px solid var(--border-subtle);
39
+ background: none;
40
+ color: inherit;
41
+ font: inherit;
42
+ cursor: pointer;
43
+ text-align: left;
44
+ }
45
+ .rail-logo {
46
+ width: 36px;
47
+ height: 36px;
48
+ border-radius: 8px;
49
+ /* A deep chip so the gold brand mark reads on either theme; the SVG sits inside, contained.
50
+ A soft drop shadow + a hairline seat the chip on the near-white light rail (so it does not
51
+ read as a flat tile pasted onto the chrome) while staying unobtrusive in the dark theme. */
52
+ background: linear-gradient(160deg, #20242c, #15171b);
53
+ object-fit: contain;
54
+ padding: 4px;
55
+ flex: none;
56
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.22);
57
+ border: 1px solid rgba(255, 255, 255, 0.06);
58
+ }
59
+ .rail-brand b {
60
+ font-weight: 600;
61
+ }
62
+ .rail-brand-sub {
63
+ margin-left: auto;
64
+ font-size: 11px;
65
+ color: var(--text-muted);
66
+ }
67
+ .rail-cmdk {
68
+ display: flex;
69
+ align-items: center;
70
+ gap: 8px;
71
+ width: calc(100% - 28px);
72
+ margin: 12px 14px;
73
+ padding: 7px 10px;
74
+ border: 1px solid var(--border);
75
+ border-radius: 8px;
76
+ background: var(--bg);
77
+ color: var(--text-muted);
78
+ font: inherit;
79
+ font-size: 13px;
80
+ cursor: pointer;
81
+ }
82
+ .rail-cmdk kbd {
83
+ margin-left: auto;
84
+ font-size: 11px;
85
+ border: 1px solid var(--border);
86
+ border-radius: 5px;
87
+ padding: 1px 6px;
88
+ color: var(--text-secondary);
89
+ }
90
+ .rail-h {
91
+ padding: 6px 16px;
92
+ font-size: 11px;
93
+ text-transform: uppercase;
94
+ letter-spacing: 0.6px;
95
+ color: var(--text-muted);
96
+ }
97
+ .rail-nav {
98
+ display: flex;
99
+ flex-direction: column;
100
+ }
101
+ .rail-link {
102
+ display: flex;
103
+ align-items: center;
104
+ gap: 9px;
105
+ padding: 6px 16px;
106
+ border: 0;
107
+ border-left: 2px solid transparent;
108
+ background: none;
109
+ color: var(--text-secondary);
110
+ font: inherit;
111
+ font-size: 13px;
112
+ cursor: pointer;
113
+ text-align: left;
114
+ }
115
+ .rail-link:hover {
116
+ background: var(--surface-2);
117
+ }
118
+ .rail-link.on {
119
+ color: var(--text-primary);
120
+ background: var(--surface-3);
121
+ border-left-color: var(--accent);
122
+ }
123
+ .rail-glyph {
124
+ width: 16px;
125
+ text-align: center;
126
+ color: var(--text-muted);
127
+ }
128
+
129
+ .app-main {
130
+ min-width: 0;
131
+ }
132
+ .app-topbar {
133
+ display: flex;
134
+ align-items: center;
135
+ gap: 12px;
136
+ padding: 10px 22px;
137
+ border-bottom: 1px solid var(--border-subtle);
138
+ /* Pinned to the top of the scroll, above the stage, with a solid background so the
139
+ content scrolling beneath it never shows through. */
140
+ position: sticky;
141
+ top: 0;
142
+ z-index: 20;
143
+ background: var(--bg);
144
+ }
145
+ .topbar-live {
146
+ display: inline-flex;
147
+ align-items: center;
148
+ gap: 6px;
149
+ font-size: 12px;
150
+ color: var(--text-muted);
151
+ }
152
+ .topbar-dot {
153
+ width: 7px;
154
+ height: 7px;
155
+ border-radius: 50%;
156
+ background: var(--verified);
157
+ }
158
+ .topbar-dot.muted {
159
+ background: var(--text-muted);
160
+ }
161
+ .topbar-crumb {
162
+ font-size: 12px;
163
+ color: var(--text-secondary);
164
+ }
165
+ .topbar-spacer {
166
+ flex: 1;
167
+ }
168
+ .btn {
169
+ display: inline-flex;
170
+ align-items: center;
171
+ gap: 7px;
172
+ border: 1px solid var(--border);
173
+ border-radius: 9px;
174
+ padding: 7px 12px;
175
+ font: inherit;
176
+ font-size: 13px;
177
+ background: var(--bg);
178
+ color: var(--text-primary);
179
+ cursor: pointer;
180
+ }
181
+ .btn:hover:not(:disabled) {
182
+ background: var(--surface-2);
183
+ }
184
+ .btn:disabled {
185
+ opacity: 0.5;
186
+ cursor: not-allowed;
187
+ }
188
+ .btn.primary {
189
+ background: var(--accent);
190
+ border-color: var(--accent);
191
+ color: #fff;
192
+ font-weight: 600;
193
+ }
194
+ .btn.primary:hover:not(:disabled) {
195
+ background: var(--accent-hover);
196
+ }
197
+ .btn-free {
198
+ font-size: 10px;
199
+ color: var(--verified);
200
+ }
201
+
202
+ .app-stage {
203
+ /* The bottom padding clears the pinned honest-key bar (see .legend-bar) so the last
204
+ row of content is never hidden behind it. */
205
+ padding: 20px 22px 96px;
206
+ /* Use the available width: wide surfaces (matrix, tree, tables) get the room they need.
207
+ A generous cap keeps long prose readable; margin-inline:auto centers it so the stage is
208
+ never a left-clipped column. */
209
+ max-width: 1600px;
210
+ margin-inline: auto;
211
+ width: 100%;
212
+ }
213
+ .app-panel {
214
+ background: var(--surface-1);
215
+ padding: 0;
216
+ /* The panel is its own scroll column: pinned full-height with its own overflow so a long
217
+ refusal reason or folded-input drill-through scrolls inside the panel instead of being
218
+ clipped. Closed, it is a zero-width slot with NO border — a left border on a 0-width grid
219
+ column would otherwise push the page 1px wider than the viewport (a spurious h-scrollbar). */
220
+ position: sticky;
221
+ top: 0;
222
+ align-self: start;
223
+ height: 100vh;
224
+ overflow-y: auto;
225
+ }
226
+ .app-panel.open {
227
+ border-left: 1px solid var(--border-subtle);
228
+ padding: 18px 18px 28px;
229
+ }
230
+
231
+ /* ── Narrow / split-screen desktop (≤900px): keep the layout overflow- and collision-free ──
232
+ The node panel becomes a right overlay SHEET instead of a reserved grid column (so it never
233
+ steals layout width or collides with the top bar), and the top bar drops its verbose live
234
+ caption so the action buttons never wrap or clip. A phone-width layout is out of scope. */
235
+ @media (max-width: 1000px) {
236
+ .app-grid:has(.app-panel.open) {
237
+ grid-template-columns: 248px 1fr 0;
238
+ }
239
+ .app-panel.open {
240
+ position: fixed;
241
+ top: 0;
242
+ right: 0;
243
+ width: min(420px, 92vw);
244
+ height: 100vh;
245
+ z-index: 40;
246
+ box-shadow: -6px 0 22px -6px rgba(0, 0, 0, 0.3);
247
+ }
248
+ .topbar-live span:not(.topbar-dot) {
249
+ display: none;
250
+ }
251
+ .topbar-crumb {
252
+ overflow: hidden;
253
+ text-overflow: ellipsis;
254
+ white-space: nowrap;
255
+ min-width: 0;
256
+ }
257
+ }
@@ -0,0 +1,39 @@
1
+ <!doctype html>
2
+ <html lang="en" data-theme="light">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Yggdrasil Portal</title>
7
+ <!-- The brand mark, inlined as a data: URI by the serializer (offline, no network). -->
8
+ <link rel="icon" type="image/svg+xml" href="__PORTAL_FAVICON_HREF__" />
9
+ <style>
10
+ /* __PORTAL_CSS__ */
11
+ </style>
12
+ </head>
13
+ <body>
14
+ <main id="portal-root" role="main"></main>
15
+
16
+ <!--
17
+ The portal data contract, inlined as one JSON blob. The consumer parses this
18
+ element's text content; nothing on the page reaches the network. type="application/json"
19
+ keeps the browser from executing it as script.
20
+ -->
21
+ <script id="portal-data" type="application/json">
22
+ /* __PORTAL_DATA__ */
23
+ </script>
24
+
25
+ <!-- Vendored layout library (offline, no CDN). Exposes the global `d3`. -->
26
+ <script>
27
+ /* __PORTAL_VENDOR__ */
28
+ </script>
29
+
30
+ <!--
31
+ Portal frontend modules, inlined in dependency order. No module system on a single
32
+ offline file — each module attaches to the shared window.YgPortal global, so a plain
33
+ sequence of <script> blocks shares code without import/require. The bootstrap runs last.
34
+ -->
35
+ <script>
36
+ /* __PORTAL_MODULES__ */
37
+ </script>
38
+ </body>
39
+ </html>