@issuegraph/editor 0.22.1 → 0.23.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 (39) hide show
  1. package/dist/audit/findings.js +4 -4
  2. package/dist/audit/findings.js.map +1 -1
  3. package/dist/audit/surface.js +1 -1
  4. package/dist/audit/surface.js.map +1 -1
  5. package/dist/firstpass/render.d.ts.map +1 -1
  6. package/dist/firstpass/render.js +10 -9
  7. package/dist/firstpass/render.js.map +1 -1
  8. package/dist/index.d.ts +2 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +1 -0
  11. package/dist/index.js.map +1 -1
  12. package/dist/overlay/render.d.ts.map +1 -1
  13. package/dist/overlay/render.js +2 -1
  14. package/dist/overlay/render.js.map +1 -1
  15. package/dist/scale/ladder.js +8 -8
  16. package/dist/scale/ladder.js.map +1 -1
  17. package/dist/scale/render.d.ts.map +1 -1
  18. package/dist/scale/render.js +31 -8
  19. package/dist/scale/render.js.map +1 -1
  20. package/dist/scale/styles.d.ts +1 -1
  21. package/dist/scale/styles.d.ts.map +1 -1
  22. package/dist/scale/styles.js +53 -1
  23. package/dist/scale/styles.js.map +1 -1
  24. package/dist/workspace/mount.d.ts.map +1 -1
  25. package/dist/workspace/mount.js +105 -3
  26. package/dist/workspace/mount.js.map +1 -1
  27. package/dist/workspace/render.d.ts +11 -0
  28. package/dist/workspace/render.d.ts.map +1 -1
  29. package/dist/workspace/render.js +31 -0
  30. package/dist/workspace/render.js.map +1 -1
  31. package/dist/workspace/skeleton.d.ts +33 -0
  32. package/dist/workspace/skeleton.d.ts.map +1 -0
  33. package/dist/workspace/skeleton.js +56 -0
  34. package/dist/workspace/skeleton.js.map +1 -0
  35. package/dist/workspace/styles.d.ts +1 -1
  36. package/dist/workspace/styles.d.ts.map +1 -1
  37. package/dist/workspace/styles.js +72 -10
  38. package/dist/workspace/styles.js.map +1 -1
  39. package/package.json +3 -3
@@ -23,5 +23,5 @@
23
23
  * imports CSS cannot be loaded by a bare Node runtime, and a string needs no
24
24
  * bundler.
25
25
  */
