@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.
- package/LICENSE +201 -0
- package/README.md +238 -0
- package/dist/audit/findings.d.ts +225 -0
- package/dist/audit/findings.d.ts.map +1 -0
- package/dist/audit/findings.js +348 -0
- package/dist/audit/findings.js.map +1 -0
- package/dist/audit/styles.d.ts +37 -0
- package/dist/audit/styles.d.ts.map +1 -0
- package/dist/audit/styles.js +72 -0
- package/dist/audit/styles.js.map +1 -0
- package/dist/audit/surface.d.ts +155 -0
- package/dist/audit/surface.d.ts.map +1 -0
- package/dist/audit/surface.js +202 -0
- package/dist/audit/surface.js.map +1 -0
- package/dist/create/draft.d.ts +119 -0
- package/dist/create/draft.d.ts.map +1 -0
- package/dist/create/draft.js +114 -0
- package/dist/create/draft.js.map +1 -0
- package/dist/create/keys.d.ts +225 -0
- package/dist/create/keys.d.ts.map +1 -0
- package/dist/create/keys.js +228 -0
- package/dist/create/keys.js.map +1 -0
- package/dist/create/placement.d.ts +69 -0
- package/dist/create/placement.d.ts.map +1 -0
- package/dist/create/placement.js +76 -0
- package/dist/create/placement.js.map +1 -0
- package/dist/firstpass/batch.d.ts +190 -0
- package/dist/firstpass/batch.d.ts.map +1 -0
- package/dist/firstpass/batch.js +143 -0
- package/dist/firstpass/batch.js.map +1 -0
- package/dist/firstpass/candidates.d.ts +128 -0
- package/dist/firstpass/candidates.d.ts.map +1 -0
- package/dist/firstpass/candidates.js +39 -0
- package/dist/firstpass/candidates.js.map +1 -0
- package/dist/firstpass/keys.d.ts +90 -0
- package/dist/firstpass/keys.d.ts.map +1 -0
- package/dist/firstpass/keys.js +112 -0
- package/dist/firstpass/keys.js.map +1 -0
- package/dist/firstpass/queue.d.ts +183 -0
- package/dist/firstpass/queue.d.ts.map +1 -0
- package/dist/firstpass/queue.js +159 -0
- package/dist/firstpass/queue.js.map +1 -0
- package/dist/firstpass/render.d.ts +100 -0
- package/dist/firstpass/render.d.ts.map +1 -0
- package/dist/firstpass/render.js +216 -0
- package/dist/firstpass/render.js.map +1 -0
- package/dist/firstpass/styles.d.ts +36 -0
- package/dist/firstpass/styles.d.ts.map +1 -0
- package/dist/firstpass/styles.js +148 -0
- package/dist/firstpass/styles.js.map +1 -0
- package/dist/firstpass/view.d.ts +82 -0
- package/dist/firstpass/view.d.ts.map +1 -0
- package/dist/firstpass/view.js +60 -0
- package/dist/firstpass/view.js.map +1 -0
- package/dist/firstpass/words.d.ts +105 -0
- package/dist/firstpass/words.d.ts.map +1 -0
- package/dist/firstpass/words.js +28 -0
- package/dist/firstpass/words.js.map +1 -0
- package/dist/index.d.ts +150 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +147 -0
- package/dist/index.js.map +1 -0
- package/dist/overlay/grammar.d.ts +259 -0
- package/dist/overlay/grammar.d.ts.map +1 -0
- package/dist/overlay/grammar.js +223 -0
- package/dist/overlay/grammar.js.map +1 -0
- package/dist/overlay/render.d.ts +125 -0
- package/dist/overlay/render.d.ts.map +1 -0
- package/dist/overlay/render.js +439 -0
- package/dist/overlay/render.js.map +1 -0
- package/dist/overlay/styles.d.ts +41 -0
- package/dist/overlay/styles.d.ts.map +1 -0
- package/dist/overlay/styles.js +124 -0
- package/dist/overlay/styles.js.map +1 -0
- package/dist/picker/render.d.ts +80 -0
- package/dist/picker/render.d.ts.map +1 -0
- package/dist/picker/render.js +112 -0
- package/dist/picker/render.js.map +1 -0
- package/dist/picker/styles.d.ts +26 -0
- package/dist/picker/styles.d.ts.map +1 -0
- package/dist/picker/styles.js +116 -0
- package/dist/picker/styles.js.map +1 -0
- package/dist/picker/view.d.ts +135 -0
- package/dist/picker/view.d.ts.map +1 -0
- package/dist/picker/view.js +117 -0
- package/dist/picker/view.js.map +1 -0
- package/dist/picker/words.d.ts +53 -0
- package/dist/picker/words.d.ts.map +1 -0
- package/dist/picker/words.js +28 -0
- package/dist/picker/words.js.map +1 -0
- package/dist/reevaluate/render.d.ts +77 -0
- package/dist/reevaluate/render.d.ts.map +1 -0
- package/dist/reevaluate/render.js +211 -0
- package/dist/reevaluate/render.js.map +1 -0
- package/dist/reevaluate/styles.d.ts +24 -0
- package/dist/reevaluate/styles.d.ts.map +1 -0
- package/dist/reevaluate/styles.js +182 -0
- package/dist/reevaluate/styles.js.map +1 -0
- package/dist/reevaluate/view.d.ts +102 -0
- package/dist/reevaluate/view.d.ts.map +1 -0
- package/dist/reevaluate/view.js +111 -0
- package/dist/reevaluate/view.js.map +1 -0
- package/dist/reevaluate/words.d.ts +54 -0
- package/dist/reevaluate/words.d.ts.map +1 -0
- package/dist/reevaluate/words.js +29 -0
- package/dist/reevaluate/words.js.map +1 -0
- package/dist/scale/commands.d.ts +60 -0
- package/dist/scale/commands.d.ts.map +1 -0
- package/dist/scale/commands.js +49 -0
- package/dist/scale/commands.js.map +1 -0
- package/dist/scale/ladder.d.ts +141 -0
- package/dist/scale/ladder.d.ts.map +1 -0
- package/dist/scale/ladder.js +243 -0
- package/dist/scale/ladder.js.map +1 -0
- package/dist/scale/render.d.ts +85 -0
- package/dist/scale/render.d.ts.map +1 -0
- package/dist/scale/render.js +194 -0
- package/dist/scale/render.js.map +1 -0
- package/dist/scale/styles.d.ts +21 -0
- package/dist/scale/styles.d.ts.map +1 -0
- package/dist/scale/styles.js +105 -0
- package/dist/scale/styles.js.map +1 -0
- package/dist/workspace/inspector.d.ts +117 -0
- package/dist/workspace/inspector.d.ts.map +1 -0
- package/dist/workspace/inspector.js +140 -0
- package/dist/workspace/inspector.js.map +1 -0
- package/dist/workspace/rail.d.ts +122 -0
- package/dist/workspace/rail.d.ts.map +1 -0
- package/dist/workspace/rail.js +216 -0
- package/dist/workspace/rail.js.map +1 -0
- package/dist/workspace/render.d.ts +139 -0
- package/dist/workspace/render.d.ts.map +1 -0
- package/dist/workspace/render.js +366 -0
- package/dist/workspace/render.js.map +1 -0
- package/dist/workspace/selection.d.ts +92 -0
- package/dist/workspace/selection.d.ts.map +1 -0
- package/dist/workspace/selection.js +83 -0
- package/dist/workspace/selection.js.map +1 -0
- package/dist/workspace/styles.d.ts +39 -0
- package/dist/workspace/styles.d.ts.map +1 -0
- package/dist/workspace/styles.js +313 -0
- package/dist/workspace/styles.js.map +1 -0
- 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"}
|