@issuegraph/editor 0.4.0 → 0.22.1
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/README.md +65 -9
- package/dist/a11y/baseline.d.ts +182 -0
- package/dist/a11y/baseline.d.ts.map +1 -0
- package/dist/a11y/baseline.js +434 -0
- package/dist/a11y/baseline.js.map +1 -0
- package/dist/audit/findings.d.ts +107 -1
- package/dist/audit/findings.d.ts.map +1 -1
- package/dist/audit/findings.js +113 -6
- package/dist/audit/findings.js.map +1 -1
- package/dist/audit/panel.d.ts +277 -0
- package/dist/audit/panel.d.ts.map +1 -0
- package/dist/audit/panel.js +462 -0
- package/dist/audit/panel.js.map +1 -0
- package/dist/audit/refused.d.ts +99 -0
- package/dist/audit/refused.d.ts.map +1 -0
- package/dist/audit/refused.js +276 -0
- package/dist/audit/refused.js.map +1 -0
- package/dist/audit/styles.d.ts +1 -1
- package/dist/audit/styles.d.ts.map +1 -1
- package/dist/audit/styles.js +429 -1
- package/dist/audit/styles.js.map +1 -1
- package/dist/audit/surface.d.ts +54 -12
- package/dist/audit/surface.d.ts.map +1 -1
- package/dist/audit/surface.js +53 -13
- package/dist/audit/surface.js.map +1 -1
- package/dist/create/draft.d.ts +36 -0
- package/dist/create/draft.d.ts.map +1 -1
- package/dist/create/draft.js +40 -0
- package/dist/create/draft.js.map +1 -1
- package/dist/create/keys.d.ts +134 -9
- package/dist/create/keys.d.ts.map +1 -1
- package/dist/create/keys.js +119 -20
- package/dist/create/keys.js.map +1 -1
- package/dist/firstpass/candidates.d.ts +17 -1
- package/dist/firstpass/candidates.d.ts.map +1 -1
- package/dist/index.d.ts +42 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +45 -14
- package/dist/index.js.map +1 -1
- package/dist/overlay/render.d.ts +11 -3
- package/dist/overlay/render.d.ts.map +1 -1
- package/dist/overlay/render.js +76 -20
- package/dist/overlay/render.js.map +1 -1
- package/dist/overlay/request.d.ts +71 -0
- package/dist/overlay/request.d.ts.map +1 -0
- package/dist/overlay/request.js +129 -0
- package/dist/overlay/request.js.map +1 -0
- package/dist/overlay/styles.d.ts +1 -1
- package/dist/overlay/styles.d.ts.map +1 -1
- package/dist/overlay/styles.js +78 -7
- package/dist/overlay/styles.js.map +1 -1
- package/dist/picker/render.d.ts +26 -16
- package/dist/picker/render.d.ts.map +1 -1
- package/dist/picker/render.js +25 -36
- package/dist/picker/render.js.map +1 -1
- package/dist/picker/styles.d.ts +7 -7
- package/dist/picker/styles.d.ts.map +1 -1
- package/dist/picker/styles.js +7 -36
- package/dist/picker/styles.js.map +1 -1
- package/dist/picker/view.d.ts +8 -4
- package/dist/picker/view.d.ts.map +1 -1
- package/dist/picker/view.js +4 -3
- package/dist/picker/view.js.map +1 -1
- package/dist/picker/words.d.ts +12 -10
- package/dist/picker/words.d.ts.map +1 -1
- package/dist/picker/words.js +12 -8
- package/dist/picker/words.js.map +1 -1
- package/dist/reevaluate/parts.d.ts +130 -0
- package/dist/reevaluate/parts.d.ts.map +1 -0
- package/dist/reevaluate/parts.js +260 -0
- package/dist/reevaluate/parts.js.map +1 -0
- package/dist/reevaluate/render.d.ts +18 -11
- package/dist/reevaluate/render.d.ts.map +1 -1
- package/dist/reevaluate/render.js +21 -135
- package/dist/reevaluate/render.js.map +1 -1
- package/dist/reevaluate/styles.d.ts +1 -1
- package/dist/reevaluate/styles.d.ts.map +1 -1
- package/dist/reevaluate/styles.js +26 -0
- package/dist/reevaluate/styles.js.map +1 -1
- package/dist/reevaluate/view.d.ts +16 -1
- package/dist/reevaluate/view.d.ts.map +1 -1
- package/dist/reevaluate/view.js +21 -1
- package/dist/reevaluate/view.js.map +1 -1
- package/dist/reevaluate/words.d.ts +7 -0
- package/dist/reevaluate/words.d.ts.map +1 -1
- package/dist/scale/ladder.d.ts +62 -6
- package/dist/scale/ladder.d.ts.map +1 -1
- package/dist/scale/ladder.js +129 -11
- package/dist/scale/ladder.js.map +1 -1
- package/dist/scale/render.d.ts +44 -0
- package/dist/scale/render.d.ts.map +1 -1
- package/dist/scale/render.js +206 -28
- package/dist/scale/render.js.map +1 -1
- package/dist/scale/styles.d.ts +11 -5
- package/dist/scale/styles.d.ts.map +1 -1
- package/dist/scale/styles.js +133 -4
- package/dist/scale/styles.js.map +1 -1
- package/dist/workspace/chrome.d.ts +1 -1
- package/dist/workspace/chrome.d.ts.map +1 -1
- package/dist/workspace/chrome.js +48 -0
- package/dist/workspace/chrome.js.map +1 -1
- package/dist/workspace/firstpass.d.ts +166 -0
- package/dist/workspace/firstpass.d.ts.map +1 -0
- package/dist/workspace/firstpass.js +151 -0
- package/dist/workspace/firstpass.js.map +1 -0
- package/dist/workspace/host.d.ts +283 -11
- package/dist/workspace/host.d.ts.map +1 -1
- package/dist/workspace/host.js +614 -43
- package/dist/workspace/host.js.map +1 -1
- package/dist/workspace/inspector.d.ts +72 -2
- package/dist/workspace/inspector.d.ts.map +1 -1
- package/dist/workspace/inspector.js +26 -0
- package/dist/workspace/inspector.js.map +1 -1
- package/dist/workspace/mount.d.ts +75 -6
- package/dist/workspace/mount.d.ts.map +1 -1
- package/dist/workspace/mount.js +1851 -60
- package/dist/workspace/mount.js.map +1 -1
- package/dist/workspace/rail.d.ts +5 -2
- package/dist/workspace/rail.d.ts.map +1 -1
- package/dist/workspace/rail.js.map +1 -1
- package/dist/workspace/recovery.d.ts +155 -0
- package/dist/workspace/recovery.d.ts.map +1 -0
- package/dist/workspace/recovery.js +156 -0
- package/dist/workspace/recovery.js.map +1 -0
- package/dist/workspace/render.d.ts +779 -2
- package/dist/workspace/render.d.ts.map +1 -1
- package/dist/workspace/render.js +1727 -61
- package/dist/workspace/render.js.map +1 -1
- package/dist/workspace/selection.d.ts +11 -0
- package/dist/workspace/selection.d.ts.map +1 -1
- package/dist/workspace/selection.js +10 -0
- package/dist/workspace/selection.js.map +1 -1
- package/dist/workspace/styles.d.ts +1 -1
- package/dist/workspace/styles.d.ts.map +1 -1
- package/dist/workspace/styles.js +1405 -42
- package/dist/workspace/styles.js.map +1 -1
- package/package.json +4 -4
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* |-----------|-------------------------------------------------------|
|
|
10
10
|
* | header | `auditOverlay` + `renderAuditHeader` |
|
|
11
11
|
* | rail | `renderViewer(…, { projection: 'linear' })`, windowed |
|
|
12
|
-
* | canvas | `renderScaleLadder`
|
|
12
|
+
* | canvas | `canvasToolbar` + `renderScaleLadder` |
|
|
13
13
|
* | inspector | `inspectorView`, rendered here |
|
|
14
14
|
*
|
|
15
15
|
* ## The audit left-bar is applied to the rail's SPEC, never to its markup
|
|
@@ -49,17 +49,83 @@
|
|
|
49
49
|
* `styles.ts` writes structure against its custom properties.
|
|
50
50
|
*/
|
|
51
51
|
import { type Theme, type ViewerDocument } from '@issuegraph/viewer';
|
|
52
|
-
import type { ProjectedEdge } from '@issuegraph/store';
|
|
52
|
+
import type { EdgeId, InvalidCode, MutationId, OrderChange, OrderStatus, ProjectedEdge } from '@issuegraph/store';
|
|
53
53
|
import type { AuditInput } from '../audit/findings.ts';
|
|
54
|
+
import { type AuditWords } from '../audit/panel.ts';
|
|
55
|
+
import { type EncodingRefusedBlockOptions } from '../audit/refused.ts';
|
|
56
|
+
import { type CreateDraft } from '../create/draft.ts';
|
|
57
|
+
import type { Point } from '../create/placement.ts';
|
|
58
|
+
import type { ChangeWords } from '../reevaluate/words.ts';
|
|
54
59
|
import { type AuditOverlay } from '../audit/surface.ts';
|
|
55
60
|
import { type ScaleState } from '../scale/commands.ts';
|
|
56
61
|
import { type InspectorView } from './inspector.ts';
|
|
57
62
|
import { type RailWindow, type RailWindowOptions } from './rail.ts';
|
|
63
|
+
import { type ConflictDiff } from './recovery.ts';
|
|
58
64
|
import { type WorkspaceSelection } from './selection.ts';
|
|
59
65
|
/** The four fixed positions. A closed union, which is what keeps `zone` safe. */
|
|
60
66
|
export declare const ZONES: readonly ["header", "rail", "canvas", "inspector"];
|
|
61
67
|
export type Zone = (typeof ZONES)[number];
|
|
62
68
|
export interface WorkspaceWords {
|
|
69
|
+
/**
|
|
70
|
+
* §17c's vocabulary — the change summary's facets, its dismiss control and
|
|
71
|
+
* its direction words.
|
|
72
|
+
*
|
|
73
|
+
* OPTIONAL, AND ITS ABSENCE DRAWS NOTHING rather than drawing a default.
|
|
74
|
+
* `ChangeWords` states the doctrine and this obeys it exactly: the store
|
|
75
|
+
* ships the change as COUNTS so a host writes the sentence in its own
|
|
76
|
+
* language, and a default supplied here would take that choice back one layer
|
|
77
|
+
* down. So a host that has not supplied these gets no summary and no chips —
|
|
78
|
+
* not an English summary it cannot translate.
|
|
79
|
+
*
|
|
80
|
+
* It is the one shape that also keeps this a non-breaking addition: every
|
|
81
|
+
* host built against 0.12 renders exactly as it did.
|
|
82
|
+
*/
|
|
83
|
+
readonly change?: ChangeWords | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* Reads the freshness stamp in §17a's header — the frame's `as of 14:32 ↻`,
|
|
86
|
+
* minus the clock.
|
|
87
|
+
*
|
|
88
|
+
* THE NUMBER IS APPENDED, NOT INTERPOLATED, exactly as {@link whyRank}'s is
|
|
89
|
+
* and for the reason stated there: a `{n}` template makes every host
|
|
90
|
+
* reimplement the substitution, which is how a package that refuses to invent
|
|
91
|
+
* English ships a tiny template language instead. `Freshness.asOf` and
|
|
92
|
+
* `Freshness.age` are the host's own strings and are printed verbatim.
|
|
93
|
+
*
|
|
94
|
+
* THE STALE FLAG CARRIES NO WORD, unlike layer 1's stamp, which appends a
|
|
95
|
+
* literal ` · stale`. §17a's stamp is `as of 14:32 ↻` and says nothing more,
|
|
96
|
+
* so staleness rides on `data-stale` for the stylesheet to treat. A word here
|
|
97
|
+
* would be a fourth thing to translate for a state the frame draws silently.
|
|
98
|
+
*/
|
|
99
|
+
readonly asOf: string;
|
|
100
|
+
/**
|
|
101
|
+
* The noun after the backlog's total — frame 17a's `312 open`.
|
|
102
|
+
*
|
|
103
|
+
* IT WORDS `Adoption.counts.total`, WHICH IS NOT `OrderCounts`. Two different
|
|
104
|
+
* pairs reach this surface and only one of them is §17a's header content: the
|
|
105
|
+
* adoption pair says how much of the backlog is encoded, and the order pair
|
|
106
|
+
* (`ranked` / `readyNow` / `held`) says how the order splits. The second stays
|
|
107
|
+
* in the rail, where it belongs to the order it describes.
|
|
108
|
+
*/
|
|
109
|
+
readonly open: string;
|
|
110
|
+
/**
|
|
111
|
+
* The noun after the encoded count — frame 17a's `64 encoded`, wording
|
|
112
|
+
* `Adoption.counts.declaring`.
|
|
113
|
+
*
|
|
114
|
+
* SEPARATE FROM {@link open} RATHER THAN ONE PHRASE, because the two numbers
|
|
115
|
+
* are the package's and the two nouns are the host's; a single string would
|
|
116
|
+
* have to carry both figures and would be a template again.
|
|
117
|
+
*/
|
|
118
|
+
readonly encoded: string;
|
|
119
|
+
/**
|
|
120
|
+
* The panel's own heading — frame 17a's `INSPECTOR`.
|
|
121
|
+
*
|
|
122
|
+
* UNCONDITIONAL, and that is what it is for: the zone used to begin with
|
|
123
|
+
* whatever the selection happened to resolve to, so a reader who had selected
|
|
124
|
+
* nothing met a bare sentence in an unnamed column. The caps are the
|
|
125
|
+
* stylesheet's, as they already are for `WHY RANK` and `RELATIONSHIPS`, so
|
|
126
|
+
* the word supplied here reads as a word.
|
|
127
|
+
*/
|
|
128
|
+
readonly inspector: string;
|
|
63
129
|
/** Shown in the inspector when nothing is selected. */
|
|
64
130
|
readonly nothingSelected: string;
|
|
65
131
|
/**
|
|
@@ -72,8 +138,532 @@ export interface WorkspaceWords {
|
|
|
72
138
|
* command it publishes is `clear`, and that is the whole of its behaviour.
|
|
73
139
|
*/
|
|
74
140
|
readonly clearSelection: string;
|
|
141
|
+
/**
|
|
142
|
+
* §17k's strip control while the canvas is collapsed — "open the canvas".
|
|
143
|
+
*
|
|
144
|
+
* REQUIRED, UNLIKE {@link CanvasWords}, AND THAT IS THE WHOLE REASON IT IS
|
|
145
|
+
* NOT IN IT. The canvas caption is optional because a surface without it
|
|
146
|
+
* still draws every fact; this control is the only way back out of §17k's
|
|
147
|
+
* strip, and a host that omitted it would ship a layout with a zone the
|
|
148
|
+
* reader cannot reach below `1120`. An unreachable zone is not a degraded
|
|
149
|
+
* caption.
|
|
150
|
+
*
|
|
151
|
+
* IT NAMES THE ZONE, NOT A DIRECTION. The strip is drawn at the canvas's own
|
|
152
|
+
* track, so "open" and "show" both read as acting on what is beside the word;
|
|
153
|
+
* the fixture and the demo both say which zone out loud, because a strip four
|
|
154
|
+
* characters wide, turned on its side, carries no other label.
|
|
155
|
+
*/
|
|
156
|
+
readonly showCanvas: string;
|
|
157
|
+
/**
|
|
158
|
+
* The same control while the canvas is open.
|
|
159
|
+
*
|
|
160
|
+
* ITS OWN MEMBER RATHER THAN REUSING {@link showCanvas}, on
|
|
161
|
+
* {@link RailWords.hide}'s reasoning exactly: the control carries
|
|
162
|
+
* `aria-expanded`, which flips, and a label that did not flip with it would
|
|
163
|
+
* tell a screen-reader user the canvas is open and offer to open it in one
|
|
164
|
+
* breath.
|
|
165
|
+
*/
|
|
166
|
+
readonly hideCanvas: string;
|
|
75
167
|
/** Names the relationships list. */
|
|
76
168
|
readonly relationships: string;
|
|
169
|
+
/**
|
|
170
|
+
* §17a's `WHY RANK n` heading, without the number.
|
|
171
|
+
*
|
|
172
|
+
* THE NUMBER IS APPENDED, NOT INTERPOLATED INTO A HOST TEMPLATE. A rank is
|
|
173
|
+
* the one part of this heading the package knows and the host does not, and
|
|
174
|
+
* a `{n}` placeholder would make every host reimplement the substitution —
|
|
175
|
+
* which is how a package that refuses to invent English ends up shipping a
|
|
176
|
+
* tiny template language instead.
|
|
177
|
+
*/
|
|
178
|
+
readonly whyRank: string;
|
|
179
|
+
/**
|
|
180
|
+
* The same heading for a HELD slot, which has no rank to name.
|
|
181
|
+
*
|
|
182
|
+
* A held slot's rank is `null` by construction (`@issuegraph/derive` assigns
|
|
183
|
+
* `ready ? (rank += 1) : null`), so "why rank —" would be a heading about a
|
|
184
|
+
* position that does not exist. The design's own §16d ruling is the same one:
|
|
185
|
+
* a held unit prints the em dash rather than a number.
|
|
186
|
+
*/
|
|
187
|
+
readonly whyHeld: string;
|
|
188
|
+
/**
|
|
189
|
+
* What the rail says when the audit filter is on and NOTHING matched it:
|
|
190
|
+
* §17's *"No flagged rows match."*
|
|
191
|
+
*
|
|
192
|
+
* A SEPARATE STRING FROM EVERY OTHER EMPTY SENTENCE, because it is about a
|
|
193
|
+
* different world. The others say the order is short; this one says the order
|
|
194
|
+
* is intact and the view is narrow.
|
|
195
|
+
*/
|
|
196
|
+
readonly filteredEmpty: string;
|
|
197
|
+
/**
|
|
198
|
+
* The count that makes the sentence above unmisreadable: *"8 issues are
|
|
199
|
+
* ranked"*.
|
|
200
|
+
*
|
|
201
|
+
* A FUNCTION, WHICH IS THE ONE PLACE THIS FILE TAKES ONE, and the reason is
|
|
202
|
+
* the reason `whyRank` refuses to: there the number is APPENDED to a fixed
|
|
203
|
+
* phrase and a host can write the phrase; here the number sits INSIDE the
|
|
204
|
+
* sentence and its grammar moves with it — *"1 issue is ranked"* against
|
|
205
|
+
* *"8 issues are ranked"*. A fixed string plus a number cannot say both, and
|
|
206
|
+
* a `{n}` placeholder would make every host implement the substitution. So
|
|
207
|
+
* the host is handed the count and writes its own sentence, in its own
|
|
208
|
+
* language, with its own plural rule.
|
|
209
|
+
*/
|
|
210
|
+
readonly filteredDenominator: (ranked: number) => string;
|
|
211
|
+
/** The way back out, which §17d requires be one click: *"clear the filter"*. */
|
|
212
|
+
readonly filteredClear: string;
|
|
213
|
+
/**
|
|
214
|
+
* What the rail says while the filter is on AND some rows still match.
|
|
215
|
+
*
|
|
216
|
+
* A SECOND HEADLINE RATHER THAN A REUSE OF `filteredEmpty`, because they are
|
|
217
|
+
* opposite statements: that one says nothing matched, this one says the list
|
|
218
|
+
* is narrowed. Saying "no flagged rows match" above three flagged rows is the
|
|
219
|
+
* false statement §2 wrote its ruling about.
|
|
220
|
+
*/
|
|
221
|
+
readonly filteredActive: string;
|
|
222
|
+
/**
|
|
223
|
+
* Joins the rest of a together unit: §17a ends *"then worked with #514 as one
|
|
224
|
+
* unit"*, and this is that phrase minus the members, which the package names.
|
|
225
|
+
*/
|
|
226
|
+
readonly workedAsOneUnit: string;
|
|
227
|
+
/**
|
|
228
|
+
* Stated when the subject has no relationships at all.
|
|
229
|
+
*
|
|
230
|
+
* A STATED EMPTY IS NOT THE SAME AS AN ABSENT LIST. The panel used to draw
|
|
231
|
+
* the `RELATIONSHIPS` heading and then nothing under it, which reads as a
|
|
232
|
+
* list that failed to load rather than as an issue that is genuinely
|
|
233
|
+
* unrelated to everything — and "this issue is connected to nothing" is one
|
|
234
|
+
* of the more actionable things a grooming surface can say.
|
|
235
|
+
*/
|
|
236
|
+
readonly noRelationships: string;
|
|
237
|
+
/**
|
|
238
|
+
* The control that begins a relationship from the selected issue — frame
|
|
239
|
+
* 17a's `+ add`, publishing `add`.
|
|
240
|
+
*
|
|
241
|
+
* MOVED HERE FROM `MountWords`, where it was while the mount drew the button.
|
|
242
|
+
* `renderWorkspace` is the published surface and the panel is now what draws
|
|
243
|
+
* it, so a host rendering the markup without mounting gets the control rather
|
|
244
|
+
* than a panel that can only be read. `MountWords` extends this, so the mount
|
|
245
|
+
* still reads the same field and no host supplies two.
|
|
246
|
+
*/
|
|
247
|
+
readonly addRelationship: string;
|
|
248
|
+
/**
|
|
249
|
+
* §17a's `ADD RELATIONSHIP` heading, over the numbered kinds.
|
|
250
|
+
*
|
|
251
|
+
* NOT {@link WorkspaceWords.addRelationship}, AND THE FRAME IS WHY. That
|
|
252
|
+
* field is the CONTROL's label — frame 17a writes it `+ add`, on the
|
|
253
|
+
* relationships header row — and this is the HEADING over the five numbered
|
|
254
|
+
* choices, which the frame writes in full. They are two strings in the frame
|
|
255
|
+
* and reusing one for both would put a control's label where a section name
|
|
256
|
+
* belongs, in a panel whose other two headings are already section names.
|
|
257
|
+
*
|
|
258
|
+
* REQUIRED, on the same rule the three header nouns took. An optional field
|
|
259
|
+
* would draw the heading only for a host that opted in, leaving every other
|
|
260
|
+
* host with the frame's two moved placements and not its third — a panel that
|
|
261
|
+
* half-applies the pass this exists to complete. The usual argument for
|
|
262
|
+
* optional, that a host built against the previous version renders
|
|
263
|
+
* byte-identically, has nothing to preserve here: this release moves `+ add`
|
|
264
|
+
* and the kind digits for every host regardless of what it passes.
|
|
265
|
+
*/
|
|
266
|
+
readonly addRelationshipHeading: string;
|
|
267
|
+
/**
|
|
268
|
+
* The control that abandons a draft — frame 17b's escape hatch beside the
|
|
269
|
+
* numbered kind list, publishing `cancel`.
|
|
270
|
+
*
|
|
271
|
+
* MOVED HERE FROM `MountWords` WITH THE STEP IT BELONGS TO. The kind list and
|
|
272
|
+
* the control that withdraws from it are one affordance; leaving the cancel
|
|
273
|
+
* in host chrome while the list moved into the package would have split a
|
|
274
|
+
* control group across two layers, and a draft the reader cannot abandon with
|
|
275
|
+
* a pointer is the failure `create/keys.ts` names for the keyboard. The
|
|
276
|
+
* mount's target-search step still reads this field, through the extension.
|
|
277
|
+
*/
|
|
278
|
+
readonly cancel: string;
|
|
279
|
+
/**
|
|
280
|
+
* Names the issue a live draft was begun from, when that is not the panel's
|
|
281
|
+
* subject — "relating from", read before the reference the package supplies.
|
|
282
|
+
*
|
|
283
|
+
* IT EXISTS BECAUSE THE TWO CAN DIVERGE AND THE DRAFT MUST STILL BE
|
|
284
|
+
* CANCELLABLE. See {@link createStep}: a draft begun by `R` on a together
|
|
285
|
+
* unit's non-lead member, or one outlived by a change of selection, leaves
|
|
286
|
+
* the kind step live under a panel headed by a different issue. The step is
|
|
287
|
+
* drawn anyway — it carries the only pointer cancel there is — so it has to
|
|
288
|
+
* say whose it is, and the package invents that phrase no more readily than
|
|
289
|
+
* it invents {@link WorkspaceWords.workedAsOneUnit}. The REFERENCE is the
|
|
290
|
+
* package's, appended as its own element, so a host writes a phrase and never
|
|
291
|
+
* a template.
|
|
292
|
+
*/
|
|
293
|
+
readonly relatingFrom: string;
|
|
294
|
+
/**
|
|
295
|
+
* The accessible name of a row's remove control — the `✕` in frame 17a's
|
|
296
|
+
* right-hand slot.
|
|
297
|
+
*
|
|
298
|
+
* ROW-INDEPENDENT WORDING. Which relationship it removes is in the markup
|
|
299
|
+
* (`data-ig-target`) and in the row the button sits on, not in this string, so
|
|
300
|
+
* a host writing "remove #488" here would be wrong on every other row.
|
|
301
|
+
*
|
|
302
|
+
* DISTINCT FROM `MountWords.deleteRelationship`, which names a different
|
|
303
|
+
* control: that is the mount's labelled button for the one SELECTED edge, and
|
|
304
|
+
* this is a glyph button repeated once per row. A glyph carries no name of its
|
|
305
|
+
* own — see {@link relationshipSpec} — so the two cannot share one string
|
|
306
|
+
* without one of them reading wrongly.
|
|
307
|
+
*/
|
|
308
|
+
readonly remove: string;
|
|
309
|
+
/**
|
|
310
|
+
* The right-hand marker on an incoming row — frame 17a's `inbound`.
|
|
311
|
+
*
|
|
312
|
+
* IT IS A STATED FACT, NOT A MISSING CONTROL. An inbound edge's field lives in
|
|
313
|
+
* the OTHER issue's body, so this panel's subject cannot declare it away; the
|
|
314
|
+
* slot says which relationship this is rather than leaving a gap where the
|
|
315
|
+
* other rows have a `✕`.
|
|
316
|
+
*/
|
|
317
|
+
readonly inbound: string;
|
|
318
|
+
/**
|
|
319
|
+
* The control that reverses a directed relationship — frame 17b's `⇅ flip`.
|
|
320
|
+
*
|
|
321
|
+
* MOVED HERE FROM `PickerWords` WITH THE CONTROL ITSELF, on the same rule
|
|
322
|
+
* that brought `addRelationship` and `cancel` down from `MountWords`: the
|
|
323
|
+
* surface that draws a control owns the word for it, so a host rendering the
|
|
324
|
+
* markup without mounting gets the affordance rather than a panel it can only
|
|
325
|
+
* read. §17b calls direction “the single most common encoding mistake”, which
|
|
326
|
+
* made this the worst control to have reachable from one rendering path only.
|
|
327
|
+
*
|
|
328
|
+
* IT IS NOT #144's MOVE, AND THE DIFFERENCE IS VISIBLE TO EVERY HOST.
|
|
329
|
+
* `addRelationship` travelled between two interfaces in one `extends` chain,
|
|
330
|
+
* so `words.addRelationship` read the same before and after. `MountWords`
|
|
331
|
+
* holds the picker's vocabulary in a NESTED field, so this word's path
|
|
332
|
+
* changed: `words.picker.flip` became `words.flip`.
|
|
333
|
+
*
|
|
334
|
+
* ROW-INDEPENDENT WORDING, exactly as {@link WorkspaceWords.remove} is. Which
|
|
335
|
+
* relationship it reverses is the selected row it sits in, never this string.
|
|
336
|
+
*/
|
|
337
|
+
readonly flip: string;
|
|
338
|
+
/**
|
|
339
|
+
* Why the store refused an edit, keyed by its code.
|
|
340
|
+
*
|
|
341
|
+
* TOTAL OVER `InvalidCode`, so a code the store adds is a compile error in
|
|
342
|
+
* every host rather than a capsule that renders blank on the one refusal
|
|
343
|
+
* nobody anticipated. The store's own `InvalidReason.message` is deliberately
|
|
344
|
+
* not used: it is one sentence in one language chosen by the layer that
|
|
345
|
+
* detected the refusal, and `overlay/grammar.ts` already records that an
|
|
346
|
+
* invalid edge's sentence is the host's, keyed off the code.
|
|
347
|
+
*/
|
|
348
|
+
readonly refusals: Readonly<Record<InvalidCode, string>>;
|
|
349
|
+
/**
|
|
350
|
+
* §17b's recovery cards, in the host's own words.
|
|
351
|
+
*
|
|
352
|
+
* `retry` AND `retryOnLatest` ARE TWO WORDS BECAUSE THEY ARE TWO CALLS. §17b
|
|
353
|
+
* names the failed affordance "retry" and the conflict affordance "retry on
|
|
354
|
+
* latest", and they reach `store.retry` and `store.retryOnLatest`
|
|
355
|
+
* respectively — the second of which re-reads and ADOPTS the upstream
|
|
356
|
+
* document as the new base before re-dispatching. One string for both would
|
|
357
|
+
* make one card lie about what pressing it does, and it would be the card
|
|
358
|
+
* whose consequence most needs stating.
|
|
359
|
+
*/
|
|
360
|
+
readonly recovery: RecoveryWords;
|
|
361
|
+
/**
|
|
362
|
+
* §17d's findings panel, in the host's own words.
|
|
363
|
+
*
|
|
364
|
+
* NESTED, like `recovery` and `refusals` beside it, because it is a record
|
|
365
|
+
* over a closed class table rather than a loose string — flattening four
|
|
366
|
+
* chips and four titles into this interface would put eight members here
|
|
367
|
+
* whose relationship to each other is only legible from their names.
|
|
368
|
+
*/
|
|
369
|
+
readonly audit: AuditWords;
|
|
370
|
+
/**
|
|
371
|
+
* The canvas toolbar's words — frame 17a's caption over the graph.
|
|
372
|
+
*
|
|
373
|
+
* OPTIONAL, AND ITS ABSENCE DRAWS NO TOOLBAR, on `change`'s reasoning
|
|
374
|
+
* exactly: a required member here would break every host built against the
|
|
375
|
+
* current version, and a default supplied here would be this package writing
|
|
376
|
+
* an English caption for a host that may not speak English. So a host that
|
|
377
|
+
* has not worded the row gets the canvas it already had.
|
|
378
|
+
*/
|
|
379
|
+
readonly canvas?: CanvasWords | undefined;
|
|
380
|
+
/**
|
|
381
|
+
* The order rail's footer — frame 17a's `248 with no relationships · show`.
|
|
382
|
+
*
|
|
383
|
+
* OPTIONAL, AND ITS ABSENCE DRAWS NO FOOTER, on `canvas`'s reasoning exactly:
|
|
384
|
+
* a required member here would break every host built against the current
|
|
385
|
+
* version, and a default supplied here would be this package writing English
|
|
386
|
+
* for a host that may not speak it.
|
|
387
|
+
*
|
|
388
|
+
* IT IS ALSO THE ONE WORDS MEMBER WHOSE PRESENCE MOVES SOMETHING IN ANOTHER
|
|
389
|
+
* ZONE, and that is worth stating where a host reads it. Supplying this draws
|
|
390
|
+
* the count and its control at the foot of the rail and turns the canvas
|
|
391
|
+
* ladder's own chip off, so the surface offers one control rather than two.
|
|
392
|
+
* The list the control opens is drawn in the CANVAS either way — the rail is
|
|
393
|
+
* virtualized and cannot hold it; `railFooter` records the arithmetic. The
|
|
394
|
+
* `isolatedChip` argument at the ladder call records why the two conditions
|
|
395
|
+
* are safely the same.
|
|
396
|
+
*/
|
|
397
|
+
readonly rail?: RailWords | undefined;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Frame 17a's canvas toolbar reads `focus: #512 · 1 hop · 6 of 312 shown`, and
|
|
401
|
+
* every word in it is the host's.
|
|
402
|
+
*
|
|
403
|
+
* NUMBERS ARE APPENDED, NEVER INTERPOLATED, which is why this is four words
|
|
404
|
+
* rather than one sentence with two `{n}` holes in it. {@link
|
|
405
|
+
* WorkspaceWords.whyRank} states the doctrine and the reason: a template makes
|
|
406
|
+
* every host reimplement the substitution. The row is assembled as spans —
|
|
407
|
+
* `<focus> <key> · <n> <of> <total> <shown>` — so a host whose language orders
|
|
408
|
+
* those differently is no worse off than it is with `whyRank`, and no host has
|
|
409
|
+
* to parse a format string.
|
|
410
|
+
*
|
|
411
|
+
* THE DOCTRINE IS THIS INTERFACE'S, NOT THE WHOLE PACKAGE'S, and saying
|
|
412
|
+
* otherwise would be refuted by the markup directly beneath this row. The scale
|
|
413
|
+
* module writes English with numbers in it — the refusal sentence
|
|
414
|
+
* (`scale/ladder.ts`), the isolated chip's label, and a literal button label in
|
|
415
|
+
* `scale/render.ts`. That is a pre-existing inconsistency; the row follows
|
|
416
|
+
* `WorkspaceWords`' rule because the row is composed in the workspace, and it
|
|
417
|
+
* neither resolves nor extends the other one.
|
|
418
|
+
*
|
|
419
|
+
* THE FRAME'S `1 hop` IS NOT HERE, and its absence is a decision rather than an
|
|
420
|
+
* omission. `scaleLadder` narrows the canvas to a whole CONNECTED COMPONENT,
|
|
421
|
+
* not to an n-hop ball around the focus: there is no hop radius in
|
|
422
|
+
* `ScaleState`, in `ScaleLadder`, or in the viewer's graph projection, and a
|
|
423
|
+
* component can be ten hops deep. Printing `1 hop` would state a bound the
|
|
424
|
+
* canvas does not honour — the same defect as a rank the rail is not sure of —
|
|
425
|
+
* so the row prints what the ladder computed and nothing else.
|
|
426
|
+
*/
|
|
427
|
+
export interface CanvasWords {
|
|
428
|
+
/**
|
|
429
|
+
* Leads the focus clause — the frame's `focus:`, minus the colon, which is
|
|
430
|
+
* punctuation this package draws rather than a word a host translates.
|
|
431
|
+
*/
|
|
432
|
+
readonly focus: string;
|
|
433
|
+
/**
|
|
434
|
+
* Sits between the two counts: the frame's `6 OF 312`.
|
|
435
|
+
*
|
|
436
|
+
* ITS OWN MEMBER RATHER THAN PART OF `shown`, because the two numbers
|
|
437
|
+
* straddle it. Folding it into either neighbour would put a number in the
|
|
438
|
+
* middle of a host's string, which is the interpolation this interface's
|
|
439
|
+
* whole shape exists to avoid.
|
|
440
|
+
*/
|
|
441
|
+
readonly of: string;
|
|
442
|
+
/** Trails the counts — the frame's `6 of 312 SHOWN`. */
|
|
443
|
+
readonly shown: string;
|
|
444
|
+
/**
|
|
445
|
+
* The trailing pill — the frame's `edit mode`.
|
|
446
|
+
*
|
|
447
|
+
* A STATE, NOT A CONTROL. The workspace is the editing surface and there is
|
|
448
|
+
* no read-only workspace to toggle into, so this names what the reader is
|
|
449
|
+
* looking at rather than offering to change it. It is drawn as text, never as
|
|
450
|
+
* a button, because a pill that looked pressable and did nothing is the dead
|
|
451
|
+
* control `headerControls` refuses to draw.
|
|
452
|
+
*
|
|
453
|
+
* OPTIONAL AND SEPARATELY SO, on `headerControls`' own reasoning rather than
|
|
454
|
+
* on this group's. `renderWorkspace` returns markup, and markup can be served
|
|
455
|
+
* without `mountWorkspace` — nothing on such a surface is editable, so a pill
|
|
456
|
+
* asserting that it is would be a claim the render layer cannot honour. The
|
|
457
|
+
* host knows which it built, so the host says: supply this only where the
|
|
458
|
+
* surface was mounted. Absent, the caption still draws and the pill does not.
|
|
459
|
+
*/
|
|
460
|
+
readonly editMode?: string | undefined;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Frame 17a's rail footer reads `248 with no relationships · show`, and every
|
|
464
|
+
* word in it is the host's.
|
|
465
|
+
*
|
|
466
|
+
* NUMBERS ARE APPENDED, NEVER INTERPOLATED — {@link CanvasWords} states the
|
|
467
|
+
* doctrine and {@link WorkspaceWords.whyRank} the reason. The row is assembled
|
|
468
|
+
* as spans, `<count> <isolated>`, so a host is never handed a format string.
|
|
469
|
+
*
|
|
470
|
+
* `IsolatedChip.label` IS NOT REUSED, and the reason is the one `CanvasWords`
|
|
471
|
+
* already records: the scale module writes English with numbers in it, that is
|
|
472
|
+
* a pre-existing inconsistency, and a row composed in the workspace follows the
|
|
473
|
+
* workspace's rule rather than inheriting it. Reusing the label here would have
|
|
474
|
+
* been the cheaper line and would have imported the defect.
|
|
475
|
+
*/
|
|
476
|
+
export interface RailWords {
|
|
477
|
+
/** Trails the count — the frame's `248 WITH NO RELATIONSHIPS`. */
|
|
478
|
+
readonly isolated: string;
|
|
479
|
+
/** The toggle while the list is closed — the frame's `show`. */
|
|
480
|
+
readonly show: string;
|
|
481
|
+
/**
|
|
482
|
+
* The toggle while the list is open.
|
|
483
|
+
*
|
|
484
|
+
* ITS OWN MEMBER RATHER THAN REUSING `show`. The control carries
|
|
485
|
+
* `aria-expanded`, which flips; a label that did not flip with it would tell
|
|
486
|
+
* a screen-reader user the list is open and offer to open it in one breath.
|
|
487
|
+
*/
|
|
488
|
+
readonly hide: string;
|
|
489
|
+
}
|
|
490
|
+
export interface RecoveryWords {
|
|
491
|
+
/** Names the state on a failed card. */
|
|
492
|
+
readonly failed: string;
|
|
493
|
+
/** Names the state on a conflicted card. */
|
|
494
|
+
readonly conflict: string;
|
|
495
|
+
readonly viewDiff: string;
|
|
496
|
+
/** `failed` → `store.retry`. */
|
|
497
|
+
readonly retry: string;
|
|
498
|
+
/** `conflict` → `store.retryOnLatest`, which adopts upstream as the new base. */
|
|
499
|
+
readonly retryOnLatest: string;
|
|
500
|
+
readonly discardMine: string;
|
|
501
|
+
/** Heads the edges the held document has and the reader's does not. */
|
|
502
|
+
readonly upstreamOnly: string;
|
|
503
|
+
/** Heads the reader's own unlanded edit, where it ADDS a relationship. */
|
|
504
|
+
readonly mineOnly: string;
|
|
505
|
+
/** Heads the reader's own unlanded edit, where it REMOVES one. */
|
|
506
|
+
readonly mineRemoved: string;
|
|
507
|
+
/** Heads relationships both sides hold pointing opposite ways. */
|
|
508
|
+
readonly carrierReversed: string;
|
|
509
|
+
/** Heads the issues the two documents disagree about. */
|
|
510
|
+
readonly issuesChanged: string;
|
|
511
|
+
/** Shown when the diff has nothing to show on THIS panel. */
|
|
512
|
+
readonly diffEmpty: string;
|
|
513
|
+
/**
|
|
514
|
+
* Why a `retry on latest` could not even read.
|
|
515
|
+
*
|
|
516
|
+
* The store's refresh is half of one operation: when it fails the record is
|
|
517
|
+
* restored verbatim and nothing is dispatched, so without this the button
|
|
518
|
+
* reads as dead on exactly the failure the store went to trouble to make
|
|
519
|
+
* observable.
|
|
520
|
+
*/
|
|
521
|
+
readonly retryFailed: string;
|
|
522
|
+
/**
|
|
523
|
+
* Heads the region for recoveries with no panel to sit on.
|
|
524
|
+
*
|
|
525
|
+
* See {@link WorkspaceRecovery.carrier}: a write about issues this backlog
|
|
526
|
+
* does not hold has no panel, and dropping it would leave the reader's work
|
|
527
|
+
* unrecoverable.
|
|
528
|
+
*/
|
|
529
|
+
readonly unplaced: string;
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* One unsettled write the reader can still act on: §17b's `failed` and
|
|
533
|
+
* `conflict`.
|
|
534
|
+
*
|
|
535
|
+
* NARROW FOR THE REASON {@link WorkspaceRefusal} IS, and shaped the same way.
|
|
536
|
+
* The whole write ledger is the larger surface this deliberately is not: a
|
|
537
|
+
* `WriteRecord` carries a `Mutation` and, on a conflict, an entire second
|
|
538
|
+
* `GraphDocument`, none of which a renderer can draw. What a card needs is the
|
|
539
|
+
* state, the reason or the difference, and which panel states it.
|
|
540
|
+
*
|
|
541
|
+
* A UNION, BECAUSE THE TWO STATES CARRY DIFFERENT FACTS. `WriteRecord` is
|
|
542
|
+
* already a union on this exact line. Two optionals would admit a conflict with
|
|
543
|
+
* no diff and a failure with one, and the renderer would need a runtime guard
|
|
544
|
+
* for a case the type can refuse outright.
|
|
545
|
+
*
|
|
546
|
+
* NO `phantom` EQUIVALENT, and the absence is deliberate rather than an
|
|
547
|
+
* oversight. {@link WorkspaceRefusal.phantom} exists because a refusal either
|
|
548
|
+
* JOINS a relationship row or REPLACES it, and getting that wrong deletes a
|
|
549
|
+
* removable relationship from the panel. A recovery card is its own element in
|
|
550
|
+
* its own region and replaces nothing, so the question does not arise.
|
|
551
|
+
*/
|
|
552
|
+
export type WorkspaceRecovery = {
|
|
553
|
+
readonly kind: 'failed';
|
|
554
|
+
readonly mutationId: MutationId;
|
|
555
|
+
readonly edgeId: EdgeId;
|
|
556
|
+
readonly carrier: string | null;
|
|
557
|
+
/**
|
|
558
|
+
* The adapter's own sentence, verbatim.
|
|
559
|
+
*
|
|
560
|
+
* NOT KEYED OFF A CODE, and the asymmetry with {@link
|
|
561
|
+
* WorkspaceWords.refusals} is the argument. The store's refusal codes are
|
|
562
|
+
* a CLOSED set the store owns, so a host can be made total over them. An
|
|
563
|
+
* adapter's failure is an open one — a status, a rate limit, a network
|
|
564
|
+
* message — and there is no code to key on, so the sentence travels.
|
|
565
|
+
*/
|
|
566
|
+
readonly reason: string;
|
|
567
|
+
} | {
|
|
568
|
+
readonly kind: 'conflict';
|
|
569
|
+
readonly mutationId: MutationId;
|
|
570
|
+
readonly edgeId: EdgeId;
|
|
571
|
+
readonly carrier: string | null;
|
|
572
|
+
/** Both sides, held apart. See `recovery.ts`. */
|
|
573
|
+
readonly diff: ConflictDiff;
|
|
574
|
+
/**
|
|
575
|
+
* Why the last refresh could not read, or `null`.
|
|
576
|
+
*
|
|
577
|
+
* A `retry on latest` whose READ fails restores the record exactly as it
|
|
578
|
+
* was and dispatches nothing — the conflict keeps its held document so it
|
|
579
|
+
* can still be compared against. Without this the card re-renders
|
|
580
|
+
* identically and the button reads as dead.
|
|
581
|
+
*/
|
|
582
|
+
readonly refreshError: string | null;
|
|
583
|
+
};
|
|
584
|
+
/**
|
|
585
|
+
* One refused edit, as the panel needs it: which edge, and why.
|
|
586
|
+
*
|
|
587
|
+
* NARROW ON PURPOSE, AND THE NARROWNESS IS THE ARGUMENT. `WorkspaceOptions.projected`
|
|
588
|
+
* already carries every edge's states, and `invalid` is among them — but a
|
|
589
|
+
* state says an edge was refused and never says why, because the code lives on
|
|
590
|
+
* `WriteRecord.reason`, which reaches no renderer. The alternative was to hand
|
|
591
|
+
* the panel the whole write ledger, which is the input #137's conflict cards
|
|
592
|
+
* need and is a much larger surface: both versions of a conflicted body, the
|
|
593
|
+
* upstream document, the retry and discard affordances. This is one code per
|
|
594
|
+
* edge id, derivable in a shell from what the store already publishes, and
|
|
595
|
+
* nothing here is blocked on that larger shape.
|
|
596
|
+
*/
|
|
597
|
+
export interface WorkspaceRefusal {
|
|
598
|
+
readonly edgeId: EdgeId;
|
|
599
|
+
readonly code: InvalidCode;
|
|
600
|
+
/**
|
|
601
|
+
* The issue whose panel states this refusal.
|
|
602
|
+
*
|
|
603
|
+
* THE EDIT'S OWN SUBJECT, DECIDED BY WHOEVER MADE THE EDIT. This panel used
|
|
604
|
+
* to work it out for itself, by asking whether the refused edge's identity
|
|
605
|
+
* named the issue on show — a derivation entirely separate from the one that
|
|
606
|
+
* produced the subject, and free to disagree with it. It did, three times
|
|
607
|
+
* over: an unfiltered orphan branch drew every refusal on every panel; a
|
|
608
|
+
* retype whose projection hid the edge the panel was filtered to left the
|
|
609
|
+
* panel with no subject and the reason nowhere; and a draft begun from a
|
|
610
|
+
* together-unit partner produced an edge naming the partner while
|
|
611
|
+
* `inspectorView` had canonicalized the panel onto the slot's lead, so
|
|
612
|
+
* nothing matched. None of the three is a question this renderer can answer
|
|
613
|
+
* from an identity, because none of them is about the identity.
|
|
614
|
+
*
|
|
615
|
+
* A MOUNT KNOWS IT WITHOUT GUESSING, BECAUSE IT WROTE IT DOWN. `editCarrier`
|
|
616
|
+
* in `host.ts` answers it — for a create from a draft, for a retype or flip
|
|
617
|
+
* whose produced identity differs from the one the reader named, and for a
|
|
618
|
+
* delete — and `mountWorkspace` asks it as the edit goes OUT, while the
|
|
619
|
+
* document still holds the relationship, then keeps the answer against the
|
|
620
|
+
* write. It cannot be asked again afterwards: by the time a refusal comes
|
|
621
|
+
* back a sibling write may have removed the edge, and an identity records
|
|
622
|
+
* which two issues a relationship was between and never which of them
|
|
623
|
+
* declared it. A host rendering without `mountWorkspace` states the same fact
|
|
624
|
+
* the same way: whichever issue's panel it wants the refusal read on, decided
|
|
625
|
+
* when it made the edit.
|
|
626
|
+
*
|
|
627
|
+
* NOT DERIVABLE FROM {@link WorkspaceRefusal.edgeId}, which is why it is a
|
|
628
|
+
* second field rather than a lookup. `unknown-issue` names an issue the
|
|
629
|
+
* document does not hold; `unknown-edge` names an edge it does not hold; and
|
|
630
|
+
* a together unit's panel is headed by a key the edge may never mention.
|
|
631
|
+
*/
|
|
632
|
+
readonly carrier: string;
|
|
633
|
+
/**
|
|
634
|
+
* Whether the edge this refusal marks exists only because the refusal does.
|
|
635
|
+
*
|
|
636
|
+
* NOT EVERY REFUSAL IS ABOUT AN EDIT THAT LEFT NO TRACE, and reading them as
|
|
637
|
+
* if they were deleted a relationship the reader still has. Three of the
|
|
638
|
+
* store's codes mark an edge that is already LANDED — measured against the
|
|
639
|
+
* store rather than reasoned from the code list: `duplicate-edge` (a create
|
|
640
|
+
* of a relationship that exists; `project` folds the phantom onto the real
|
|
641
|
+
* edge), and `unchanged-kind` and `symmetric-edge` (a retype or flip whose
|
|
642
|
+
* result has the identity it started from, so `edgeChangeFor` marks the
|
|
643
|
+
* original rather than swapping it). `cardinality` looks like a fourth and is
|
|
644
|
+
* not: through a create it marks the edge it would have made, and through a
|
|
645
|
+
* retype it marks the edge it would have BECOME — a phantom either way, with
|
|
646
|
+
* the original hidden — which is exactly why this is asked of the document
|
|
647
|
+
* rather than keyed off a list of codes. With the capsule REPLACING the row
|
|
648
|
+
* for all of them, telling a reader "this relationship is already declared"
|
|
649
|
+
* removed the declared relationship's row from the panel, remove control
|
|
650
|
+
* included — so the one message they were given was contradicted by the list
|
|
651
|
+
* beside it, and nothing on the surface could undo the edge they had just
|
|
652
|
+
* been told about.
|
|
653
|
+
*
|
|
654
|
+
* TRUE MEANS THE ROW IS THE REFUSAL. A refused CREATE of a genuinely new
|
|
655
|
+
* relationship draws as a capsule in the row it would have been: there is no
|
|
656
|
+
* relationship to operate, and a `select-edge` and a `✕` on it would offer to
|
|
657
|
+
* remove something that was never added. False means the relationship is
|
|
658
|
+
* real, so the row stands and the reason is attached to it.
|
|
659
|
+
*
|
|
660
|
+
* REQUIRED, BECAUSE NEITHER DEFAULT IS SAFE. Defaulted to `true` the panel
|
|
661
|
+
* eats real rows; defaulted to `false` it draws a live remove control for an
|
|
662
|
+
* edge the document does not have. A host derives it in one line — the
|
|
663
|
+
* refused id is in `snapshot.landed` or it is not — and the store's own
|
|
664
|
+
* projection is where both halves already come from.
|
|
665
|
+
*/
|
|
666
|
+
readonly phantom: boolean;
|
|
77
667
|
}
|
|
78
668
|
export interface WorkspaceOptions {
|
|
79
669
|
/**
|
|
@@ -81,11 +671,43 @@ export interface WorkspaceOptions {
|
|
|
81
671
|
* not invent an English sentence, and a default would be one.
|
|
82
672
|
*/
|
|
83
673
|
readonly words: WorkspaceWords;
|
|
674
|
+
/**
|
|
675
|
+
* The store's `lastChange` — what the last landed edit did to the order.
|
|
676
|
+
*
|
|
677
|
+
* ABSENT MEANS NO EDIT HAS LANDED TO REPORT, which is not the same as an edit
|
|
678
|
+
* that landed and moved nothing. The second is a `change` whose summary is
|
|
679
|
+
* `unchanged`, and §17c draws it in the summary's own place because "landed
|
|
680
|
+
* and moved nothing" is the finding an owner auditing an encoding most needs.
|
|
681
|
+
*/
|
|
682
|
+
readonly change?: OrderChange | null | undefined;
|
|
683
|
+
/**
|
|
684
|
+
* The store's `order.status`. `held` labels the rail and greys it one step.
|
|
685
|
+
*
|
|
686
|
+
* THE RANKS DRAWN ARE STILL THE ONES THE CALLER VOUCHED FOR. This says the
|
|
687
|
+
* order is being re-evaluated; it does not re-rank anything, and nothing in
|
|
688
|
+
* this render moves a row because a write is in flight. §122's rule —
|
|
689
|
+
* optimistic rendering yes, optimistic re-ORDERING no — is held by there
|
|
690
|
+
* being no code path here that reorders at all.
|
|
691
|
+
*/
|
|
692
|
+
readonly orderStatus?: OrderStatus | undefined;
|
|
84
693
|
readonly selection?: WorkspaceSelection | undefined;
|
|
85
694
|
/** The ladder's reader position — search, focus, opened isolates. */
|
|
86
695
|
readonly scale?: ScaleState | undefined;
|
|
87
696
|
/** Which slice of the order the rail draws. See {@link railWindow}. */
|
|
88
697
|
readonly rail?: RailWindowOptions | undefined;
|
|
698
|
+
/**
|
|
699
|
+
* The rail rows whose provenance is open, by lead key.
|
|
700
|
+
*
|
|
701
|
+
* §16f's `→ expands provenance` names a per-row state, and the VIEWER CANNOT
|
|
702
|
+
* HOLD ONE: it is a pure renderer, re-run from scratch on every change. So
|
|
703
|
+
* the host owns the set and hands it back in, exactly as `selection` is.
|
|
704
|
+
*
|
|
705
|
+
* ONLY THE RAIL READS IT. The canvas draws stations rather than rows and has
|
|
706
|
+
* no provenance to open, and §17j already shows provenance inline on the wide
|
|
707
|
+
* panel — where the expand affordance would be a control for something
|
|
708
|
+
* already visible.
|
|
709
|
+
*/
|
|
710
|
+
readonly expanded?: readonly string[] | undefined;
|
|
89
711
|
/**
|
|
90
712
|
* The audit's input, when the host has one.
|
|
91
713
|
*
|
|
@@ -108,6 +730,73 @@ export interface WorkspaceOptions {
|
|
|
108
730
|
* Ignored with no audit, because there is nothing to filter by.
|
|
109
731
|
*/
|
|
110
732
|
readonly auditFiltered?: boolean | undefined;
|
|
733
|
+
/**
|
|
734
|
+
* Whether §17d's audit overlay is open.
|
|
735
|
+
*
|
|
736
|
+
* SEPARATE FROM `auditFiltered`, and they used to be one thing. The header
|
|
737
|
+
* count was the filter toggle; `RULINGS.md` §3 makes it this overlay's anchor
|
|
738
|
+
* and moves the filter into the panel head, so the two states are now two
|
|
739
|
+
* questions: which rows the rail shows, and whether the panel is on screen.
|
|
740
|
+
*
|
|
741
|
+
* Ignored with no audit, because there is no panel to open.
|
|
742
|
+
*/
|
|
743
|
+
readonly auditOpen?: boolean | undefined;
|
|
744
|
+
/**
|
|
745
|
+
* Whether §17k's collapsed canvas has been opened — `HostState.canvasOpen`.
|
|
746
|
+
*
|
|
747
|
+
* PUBLISHED AS AN ATTRIBUTE ON THE SURFACE ROOT, and read only by the
|
|
748
|
+
* stylesheet's narrow block. Nothing here decides whether the canvas IS a
|
|
749
|
+
* strip: that is a question about the box the surface was given, the
|
|
750
|
+
* container query is the only party that can answer it, and §17k's
|
|
751
|
+
* implementation note is explicit that the package must not ask the window
|
|
752
|
+
* instead. So this says what the reader asked for and the sheet says whether
|
|
753
|
+
* it is currently drawn.
|
|
754
|
+
*
|
|
755
|
+
* Absent means shut, which is §17k's resting state for the strip.
|
|
756
|
+
*/
|
|
757
|
+
readonly canvasOpen?: boolean | undefined;
|
|
758
|
+
/**
|
|
759
|
+
* Whether the reader has dismissed §17k's lifted inspector —
|
|
760
|
+
* `HostState.inspectorDismissed`.
|
|
761
|
+
*
|
|
762
|
+
* COMBINED WITH THE SELECTION, NEVER READ ALONE. §17k opens the lifted panel
|
|
763
|
+
* "by selection", so the attribute this becomes is `lifted` only when
|
|
764
|
+
* something is selected AND the reader has not pressed Escape since. A render
|
|
765
|
+
* with nothing selected draws the panel dismissed however this is set, which
|
|
766
|
+
* is what keeps an empty "nothing selected" card from floating over the
|
|
767
|
+
* canvas at the middle width.
|
|
768
|
+
*
|
|
769
|
+
* Ignored at the two other widths, where the inspector is a zone.
|
|
770
|
+
*/
|
|
771
|
+
readonly inspectorDismissed?: boolean | undefined;
|
|
772
|
+
/**
|
|
773
|
+
* Take over §17j's rail density, instead of letting the rail's own box decide.
|
|
774
|
+
*
|
|
775
|
+
* FORWARDED, NOT INVENTED. `@issuegraph/viewer` owns the rule and the
|
|
776
|
+
* default: the rail reads its density off the column it was given, and the
|
|
777
|
+
* crossover it uses — `430px` — is that package's default rather than a
|
|
778
|
+
* figure Design published. This exists so a consumer of the EDITOR can reach
|
|
779
|
+
* the same escape hatch without rendering the rail themselves.
|
|
780
|
+
*
|
|
781
|
+
* THE WORKSPACE ITSELF NEEDS IT FOR NOTHING. §17k holds the rail at one
|
|
782
|
+
* measure now — 390 at every width, which is the rule §17k states outright —
|
|
783
|
+
* so this surface never crosses the threshold and never has an opinion. It is
|
|
784
|
+
* threaded for the host's sake, and absent it changes nothing.
|
|
785
|
+
*/
|
|
786
|
+
readonly railDensity?: 'dense' | 'wide' | undefined;
|
|
787
|
+
/**
|
|
788
|
+
* Where an issue lives in the host's own world, for §17d's refused block.
|
|
789
|
+
*
|
|
790
|
+
* THE HOST'S ANSWER BECAUSE IT IS THE ONLY PARTY THAT HAS ONE. A store ref is
|
|
791
|
+
* opaque in here and this package holds no repository identity, so a URL is
|
|
792
|
+
* supplied rather than derived. Absent, `null`, or a scheme the viewer's
|
|
793
|
+
* allowlist refuses all mean the same thing — no link is drawn. See
|
|
794
|
+
* {@link EncodingRefusedBlockOptions.issueUrl}, which owns that rule and the
|
|
795
|
+
* reason an `href` cannot be trusted to escaping alone.
|
|
796
|
+
*
|
|
797
|
+
* Ignored with no audit, because a refusal arrives on the audit's input.
|
|
798
|
+
*/
|
|
799
|
+
readonly issueUrl?: EncodingRefusedBlockOptions['issueUrl'];
|
|
111
800
|
readonly theme?: Theme | undefined;
|
|
112
801
|
/** The selector the theme's custom properties are written onto. */
|
|
113
802
|
readonly themeSelector?: string | undefined;
|
|
@@ -115,8 +804,96 @@ export interface WorkspaceOptions {
|
|
|
115
804
|
* The store's projection, for the canvas to draw each edge's write states.
|
|
116
805
|
* See `ScaleLadderOptions.projected`; the workspace forwards it and reads
|
|
117
806
|
* none of it, because the rail and the inspector draw no line to overlay.
|
|
807
|
+
*
|
|
808
|
+
* AND IT IS NOT WHERE THE INSPECTOR'S REFUSALS COME FROM, which is worth
|
|
809
|
+
* saying here because it is the obvious place to look. A `ProjectedEdge`
|
|
810
|
+
* carries `states` and `writes` — so it says an edge is `invalid` and never
|
|
811
|
+
* says why. The code is on the write record, and the panel takes it through
|
|
812
|
+
* {@link WorkspaceOptions.refusals}.
|
|
118
813
|
*/
|
|
119
814
|
readonly projected?: readonly ProjectedEdge[] | undefined;
|
|
815
|
+
/**
|
|
816
|
+
* The create draft in flight, so the panel can draw the step the reader is on.
|
|
817
|
+
*
|
|
818
|
+
* §17b's inspector path is `+ add` → kind → target, and `create/draft.ts`
|
|
819
|
+
* makes `begin` RESET the kind and the target on purpose. An always-visible
|
|
820
|
+
* kind list would therefore let a reader fill a slot that the next `+ add`
|
|
821
|
+
* silently discards, so the list is drawn only while a draft is live —
|
|
822
|
+
* which the panel cannot know without being told.
|
|
823
|
+
*
|
|
824
|
+
* Absent means idle, which is the state a host that has no create path is in.
|
|
825
|
+
*/
|
|
826
|
+
readonly draft?: CreateDraft | undefined;
|
|
827
|
+
/**
|
|
828
|
+
* Where a canvas drop landed, or `null`. Read for its NULLNESS alone.
|
|
829
|
+
*
|
|
830
|
+
* THE PANEL SUPPRESSES ITS KIND LIST WHILE A DROP IS LIVE, which is the
|
|
831
|
+
* invariant that keeps the two choosers mutually exclusive. A drag that ends
|
|
832
|
+
* on the canvas opens a chooser AT THE DROP POINT; a panel list drawn at the
|
|
833
|
+
* same moment would be a second copy of the same step, one under the
|
|
834
|
+
* reader's pointer and one in the column beside it, both writing to the one
|
|
835
|
+
* draft. The shell held that rule alone while it drew both, and it cannot
|
|
836
|
+
* hold it any more now that one of them is package markup.
|
|
837
|
+
*
|
|
838
|
+
* The host's own value is forwarded rather than a boolean derived from it:
|
|
839
|
+
* a `dropInFlight: boolean` would be a second spelling of a fact the shell
|
|
840
|
+
* already holds, free to disagree with it on the render where it matters.
|
|
841
|
+
*/
|
|
842
|
+
readonly drop?: Point | null | undefined;
|
|
843
|
+
/**
|
|
844
|
+
* The edits the store refused, so the panel can say so where the reader made
|
|
845
|
+
* them. See {@link WorkspaceRefusal}.
|
|
846
|
+
*
|
|
847
|
+
* IN THE ORDER THE STORE RECORDED THEM, and that is part of the contract
|
|
848
|
+
* rather than an accident of how a caller happened to build it. One row
|
|
849
|
+
* states one reason, so two refusals naming one edge collapse to the LAST —
|
|
850
|
+
* the one the reader just caused, rather than the one they have read and
|
|
851
|
+
* moved past. A caller assembling this from two passes and concatenating
|
|
852
|
+
* them loses that chronology at the join; `mountWorkspace` walks
|
|
853
|
+
* `snapshot.writes` once for exactly this reason.
|
|
854
|
+
*
|
|
855
|
+
* ABSENT MEANS "NOTHING REFUSED", which is unlike {@link WorkspaceOptions.audit}
|
|
856
|
+
* and is safe for the reason that one is not: a refusal is a fact the store
|
|
857
|
+
* produces, so a host with no refusals to report and a host that never asked
|
|
858
|
+
* are in the same position — there is nothing to draw either way. An audit
|
|
859
|
+
* count is a NUMBER, and inventing a zero for one nobody computed is the
|
|
860
|
+
* distinction that option exists to keep.
|
|
861
|
+
*/
|
|
862
|
+
readonly refusals?: readonly WorkspaceRefusal[] | undefined;
|
|
863
|
+
/**
|
|
864
|
+
* The failed and conflicted writes the reader can still act on.
|
|
865
|
+
*
|
|
866
|
+
* OPTIONAL, on the same reasoning as {@link WorkspaceOptions.refusals}: an
|
|
867
|
+
* unsettled write is a fact the store produces, so "none" and "never asked"
|
|
868
|
+
* leave the panel with the same nothing to draw.
|
|
869
|
+
*/
|
|
870
|
+
readonly recoveries?: readonly WorkspaceRecovery[] | undefined;
|
|
871
|
+
/**
|
|
872
|
+
* Which conflict has its difference open, if any.
|
|
873
|
+
*
|
|
874
|
+
* ONE AT A TIME, because the region is inside a panel rather than a dialog.
|
|
875
|
+
* The shell holds it for the reason `auditFiltered` is held: the card
|
|
876
|
+
* publishes a toggle, so something has to carry its state or every render
|
|
877
|
+
* answers "closed" and the control cannot complete what it advertises.
|
|
878
|
+
*/
|
|
879
|
+
readonly diffOpen?: MutationId | null | undefined;
|
|
880
|
+
/**
|
|
881
|
+
* A value unique to this surface, used to bind §17a's rail footer to the
|
|
882
|
+
* isolated list the canvas draws.
|
|
883
|
+
*
|
|
884
|
+
* SUPPLIED BY WHOEVER KNOWS THE PAGE. The footer's toggle and the list are in
|
|
885
|
+
* different zones, so the toggle names the list with `aria-controls` — and an
|
|
886
|
+
* id invented in here would be emitted twice by a host rendering two
|
|
887
|
+
* workspaces, leaving the second toggle pointing at the first surface's list.
|
|
888
|
+
* `scale/render.ts`'s `searchSpec` records the same hazard for its own label.
|
|
889
|
+
*
|
|
890
|
+
* ABSENT, NO ASSOCIATION IS DRAWN, and that is the honest default rather than
|
|
891
|
+
* a degraded one: `mountWorkspace` mints a value per mount, and markup served
|
|
892
|
+
* WITHOUT a mount is not operable anyway — the same argument
|
|
893
|
+
* `CanvasWords.editMode` is optional under. A toggle nobody can press needs no
|
|
894
|
+
* `aria-controls`.
|
|
895
|
+
*/
|
|
896
|
+
readonly surfaceId?: string | undefined;
|
|
120
897
|
}
|
|
121
898
|
export interface WorkspaceView {
|
|
122
899
|
readonly selection: WorkspaceSelection;
|