26
- export declare const scaleLadderStylesheet = "\n.ig-ladder {\n display: flex;\n flex-direction: column;\n gap: var(--ig-space);\n font-family: var(--ig-font-ui);\n font-size: var(--ig-font-size);\n line-height: var(--ig-line-height);\n color: var(--ig-text-body);\n}\n\n.ig-ladder-routes {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: var(--ig-space-tight);\n color: var(--ig-text-muted);\n font-size: var(--ig-font-size-small);\n}\n\n.ig-ladder-search {\n display: flex;\n flex-direction: column;\n gap: var(--ig-space-tight);\n}\n\n.ig-ladder-search input {\n background: var(--ig-surface);\n color: var(--ig-text);\n border: var(--ig-stroke) solid var(--ig-line);\n border-radius: var(--ig-radius);\n padding: var(--ig-space-tight);\n font-family: var(--ig-font-ui);\n font-size: var(--ig-font-size);\n}\n\n.ig-ladder-search input:focus-visible {\n outline: var(--ig-focus-ring) solid var(--ig-focus);\n outline-offset: var(--ig-space-tight);\n}\n\n.ig-ladder-match {\n display: flex;\n gap: var(--ig-space-tight);\n align-items: baseline;\n}\n\n.ig-chip {\n align-self: flex-start;\n background: var(--ig-surface-2);\n color: var(--ig-text-body);\n border: var(--ig-stroke) solid var(--ig-line);\n border-radius: var(--ig-radius);\n padding: var(--ig-space-tight) var(--ig-space);\n font-family: var(--ig-font-ui);\n font-size: var(--ig-font-size-small);\n cursor: pointer;\n}\n\n.ig-chip:focus-visible {\n outline: var(--ig-focus-ring) solid var(--ig-focus);\n outline-offset: var(--ig-space-tight);\n}\n\n.ig-chip[aria-expanded='true'] {\n border-color: var(--ig-accent);\n color: var(--ig-text);\n}\n\n/* THE LIST ITEM IS A GRID CELL AND NOTHING ELSE. It deliberately does not\n carry .ig-capsule: that is layer 1's own card, and a card inside a card put\n the stuck tint and the hover on the inner border only. */\n.ig-capsule-cell {\n display: flex;\n min-width: 0;\n}\n\n/* FOUR CARDS ACROSS A COLUMN, NOT FOUR ROWS DOWN IT. Frame 17f lays the\n capsules out as a board, which is what makes the counts comparable at a\n glance \u2014 the whole reason the count leads. minmax(0, 1fr) rather than a\n width, because this stylesheet declares no fixed length: a title is arbitrary\n and auto would let the longest one set every column. Scoped to the ladder's\n own refusal so \u00A716's single-column list, drawn from the same class in layer\n 1, is untouched. */\n.ig-ladder .ig-refusal .ig-list {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: var(--ig-space);\n}\n\n/* THE WHOLE CAPSULE IS THE CONTROL, so the button carries the card and the\n list item carries nothing. A button wrapping a card needs its own alignment\n and wrapping reset: a user agent centres and nowraps button content, and both\n are wrong for a four-part card with a title in it. */\n.ig-capsule-enter {\n display: grid;\n grid-template-columns: 1fr auto;\n align-items: start;\n gap: var(--ig-space-tight) var(--ig-space);\n width: 100%;\n height: 100%;\n text-align: left;\n background: var(--ig-surface-2);\n color: var(--ig-text-body);\n border: var(--ig-stroke) solid var(--ig-line);\n border-radius: var(--ig-radius);\n padding: var(--ig-space);\n font-family: var(--ig-font-ui);\n font-size: var(--ig-font-size);\n cursor: pointer;\n}\n\n.ig-capsule-enter:focus-visible {\n outline: var(--ig-focus-ring) solid var(--ig-focus);\n outline-offset: var(--ig-space-tight);\n}\n\n.ig-capsule-enter:hover {\n border-color: var(--ig-accent);\n}\n\n/* THE WHOLE STUCK CARD IS TINTED, as frame 17f draws it \u2014 the badge alone puts\n the one finding that stops work outright at the size of every other chip on\n the board. Same hue as the badge inside it, which is blocked-by's, because\n that is the edge the cycle is made of. */\n.ig-capsule-enter[data-reach='cyclic'] {\n border-color: var(--ig-edge-blocked-by);\n}\n\n.ig-capsule-enter[data-reach='cyclic'] .ig-capsule-reach {\n color: var(--ig-edge-blocked-by);\n}\n\n/* THE COUNT LEADS, so it is the one part sized to be read down the board rather\n than within a card. The --ig-font-size-rank token is the one the theme defines\n for exactly this \u2014 \"the rank number itself\" \u2014 so a capsule's count and a rail\n rank are one size by declaration rather than by coincidence. */\n.ig-capsule-size {\n grid-column: 1;\n font-family: var(--ig-font-mono);\n font-size: var(--ig-font-size-rank);\n font-variant-numeric: tabular-nums;\n color: var(--ig-text);\n}\n\n/* END-ALIGNED AND CONTENT-WIDTH. A grid item stretches by default, which drew\n the cycle badge as a full-width bar across the top of the card. The pill is\n the frame's: the blocked count and the cycle sit in one slot, so they read as\n one kind of thing rather than as a bordered badge beside bare text. */\n.ig-capsule-load {\n grid-column: 2;\n justify-self: end;\n background: var(--ig-bg);\n border: var(--ig-stroke) solid var(--ig-line);\n border-radius: var(--ig-radius);\n padding: var(--ig-space-micro) var(--ig-space-snug);\n font-family: var(--ig-font-mono);\n font-size: var(--ig-font-size-small);\n font-variant-numeric: tabular-nums;\n color: var(--ig-text-muted);\n}\n\n/* THE NAME AND THE REACH SPAN BOTH COLUMNS, and the name is the one part\n allowed to wrap: a title is arbitrary length, and letting it size the grid\n would make every card on the board a different shape. */\n.ig-capsule-name {\n grid-column: 1 / -1;\n color: var(--ig-text);\n min-width: 0;\n overflow-wrap: anywhere;\n}\n\n.ig-capsule-reach {\n grid-column: 1 / -1;\n color: var(--ig-text-muted);\n font-size: var(--ig-font-size-small);\n font-variant-numeric: tabular-nums;\n}\n\n.ig-isolated-list {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: var(--ig-space-tight);\n}\n\n/* A ROW PER ENTRY, WITH A GAP THE MARKUP DOES NOT SUPPLY. The element helper\n concatenates its children with no whitespace, so the key and the title\n rendered as one run of text \u2014 #492Audit log pagination. Latent until \u00A717a\n moved the CONTROL to the rail footer and made this list the thing a reader is\n sent to, rather than a chip's afterthought. */\n.ig-isolated-list li {\n display: flex;\n gap: var(--ig-space-tight);\n align-items: baseline;\n min-width: 0;\n}\n\n.ig-isolated-list .ig-id {\n font-family: var(--ig-font-mono);\n color: var(--ig-text-muted);\n flex: none;\n}\n";
26
+ export declare const scaleLadderStylesheet = "\n.ig-ladder {\n display: flex;\n flex-direction: column;\n gap: var(--ig-space);\n font-family: var(--ig-font-ui);\n font-size: var(--ig-font-size);\n line-height: var(--ig-line-height);\n color: var(--ig-text-body);\n}\n\n.ig-ladder-routes {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: var(--ig-space-tight);\n color: var(--ig-text-muted);\n font-size: var(--ig-font-size-small);\n}\n\n.ig-ladder-search {\n display: flex;\n flex-direction: column;\n gap: var(--ig-space-tight);\n}\n\n.ig-ladder-search input {\n background: var(--ig-surface);\n color: var(--ig-text);\n border: var(--ig-stroke) solid var(--ig-line);\n border-radius: var(--ig-radius);\n padding: var(--ig-space-tight);\n font-family: var(--ig-font-ui);\n font-size: var(--ig-font-size);\n}\n\n.ig-ladder-search input:focus-visible {\n outline: var(--ig-focus-ring) solid var(--ig-focus);\n outline-offset: var(--ig-space-tight);\n}\n\n.ig-ladder-match {\n display: flex;\n gap: var(--ig-space-tight);\n align-items: baseline;\n}\n\n.ig-chip {\n align-self: flex-start;\n background: var(--ig-surface-2);\n color: var(--ig-text-body);\n border: var(--ig-stroke) solid var(--ig-line);\n border-radius: var(--ig-radius);\n padding: var(--ig-space-tight) var(--ig-space);\n font-family: var(--ig-font-ui);\n font-size: var(--ig-font-size-small);\n cursor: pointer;\n}\n\n.ig-chip:focus-visible {\n outline: var(--ig-focus-ring) solid var(--ig-focus);\n outline-offset: var(--ig-space-tight);\n}\n\n.ig-chip[aria-expanded='true'] {\n border-color: var(--ig-accent);\n color: var(--ig-text);\n}\n\n/* THE LIST ITEM IS A GRID CELL AND NOTHING ELSE. It deliberately does not\n carry .ig-capsule: that is layer 1's own card, and a card inside a card put\n the stuck tint and the hover on the inner border only. */\n.ig-capsule-cell {\n display: flex;\n min-width: 0;\n}\n\n/* FOUR CARDS ACROSS A COLUMN, NOT FOUR ROWS DOWN IT. Frame 17f lays the\n capsules out as a board, which is what makes the counts comparable at a\n glance \u2014 the whole reason the count leads. minmax(0, 1fr) rather than a\n width, because this stylesheet declares no fixed length: a title is arbitrary\n and auto would let the longest one set every column. Scoped to the ladder's\n own refusal so \u00A716's single-column list, drawn from the same class in layer\n 1, is untouched. */\n.ig-ladder .ig-refusal .ig-list {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: var(--ig-space);\n}\n\n/* THE WHOLE CAPSULE IS THE CONTROL, so the button carries the card and the\n list item carries nothing. A button wrapping a card needs its own alignment\n and wrapping reset: a user agent centres and nowraps button content, and both\n are wrong for a four-part card with a title in it. */\n.ig-capsule-enter {\n display: grid;\n grid-template-columns: 1fr auto;\n align-items: start;\n gap: var(--ig-space-tight) var(--ig-space);\n width: 100%;\n height: 100%;\n text-align: left;\n background: var(--ig-surface-2);\n color: var(--ig-text-body);\n border: var(--ig-stroke) solid var(--ig-line);\n border-radius: var(--ig-radius);\n padding: var(--ig-space);\n font-family: var(--ig-font-ui);\n font-size: var(--ig-font-size);\n cursor: pointer;\n}\n\n.ig-capsule-enter:focus-visible {\n outline: var(--ig-focus-ring) solid var(--ig-focus);\n outline-offset: var(--ig-space-tight);\n}\n\n.ig-capsule-enter:hover {\n border-color: var(--ig-accent);\n}\n\n/* THE WHOLE STUCK CARD IS TINTED, as frame 17f draws it \u2014 the badge alone puts\n the one finding that stops work outright at the size of every other chip on\n the board. Same hue as the badge inside it, which is blocked-by's, because\n that is the edge the cycle is made of. */\n.ig-capsule-enter[data-reach='cyclic'] {\n border-color: var(--ig-edge-blocked-by);\n}\n\n.ig-capsule-enter[data-reach='cyclic'] .ig-capsule-reach {\n color: var(--ig-edge-blocked-by);\n}\n\n/* THE COUNT LEADS, so it is the one part sized to be read down the board rather\n than within a card. The --ig-font-size-rank token is the one the theme defines\n for exactly this \u2014 \"the rank number itself\" \u2014 so a capsule's count and a rail\n rank are one size by declaration rather than by coincidence. */\n.ig-capsule-size {\n grid-column: 1;\n font-family: var(--ig-font-mono);\n font-size: var(--ig-font-size-rank);\n font-variant-numeric: tabular-nums;\n color: var(--ig-text);\n}\n\n/* END-ALIGNED AND CONTENT-WIDTH. A grid item stretches by default, which drew\n the cycle badge as a full-width bar across the top of the card. The pill is\n the frame's: the blocked count and the cycle sit in one slot, so they read as\n one kind of thing rather than as a bordered badge beside bare text. */\n.ig-capsule-load {\n grid-column: 2;\n justify-self: end;\n background: var(--ig-bg);\n border: var(--ig-stroke) solid var(--ig-line);\n border-radius: var(--ig-radius);\n padding: var(--ig-space-micro) var(--ig-space-snug);\n font-family: var(--ig-font-mono);\n font-size: var(--ig-font-size-small);\n font-variant-numeric: tabular-nums;\n color: var(--ig-text-muted);\n}\n\n/* THE NAME AND THE REACH SPAN BOTH COLUMNS, and the name is the one part\n allowed to wrap: a title is arbitrary length, and letting it size the grid\n would make every card on the board a different shape. */\n.ig-capsule-name {\n grid-column: 1 / -1;\n color: var(--ig-text);\n min-width: 0;\n overflow-wrap: anywhere;\n}\n\n.ig-capsule-reach {\n grid-column: 1 / -1;\n color: var(--ig-text-muted);\n font-size: var(--ig-font-size-small);\n font-variant-numeric: tabular-nums;\n}\n\n/* A PANEL, NOT A LOOSE COLUMN OF TEXT. With the control moved to the rail\n footer (\u00A717a) this wrapper was left holding an unstyled ol at zero inset:\n it began hard against the zone's own rule, ran on from the graph above it\n with nothing between them, and read as content that had escaped a container\n rather than as the thing the reader had just asked to see.\n\n THE TOP RULE AND THE PADDING ARE THE CANVAS TOOLBAR'S, because this is the\n same kind of band one row lower \u2014 a full-bleed section of the canvas zone,\n not a card floating on it. --ig-bg for the same reason the toolbar takes\n it: the ground this zone already paints, so the two read as one surface. */\n.ig-ladder-isolated {\n border-top: var(--ig-stroke) solid var(--ig-line);\n padding: var(--ig-space) var(--ig-space-wide);\n}\n\n/* A SECTION HEAD IN THE READ-OUT VOICE the canvas caption and the rail footer\n count already use on this surface \u2014 mono, micro, muted, tracked \u2014 because it\n carries the same kind of fact and a heavier treatment would make a list of\n issues nobody has to act on the loudest thing in the zone. */\n.ig-isolated-caption {\n margin: 0 0 var(--ig-space-tight);\n color: var(--ig-text-muted);\n font-family: var(--ig-font-mono);\n font-size: var(--ig-font-size-micro);\n letter-spacing: var(--ig-tracking-group);\n text-transform: uppercase;\n}\n\n.ig-isolated-list {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n}\n\n/* A ROW PER ENTRY, WITH A GAP THE MARKUP DOES NOT SUPPLY. The element helper\n concatenates its children with no whitespace, so the key and the title\n rendered as one run of text \u2014 #492Audit log pagination. Latent until \u00A717a\n moved the CONTROL to the rail footer and made this list the thing a reader is\n sent to, rather than a chip's afterthought. */\n/* SEPARATED THE WAY EVERY OTHER LIST OF ISSUES ON THIS SURFACE IS, with a\n hairline between rows rather than a gap between them. A gap left five lines\n of text floating in a column; the rule is what makes them read as rows, and\n it is the treatment the viewer's own order rows already carry. Last row\n included would double the zone's next edge, so it stops one short. */\n.ig-isolated-list li {\n display: flex;\n gap: var(--ig-space-tight);\n align-items: baseline;\n min-width: 0;\n padding: var(--ig-space-tight) 0;\n}\n\n.ig-isolated-list li + li {\n border-top: var(--ig-stroke) solid var(--ig-line);\n}\n\n/* THE KEY IN ITS OWN COLUMN, so five titles start on one edge rather than\n wherever their number happened to end. tabular-nums and a measure in\n characters are what the capsule's own count line already uses for the same\n reason: a ragged left edge on a column of ids reads as a mistake. */\n.ig-isolated-list .ig-id {\n font-family: var(--ig-font-mono);\n font-size: var(--ig-font-size-small);\n font-variant-numeric: tabular-nums;\n color: var(--ig-text-muted);\n flex: none;\n min-inline-size: calc(var(--ig-char-width) * 5);\n}\n\n/* The title carries the row, so it takes the body colour and wraps rather than\n setting the row's width \u2014 the rule .ig-capsule-name states one file up. */\n.ig-isolated-list .ig-title {\n color: var(--ig-text);\n min-width: 0;\n overflow-wrap: anywhere;\n}\n";
27
27
  //# sourceMappingURL=styles.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../src/scale/styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,eAAO,MAAM,qBAAqB,q3MA+MjC,CAAC"}
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../src/scale/styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,eAAO,MAAM,qBAAqB,g5RAmQjC,CAAC"}
@@ -204,13 +204,40 @@ export const scaleLadderStylesheet = `
204
204
  font-variant-numeric: tabular-nums;
205
205
  }
206
206
 
207
+ /* A PANEL, NOT A LOOSE COLUMN OF TEXT. With the control moved to the rail
208
+ footer (§17a) this wrapper was left holding an unstyled ol at zero inset:
209
+ it began hard against the zone's own rule, ran on from the graph above it
210
+ with nothing between them, and read as content that had escaped a container
211
+ rather than as the thing the reader had just asked to see.
212
+
213
+ THE TOP RULE AND THE PADDING ARE THE CANVAS TOOLBAR'S, because this is the
214
+ same kind of band one row lower — a full-bleed section of the canvas zone,
215
+ not a card floating on it. --ig-bg for the same reason the toolbar takes
216
+ it: the ground this zone already paints, so the two read as one surface. */
217
+ .ig-ladder-isolated {
218
+ border-top: var(--ig-stroke) solid var(--ig-line);
219
+ padding: var(--ig-space) var(--ig-space-wide);
220
+ }
221
+
222
+ /* A SECTION HEAD IN THE READ-OUT VOICE the canvas caption and the rail footer
223
+ count already use on this surface — mono, micro, muted, tracked — because it
224
+ carries the same kind of fact and a heavier treatment would make a list of
225
+ issues nobody has to act on the loudest thing in the zone. */
226
+ .ig-isolated-caption {
227
+ margin: 0 0 var(--ig-space-tight);
228
+ color: var(--ig-text-muted);
229
+ font-family: var(--ig-font-mono);
230
+ font-size: var(--ig-font-size-micro);
231
+ letter-spacing: var(--ig-tracking-group);
232
+ text-transform: uppercase;
233
+ }
234
+
207
235
  .ig-isolated-list {
208
236
  list-style: none;
209
237
  margin: 0;
210
238
  padding: 0;
211
239
  display: flex;
212
240
  flex-direction: column;
213
- gap: var(--ig-space-tight);
214
241
  }
215
242
 
216
243
  /* A ROW PER ENTRY, WITH A GAP THE MARKUP DOES NOT SUPPLY. The element helper
@@ -218,17 +245,42 @@ export const scaleLadderStylesheet = `
218
245
  rendered as one run of text — #492Audit log pagination. Latent until §17a
