@issuegraph/editor 0.1.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 (143) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +238 -0
  3. package/dist/audit/findings.d.ts +225 -0
  4. package/dist/audit/findings.d.ts.map +1 -0
  5. package/dist/audit/findings.js +348 -0
  6. package/dist/audit/findings.js.map +1 -0
  7. package/dist/audit/styles.d.ts +37 -0
  8. package/dist/audit/styles.d.ts.map +1 -0
  9. package/dist/audit/styles.js +72 -0
  10. package/dist/audit/styles.js.map +1 -0
  11. package/dist/audit/surface.d.ts +155 -0
  12. package/dist/audit/surface.d.ts.map +1 -0
  13. package/dist/audit/surface.js +202 -0
  14. package/dist/audit/surface.js.map +1 -0
  15. package/dist/create/draft.d.ts +119 -0
  16. package/dist/create/draft.d.ts.map +1 -0
  17. package/dist/create/draft.js +114 -0
  18. package/dist/create/draft.js.map +1 -0
  19. package/dist/create/keys.d.ts +225 -0
  20. package/dist/create/keys.d.ts.map +1 -0
  21. package/dist/create/keys.js +228 -0
  22. package/dist/create/keys.js.map +1 -0
  23. package/dist/create/placement.d.ts +69 -0
  24. package/dist/create/placement.d.ts.map +1 -0
  25. package/dist/create/placement.js +76 -0
  26. package/dist/create/placement.js.map +1 -0
  27. package/dist/firstpass/batch.d.ts +190 -0
  28. package/dist/firstpass/batch.d.ts.map +1 -0
  29. package/dist/firstpass/batch.js +143 -0
  30. package/dist/firstpass/batch.js.map +1 -0
  31. package/dist/firstpass/candidates.d.ts +128 -0
  32. package/dist/firstpass/candidates.d.ts.map +1 -0
  33. package/dist/firstpass/candidates.js +39 -0
  34. package/dist/firstpass/candidates.js.map +1 -0
  35. package/dist/firstpass/keys.d.ts +90 -0
  36. package/dist/firstpass/keys.d.ts.map +1 -0
  37. package/dist/firstpass/keys.js +112 -0
  38. package/dist/firstpass/keys.js.map +1 -0
  39. package/dist/firstpass/queue.d.ts +183 -0
  40. package/dist/firstpass/queue.d.ts.map +1 -0
  41. package/dist/firstpass/queue.js +159 -0
  42. package/dist/firstpass/queue.js.map +1 -0
  43. package/dist/firstpass/render.d.ts +100 -0
  44. package/dist/firstpass/render.d.ts.map +1 -0
  45. package/dist/firstpass/render.js +216 -0
  46. package/dist/firstpass/render.js.map +1 -0
  47. package/dist/firstpass/styles.d.ts +36 -0
  48. package/dist/firstpass/styles.d.ts.map +1 -0
  49. package/dist/firstpass/styles.js +148 -0
  50. package/dist/firstpass/styles.js.map +1 -0
  51. package/dist/firstpass/view.d.ts +82 -0
  52. package/dist/firstpass/view.d.ts.map +1 -0
  53. package/dist/firstpass/view.js +60 -0
  54. package/dist/firstpass/view.js.map +1 -0
  55. package/dist/firstpass/words.d.ts +105 -0
  56. package/dist/firstpass/words.d.ts.map +1 -0
  57. package/dist/firstpass/words.js +28 -0
  58. package/dist/firstpass/words.js.map +1 -0
  59. package/dist/index.d.ts +150 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +147 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/overlay/grammar.d.ts +259 -0
  64. package/dist/overlay/grammar.d.ts.map +1 -0
  65. package/dist/overlay/grammar.js +223 -0
  66. package/dist/overlay/grammar.js.map +1 -0
  67. package/dist/overlay/render.d.ts +125 -0
  68. package/dist/overlay/render.d.ts.map +1 -0
  69. package/dist/overlay/render.js +439 -0
  70. package/dist/overlay/render.js.map +1 -0
  71. package/dist/overlay/styles.d.ts +41 -0
  72. package/dist/overlay/styles.d.ts.map +1 -0
  73. package/dist/overlay/styles.js +124 -0
  74. package/dist/overlay/styles.js.map +1 -0
  75. package/dist/picker/render.d.ts +80 -0
  76. package/dist/picker/render.d.ts.map +1 -0
  77. package/dist/picker/render.js +112 -0
  78. package/dist/picker/render.js.map +1 -0
  79. package/dist/picker/styles.d.ts +26 -0
  80. package/dist/picker/styles.d.ts.map +1 -0
  81. package/dist/picker/styles.js +116 -0
  82. package/dist/picker/styles.js.map +1 -0
  83. package/dist/picker/view.d.ts +135 -0
  84. package/dist/picker/view.d.ts.map +1 -0
  85. package/dist/picker/view.js +117 -0
  86. package/dist/picker/view.js.map +1 -0
  87. package/dist/picker/words.d.ts +53 -0
  88. package/dist/picker/words.d.ts.map +1 -0
  89. package/dist/picker/words.js +28 -0
  90. package/dist/picker/words.js.map +1 -0
  91. package/dist/reevaluate/render.d.ts +77 -0
  92. package/dist/reevaluate/render.d.ts.map +1 -0
  93. package/dist/reevaluate/render.js +211 -0
  94. package/dist/reevaluate/render.js.map +1 -0
  95. package/dist/reevaluate/styles.d.ts +24 -0
  96. package/dist/reevaluate/styles.d.ts.map +1 -0
  97. package/dist/reevaluate/styles.js +182 -0
  98. package/dist/reevaluate/styles.js.map +1 -0
  99. package/dist/reevaluate/view.d.ts +102 -0
  100. package/dist/reevaluate/view.d.ts.map +1 -0
  101. package/dist/reevaluate/view.js +111 -0
  102. package/dist/reevaluate/view.js.map +1 -0
  103. package/dist/reevaluate/words.d.ts +54 -0
  104. package/dist/reevaluate/words.d.ts.map +1 -0
  105. package/dist/reevaluate/words.js +29 -0
  106. package/dist/reevaluate/words.js.map +1 -0
  107. package/dist/scale/commands.d.ts +60 -0
  108. package/dist/scale/commands.d.ts.map +1 -0
  109. package/dist/scale/commands.js +49 -0
  110. package/dist/scale/commands.js.map +1 -0
  111. package/dist/scale/ladder.d.ts +141 -0
  112. package/dist/scale/ladder.d.ts.map +1 -0
  113. package/dist/scale/ladder.js +243 -0
  114. package/dist/scale/ladder.js.map +1 -0
  115. package/dist/scale/render.d.ts +85 -0
  116. package/dist/scale/render.d.ts.map +1 -0
  117. package/dist/scale/render.js +194 -0
  118. package/dist/scale/render.js.map +1 -0
  119. package/dist/scale/styles.d.ts +21 -0
  120. package/dist/scale/styles.d.ts.map +1 -0
  121. package/dist/scale/styles.js +105 -0
  122. package/dist/scale/styles.js.map +1 -0
  123. package/dist/workspace/inspector.d.ts +117 -0
  124. package/dist/workspace/inspector.d.ts.map +1 -0
  125. package/dist/workspace/inspector.js +140 -0
  126. package/dist/workspace/inspector.js.map +1 -0
  127. package/dist/workspace/rail.d.ts +122 -0
  128. package/dist/workspace/rail.d.ts.map +1 -0
  129. package/dist/workspace/rail.js +216 -0
  130. package/dist/workspace/rail.js.map +1 -0
  131. package/dist/workspace/render.d.ts +139 -0
  132. package/dist/workspace/render.d.ts.map +1 -0
  133. package/dist/workspace/render.js +366 -0
  134. package/dist/workspace/render.js.map +1 -0
  135. package/dist/workspace/selection.d.ts +92 -0
  136. package/dist/workspace/selection.d.ts.map +1 -0
  137. package/dist/workspace/selection.js +83 -0
  138. package/dist/workspace/selection.js.map +1 -0
  139. package/dist/workspace/styles.d.ts +39 -0
  140. package/dist/workspace/styles.d.ts.map +1 -0
  141. package/dist/workspace/styles.js +313 -0
  142. package/dist/workspace/styles.js.map +1 -0
  143. package/package.json +58 -0
