@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,259 @@
1
+ /**
2
+ * The edge mutation-state grammar, as data.
3
+ *
4
+ * `@issuegraph/store` already ships the state MODEL — `EDGE_STATES`,
5
+ * `edgeStateOf`, `project` and a `ProjectedEdge` that carries a *list* of
6
+ * states. What was missing is the grammar that draws them, and this is it.
7
+ *
8
+ * ## Overlay, not variant — and why that is a property of this table
9
+ *
10
+ * The design's §17b: *"An edge keeps its type identity (dash + terminal + glyph
11
+ * + hue) and gains a state OVERLAY, so a pending `blocked-by` is still
12
+ * recognisably `blocked-by` and combinations need no new symbols."*
13
+ *
14
+ * Five states and five relationships would be twenty-five hand-drawn cases if a
15
+ * state were a variant of an edge. It is not one here: the viewer's
16
+ * `treatmentFor` supplies the kind on four channels, and this table supplies
17
+ * the state on channels of its own, so a `selected` `pending-write`
18
+ * `blocked-by` composes from three independent sources rather than being a
19
+ * twenty-sixth entry somewhere. That is the same construction `vocabulary.ts`
20
+ * uses for `EDGE_TREATMENTS`, and it is what AGENTS.md's branching-boundary
21
+ * rule asks for: the treatments are a table keyed on state, never a switch.
22
+ *
23
+ * It is declared `satisfies Record<EdgeState, OverlayTreatment>` so a sixth
24
+ * state added to the store fails the BUILD here, rather than rendering as an
25
+ * untreated line nobody notices.
26
+ *
27
+ * ## Two channels, and the reason they cannot be one
28
+ *
29
+ * `selected` is *"the only state that is not about a write"*, and it is drawn
30
+ * as a halo — a mark beside the line rather than a change to it. Every other
31
+ * state paints the line itself. Those are different channels, so a selected
32
+ * pending edge shows both and neither has to win.
33
+ *
34
+ * The write states DO compete for the one line, which is why {@link
35
+ * OverlayTreatment.precedence} exists. Two unsettled edits can mark one edge —
36
+ * `project` accumulates a state per record — so "which treatment does the line
37
+ * take" is a real question with a real answer, and the answer is declared as a
38
+ * number in the table instead of being decided by a comparison written out in
39
+ * code. A reader can see the whole ordering at once, and adding a state means
40
+ * choosing its rank rather than finding the branch that ranks things.
41
+ *
42
+ * ## What this module will not do
43
+ *
44
+ * - **It never re-evaluates the order.** `pending-write` is an optimistic
45
+ * *draw*; the order does not move until the write lands. Nothing here imports
46
+ * a deriver, and nothing here can.
47
+ * - **It never invents state.** `invalid` carries the store's `InvalidCode` and
48
+ * nothing else — the sentence beside the ghost is the host's, keyed off the
49
+ * code, for the same reason `change.ts` ships counts rather than prose.
50
+ * - **It never replaces a terminal.** The four redundant channels have to
51
+ * survive every overlay, so the mark vocabulary below has no member that
52
+ * occupies the terminal's place. `failed` ADDS a ✕ beside the type's own
53
+ * marker rather than standing in for it.
54
+ */
55
+ import { type EdgeId, type EdgeState, type ProjectedEdge } from '@issuegraph/store';
56
+ /**
57
+ * What a state does to the edge's own stroke.
58
+ *
59
+ * `halo` is the odd one and is deliberately in the same vocabulary: it is a
60
+ * second stroke drawn behind the line, so naming it here keeps "what happens to
61
+ * the line" answerable from one field.
62
+ *
63
+ * `doubled` is drawn by the COMPOSER, not by `attachEdgeOverlays`. A second
64
+ * version has to sit beside the line, and "beside" is the path's perpendicular
65
+ * — geometry this layer does not have. So it is declared here and carried by
66
+ * the `second-version` mark, and `grammar.test.ts` holds the two together so
67
+ * the field cannot become a claim nothing renders.
68
+ */
69
+ export type OverlayStroke = 'none' | 'halo' | 'ghost' | 'doubled';
70
+ /** A dash the overlay lays over the edge, on top of the kind's own pattern. */
71
+ export type OverlayDash = 'marching' | 'dotted';
72
+ /**
73
+ * A mark the overlay adds. Every one is ADDITIVE — see the module note.
74
+ *
75
+ * `node-chip` sits on both endpoints rather than on the line, which is what
76
+ * §17b specifies: a write is about the pair, and a chip on the line alone is
77
+ * unreadable once the line is short.
78
+ */
79
+ export type OverlayMark = 'node-chip' | 'terminal-cross' | 'inline-reason' | 'second-version';
80
+ /**
81
+ * What a host may offer for a state.
82
+ *
83
+ * There is deliberately no `merge`. §17b is explicit that a conflict offers
84
+ * view-diff, retry-on-latest and discard-mine and **never** auto-merges, so the
85
+ * absence is encoded in the vocabulary rather than left to a reviewer to
86
+ * notice: a merge affordance cannot be spelled, not merely should not be.
87
+ */
88
+ export type OverlayAffordance = 'retry' | 'discard-mine' | 'view-diff';
89
+ export interface OverlayTreatment {
90
+ readonly stroke: OverlayStroke;
91
+ /** The dash laid over the kind's own, or `null` when the state adds none. */
92
+ readonly dash: OverlayDash | null;
93
+ /**
94
+ * The opacity the state draws the edge at, or `null` at full strength.
95
+ *
96
+ * A number rather than a token because it is not a colour: opacity is
97
+ * structural, and a host retheming the palette does not re-decide how ghostly
98
+ * a refused edge is.
99
+ */
100
+ readonly opacity: number | null;
101
+ /**
102
+ * The custom property carrying this state's hue, or `null` when it adds none.
103
+ * Never a literal colour, and never an `--ig-edge-*` token: those name what an
104
+ * edge IS, and spending one here would make a host retheming a relationship
105
+ * silently recolour a state.
106
+ */
107
+ readonly hueToken: string | null;
108
+ readonly marks: readonly OverlayMark[];
109
+ readonly affordances: readonly OverlayAffordance[];
110
+ /**
111
+ * Which treatment the LINE takes when several write states mark one edge.
112
+ * Higher wins. `selected` sits on its own channel and is given `0` because it
113
+ * never competes — see the module note.
114
+ */
115
+ readonly precedence: number;
116
+ /** Announced to a screen reader. The one place this package names a state. */
117
+ readonly label: string;
118
+ }
119
+ /**
120
+ * The five states, drawn.
121
+ *
122
+ * The precedence order reads: a conflict outranks a rejection outranks a
123
+ * refusal outranks an edit still in flight. It is severity order, and it is
124
+ * also *settledness* order — the further a write got from succeeding, the more
125
+ * the line should say so.
126
+ */
127
+ export declare const OVERLAY_TREATMENTS: Readonly<{
128
+ readonly selected: {
129
+ readonly stroke: "halo";
130
+ readonly dash: null;
131
+ readonly opacity: null;
132
+ readonly hueToken: "--ig-focus";
133
+ readonly marks: readonly [];
134
+ readonly affordances: readonly [];
135
+ readonly precedence: 0;
136
+ readonly label: "selected";
137
+ };
138
+ readonly 'pending-write': {
139
+ readonly stroke: "none";
140
+ readonly dash: "marching";
141
+ readonly opacity: 0.7;
142
+ readonly hueToken: null;
143
+ readonly marks: readonly ["node-chip"];
144
+ readonly affordances: readonly [];
145
+ readonly precedence: 1;
146
+ readonly label: "writing";
147
+ };
148
+ readonly invalid: {
149
+ readonly stroke: "ghost";
150
+ readonly dash: "dotted";
151
+ readonly opacity: 0.5;
152
+ readonly hueToken: "--ig-state-invalid";
153
+ readonly marks: readonly ["inline-reason"];
154
+ readonly affordances: readonly ["discard-mine"];
155
+ readonly precedence: 2;
156
+ readonly label: "invalid";
157
+ };
158
+ readonly failed: {
159
+ readonly stroke: "ghost";
160
+ readonly dash: null;
161
+ readonly opacity: 0.5;
162
+ readonly hueToken: "--ig-state-failed";
163
+ readonly marks: readonly ["terminal-cross"];
164
+ readonly affordances: readonly ["retry", "discard-mine"];
165
+ readonly precedence: 3;
166
+ readonly label: "failed";
167
+ };
168
+ readonly conflict: {
169
+ readonly stroke: "doubled";
170
+ readonly dash: null;
171
+ readonly opacity: null;
172
+ readonly hueToken: "--ig-state-conflict";
173
+ readonly marks: readonly ["second-version"];
174
+ readonly affordances: readonly ["view-diff", "retry", "discard-mine"];
175
+ readonly precedence: 4;
176
+ readonly label: "conflict";
177
+ };
178
+ }>;
179
+ /**
180
+ * The alpha the selection halo is drawn at.
181
+ *
182
+ * IT LIVES HERE, BESIDE THE TABLE, AND NOT IN THE STYLESHEET — which is where
183
+ * it started, and that is the whole reason it is worth a named export.
184
+ *
185
+ * `styles.ts` already carried the rule that a state's opacity belongs to this
186
+ * table because a second copy would drift. The halo broke that rule quietly: it
187
+ * is not a state opacity, so it was written as a CSS declaration instead, and
188
+ * `grammar.test.ts`'s composited-contrast check reads this table — so the one
189
+ * alpha the check could not see was the one that failed. At 0.35 the halo
190
+ * measured about 2.0:1 on the dark surfaces and 1.75:1 on the light ones, under
191
+ * the 3:1 non-text bar, while every assertion stayed green.
192
+ *
193
+ * 0.75 clears the bar on both documented themes with margin. The halo is the
194
+ * selection cue; if it is not legible, selection is not visible.
195
+ */
196
+ export declare const HALO_OPACITY = 0.75;
197
+ /**
198
+ * Every (token, alpha) pair this package composites, as data.
199
+ *
200
+ * The contrast claim is about what LANDS on the surface, so it has to range
201
+ * over the alphas actually applied — and an alpha that is not in this list is
202
+ * one the check cannot see. Deriving the list rather than writing it by hand is
203
+ * what stops a third opacity appearing somewhere and going unmeasured.
204
+ */
205
+ export declare function compositedHues(): readonly {
206
+ token: string;
207
+ alpha: number;
208
+ }[];
209
+ /** The treatment for a state. Total over the store's state set. */
210
+ export declare function treatmentForState(state: EdgeState): OverlayTreatment;
211
+ /** The attribute an overlaid edge announces its states on. */
212
+ export declare const STATE_ATTRIBUTE = "data-ig-state";
213
+ /**
214
+ * One edge's overlay: every state it carries, resolved into what to draw.
215
+ *
216
+ * The states are kept alongside the resolution rather than thrown away, because
217
+ * a host writing an accessible name needs all of them — "selected, writing" —
218
+ * while the line can only be drawn one way.
219
+ */
220
+ export interface EdgeOverlay {
221
+ readonly edgeId: EdgeId;
222
+ /** Every state, in `EDGE_STATES` order. Empty for an edge with no overlay. */
223
+ readonly states: readonly EdgeState[];
224
+ /**
225
+ * The treatment the LINE takes — the highest-precedence write state, or
226
+ * `null` when the edge carries none. `selected` is not a candidate here.
227
+ */
228
+ readonly line: OverlayTreatment | null;
229
+ /** Whether the selection halo is drawn. Orthogonal to {@link line}. */
230
+ readonly halo: boolean;
231
+ /** Every mark owed, in state order, de-duplicated. */
232
+ readonly marks: readonly OverlayMark[];
233
+ /** Every affordance offered, in state order, de-duplicated. Never a merge. */
234
+ readonly affordances: readonly OverlayAffordance[];
235
+ /** The value for {@link STATE_ATTRIBUTE}: the states, space separated. */
236
+ readonly attribute: string | null;
237
+ }
238
+ /**
239
+ * Fold an edge's states into one overlay.
240
+ *
241
+ * `ProjectedEdge.states` already arrives in `EDGE_STATES` order — `project`
242
+ * canonicalises it so two projections of the same state compare equal — and
243
+ * this preserves that order rather than re-sorting, so a host memoising on the
244
+ * attribute string is not defeated by a second ordering rule appearing here.
245
+ */
246
+ export declare function overlayFor(edge: ProjectedEdge): EdgeOverlay;
247
+ /**
248
+ * The accessible name for an overlaid edge, given the viewer's own name for it.
249
+ *
250
+ * The kind's sentence leads and the states follow, because the relationship is
251
+ * what the edge IS and the state is what is happening to it. A reader who stops
252
+ * after the first clause still has the fact.
253
+ *
254
+ * This is the ONLY English this module produces, and it is state names rather
255
+ * than a reason: an `invalid` edge's sentence is the host's, keyed off the
256
+ * store's `InvalidCode`.
257
+ */
258
+ export declare function overlayLabel(base: string, overlay: EdgeOverlay): string;
259
+ //# sourceMappingURL=grammar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grammar.d.ts","sourceRoot":"","sources":["../../src/overlay/grammar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,OAAO,EAAe,KAAK,MAAM,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEjG;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;AAElE,+EAA+E;AAC/E,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,QAAQ,CAAC;AAEhD;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,gBAAgB,GAAG,eAAe,GAAG,gBAAgB,CAAC;AAE9F;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,cAAc,GAAG,WAAW,CAAC;AAEvE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC;IAClC;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IACvC,QAAQ,CAAC,WAAW,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACnD;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyD0B,CAAC;AAE1D;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,YAAY,OAAO,CAAC;AAEjC;;;;;;;GAOG;AACH,wBAAgB,cAAc,IAAI,SAAS;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CAa5E;AAED,mEAAmE;AACnE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,GAAG,gBAAgB,CAEpE;AAED,8DAA8D;AAC9D,eAAO,MAAM,eAAe,kBAAkB,CAAC;AAE/C;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IACtC;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACvC,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IACvC,8EAA8E;IAC9E,QAAQ,CAAC,WAAW,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACnD,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,aAAa,GAAG,WAAW,CA6B3D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,MAAM,CAGvE"}
@@ -0,0 +1,223 @@
1
+ /**
2
+ * The edge mutation-state grammar, as data.
3
+ *
4
+ * `@issuegraph/store` already ships the state MODEL — `EDGE_STATES`,
5
+ * `edgeStateOf`, `project` and a `ProjectedEdge` that carries a *list* of
6
+ * states. What was missing is the grammar that draws them, and this is it.
7
+ *
8
+ * ## Overlay, not variant — and why that is a property of this table
9
+ *
10
+ * The design's §17b: *"An edge keeps its type identity (dash + terminal + glyph
11
+ * + hue) and gains a state OVERLAY, so a pending `blocked-by` is still
12
+ * recognisably `blocked-by` and combinations need no new symbols."*
13
+ *
14
+ * Five states and five relationships would be twenty-five hand-drawn cases if a
15
+ * state were a variant of an edge. It is not one here: the viewer's
16
+ * `treatmentFor` supplies the kind on four channels, and this table supplies
17
+ * the state on channels of its own, so a `selected` `pending-write`
18
+ * `blocked-by` composes from three independent sources rather than being a
19
+ * twenty-sixth entry somewhere. That is the same construction `vocabulary.ts`
20
+ * uses for `EDGE_TREATMENTS`, and it is what AGENTS.md's branching-boundary
21
+ * rule asks for: the treatments are a table keyed on state, never a switch.
22
+ *
23
+ * It is declared `satisfies Record<EdgeState, OverlayTreatment>` so a sixth
24
+ * state added to the store fails the BUILD here, rather than rendering as an
25
+ * untreated line nobody notices.
26
+ *
27
+ * ## Two channels, and the reason they cannot be one
28
+ *
29
+ * `selected` is *"the only state that is not about a write"*, and it is drawn
30
+ * as a halo — a mark beside the line rather than a change to it. Every other
31
+ * state paints the line itself. Those are different channels, so a selected
32
+ * pending edge shows both and neither has to win.
33
+ *
34
+ * The write states DO compete for the one line, which is why {@link
35
+ * OverlayTreatment.precedence} exists. Two unsettled edits can mark one edge —
36
+ * `project` accumulates a state per record — so "which treatment does the line
37
+ * take" is a real question with a real answer, and the answer is declared as a
38
+ * number in the table instead of being decided by a comparison written out in
39
+ * code. A reader can see the whole ordering at once, and adding a state means
40
+ * choosing its rank rather than finding the branch that ranks things.
41
+ *
42
+ * ## What this module will not do
43
+ *
44
+ * - **It never re-evaluates the order.** `pending-write` is an optimistic
45
+ * *draw*; the order does not move until the write lands. Nothing here imports
46
+ * a deriver, and nothing here can.
47
+ * - **It never invents state.** `invalid` carries the store's `InvalidCode` and
48
+ * nothing else — the sentence beside the ghost is the host's, keyed off the
49
+ * code, for the same reason `change.ts` ships counts rather than prose.
50
+ * - **It never replaces a terminal.** The four redundant channels have to
51
+ * survive every overlay, so the mark vocabulary below has no member that
52
+ * occupies the terminal's place. `failed` ADDS a ✕ beside the type's own
53
+ * marker rather than standing in for it.
54
+ */
55
+ import { EDGE_STATES } from '@issuegraph/store';
56
+ /**
57
+ * The five states, drawn.
58
+ *
59
+ * The precedence order reads: a conflict outranks a rejection outranks a
60
+ * refusal outranks an edit still in flight. It is severity order, and it is
61
+ * also *settledness* order — the further a write got from succeeding, the more
62
+ * the line should say so.
63
+ */
64
+ export const OVERLAY_TREATMENTS = Object.freeze({
65
+ selected: {
66
+ stroke: 'halo',
67
+ dash: null,
68
+ opacity: null,
69
+ hueToken: '--ig-focus',
70
+ marks: [],
71
+ affordances: [],
72
+ precedence: 0,
73
+ label: 'selected',
74
+ },
75
+ 'pending-write': {
76
+ stroke: 'none',
77
+ dash: 'marching',
78
+ // §17b's number. The edge stays legible — this is an optimistic draw of
79
+ // something that is probably about to be true, not a warning.
80
+ opacity: 0.7,
81
+ hueToken: null,
82
+ marks: ['node-chip'],
83
+ affordances: [],
84
+ precedence: 1,
85
+ label: 'writing',
86
+ },
87
+ invalid: {
88
+ stroke: 'ghost',
89
+ dash: 'dotted',
90
+ opacity: 0.5,
91
+ hueToken: '--ig-state-invalid',
92
+ marks: ['inline-reason'],
93
+ // Nothing to retry: the edit was refused before any write was attempted, so
94
+ // the same edit refuses again. The user changes it or discards it.
95
+ affordances: ['discard-mine'],
96
+ precedence: 2,
97
+ label: 'invalid',
98
+ },
99
+ failed: {
100
+ stroke: 'ghost',
101
+ dash: null,
102
+ opacity: 0.5,
103
+ hueToken: '--ig-state-failed',
104
+ marks: ['terminal-cross'],
105
+ affordances: ['retry', 'discard-mine'],
106
+ precedence: 3,
107
+ label: 'failed',
108
+ },
109
+ conflict: {
110
+ stroke: 'doubled',
111
+ dash: null,
112
+ opacity: null,
113
+ hueToken: '--ig-state-conflict',
114
+ // Both versions are held and drawn. The store keeps the upstream document
115
+ // on the record precisely so this can show one beside the other.
116
+ marks: ['second-version'],
117
+ affordances: ['view-diff', 'retry', 'discard-mine'],
118
+ precedence: 4,
119
+ label: 'conflict',
120
+ },
121
+ });
122
+ /**
123
+ * The alpha the selection halo is drawn at.
124
+ *
125
+ * IT LIVES HERE, BESIDE THE TABLE, AND NOT IN THE STYLESHEET — which is where
126
+ * it started, and that is the whole reason it is worth a named export.
127
+ *
128
+ * `styles.ts` already carried the rule that a state's opacity belongs to this
129
+ * table because a second copy would drift. The halo broke that rule quietly: it
130
+ * is not a state opacity, so it was written as a CSS declaration instead, and
131
+ * `grammar.test.ts`'s composited-contrast check reads this table — so the one
132
+ * alpha the check could not see was the one that failed. At 0.35 the halo
133
+ * measured about 2.0:1 on the dark surfaces and 1.75:1 on the light ones, under
134
+ * the 3:1 non-text bar, while every assertion stayed green.
135
+ *
136
+ * 0.75 clears the bar on both documented themes with margin. The halo is the
137
+ * selection cue; if it is not legible, selection is not visible.
138
+ */
139
+ export const HALO_OPACITY = 0.75;
140
+ /**
141
+ * Every (token, alpha) pair this package composites, as data.
142
+ *
143
+ * The contrast claim is about what LANDS on the surface, so it has to range
144
+ * over the alphas actually applied — and an alpha that is not in this list is
145
+ * one the check cannot see. Deriving the list rather than writing it by hand is
146
+ * what stops a third opacity appearing somewhere and going unmeasured.
147
+ */
148
+ export function compositedHues() {
149
+ const pairs = [];
150
+ for (const state of EDGE_STATES) {
151
+ const { hueToken, opacity } = treatmentForState(state);
152
+ if (hueToken === null)
153
+ continue;
154
+ // The halo is drawn as its own element at its own alpha; every other state
155
+ // paints the edge, which carries the treatment's opacity.
156
+ pairs.push({
157
+ token: hueToken,
158
+ alpha: state === 'selected' ? HALO_OPACITY : (opacity ?? 1),
159
+ });
160
+ }
161
+ return pairs;
162
+ }
163
+ /** The treatment for a state. Total over the store's state set. */
164
+ export function treatmentForState(state) {
165
+ return OVERLAY_TREATMENTS[state];
166
+ }
167
+ /** The attribute an overlaid edge announces its states on. */
168
+ export const STATE_ATTRIBUTE = 'data-ig-state';
169
+ /**
170
+ * Fold an edge's states into one overlay.
171
+ *
172
+ * `ProjectedEdge.states` already arrives in `EDGE_STATES` order — `project`
173
+ * canonicalises it so two projections of the same state compare equal — and
174
+ * this preserves that order rather than re-sorting, so a host memoising on the
175
+ * attribute string is not defeated by a second ordering rule appearing here.
176
+ */
177
+ export function overlayFor(edge) {
178
+ const states = EDGE_STATES.filter((state) => edge.states.includes(state));
179
+ let line = null;
180
+ const marks = [];
181
+ const affordances = [];
182
+ for (const state of states) {
183
+ const treatment = treatmentForState(state);
184
+ // The halo is a channel of its own, so `selected` contributes no line
185
+ // treatment and cannot lose to — or beat — a write state.
186
+ if (state !== 'selected' && (line === null || treatment.precedence > line.precedence)) {
187
+ line = treatment;
188
+ }
189
+ for (const mark of treatment.marks)
190
+ if (!marks.includes(mark))
191
+ marks.push(mark);
192
+ for (const offer of treatment.affordances) {
193
+ if (!affordances.includes(offer))
194
+ affordances.push(offer);
195
+ }
196
+ }
197
+ return {
198
+ edgeId: edge.id,
199
+ states,
200
+ line,
201
+ halo: states.includes('selected'),
202
+ marks,
203
+ affordances,
204
+ attribute: states.length === 0 ? null : states.join(' '),
205
+ };
206
+ }
207
+ /**
208
+ * The accessible name for an overlaid edge, given the viewer's own name for it.
209
+ *
210
+ * The kind's sentence leads and the states follow, because the relationship is
211
+ * what the edge IS and the state is what is happening to it. A reader who stops
212
+ * after the first clause still has the fact.
213
+ *
214
+ * This is the ONLY English this module produces, and it is state names rather
215
+ * than a reason: an `invalid` edge's sentence is the host's, keyed off the
216
+ * store's `InvalidCode`.
217
+ */
218
+ export function overlayLabel(base, overlay) {
219
+ if (overlay.states.length === 0)
220
+ return base;
221
+ return `${base} — ${overlay.states.map((state) => treatmentForState(state).label).join(', ')}`;
222
+ }
223
+ //# sourceMappingURL=grammar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grammar.js","sourceRoot":"","sources":["../../src/overlay/grammar.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * The overlay grammar, attached to a viewer scene.
3
+ *
4
+ * ## The constraint this module is shaped by
5
+ *
6
+ * Layer 2 does not know where an edge is. `index.ts` records the `together-with`
7
+ * connector as *the one declared crossing* precisely because "only the layer
8
+ * that computes the layout knows where its endpoints are", and that argument
9
+ * binds anything that needs a position — an arrowhead, a chip on a node, a
10
+ * sentence beside a line.
11
+ *
12
+ * So this module draws every treatment that can be stated WITHOUT a position,
13
+ * and declares the rest as marks for whoever owns the layout.
14
+ *
15
+ * ## The line, and it was drawn by evidence rather than by taste
16
+ *
17
+ * **An overlay that REUSES the path's own position can be drawn here. One that
18
+ * needs a NEW position cannot.**
19
+ *
20
+ * A halo is the path again, stroked wider. A marching or dotted ghost is the
21
+ * path again, patterned differently. Neither moves, so neither needs to know
22
+ * where the path goes, and cloning a solved `d` is enough.
23
+ *
24
+ * A conflict's second version is the case that looks identical and is not: it
25
+ * has to sit BESIDE the line, and "beside" is the path's perpendicular — which
26
+ * cannot be recovered from a `d` this layer does not interpret. Four review
27
+ * rounds each found a different way that goes wrong: three strokes where a pair
28
+ * was promised, a companion hidden underneath its twin, a companion that had
29
+ * lost the relationship's own double-line shape, and finally a vertical offset
30
+ * sliding ALONG a `together-with` connector that happens to run vertically —
31
+ * `connectorPath` emits exactly that whenever the two boxes are equal width.
32
+ *
33
+ * Each fix was correct and the next round found the next property, because the
34
+ * missing thing was never a property: it was the geometry. So the companion is
35
+ * no longer drawn here at all. It travels as the `second-version` mark, placed
36
+ * by the layer that computed the layout — the same treatment `node-chip`,
37
+ * `terminal-cross` and `inline-reason` already get, and the same argument
38
+ * `index.ts` makes for the `together-with` connector living in the viewer.
39
+ *
40
+ * What is left genuinely cannot be drawn here: `node-chip` sits on two nodes,
41
+ * `terminal-cross` and `inline-reason` need the line's end, and
42
+ * `second-version` needs its perpendicular. Those travel as
43
+ * {@link EdgeOverlay.marks} for the composer to place, declared rather than
44
+ * half-drawn here.
45
+ *
46
+ * ## How an edge is recognised
47
+ *
48
+ * Viewer edge paths publish no per-edge identity — only the `together-with`
49
+ * connector carries one — so an edge is matched by the accessible name the
50
+ * viewer gives it: `${from} ${treatment.label} ${to}`.
51
+ *
52
+ * The label is read from the viewer's OWN `treatmentFor`, never from a copy, so
53
+ * a renamed relationship moves both sides at once. What stays local is the
54
+ * SHAPE of that sentence, and a shape cannot be imported — so `render.test.ts`
55
+ * pins it with a positive control that attaches to a real rendered scene. If
56
+ * the viewer restyles its label, that control goes red rather than the overlays
57
+ * silently ceasing to attach, which is the failure mode worth buying a test.
58
+ *
59
+ * Publishing an identity on edge paths would be better and is deliberately NOT
60
+ * done here: `keyAt` reads `GROUP_ATTRIBUTE` as POINTER identity, so adding one
61
+ * would change what a click on an edge means. That belongs to the leaf that
62
+ * owns edge selection, not to a grammar.
63
+ *
64
+ * ## Attach to a freshly rendered scene, never to your own output
65
+ *
66
+ * The consequence of matching on the accessible name is that overlaying REWRITES
67
+ * that name — the states are announced there — so an overlaid edge no longer
68
+ * matches. A host renders a new scene on every state change and attaches to
69
+ * that, which is the intended shape and is why this is a boundary rather than a
70
+ * defect.
71
+ *
72
+ * Attaching to an already-overlaid scene is out of contract. It fails SAFELY
73
+ * rather than silently: nothing matches, the scene keeps the overlays it
74
+ * already had, and every edge is reported in {@link AttachResult.unattached}.
75
+ * `render.test.ts` pins that, so the behaviour is a decision rather than an
76
+ * accident. What it must never do is double the announcement, and it does not.
77
+ *
78
+ * ## No timer
79
+ *
80
+ * The marching dash is a CSS animation, which is what §17b asks for. The rule
81
+ * is that nothing changes STATE on its own — no `setTimeout`, no `setInterval`,
82
+ * no self-dismissal. A chip persists until the write settles, and a settled
83
+ * failure persists until the user acts on it.
84
+ */
85
+ import type { ProjectedEdge } from '@issuegraph/store';
86
+ import { type ElementSpec, type Scene, type Theme } from '@issuegraph/viewer';
87
+ import { type EdgeOverlay } from './grammar.ts';
88
+ /** The class every mark this module adds carries, so a host can find them. */
89
+ export declare const OVERLAY_CLASS = "ig-overlay";
90
+ export interface OverlayOptions {
91
+ readonly theme?: Theme | undefined;
92
+ }
93
+ export interface AttachResult {
94
+ readonly scene: Scene;
95
+ /**
96
+ * Every overlay applied, keyed by edge. Marks this module could not place —
97
+ * the ones needing a position — travel here for the composer that owns the
98
+ * layout.
99
+ */
100
+ readonly overlays: readonly EdgeOverlay[];
101
+ /** Overlays whose edge the scene does not draw. Never silently dropped. */
102
+ readonly unattached: readonly EdgeOverlay[];
103
+ }
104
+ /**
105
+ * Overlay a scene's edges with the states their projection carries.
106
+ *
107
+ * Takes `ProjectedEdge`s rather than resolved overlays because an edge's kind
108
+ * and endpoints are what identify it in the scene, and asking a caller to hand
109
+ * those over separately is asking it to keep two things in step.
110
+ */
111
+ export declare function attachEdgeOverlays(scene: Scene, edges: readonly ProjectedEdge[], options?: OverlayOptions): AttachResult;
112
+ /**
113
+ * The `writing…`, `retry` and reason marks, as controls a host can dispatch.
114
+ *
115
+ * Positioned by whoever calls it — see the module note. It renders the control,
116
+ * publishing what it does as data on `data-ig-overlay`, the same contract
117
+ * `scale/render.ts` established: layer 2 publishes the control, and the mount
118
+ * that owns the state wires it.
119
+ *
120
+ * The reason mark carries the store's `InvalidCode` and NO sentence. A host
121
+ * keys its own message off the code, for the same reason `change.ts` ships
122
+ * counts rather than prose.
123
+ */
124
+ export declare function renderOverlayMark(overlay: EdgeOverlay, mark: EdgeOverlay['marks'][number], reasonCode?: string): ElementSpec;
125
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/overlay/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmFG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,KAAK,EAEV,KAAK,KAAK,EAIX,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAGL,KAAK,WAAW,EAGjB,MAAM,cAAc,CAAC;AAEtB,8EAA8E;AAC9E,eAAO,MAAM,aAAa,eAAe,CAAC;AAsB1C,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;CACpC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;IAC1C,2EAA2E;IAC3E,QAAQ,CAAC,UAAU,EAAE,SAAS,WAAW,EAAE,CAAC;CAC7C;AAqRD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,SAAS,aAAa,EAAE,EAC/B,OAAO,GAAE,cAAmB,GAC3B,YAAY,CAyCd;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,WAAW,EACpB,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,EAClC,UAAU,CAAC,EAAE,MAAM,GAClB,WAAW,CAuDb"}