219
246
  moved the CONTROL to the rail footer and made this list the thing a reader is
220
247
  sent to, rather than a chip's afterthought. */
248
+ /* SEPARATED THE WAY EVERY OTHER LIST OF ISSUES ON THIS SURFACE IS, with a
249
+ hairline between rows rather than a gap between them. A gap left five lines
250
+ of text floating in a column; the rule is what makes them read as rows, and
251
+ it is the treatment the viewer's own order rows already carry. Last row
252
+ included would double the zone's next edge, so it stops one short. */
221
253
  .ig-isolated-list li {
222
254
  display: flex;
223
255
  gap: var(--ig-space-tight);
224
256
  align-items: baseline;
225
257
  min-width: 0;
258
+ padding: var(--ig-space-tight) 0;
226
259
  }
227
260
 
261
+ .ig-isolated-list li + li {
262
+ border-top: var(--ig-stroke) solid var(--ig-line);
263
+ }
264
+
265
+ /* THE KEY IN ITS OWN COLUMN, so five titles start on one edge rather than
266
+ wherever their number happened to end. tabular-nums and a measure in
267
+ characters are what the capsule's own count line already uses for the same
268
+ reason: a ragged left edge on a column of ids reads as a mistake. */
228
269
  .ig-isolated-list .ig-id {
229
270
  font-family: var(--ig-font-mono);
271
+ font-size: var(--ig-font-size-small);
272
+ font-variant-numeric: tabular-nums;
230
273
  color: var(--ig-text-muted);
231
274
  flex: none;
275
+ min-inline-size: calc(var(--ig-char-width) * 5);
276
+ }
277
+
278
+ /* The title carries the row, so it takes the body colour and wraps rather than
279
+ setting the row's width — the rule .ig-capsule-name states one file up. */
280
+ .ig-isolated-list .ig-title {
281
+ color: var(--ig-text);
282
+ min-width: 0;
283
+ overflow-wrap: anywhere;
232
284
  }