@@ -0,0 +1,366 @@
1
+ /**
2
+ * The three-zone workspace: the package's top-level surface.
3
+ *
4
+ * Rail on the left, canvas in the centre, inspector on the right, with the
5
+ * ambient audit count in the header. Every zone is COMPOSED from the leaf that
6
+ * already owns it — this file assembles, it does not re-derive.
7
+ *
8
+ * | zone | comes from |
9
+ * |-----------|-------------------------------------------------------|
10
+ * | header | `auditOverlay` + `renderAuditHeader` |
11
+ * | rail | `renderViewer(…, { projection: 'linear' })`, windowed |
12
+ * | canvas | `renderScaleLadder` |
13
+ * | inspector | `inspectorView`, rendered here |
14
+ *
15
+ * ## The audit left-bar is applied to the rail's SPEC, never to its markup
16
+ *
17
+ * The rail's rows are layer 1's, and this package's standing rule is that
18
+ * nothing splices a rendered string — that is the hand-rolled escaping surface
19
+ * the spec grammar exists to remove, and `reevaluate/render.ts` records the
20
+ * same decision when it nests `scene.root` rather than concatenating chips into
21
+ * the rail.
22
+ *
23
+ * `scene.root` is DATA, and `KEY_ATTRIBUTE` is published, so {@link markRail}
24
+ * walks the spec tree and adds `data-ig-audit` to the keyed rows. That is a
25
+ * pure transform over a public value rather than a reach past a surface, and it
26
+ * means no attribute in this file is ever escaped by anything but `renderMarkup`.
27
+ *
28
+ * ## A row's severity is the heaviest across its MEMBERS
29
+ *
30
+ * A `together-with` unit is one row and several refs, and a finding can name a
31
+ * member that does not lead. Read off the lead alone, an affected unit renders
32
+ * clean — which is the audit failing silently on exactly the rows where an
33
+ * encoding error is hardest to see.
34
+ *
35
+ * ## The zone frames, and the one place a tag is written here
36
+ *
37
+ * Two of the four zones hand back a STRING rather than a spec — the ladder's
38
+ * chrome and the audit header — so the frames are assembled by concatenation,
39
+ * as `renderScaleLadder` already assembles its own canvas and chrome. What
40
+ * makes that safe is not care: {@link ZONES} is a closed union of literals, so
41
+ * no caller value can reach an attribute here at all, and `zones.test.ts` pins
42
+ * it. Everything with a dynamic value in it goes through `renderMarkup`.
43
+ *
44
+ * ## Dark only
45
+ *
46
+ * The pass-2 brief carries "light + dark" over from pass 1; light was cut after
47
+ * that pass, and dark-only is the decision rather than an omission. There is no
48
+ * forked token set here and no second palette: the theme is the viewer's, and
49
+ * `styles.ts` writes structure against its custom properties.
50
+ */
51
+ import { KEY_ATTRIBUTE, element, normalizeDocument, renderMarkup, renderViewer, resolveTheme, themeCss, viewerStylesheet, } from '@issuegraph/viewer';
52
+ import { auditStylesheet } from "../audit/styles.js";
53
+ import { edgeOverlayStylesheet } from "../overlay/styles.js";
54
+ import { AUDIT_SEVERITY_ATTRIBUTE, auditFilterKeeps, auditOverlay, heaviestRow, renderAuditHeader, } from "../audit/surface.js";
55
+ import { INITIAL_SCALE_STATE } from "../scale/commands.js";
56
+ import { renderScaleLadder } from "../scale/render.js";
57
+ import { scaleLadderStylesheet } from "../scale/styles.js";
58
+ import { inspectorView } from "./inspector.js";
59
+ import { railWindow } from "./rail.js";
60
+ import { INITIAL_SELECTION, selectedEdgeId, selectedKey, } from "./selection.js";
61
+ import { workspaceStylesheet } from "./styles.js";
62
+ /** The four fixed positions. A closed union, which is what keeps `zone` safe. */
63
+ export const ZONES = Object.freeze(['header', 'rail', 'canvas', 'inspector']);
64
+ /**
65
+ * A zone frame.
66
+ *
67
+ * The only tag written by hand in this package, and it takes no caller value:
68
+ * `zone` is one of {@link ZONES} and `inner` is already-rendered markup from
69
+ * `renderMarkup` or from a sibling leaf's renderer.
70
+ */
71
+ function zone(name, inner) {
72
+ return `<section class="ig-zone" data-zone="${name}">${inner}</section>`;
73
+ }
74
+ /**
75
+ * The height the rows outside the window would have taken.
76
+ *
77
+ * WITHOUT THESE THE SCROLL CONTAINER CANNOT REACH THE ORDER. The rail zone
78
+ * scrolls, and a zone containing only the drawn rows is exactly as tall as
79
+ * those rows — so native scrolling stops at the end of the first window, and a
80
+ * host has no scroll offset to turn into the next `start`. `addressOf` keeps
81
+ * the MODEL complete, and a reader who cannot scroll to rank 287 does not care.
82
+ *
83
+ * ONE ROW HEIGHT FOR ALL OF THEM, which is an approximation and is stated as
84
+ * one: a row carrying holds is taller than a bare one, so the scrollbar is
85
+ * proportional rather than exact. That is the standard cost of fixed-height
86
+ * virtualisation and the alternative — measuring rows — needs a mount, which
87
+ * this package does not have and will not grow.
88
+ *
89
+ * `aria-hidden`, because a spacer is geometry: it names no row, and a reader
90
+ * moving by rank uses the order rather than the scrollbar.
91
+ */
92
+ function railSpacer(rows, edge) {
93
+ return rows === 0
94
+ ? ''
95
+ : renderMarkup(element('div', {
96
+ class: 'ig-rail-spacer',
97
+ 'data-edge': edge,
98
+ 'aria-hidden': 'true',
99
+ // Through `element`, so the one dynamic value here is escaped by the
100
+ // same renderer as every other attribute in this package.
101
+ style: `--ig-rail-rows:${String(rows)}`,
102
+ }));
103
+ }
104
+ /** The heaviest severity across a row's members, or `undefined` when clean. */
105
+ function severityForRow(overlay, members) {
106
+ // THE RANKING BELONGS NEXT TO THE WEIGHTS, which is why this delegates rather
107
+ // than scanning. An earlier version walked `overlay.rows` and took the first
108
+ // member it matched, on the stated grounds that those rows are "sorted" — and
109
+ // they are, by `ref`, LEXICOGRAPHICALLY. So it returned whichever member
110
+ // sorted earliest, and a `stale-blocker` on `a` masked a `cycle` on `b`: the
111
+ // bar still appeared, understating what it was about. The comment asserting
112
+ // the justification was the defect, not the loop.
113
+ return overlay === null ? undefined : heaviestRow(overlay, members)?.severity;
114
+ }
115
+ /**
116
+ * Add `data-ig-audit` to the rail's keyed rows.
117
+ *
118
+ * A total walk that rebuilds the tree rather than mutating it: `ElementSpec` is
119
+ * `readonly` throughout, and a mutating walk would also be visible to the
120
+ * caller's own copy of `scene.root`.
121
+ */
122
+ function markRail(root, severityOf) {
123
+ // TYPED AS `ElementSpec -> ElementSpec` AT THE BOUNDARY, with the child walk
124
+ // kept inside. A single function over `SpecChild` would hand `renderMarkup` a
125
+ // union it does not take, and the obvious repair — casting the result back —
126
+ // is the one this repository bans outright. The narrowing belongs where the
127
+ // string case actually lives.
128
+ const markChild = (child) => typeof child === 'string' ? child : markSpec(child);
129
+ function markSpec(spec) {
130
+ const key = spec.attrs?.[KEY_ATTRIBUTE];
131
+ // A KEY IS A STRING OR IT IS NOT A KEY. `AttrValue` admits numbers and
132
+ // booleans, and `String(true)` would look up a row named "true" — which
133
+ // resolves to nothing today and to something the day a host names an issue
134
+ // that. Narrowed rather than coerced.
135
+ const severity = typeof key === 'string' ? severityOf(key) : undefined;
136
+ const children = spec.children?.map(markChild);
137
+ const attrs = severity === undefined
138
+ ? spec.attrs
139
+ : { ...spec.attrs, [AUDIT_SEVERITY_ATTRIBUTE]: severity };
140
+ return {
141
+ ...spec,
142
+ ...(attrs === undefined ? {} : { attrs }),
143
+ ...(children === undefined ? {} : { children }),
144
+ };
145
+ }
146
+ return markSpec(root);
147
+ }
148
+ /**
149
+ * One relationship, as a row the reader can actually operate.
150
+ *
151
+ * THE COMMAND SITS ON A BUTTON, NOT ON THE `li`. A plain list item has no tab
152
+ * stop and no native Enter/Space activation, so a `data-ig-command` on one is
153
+ * reachable by pointer and by nothing else — and a host wiring the published
154
+ * attributes cannot fix that without rebuilding the semantics this package
155
+ * should have supplied. Every other command in the package is already on a
156
+ * button; `refusalSpec`'s capsule is the same `li` + `button` shape.
157
+ *
158
+ * The `li` keeps the hue and the direction, because those describe the
159
+ * relationship rather than the action.
160
+ */
161
+ function relationshipSpec(relationship) {
162
+ return element('li', {
163
+ class: 'ig-relationship',
164
+ 'data-edge': relationship.field,
165
+ // Omitted rather than falsified when the subject is not an issue: an edge
166
+ // selection has no "my end", and `data-direction=""` would claim one.
167
+ 'data-direction': relationship.direction ?? undefined,
168
+ }, [
169
+ element('button', {
170
+ type: 'button',
171
+ class: 'ig-relationship-select',
172
+ 'data-ig-command': 'select-edge',
173
+ 'data-ig-target': relationship.edgeId,
174
+ }, [
175
+ element('span', { class: 'ig-relationship-kind' }, [relationship.field]),
176
+ element('span', { class: 'ig-relationship-ref' }, [relationship.from]),
177
+ element('span', { class: 'ig-relationship-ref' }, [relationship.to]),
178
+ ]),
179
+ ]);
180
+ }
181
+ function inspectorSpec(view, words) {
182
+ const subject = view.subject;
183
+ return element('div', { class: 'ig-inspector', 'data-subject': subject.kind }, [
184
+ subject.kind === 'none'
185
+ ? element('p', { class: 'ig-inspector-empty' }, [words.nothingSelected])
186
+ : null,
187
+ subject.kind === 'issue'
188
+ ? element('div', { class: 'ig-inspector-issue' }, [
189
+ element('h2', { class: 'ig-inspector-title' }, [subject.issue.title]),
190
+ element('span', { class: 'ig-inspector-key' }, [subject.issue.key]),
191
+ subject.position === null
192
+ ? null
193
+ : element('p', {
194
+ class: 'ig-inspector-position',
195
+ 'data-ready': subject.position.ready ? 'true' : 'false',
196
+ },
197
+ // A HELD SLOT PRINTS THE VIEWER'S EM DASH, not a number. It has
198
+ // no position in the sequence, and printing one would claim work
199
+ // is queued that nothing can start.
200
+ [subject.position.rank === null ? '—' : String(subject.position.rank)]),
201
+ subject.position === null || subject.position.holds.length === 0
202
+ ? null
203
+ : element('ul', { class: 'ig-inspector-holds' }, subject.position.holds.map((hold) => element('li', { class: 'ig-inspector-hold', 'data-family': hold.family }, [
204
+ hold.reason,
205
+ ]))),
206
+ ])
207
+ : null,
208
+ element('div', { class: 'ig-inspector-relationships', 'data-filtered': view.filtered ? 'true' : 'false' }, [
209
+ element('h3', { class: 'ig-inspector-heading' }, [words.relationships]),
210
+ view.filtered
211
+ ? element('button', { type: 'button', class: 'ig-inspector-clear', 'data-ig-command': 'clear' }, [words.clearSelection])
212
+ : null,
213
+ view.relationships.length === 0
214
+ ? null
215
+ : element('ul', { class: 'ig-relationship-list' }, view.relationships.map((relationship) => relationshipSpec(relationship))),
216
+ ]),
217
+ ]);
218
+ }
219
+ /** Render one document at one reader position, as the whole workspace. */
220
+ export function renderWorkspace(input, options) {
221
+ const selection = options.selection ?? INITIAL_SELECTION;
222
+ const theme = resolveTheme(options.theme);
223
+ const overlay = options.audit === undefined ? null : auditOverlay(options.audit);
224
+ // NORMALIZE ONCE, AT THE TOP, AND DERIVE EVERYTHING FROM THAT — this replaces
225
+ // four separate defects rather than fixing them one at a time, and the class
226
+ // is worth naming because it is not obvious from any one of them.
227
+ //
228
+ // Every zone normalizes the document it is handed; this function did NOT, so
229
+ // it derived the window, the severity map, the filter set and the inspector's
230
+ // relationships from the RAW input while the zones drew the normalized one.
231
+ // Anything layer 1 drops — a duplicate placement, a self-edge, an edge naming
232
+ // an issue the document does not carry — therefore survived in this
233
+ // function's answers and vanished from the picture beside them. Two of the
234
+ // shapes that reached: the inspector published a `select-edge` command for an
235
+ // edge no zone had drawn, and a duplicate placement straddling a window
236
+ // boundary became VALID whenever its earlier copy fell outside the window, so
237
+ // the visible order changed with the scroll position.
238
+ //
239
+ // Normalizing here makes those unrepresentable instead of handled.
240
+ // `normalizeDocument` is idempotent — measured: re-normalizing its own output
241
+ // yields zero further diagnostics — so the zones' own passes now find nothing
242
+ // left to drop, and this is the one place that reports what was dropped.
243
+ const sound = normalizeDocument(input);
244
+ const document = sound.document;
245
+ // THE FILTER NARROWS THE RAIL, AND ONLY THE RAIL. §17a gives the audit a
246
+ // filter for focus and deliberately no mode; the canvas answers "what
247
+ // surrounds this issue", which the filter says nothing about.
248
+ //
249
+ // It narrows BEFORE the window, or it would narrow only whichever rows the
250
+ // window had already reached and read as doing nothing on a long backlog. On
251
+ // members rather than the lead, for the reason the bar is: a finding can name
252
+ // a member that does not lead its unit.
253
+ const filtered = overlay !== null && options.auditFiltered === true;
254
+ const railInput = filtered
255
+ ? {
256
+ ...document,
257
+ order: {
258
+ // EXCLUSIONS ARE ROWS TOO, and filtering only the slots left the clean
259
+ // ones on screen while the header said the filter was on — the toggle
260
+ // narrowing part of the rail and claiming to have narrowed it.
261
+ slots: document.order.slots.filter((slot) => slot.members.some((member) => auditFilterKeeps(overlay, member))),
262
+ excluded: document.order.excluded.filter((exclusion) => auditFilterKeeps(overlay, exclusion.key)),
263
+ },
264
+ }
265
+ : document;
266
+ const rail = railWindow(railInput, options.rail ?? {});
267
+ const railRender = renderViewer(rail.document, {
268
+ projection: 'linear',
269
+ theme,
270
+ // The rail is where a selected ISSUE reads as current. An edge selection
271
+ // resolves to no key, which is `selectedKey`'s whole job.
272
+ selected: selectedKey(selection),
273
+ });
274
+ // Built once, over the window's rows, so a rail of 312 costs one pass rather
275
+ // than one scan of `overlay.rows` per drawn row.
276
+ const severityByKey = new Map();
277
+ for (const slot of rail.rows) {
278
+ const severity = severityForRow(overlay, slot.members);
279
+ if (severity !== undefined)
280
+ severityByKey.set(slot.lead, severity);
281
+ }
282
+ // EXCLUSIONS CARRY A KEY AND RENDER A ROW, so the bar belongs on them too.
283
+ // Built from the slots alone, the map missed exactly the row a
284
+ // `dead-duplicate-ref` finding is about — the class most associated with an
285
+ // exclusion in the first place — and the ambient warning went missing on the
286
+ // one row it most obviously described.
287
+ for (const exclusion of rail.document.order.excluded) {
288
+ const severity = severityForRow(overlay, [exclusion.key]);
289
+ if (severity !== undefined)
290
+ severityByKey.set(exclusion.key, severity);
291
+ }
292
+ // THE CANVAS IS THE WHOLE DOCUMENT, NOT THE WINDOW. The window is the rail's
293
+ // scrolling position and says nothing about what surrounds the selected
294
+ // issue; handing the ladder a windowed document would make its budgets — and
295
+ // therefore its refusal — depend on where the reader had scrolled to.
296
+ const canvas = renderScaleLadder(document, {
297
+ state: options.scale ?? INITIAL_SCALE_STATE,
298
+ theme,
299
+ // THE SAME ONE VALUE THE RAIL READ. Without this the canvas drew the
300
+ // selected issue as ordinary while the rail marked it current, so the
301
+ // single selection this surface advertises disagreed with itself between
302
+ // two zones on every render.
303
+ selected: selectedKey(selection),
304
+ // AND ITS OTHER HALF. The union has two payloads, and reading only the
305
+ // issue one left the canvas the single zone that could not see an edge
306
+ // selection: the inspector filtered to the edge while the canvas drew it
307
+ // as ordinary — the same disagreement the line above closed for issues,
308
+ // still open for the other kind.
309
+ selectedEdge: selectedEdgeId(selection),
310
+ });
311
+ const inspector = inspectorView(document, selection);
312
+ const markup = [
313
+ `<div class="ig-workspace">`,
314
+ overlay === null ? '' : zone('header', renderAuditHeader(overlay, { filtered })),
315
+ zone('rail', [
316
+ railSpacer(rail.before, 'before'),
317
+ renderMarkup(markRail(railRender.scene.root, (key) => severityByKey.get(key))),
318
+ railSpacer(rail.after, 'after'),
319
+ ].join('')),
320
+ zone('canvas', canvas.markup),
321
+ zone('inspector', renderMarkup(inspectorSpec(inspector, options.words))),
322
+ `</div>`,
323
+ ].join('');
324
+ return {
325
+ view: { selection, rail, inspector, audit: overlay, auditFiltered: filtered },
326
+ markup,
327
+ // THE THEME IS WRITTEN ONCE. Both leaves below emit their own copy of the
328
+ // viewer's stylesheet and the theme rule, so taking `canvas.styles`
329
+ // wholesale would install the custom properties two or three times over —
330
+ // harmless to render and impossible to debug when a host overrides one.
331
+ styles: [
332
+ viewerStylesheet,
333
+ themeCss(theme, options.themeSelector ?? ':root'),
334
+ scaleLadderStylesheet,
335
+ // THE CANVAS CAN DRAW A SELECTION HALO NOW, so the sheet that styles one
336
+ // has to be installed. It is listed here rather than inherited from
337
+ // `canvas.styles` for the reason directly above: that value carries its
338
+ // own copy of the viewer sheet and the theme.
339
+ // UNCONDITIONAL, unlike the audit sheet beside it. An audit overlay is a
340
+ // whole zone a caller opts into; an edge selection arrives from a click
341
+ // AFTER this render, so a sheet installed only when something is already
342
+ // selected is one that is missing on exactly the render that first needs
343
+ // it.
344
+ edgeOverlayStylesheet,
345
+ ...(overlay === null ? [] : [auditStylesheet]),
346
+ workspaceStylesheet,
347
+ ].join('\n'),
348
+ // CONCATENATED, NOT DEDUPED — and the dedupe that used to sit here is worth
349
+ // recording rather than deleting quietly, because it was CORRECT when it
350
+ // was added and became wrong two commits later without being touched.
351
+ //
352
+ // It was added when both zones normalized the raw document independently
353
+ // and reported the same defect twice. Normalizing once removed that source,
354
+ // and the same `Set` then had only one thing left to collapse: the
355
+ // deliberately REPEATED diagnostics a single pass emits, one per occurrence
356
+ // — two identical self-edges, the same unknown member in several slots.
357
+ // Understating how many times an input is malformed is worse than the
358
+ // duplication it was guarding against, and it is a count a host acts on.
359
+ //
360
+ // Nothing replaces it, because nothing needs to: the zones now receive an
361
+ // already-sound document, so they contribute nothing to this list at all.
362
+ // `reports every occurrence, and the zones add nothing` pins both halves.
363
+ diagnostics: [...sound.diagnostics, ...railRender.diagnostics, ...canvas.diagnostics],
364
+ };
365
+ }
366
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/workspace/render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * The one piece of state that crosses all three zones.
3
+ *
4
+ * §17b: `selected` is the only edge state that also filters the inspector. So
5
+ * the workspace owns exactly one cross-zone value, and it owns it as ONE value
6
+ * rather than three synchronised copies — the rail, the canvas and the
7
+ * inspector all read this, and none of them mints a second.
8
+ *
9
+ * ## Why a union rather than two nullable fields
10
+ *
11
+ * The obvious shape is `{ issue: string | null; edge: string | null }`, and it
12
+ * is wrong in a way that is invisible until it bites: it can represent
13
+ * "an issue AND an edge are both selected", which is not a state this design
14
+ * has. Every reader would then need a rule for it, the three zones would be
15
+ * free to pick different rules, and the bug would show up as two zones
16
+ * disagreeing about what is selected rather than as a type error.
17
+ *
18
+ * A discriminated union has no such cell. `kind` answers what is selected, and
19
+ * the payload that belongs to that answer is the only payload in scope.
20
+ *
21
+ * ## It carries no document
22
+ *
23
+ * A selection names a key or an edge id and nothing else. It is deliberately
24
+ * not a resolved issue or a resolved edge: the document changes under it — a
25
+ * write lands, the order recomputes — and a selection holding a snapshot of the
26
+ * thing it names would go stale silently. Every zone resolves the name against
27
+ * the document it is rendering, so a selection that no longer resolves renders
28
+ * as nothing selected rather than as last render's answer.
29
+ */
30
+ /** What the reader has selected. Exactly one thing, or nothing. */
31
+ export type WorkspaceSelection = {
32
+ readonly kind: 'none';
33
+ } | {
34
+ readonly kind: 'issue';
35
+ readonly key: string;
36
+ } | {
37
+ readonly kind: 'edge';
38
+ readonly edgeId: string;
39
+ };
40
+ /** Nothing selected. The state a freshly-loaded workspace is in. */
41
+ export declare const INITIAL_SELECTION: WorkspaceSelection;
42
+ /**
43
+ * What a zone can ask for.
44
+ *
45
+ * `select-issue` and `select-edge` REPLACE rather than add, which is the
46
+ * reducer half of the union above: there is no command that could put the
47
+ * workspace in two selections at once, so no reader has to handle one.
48
+ */
49
+ export type SelectionCommand = {
50
+ readonly kind: 'select-issue';
51
+ readonly key: string;
52
+ } | {
53
+ readonly kind: 'select-edge';
54
+ readonly edgeId: string;
55
+ } | {
56
+ readonly kind: 'clear';
57
+ };
58
+ /**
59
+ * The next selection.
60
+ *
61
+ * Pure and total, like `scaleReducer` beside it: a host reads a
62
+ * `data-ig-command` off a control, calls this, and renders again. Selecting
63
+ * what is already selected CLEARS it — a second click on the same row is how a
64
+ * reader gets back to the whole document without hunting for a control, and it
65
+ * is the behaviour the canvas and the rail both need.
66
+ */
67
+ export declare function selectionReducer(selection: WorkspaceSelection, command: SelectionCommand): WorkspaceSelection;
68
+ /**
69
+ * The key the VIEWER should draw as selected, if any.
70
+ *
71
+ * An edge selection resolves to no key on purpose: the viewer's `selected`
72
+ * renders `aria-current` on a NODE, and an edge is not a node. Handing it an
73
+ * edge id would either match nothing — the quiet failure — or, worse, match an
74
+ * issue whose key happened to collide with an edge id.
75
+ */
76
+ export declare function selectedKey(selection: WorkspaceSelection): string | null;
77
+ /**
78
+ * The edge identity the CANVAS should draw as selected, if any.
79
+ *
80
+ * The mirror of {@link selectedKey}, and it exists for the same reason that one
81
+ * does: the union has two payloads and a zone reads exactly the one that
82
+ * belongs to it. `selectedKey` answers the viewer's `aria-current`, which is a
83
+ * NODE question; this answers the canvas's selection overlay, which is an EDGE
84
+ * question. Neither zone gets to ask the other's.
85
+ *
86
+ * An issue selection resolves to no edge for the same reason the other returns
87
+ * no key: an issue key and an edge identity are different name spaces, and
88
+ * handing one to the reader of the other either matches nothing — the quiet
89
+ * failure — or matches the wrong thing.
90
+ */
91
+ export declare function selectedEdgeId(selection: WorkspaceSelection): string | null;
92
+ //# sourceMappingURL=selection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection.d.ts","sourceRoot":"","sources":["../../src/workspace/selection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,mEAAmE;AACnE,MAAM,MAAM,kBAAkB,GAC1B;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAChD;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD,oEAAoE;AACpE,eAAO,MAAM,iBAAiB,EAAE,kBAAqC,CAAC;AAEtE;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACzD;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAE/B;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,kBAAkB,EAC7B,OAAO,EAAE,gBAAgB,GACxB,kBAAkB,CAapB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,kBAAkB,GAAG,MAAM,GAAG,IAAI,CAExE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,kBAAkB,GAAG,MAAM,GAAG,IAAI,CAE3E"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * The one piece of state that crosses all three zones.
3
+ *
4
+ * §17b: `selected` is the only edge state that also filters the inspector. So
5
+ * the workspace owns exactly one cross-zone value, and it owns it as ONE value
6
+ * rather than three synchronised copies — the rail, the canvas and the
7
+ * inspector all read this, and none of them mints a second.
8
+ *
9
+ * ## Why a union rather than two nullable fields
10
+ *
11
+ * The obvious shape is `{ issue: string | null; edge: string | null }`, and it
12
+ * is wrong in a way that is invisible until it bites: it can represent
13
+ * "an issue AND an edge are both selected", which is not a state this design
14
+ * has. Every reader would then need a rule for it, the three zones would be
15
+ * free to pick different rules, and the bug would show up as two zones
16
+ * disagreeing about what is selected rather than as a type error.
17
+ *
18
+ * A discriminated union has no such cell. `kind` answers what is selected, and
19
+ * the payload that belongs to that answer is the only payload in scope.
20
+ *
21
+ * ## It carries no document
22
+ *
23
+ * A selection names a key or an edge id and nothing else. It is deliberately
24
+ * not a resolved issue or a resolved edge: the document changes under it — a
25
+ * write lands, the order recomputes — and a selection holding a snapshot of the
26
+ * thing it names would go stale silently. Every zone resolves the name against
27
+ * the document it is rendering, so a selection that no longer resolves renders
28
+ * as nothing selected rather than as last render's answer.
29
+ */
30
+ /** Nothing selected. The state a freshly-loaded workspace is in. */
31
+ export const INITIAL_SELECTION = { kind: 'none' };
32
+ /**
33
+ * The next selection.
34
+ *
35
+ * Pure and total, like `scaleReducer` beside it: a host reads a
36
+ * `data-ig-command` off a control, calls this, and renders again. Selecting
37
+ * what is already selected CLEARS it — a second click on the same row is how a
38
+ * reader gets back to the whole document without hunting for a control, and it
39
+ * is the behaviour the canvas and the rail both need.
40
+ */
41
+ export function selectionReducer(selection, command) {
42
+ switch (command.kind) {
43
+ case 'select-issue':
44
+ return selection.kind === 'issue' && selection.key === command.key
45
+ ? INITIAL_SELECTION
46
+ : { kind: 'issue', key: command.key };
47
+ case 'select-edge':
48
+ return selection.kind === 'edge' && selection.edgeId === command.edgeId
49
+ ? INITIAL_SELECTION
50
+ : { kind: 'edge', edgeId: command.edgeId };
51
+ case 'clear':
52
+ return INITIAL_SELECTION;
53
+ }
54
+ }
55
+ /**
56
+ * The key the VIEWER should draw as selected, if any.
57
+ *
58
+ * An edge selection resolves to no key on purpose: the viewer's `selected`
59
+ * renders `aria-current` on a NODE, and an edge is not a node. Handing it an
60
+ * edge id would either match nothing — the quiet failure — or, worse, match an
61
+ * issue whose key happened to collide with an edge id.
62
+ */
63
+ export function selectedKey(selection) {
64
+ return selection.kind === 'issue' ? selection.key : null;
65
+ }
66
+ /**
67
+ * The edge identity the CANVAS should draw as selected, if any.
68
+ *
69
+ * The mirror of {@link selectedKey}, and it exists for the same reason that one
70
+ * does: the union has two payloads and a zone reads exactly the one that
71
+ * belongs to it. `selectedKey` answers the viewer's `aria-current`, which is a
72
+ * NODE question; this answers the canvas's selection overlay, which is an EDGE
73
+ * question. Neither zone gets to ask the other's.
74
+ *
75
+ * An issue selection resolves to no edge for the same reason the other returns
76
+ * no key: an issue key and an edge identity are different name spaces, and
77
+ * handing one to the reader of the other either matches nothing — the quiet
78
+ * failure — or matches the wrong thing.
79
+ */
80
+ export function selectedEdgeId(selection) {
81
+ return selection.kind === 'edge' ? selection.edgeId : null;
82
+ }
83
+ //# sourceMappingURL=selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection.js","sourceRoot":"","sources":["../../src/workspace/selection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAQH,oEAAoE;AACpE,MAAM,CAAC,MAAM,iBAAiB,GAAuB,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AActE;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAC9B,SAA6B,EAC7B,OAAyB;IAEzB,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;QACrB,KAAK,cAAc;YACjB,OAAO,SAAS,CAAC,IAAI,KAAK,OAAO,IAAI,SAAS,CAAC,GAAG,KAAK,OAAO,CAAC,GAAG;gBAChE,CAAC,CAAC,iBAAiB;gBACnB,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;QAC1C,KAAK,aAAa;YAChB,OAAO,SAAS,CAAC,IAAI,KAAK,MAAM,IAAI,SAAS,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM;gBACrE,CAAC,CAAC,iBAAiB;gBACnB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;QAC/C,KAAK,OAAO;YACV,OAAO,iBAAiB,CAAC;IAC7B,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,SAA6B;IACvD,OAAO,SAAS,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AAC3D,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,cAAc,CAAC,SAA6B;IAC1D,OAAO,SAAS,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The workspace's structural stylesheet: three fixed zones, and nothing else.
3
+ *
4
+ * Same contract as the viewer's, the ladder's and the re-evaluate surface's —
5
+ * layout, weight and state, never a value. Every colour, length and font is a
6
+ * `var(--ig-…)` the host's theme resolves, and `styles.test.ts` scans these
7
+ * bytes for a literal or an unresolvable token.
8
+ *
9
+ * ## Fixed, and that is the requirement
10
+ *
11
+ * §17f gives the rail and the canvas opposite obligations — the rail is
12
+ * complete at any backlog size, the canvas refuses above its budget — and
13
+ * assembling them must not average the two. Positions are therefore FIXED
14
+ * rather than negotiated: `grid-template-columns` gives the rail and the
15
+ * inspector their own tracks and the canvas the remaining `1fr`, so a large
16
+ * document grows the canvas's refusal rather than squeezing the rail out of
17
+ * the layout.
18
+ *
19
+ * The rail scrolls in its own track. That is what makes windowing a rendering
20
+ * decision rather than a visible one: the reader scrolls a complete order, and
21
+ * which slice is currently drawn is the host's business.
22
+ *
23
+ * ## Dark only, with no forked token set
24
+ *
25
+ * Light was cut after pass 1, so there is no second palette here and no
26
+ * `prefers-color-scheme` block. The single palette is the viewer's, reached
27
+ * through its custom properties — a forked set would be exactly the drifting
28
+ * second implementation the package split exists to remove.
29
+ *
30
+ * ## No animation, anywhere
31
+ *
32
+ * §17a: the audit is ambient, and a count that animates is a count demanding
33
+ * attention it has not earned. The re-evaluate surface already rejected the
34
+ * animated re-sort for a stronger reason — rows moving under the cursor mean
35
+ * the next edit targets the wrong thing — and this is the file where both
36
+ * could creep back in without touching a line of TypeScript.
37
+ */
38
+ export declare const workspaceStylesheet = "\n.ig-workspace {\n display: grid;\n /* HEADER SPANS, THE THREE ZONES SIT UNDER IT. The audit count is ambient: it\n belongs to the whole workspace rather than to one zone, and putting it in a\n zone's own track would make it move when that zone resized. */\n grid-template-areas:\n 'header header header'\n 'rail canvas inspector';\n /* The rail and the inspector are sized in TYPE, not in pixels: a host that\n scales the type scales the zones with it, which a fixed track would not.\n\n A LENGTH TIMES A UNITLESS COUNT, which is the viewer's own idiom\n (styles.ts writes min-width: calc(var(--ig-char-width) * 4)). An earlier\n version multiplied --ig-char-width by --ig-label-char-width, having read\n the second as \"how many characters wide a label is\". It is not: it is the\n average width OF one character, and themeCss renders every metric with px\n \u2014 so the expression was calc(7.8px * 6px), which is not a length. CSS\n discards a declaration it cannot parse, so the whole template went with it\n and all three zones fell back to auto columns: long content could then\n squeeze the canvas, which is the one thing fixed positions exist to stop.\n Nothing failed loudly, because an invalid declaration is simply absent.\n\n 40 is a reading measure for a title plus its id, in characters. */\n grid-template-columns:\n calc(var(--ig-char-width) * 40)\n 1fr\n calc(var(--ig-char-width) * 40);\n grid-template-rows: auto 1fr;\n gap: var(--ig-space);\n background: var(--ig-bg);\n color: var(--ig-text-body);\n font-family: var(--ig-font-ui);\n font-size: var(--ig-font-size);\n line-height: var(--ig-line-height);\n}\n\n.ig-zone {\n min-width: 0;\n /* Without this a long title in any zone sets that track's minimum width and\n the grid stops honouring its own template. */\n min-height: 0;\n}\n\n.ig-zone[data-zone='header'] {\n grid-area: header;\n}\n\n.ig-zone[data-zone='rail'] {\n grid-area: rail;\n /* The rail scrolls; the window slides underneath. */\n overflow-y: auto;\n border-right: var(--ig-stroke) solid var(--ig-line);\n}\n\n.ig-zone[data-zone='canvas'] {\n grid-area: canvas;\n overflow: auto;\n}\n\n.ig-zone[data-zone='inspector'] {\n grid-area: inspector;\n overflow-y: auto;\n border-left: var(--ig-stroke) solid var(--ig-line);\n}\n\n/* THE AMBIENT LEFT-BAR. A 2px gold rule on the affected row and nothing else \u2014\n no fill, no icon, no badge. \u00A717a's whole ask is that encoding errors stay\n visible while you work rather than pulling you out of it.\n\n An inset box-shadow rather than a border: a border changes the row's box and\n would shift every marked row against its neighbours, which is a layout jump\n the eye reads as movement. The width is the theme's spine, so the bar scales\n with the rest of the rail. */\n.ig-zone[data-zone='rail'] [data-ig-audit] {\n box-shadow: inset var(--ig-spine-width) 0 0 0 var(--ig-accent);\n}\n\n.ig-zone[data-zone='rail'] [data-ig-audit='misleading'] {\n box-shadow: inset var(--ig-spine-width) 0 0 0 var(--ig-state-invalid);\n}\n\n/* THE ROWS OUTSIDE THE WINDOW, AS HEIGHT. The rail scrolls, so without a spacer\n at each end the container is exactly as tall as the drawn rows and native\n scrolling stops at the end of the first window \u2014 leaving a host no offset to\n turn into the next start, and a reader no way to reach rank 287 of 312.\n\n NO BACKTICKS ANYWHERE IN THIS FILE'S COMMENTS. The stylesheet is a template\n literal, so a backtick closes it \u2014 and the failure is a parse error pages\n away from the character that caused it.\n\n THE PITCH IS THE OUTER BOX, NOT THE ROW HEIGHT. A .ig-slot is\n min-height: --ig-row-height PLUS margin-bottom: --ig-space-tight, so\n row-to-row is the sum of the two. Sized on --ig-row-height alone, this\n undercounted EVERY omitted row by the gap \u2014 44 of a 50px pitch on the default\n theme, so 300 omitted rows left the scroll extent 1,800px short and a host\n dividing its scroll position by the measured pitch could reach about offset\n 264 of 300. The tail of the order became unreachable by the very mechanism\n added to make it reachable.\n\n That is a SYSTEMATIC undercount, not the approximation below, which is why it\n is corrected exactly rather than tolerated: the gap is constant per row and\n both terms are theme metrics.\n\n --ig-rail-rows is the count, set inline by the renderer. What remains\n approximate is only VARIABLE row height \u2014 a row carrying holds is taller than\n a bare one \u2014 so the scrollbar stays proportional rather than exact. Measuring\n that needs a mount, which this package does not have. */\n.ig-rail-spacer {\n height: calc((var(--ig-row-height) + var(--ig-space-tight)) * var(--ig-rail-rows, 0));\n}\n\n.ig-inspector {\n display: flex;\n flex-direction: column;\n gap: var(--ig-space);\n padding: var(--ig-space);\n}\n\n.ig-inspector-empty {\n margin: 0;\n color: var(--ig-text-muted);\n font-size: var(--ig-font-size-small);\n}\n\n.ig-inspector-issue {\n display: flex;\n flex-direction: column;\n gap: var(--ig-space-tight);\n}\n\n.ig-inspector-title {\n margin: 0;\n font-size: var(--ig-font-size);\n color: var(--ig-text);\n}\n\n.ig-inspector-key {\n font-family: var(--ig-font-mono);\n color: var(--ig-text-muted);\n}\n\n.ig-inspector-position {\n margin: 0;\n font-family: var(--ig-font-mono);\n color: var(--ig-text-muted);\n}\n\n.ig-inspector-position[data-ready='true'] {\n color: var(--ig-station-ready);\n}\n\n.ig-inspector-holds {\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/* The two hold families never share a treatment \u2014 layer 1's rule, carried\n across so the inspector cannot contradict the rail it sits beside. */\n.ig-inspector-hold[data-family='graph'] {\n color: var(--ig-station-held);\n}\n\n.ig-inspector-hold[data-family='tracker'] {\n color: var(--ig-text-muted);\n}\n\n.ig-inspector-relationships {\n display: flex;\n flex-direction: column;\n gap: var(--ig-space-tight);\n}\n\n.ig-inspector-heading {\n margin: 0;\n font-size: var(--ig-font-size-small);\n color: var(--ig-text-muted);\n}\n\n.ig-inspector-clear {\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-inspector-clear:focus-visible {\n outline: var(--ig-focus-ring) solid var(--ig-focus);\n outline-offset: var(--ig-space-tight);\n}\n\n.ig-relationship-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.ig-relationship {\n display: flex;\n gap: var(--ig-space-tight);\n align-items: baseline;\n padding: var(--ig-space-tight);\n border: var(--ig-stroke) solid var(--ig-line);\n border-radius: var(--ig-radius);\n background: var(--ig-surface);\n}\n\n/* Hue by field, from the theme's own edge tokens \u2014 the same channel layer 1\n uses, so a relationship reads the same colour in the list as on the canvas. */\n.ig-relationship[data-edge='blocked-by'] {\n border-left-color: var(--ig-edge-blocked-by);\n}\n\n.ig-relationship[data-edge='duplicate-of'] {\n border-left-color: var(--ig-edge-duplicate-of);\n}\n\n.ig-relationship[data-edge='serialize-with'] {\n border-left-color: var(--ig-edge-serialize-with);\n}\n\n.ig-relationship[data-edge='together-with'] {\n border-left-color: var(--ig-edge-together-with);\n}\n\n.ig-relationship[data-edge='decomposed-from'] {\n border-left-color: var(--ig-edge-decomposed-from);\n}\n\n/* The button fills its row, so the whole relationship is the hit target rather\n than the words inside it. Transparent and borderless: the li already carries\n the border and the hue, and a second box around it would read as two\n controls. */\n.ig-relationship-select {\n flex: 1;\n display: flex;\n gap: var(--ig-space-tight);\n align-items: baseline;\n background: none;\n border: none;\n padding: 0;\n font-family: inherit;\n font-size: inherit;\n color: inherit;\n text-align: left;\n cursor: pointer;\n}\n\n.ig-relationship-select:focus-visible {\n outline: var(--ig-focus-ring) solid var(--ig-focus);\n outline-offset: var(--ig-space-tight);\n}\n\n.ig-relationship-kind {\n font-family: var(--ig-font-mono);\n color: var(--ig-text-body);\n}\n\n.ig-relationship-ref {\n font-family: var(--ig-font-mono);\n color: var(--ig-text-muted);\n}\n";
39
+ //# sourceMappingURL=styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../src/workspace/styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,eAAO,MAAM,mBAAmB,2jRAkR/B,CAAC"}