233
285
  `;
234
286
  //# sourceMappingURL=styles.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"styles.js","sourceRoot":"","sources":["../../src/scale/styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+MpC,CAAC"}
1
+ {"version":3,"file":"styles.js","sourceRoot":"","sources":["../../src/scale/styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmQpC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../src/workspace/mount.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../src/workspace/mount.ts"],"names":[],"mappings":"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"}
@@ -61,6 +61,7 @@ import { renderPicker } from "../picker/render.js";
61
61
  import { pickerStylesheet } from "../picker/styles.js";
62
62
  import { scaleLadder } from "../scale/ladder.js";
63
63
  import { mountStylesheet } from "./chrome.js";
64
+ import { renderWorkspaceSkeleton } from "./skeleton.js";
64
65
  import { INITIAL_HOST_STATE, editCarrier, narrowOverlayOpen, railRowAt, railSlackFor, railWindowTarget, reconcileHost, reduceHost, targetMatches, } from "./host.js";
65
66
  import { conflictDiff } from "./recovery.js";
66
67
  import { renderWorkspace, } from "./render.js";
@@ -932,15 +933,19 @@ export function mountWorkspace(element, options) {
932
933
  const sceneFor = (zoneName) => {
933
934
  if (drawn === null)
934
935
  return null;
936
+ // `frame: false` MIRRORS WHAT IS DRAWN. These rebuild a scene to answer a
937
+ // navigation question, and a scene built from different options than the
938
+ // markup is a second opinion about the surface. See `SceneOptions.frame`.
935
939
  if (zoneName === 'rail')
936
- return renderViewer(drawn.rail.document, { projection: 'linear', theme: theme() }).scene;
940
+ return renderViewer(drawn.rail.document, { projection: 'linear', theme: theme(), frame: false, dockLegend: true })
941
+ .scene;
937
942
  if (zoneName !== 'canvas')
938
943
  return null;
939
944
  if (current.canvas === 'tree')
940
- return renderViewer(withoutChrome(drawn.viewer), { projection: 'tree', theme: theme(), chrome: false }).scene;
945
+ return renderViewer(withoutChrome(drawn.viewer), { projection: 'tree', theme: theme(), chrome: false, frame: false, dockLegend: true }).scene;
941
946
  const ladder = scaleLadder(drawn.viewer, state.scale);
942
947
  return ladder.tier === 'direct'
943
- ? renderViewer(ladder.canvas, { projection: 'graph', theme: theme(), chrome: false }).scene
948
+ ? renderViewer(ladder.canvas, { projection: 'graph', theme: theme(), chrome: false, frame: false, dockLegend: true }).scene
944
949
  : null;
945
950
  };
946
951
  /** The row or node that owns keyboard focus, if focus is on one at all. */
@@ -985,6 +990,29 @@ export function mountWorkspace(element, options) {
985
990
  if (destroyed)
986
991
  return;
987
992
  const snapshot = store.getSnapshot();
993
+ // THE FIRST READ IS IN FLIGHT, SO THERE IS NOTHING TO SAY YET. Drawn over
994
+ // the store's empty document, the workspace announced an empty backlog —
995
+ // "no relationships yet", no rows — about issues nobody had read. The
996
+ // placeholder holds the three zones' shape instead, and the first render
997
+ // after the load replaces it.
998
+ //
999
+ // `hydrating` ONLY, NEVER `idle`. A store nobody has asked to load is not
1000
+ // loading, and a host that fills its store another way (or a test that
1001
+ // never hydrates) must still get the document it has. A REHYDRATE is not
1002
+ // this state either: the store keeps its last good document and stays
1003
+ // `ready`, so a refresh never blanks the surface.
1004
+ if (snapshot.status === 'hydrating') {
1005
+ const skeleton = renderWorkspaceSkeleton({
1006
+ label: current.words.loading,
1007
+ theme: current.theme === undefined ? undefined : theme(),
1008
+ themeSelector: current.themeSelector,
1009
+ });
1010
+ const skeletonSheet = [skeleton.styles, mountStylesheet].join('\n');
1011
+ if (styles.textContent !== skeletonSheet)
1012
+ styles.textContent = skeletonSheet;
1013
+ surface.innerHTML = skeleton.markup;
1014
+ return;
1015
+ }
988
1016
  const document_ = landed();
989
1017
  // WHAT THE STORE IS NOT SHOWING, computed before the reconcile rather than
990
1018
  // beside the edges below, because the reconcile is the first reader of it:
@@ -1395,6 +1423,15 @@ export function mountWorkspace(element, options) {
1395
1423
  selected: selectedKey(state.selection),
1396
1424
  // The rail beside this canvas draws the panel's one header.
1397
1425
  chrome: false,
1426
+ // THE WORKSPACE RULES ITS OWN ZONES OFF, so the viewer draws no
1427
+ // frame of its own here: the rail track carries `border-right`, the
1428
+ // canvas toolbar carries `border-bottom`, and a second edge one
1429
+ // pixel away reads as a doubled hairline. See `SceneOptions.frame`.
1430
+ frame: false,
1431
+ // §16b's KEY, KEPT IN VIEW. It explains every line and station on this
1432
+ // zone, and below a scroll it was the one thing a reader had to leave the
1433
+ // reading to reach. See `SceneOptions.dockLegend`.
1434
+ dockLegend: true,
1398
1435
  }).markup;
1399
1436
  // Re-inserted rather than re-rendered: the row is already assembled,
1400
1437
  // and rebuilding it here would be a second place that decides what it
@@ -1407,6 +1444,13 @@ export function mountWorkspace(element, options) {
1407
1444
  // emptied husk here left a sticky padded band with a border and no
1408
1445
  // content over the tree. Reading the element rather than re-deriving
1409
1446
  // the condition keeps the rule in one place.
1447
+ // THE TREE ALWAYS DRAWS A KEY, WHATEVER THE GRAPH'S TIER SAID. render.ts
1448
+ // set `data-canvas-legend` from the scale ladder, which is a fact about
1449
+ // the GRAPH — and this branch has just replaced the graph with a
1450
+ // projection that has no budget and no refusal. Left as it was, a tree
1451
+ // over a backlog past the node budget reported no key in this zone and
1452
+ // the rail drew a second one beside the one on screen.
1453
+ surface.firstElementChild?.setAttribute('data-canvas-legend', 'drawn');
1410
1454
  if (isolated !== null)
1411
1455
  canvas.append(isolated);
1412
1456
  if (toolbar !== null && toolbar.childElementCount > 0)
@@ -1438,6 +1482,8 @@ export function mountWorkspace(element, options) {
1438
1482
  const rail = zone('rail');
1439
1483
  if (rail !== null)
1440
1484
  rail.scrollTop = scrollTop;
1485
+ // AFTER the subtree is in place, because it measures what was just drawn.
1486
+ dockRailLegend();
1441
1487
  // RE-QUERIED, NEVER REUSED: `surface.innerHTML` above replaced the subtree,
1442
1488
  // so the element captured from is gone. A region that shrank clamps this on
1443
1489
  // assignment, and one the redraw removed — the audit going clean — is
@@ -1876,6 +1922,49 @@ export function mountWorkspace(element, options) {
1876
1922
  const onCompositionEnd = (event) => {
1877
1923
  readInput(event);
1878
1924
  };
1925
+ /**
1926
+ * Move the rail's docked legend clear of this zone's own footer.
1927
+ *
1928
+ * TWO THINGS ARE PINNED TO ONE EDGE, AND ONLY THIS PACKAGE KNOWS BOTH. The
1929
+ * viewer docks its legend against the bottom of whatever scrollport it was
1930
+ * given (`SceneOptions.dockLegend`), with `bottom: 0` as the default; this
1931
+ * zone already holds `.ig-rail-footer` there. Left at the default the two
1932
+ * stack in the same place, and the footer — later in the subtree and carrying
1933
+ * its own layer — paints over the bottom of the key. The viewer cannot fix
1934
+ * that: it has never been told the footer exists.
1935
+ *
1936
+ * MEASURED, NOT DERIVED FROM THE TOKENS. The footer is one line of type at a
1937
+ * known size inside known padding, so its height looks like something the
1938
+ * stylesheet could state — and then the rail narrows, `5 with no
1939
+ * relationships` and its control wrap to two lines, and the written-down
1940
+ * number is quietly wrong by a line. The same argument `onScroll` makes about
1941
+ * the header it measures rather than derives.
1942
+ *
1943
+ * ON EVERY REDRAW AND ON EVERY RESIZE, because the wrap depends on the width
1944
+ * and a resize alone redraws nothing. The observer is guarded on the
1945
+ * constructor rather than assumed: this module renders into whatever document
1946
+ * a host hands it, and a host rendering server-side or in a minimal DOM has
1947
+ * no need of the correction — the default is simply left in place.
1948
+ */
1949
+ const dockRailLegend = () => {
1950
+ const rail = zone('rail');
1951
+ const legend = rail?.querySelector('.ig-viewer > .ig-legend');
1952
+ if (rail === null || legend == null)
1953
+ return;
1954
+ const footer = rail.querySelector('.ig-rail-footer');
1955
+ // RE-OBSERVED EVERY REDRAW, because `surface.innerHTML` replaced the zone and
1956
+ // the element the last call watched is gone. `disconnect` first so the
1957
+ // observer never accumulates dead targets.
1958
+ if (railResize !== null) {
1959
+ railResize.disconnect();
1960
+ railResize.observe(rail);
1961
+ }
1962
+ const clearance = footer === null ? 0 : Math.ceil(footer.getBoundingClientRect().height);
1963
+ // AN INLINE STYLE, because it is a measurement rather than a rule: it is
1964
+ // true of this rail at this width and of nothing else, which is exactly what
1965
+ // a stylesheet cannot say.
1966
+ legend.style.bottom = `${String(clearance)}px`;
1967
+ };
1879
1968
  const onScroll = (event) => {
1880
1969
  const rail = zone('rail');
1881
1970
  if (rail === null || event.target !== rail)
@@ -2629,10 +2718,23 @@ export function mountWorkspace(element, options) {
2629
2718
  element.addEventListener('lostpointercapture', onPointerCancel);
2630
2719
  doc.addEventListener('pointerup', onDocumentPointerUp);
2631
2720
  doc.addEventListener('pointercancel', onDocumentPointerUp);
2721
+ /**
2722
+ * The rail's own width, watched only so the docked legend's clearance stays
2723
+ * true. See `dockRailLegend` for why a resize and not just a redraw.
2724
+ *
2725
+ * THE ELEMENT, NOT THE WINDOW. A package that reads the window cannot be
2726
+ * dropped into someone else's page — the same rule §17j states about the
2727
+ * density crossover and the workspace's own container query rests on. The
2728
+ * rail can narrow because the host resized, because a sibling grew, or
2729
+ * because a container query re-tracked the grid, and only the box itself sees
2730
+ * all three.
2731
+ */
2732
+ const railResize = typeof ResizeObserver === 'function' ? new ResizeObserver(() => { dockRailLegend(); }) : null;
2632
2733
  const unsubscribe = store.subscribe(schedule);
2633
2734
  /** Every listener off and every node this mount built removed. Shared by `destroy` and a failed first render. */
2634
2735
  const teardown = () => {
2635
2736
  unsubscribe();
2737
+ railResize?.disconnect();
2636
2738
  element.removeEventListener('click', onClick);
2637
2739
  element.removeEventListener('input', onInput);
2638
2740
  element.removeEventListener('compositionend', onCompositionEnd);