@issuegraph/editor 0.3.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +31 -6
  2. package/dist/a11y/baseline.d.ts +182 -0
  3. package/dist/a11y/baseline.d.ts.map +1 -0
  4. package/dist/a11y/baseline.js +430 -0
  5. package/dist/a11y/baseline.js.map +1 -0
  6. package/dist/audit/styles.d.ts +1 -1
  7. package/dist/audit/styles.d.ts.map +1 -1
  8. package/dist/audit/styles.js +1 -1
  9. package/dist/create/draft.d.ts +17 -0
  10. package/dist/create/draft.d.ts.map +1 -1
  11. package/dist/create/draft.js +19 -0
  12. package/dist/create/draft.js.map +1 -1
  13. package/dist/create/keys.d.ts +70 -5
  14. package/dist/create/keys.d.ts.map +1 -1
  15. package/dist/create/keys.js +78 -17
  16. package/dist/create/keys.js.map +1 -1
  17. package/dist/firstpass/candidates.d.ts +17 -1
  18. package/dist/firstpass/candidates.d.ts.map +1 -1
  19. package/dist/index.d.ts +6 -4
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +12 -2
  22. package/dist/index.js.map +1 -1
  23. package/dist/overlay/render.d.ts.map +1 -1
  24. package/dist/overlay/render.js +54 -14
  25. package/dist/overlay/render.js.map +1 -1
  26. package/dist/overlay/styles.d.ts +1 -1
  27. package/dist/overlay/styles.d.ts.map +1 -1
  28. package/dist/overlay/styles.js +40 -0
  29. package/dist/overlay/styles.js.map +1 -1
  30. package/dist/picker/render.d.ts +26 -16
  31. package/dist/picker/render.d.ts.map +1 -1
  32. package/dist/picker/render.js +25 -36
  33. package/dist/picker/render.js.map +1 -1
  34. package/dist/picker/styles.d.ts +7 -7
  35. package/dist/picker/styles.d.ts.map +1 -1
  36. package/dist/picker/styles.js +7 -36
  37. package/dist/picker/styles.js.map +1 -1
  38. package/dist/picker/view.d.ts +8 -4
  39. package/dist/picker/view.d.ts.map +1 -1
  40. package/dist/picker/view.js +4 -3
  41. package/dist/picker/view.js.map +1 -1
  42. package/dist/picker/words.d.ts +12 -10
  43. package/dist/picker/words.d.ts.map +1 -1
  44. package/dist/picker/words.js +12 -8
  45. package/dist/picker/words.js.map +1 -1
  46. package/dist/scale/ladder.d.ts.map +1 -1
  47. package/dist/scale/ladder.js +10 -0
  48. package/dist/scale/ladder.js.map +1 -1
  49. package/dist/scale/render.d.ts.map +1 -1
  50. package/dist/scale/render.js +9 -1
  51. package/dist/scale/render.js.map +1 -1
  52. package/dist/workspace/chrome.d.ts +1 -1
  53. package/dist/workspace/chrome.d.ts.map +1 -1
  54. package/dist/workspace/chrome.js +48 -0
  55. package/dist/workspace/chrome.js.map +1 -1
  56. package/dist/workspace/firstpass.d.ts +166 -0
  57. package/dist/workspace/firstpass.d.ts.map +1 -0
  58. package/dist/workspace/firstpass.js +151 -0
  59. package/dist/workspace/firstpass.js.map +1 -0
  60. package/dist/workspace/host.d.ts +212 -10
  61. package/dist/workspace/host.d.ts.map +1 -1
  62. package/dist/workspace/host.js +434 -42
  63. package/dist/workspace/host.js.map +1 -1
  64. package/dist/workspace/inspector.d.ts +45 -1
  65. package/dist/workspace/inspector.d.ts.map +1 -1
  66. package/dist/workspace/inspector.js +11 -0
  67. package/dist/workspace/inspector.js.map +1 -1
  68. package/dist/workspace/mount.d.ts +52 -5
  69. package/dist/workspace/mount.d.ts.map +1 -1
  70. package/dist/workspace/mount.js +1391 -56
  71. package/dist/workspace/mount.js.map +1 -1
  72. package/dist/workspace/rail.d.ts.map +1 -1
  73. package/dist/workspace/rail.js +12 -0
  74. package/dist/workspace/rail.js.map +1 -1
  75. package/dist/workspace/recovery.d.ts +155 -0
  76. package/dist/workspace/recovery.d.ts.map +1 -0
  77. package/dist/workspace/recovery.js +156 -0
  78. package/dist/workspace/recovery.js.map +1 -0
  79. package/dist/workspace/render.d.ts +402 -1
  80. package/dist/workspace/render.d.ts.map +1 -1
  81. package/dist/workspace/render.js +947 -36
  82. package/dist/workspace/render.js.map +1 -1
  83. package/dist/workspace/styles.d.ts +1 -1
  84. package/dist/workspace/styles.d.ts.map +1 -1
  85. package/dist/workspace/styles.js +554 -41
  86. package/dist/workspace/styles.js.map +1 -1
  87. package/package.json +3 -3
@@ -49,17 +49,30 @@
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, ProjectedEdge } from '@issuegraph/store';
53
53
  import type { AuditInput } from '../audit/findings.ts';
54
+ import { type CreateDraft } from '../create/draft.ts';
55
+ import type { Point } from '../create/placement.ts';
54
56
  import { type AuditOverlay } from '../audit/surface.ts';
55
57
  import { type ScaleState } from '../scale/commands.ts';
56
58
  import { type InspectorView } from './inspector.ts';
57
59
  import { type RailWindow, type RailWindowOptions } from './rail.ts';
60
+ import { type ConflictDiff } from './recovery.ts';
58
61
  import { type WorkspaceSelection } from './selection.ts';
59
62
  /** The four fixed positions. A closed union, which is what keeps `zone` safe. */
60
63
  export declare const ZONES: readonly ["header", "rail", "canvas", "inspector"];
61
64
  export type Zone = (typeof ZONES)[number];
62
65
  export interface WorkspaceWords {
66
+ /**
67
+ * The panel's own heading — frame 17a's `INSPECTOR`.
68
+ *
69
+ * UNCONDITIONAL, and that is what it is for: the zone used to begin with
70
+ * whatever the selection happened to resolve to, so a reader who had selected
71
+ * nothing met a bare sentence in an unnamed column. The caps are the
72
+ * stylesheet's, as they already are for `WHY RANK` and `RELATIONSHIPS`, so
73
+ * the word supplied here reads as a word.
74
+ */
75
+ readonly inspector: string;
63
76
  /** Shown in the inspector when nothing is selected. */
64
77
  readonly nothingSelected: string;
65
78
  /**
@@ -74,6 +87,323 @@ export interface WorkspaceWords {
74
87
  readonly clearSelection: string;
75
88
  /** Names the relationships list. */
76
89
  readonly relationships: string;
90
+ /**
91
+ * §17a's `WHY RANK n` heading, without the number.
92
+ *
93
+ * THE NUMBER IS APPENDED, NOT INTERPOLATED INTO A HOST TEMPLATE. A rank is
94
+ * the one part of this heading the package knows and the host does not, and
95
+ * a `{n}` placeholder would make every host reimplement the substitution —
96
+ * which is how a package that refuses to invent English ends up shipping a
97
+ * tiny template language instead.
98
+ */
99
+ readonly whyRank: string;
100
+ /**
101
+ * The same heading for a HELD slot, which has no rank to name.
102
+ *
103
+ * A held slot's rank is `null` by construction (`@issuegraph/derive` assigns
104
+ * `ready ? (rank += 1) : null`), so "why rank —" would be a heading about a
105
+ * position that does not exist. The design's own §16d ruling is the same one:
106
+ * a held unit prints the em dash rather than a number.
107
+ */
108
+ readonly whyHeld: string;
109
+ /**
110
+ * Joins the rest of a together unit: §17a ends *"then worked with #514 as one
111
+ * unit"*, and this is that phrase minus the members, which the package names.
112
+ */
113
+ readonly workedAsOneUnit: string;
114
+ /**
115
+ * Stated when the subject has no relationships at all.
116
+ *
117
+ * A STATED EMPTY IS NOT THE SAME AS AN ABSENT LIST. The panel used to draw
118
+ * the `RELATIONSHIPS` heading and then nothing under it, which reads as a
119
+ * list that failed to load rather than as an issue that is genuinely
120
+ * unrelated to everything — and "this issue is connected to nothing" is one
121
+ * of the more actionable things a grooming surface can say.
122
+ */
123
+ readonly noRelationships: string;
124
+ /**
125
+ * The control that begins a relationship from the selected issue — frame
126
+ * 17a's `+ add`, publishing `add`.
127
+ *
128
+ * MOVED HERE FROM `MountWords`, where it was while the mount drew the button.
129
+ * `renderWorkspace` is the published surface and the panel is now what draws
130
+ * it, so a host rendering the markup without mounting gets the control rather
131
+ * than a panel that can only be read. `MountWords` extends this, so the mount
132
+ * still reads the same field and no host supplies two.
133
+ */
134
+ readonly addRelationship: string;
135
+ /**
136
+ * The control that abandons a draft — frame 17b's escape hatch beside the
137
+ * numbered kind list, publishing `cancel`.
138
+ *
139
+ * MOVED HERE FROM `MountWords` WITH THE STEP IT BELONGS TO. The kind list and
140
+ * the control that withdraws from it are one affordance; leaving the cancel
141
+ * in host chrome while the list moved into the package would have split a
142
+ * control group across two layers, and a draft the reader cannot abandon with
143
+ * a pointer is the failure `create/keys.ts` names for the keyboard. The
144
+ * mount's target-search step still reads this field, through the extension.
145
+ */
146
+ readonly cancel: string;
147
+ /**
148
+ * Names the issue a live draft was begun from, when that is not the panel's
149
+ * subject — "relating from", read before the reference the package supplies.
150
+ *
151
+ * IT EXISTS BECAUSE THE TWO CAN DIVERGE AND THE DRAFT MUST STILL BE
152
+ * CANCELLABLE. See {@link addStepSpec}: a draft begun by `R` on a together
153
+ * unit's non-lead member, or one outlived by a change of selection, leaves
154
+ * the kind step live under a panel headed by a different issue. The step is
155
+ * drawn anyway — it carries the only pointer cancel there is — so it has to
156
+ * say whose it is, and the package invents that phrase no more readily than
157
+ * it invents {@link WorkspaceWords.workedAsOneUnit}. The REFERENCE is the
158
+ * package's, appended as its own element, so a host writes a phrase and never
159
+ * a template.
160
+ */
161
+ readonly relatingFrom: string;
162
+ /**
163
+ * The accessible name of a row's remove control — the `✕` in frame 17a's
164
+ * right-hand slot.
165
+ *
166
+ * ROW-INDEPENDENT WORDING. Which relationship it removes is in the markup
167
+ * (`data-ig-target`) and in the row the button sits on, not in this string, so
168
+ * a host writing "remove #488" here would be wrong on every other row.
169
+ *
170
+ * DISTINCT FROM `MountWords.deleteRelationship`, which names a different
171
+ * control: that is the mount's labelled button for the one SELECTED edge, and
172
+ * this is a glyph button repeated once per row. A glyph carries no name of its
173
+ * own — see {@link relationshipSpec} — so the two cannot share one string
174
+ * without one of them reading wrongly.
175
+ */
176
+ readonly remove: string;
177
+ /**
178
+ * The right-hand marker on an incoming row — frame 17a's `inbound`.
179
+ *
180
+ * IT IS A STATED FACT, NOT A MISSING CONTROL. An inbound edge's field lives in
181
+ * the OTHER issue's body, so this panel's subject cannot declare it away; the
182
+ * slot says which relationship this is rather than leaving a gap where the
183
+ * other rows have a `✕`.
184
+ */
185
+ readonly inbound: string;
186
+ /**
187
+ * The control that reverses a directed relationship — frame 17b's `⇅ flip`.
188
+ *
189
+ * MOVED HERE FROM `PickerWords` WITH THE CONTROL ITSELF, on the same rule
190
+ * that brought `addRelationship` and `cancel` down from `MountWords`: the
191
+ * surface that draws a control owns the word for it, so a host rendering the
192
+ * markup without mounting gets the affordance rather than a panel it can only
193
+ * read. §17b calls direction “the single most common encoding mistake”, which
194
+ * made this the worst control to have reachable from one rendering path only.
195
+ *
196
+ * IT IS NOT #144's MOVE, AND THE DIFFERENCE IS VISIBLE TO EVERY HOST.
197
+ * `addRelationship` travelled between two interfaces in one `extends` chain,
198
+ * so `words.addRelationship` read the same before and after. `MountWords`
199
+ * holds the picker's vocabulary in a NESTED field, so this word's path
200
+ * changed: `words.picker.flip` became `words.flip`.
201
+ *
202
+ * ROW-INDEPENDENT WORDING, exactly as {@link WorkspaceWords.remove} is. Which
203
+ * relationship it reverses is the selected row it sits in, never this string.
204
+ */
205
+ readonly flip: string;
206
+ /**
207
+ * Why the store refused an edit, keyed by its code.
208
+ *
209
+ * TOTAL OVER `InvalidCode`, so a code the store adds is a compile error in
210
+ * every host rather than a capsule that renders blank on the one refusal
211
+ * nobody anticipated. The store's own `InvalidReason.message` is deliberately
212
+ * not used: it is one sentence in one language chosen by the layer that
213
+ * detected the refusal, and `overlay/grammar.ts` already records that an
214
+ * invalid edge's sentence is the host's, keyed off the code.
215
+ */
216
+ readonly refusals: Readonly<Record<InvalidCode, string>>;
217
+ /**
218
+ * §17b's recovery cards, in the host's own words.
219
+ *
220
+ * `retry` AND `retryOnLatest` ARE TWO WORDS BECAUSE THEY ARE TWO CALLS. §17b
221
+ * names the failed affordance "retry" and the conflict affordance "retry on
222
+ * latest", and they reach `store.retry` and `store.retryOnLatest`
223
+ * respectively — the second of which re-reads and ADOPTS the upstream
224
+ * document as the new base before re-dispatching. One string for both would
225
+ * make one card lie about what pressing it does, and it would be the card
226
+ * whose consequence most needs stating.
227
+ */
228
+ readonly recovery: RecoveryWords;
229
+ }
230
+ export interface RecoveryWords {
231
+ /** Names the state on a failed card. */
232
+ readonly failed: string;
233
+ /** Names the state on a conflicted card. */
234
+ readonly conflict: string;
235
+ readonly viewDiff: string;
236
+ /** `failed` → `store.retry`. */
237
+ readonly retry: string;
238
+ /** `conflict` → `store.retryOnLatest`, which adopts upstream as the new base. */
239
+ readonly retryOnLatest: string;
240
+ readonly discardMine: string;
241
+ /** Heads the edges the held document has and the reader's does not. */
242
+ readonly upstreamOnly: string;
243
+ /** Heads the reader's own unlanded edit, where it ADDS a relationship. */
244
+ readonly mineOnly: string;
245
+ /** Heads the reader's own unlanded edit, where it REMOVES one. */
246
+ readonly mineRemoved: string;
247
+ /** Heads relationships both sides hold pointing opposite ways. */
248
+ readonly carrierReversed: string;
249
+ /** Heads the issues the two documents disagree about. */
250
+ readonly issuesChanged: string;
251
+ /** Shown when the diff has nothing to show on THIS panel. */
252
+ readonly diffEmpty: string;
253
+ /**
254
+ * Why a `retry on latest` could not even read.
255
+ *
256
+ * The store's refresh is half of one operation: when it fails the record is
257
+ * restored verbatim and nothing is dispatched, so without this the button
258
+ * reads as dead on exactly the failure the store went to trouble to make
259
+ * observable.
260
+ */
261
+ readonly retryFailed: string;
262
+ /**
263
+ * Heads the region for recoveries with no panel to sit on.
264
+ *
265
+ * See {@link WorkspaceRecovery.carrier}: a write about issues this backlog
266
+ * does not hold has no panel, and dropping it would leave the reader's work
267
+ * unrecoverable.
268
+ */
269
+ readonly unplaced: string;
270
+ }
271
+ /**
272
+ * One unsettled write the reader can still act on: §17b's `failed` and
273
+ * `conflict`.
274
+ *
275
+ * NARROW FOR THE REASON {@link WorkspaceRefusal} IS, and shaped the same way.
276
+ * The whole write ledger is the larger surface this deliberately is not: a
277
+ * `WriteRecord` carries a `Mutation` and, on a conflict, an entire second
278
+ * `GraphDocument`, none of which a renderer can draw. What a card needs is the
279
+ * state, the reason or the difference, and which panel states it.
280
+ *
281
+ * A UNION, BECAUSE THE TWO STATES CARRY DIFFERENT FACTS. `WriteRecord` is
282
+ * already a union on this exact line. Two optionals would admit a conflict with
283
+ * no diff and a failure with one, and the renderer would need a runtime guard
284
+ * for a case the type can refuse outright.
285
+ *
286
+ * NO `phantom` EQUIVALENT, and the absence is deliberate rather than an
287
+ * oversight. {@link WorkspaceRefusal.phantom} exists because a refusal either
288
+ * JOINS a relationship row or REPLACES it, and getting that wrong deletes a
289
+ * removable relationship from the panel. A recovery card is its own element in
290
+ * its own region and replaces nothing, so the question does not arise.
291
+ */
292
+ export type WorkspaceRecovery = {
293
+ readonly kind: 'failed';
294
+ readonly mutationId: MutationId;
295
+ readonly edgeId: EdgeId;
296
+ readonly carrier: string | null;
297
+ /**
298
+ * The adapter's own sentence, verbatim.
299
+ *
300
+ * NOT KEYED OFF A CODE, and the asymmetry with {@link
301
+ * WorkspaceWords.refusals} is the argument. The store's refusal codes are
302
+ * a CLOSED set the store owns, so a host can be made total over them. An
303
+ * adapter's failure is an open one — a status, a rate limit, a network
304
+ * message — and there is no code to key on, so the sentence travels.
305
+ */
306
+ readonly reason: string;
307
+ } | {
308
+ readonly kind: 'conflict';
309
+ readonly mutationId: MutationId;
310
+ readonly edgeId: EdgeId;
311
+ readonly carrier: string | null;
312
+ /** Both sides, held apart. See `recovery.ts`. */
313
+ readonly diff: ConflictDiff;
314
+ /**
315
+ * Why the last refresh could not read, or `null`.
316
+ *
317
+ * A `retry on latest` whose READ fails restores the record exactly as it
318
+ * was and dispatches nothing — the conflict keeps its held document so it
319
+ * can still be compared against. Without this the card re-renders
320
+ * identically and the button reads as dead.
321
+ */
322
+ readonly refreshError: string | null;
323
+ };
324
+ /**
325
+ * One refused edit, as the panel needs it: which edge, and why.
326
+ *
327
+ * NARROW ON PURPOSE, AND THE NARROWNESS IS THE ARGUMENT. `WorkspaceOptions.projected`
328
+ * already carries every edge's states, and `invalid` is among them — but a
329
+ * state says an edge was refused and never says why, because the code lives on
330
+ * `WriteRecord.reason`, which reaches no renderer. The alternative was to hand
331
+ * the panel the whole write ledger, which is the input #137's conflict cards
332
+ * need and is a much larger surface: both versions of a conflicted body, the
333
+ * upstream document, the retry and discard affordances. This is one code per
334
+ * edge id, derivable in a shell from what the store already publishes, and
335
+ * nothing here is blocked on that larger shape.
336
+ */
337
+ export interface WorkspaceRefusal {
338
+ readonly edgeId: EdgeId;
339
+ readonly code: InvalidCode;
340
+ /**
341
+ * The issue whose panel states this refusal.
342
+ *
343
+ * THE EDIT'S OWN SUBJECT, DECIDED BY WHOEVER MADE THE EDIT. This panel used
344
+ * to work it out for itself, by asking whether the refused edge's identity
345
+ * named the issue on show — a derivation entirely separate from the one that
346
+ * produced the subject, and free to disagree with it. It did, three times
347
+ * over: an unfiltered orphan branch drew every refusal on every panel; a
348
+ * retype whose projection hid the edge the panel was filtered to left the
349
+ * panel with no subject and the reason nowhere; and a draft begun from a
350
+ * together-unit partner produced an edge naming the partner while
351
+ * `inspectorView` had canonicalized the panel onto the slot's lead, so
352
+ * nothing matched. None of the three is a question this renderer can answer
353
+ * from an identity, because none of them is about the identity.
354
+ *
355
+ * A MOUNT KNOWS IT WITHOUT GUESSING, BECAUSE IT WROTE IT DOWN. `editCarrier`
356
+ * in `host.ts` answers it — for a create from a draft, for a retype or flip
357
+ * whose produced identity differs from the one the reader named, and for a
358
+ * delete — and `mountWorkspace` asks it as the edit goes OUT, while the
359
+ * document still holds the relationship, then keeps the answer against the
360
+ * write. It cannot be asked again afterwards: by the time a refusal comes
361
+ * back a sibling write may have removed the edge, and an identity records
362
+ * which two issues a relationship was between and never which of them
363
+ * declared it. A host rendering without `mountWorkspace` states the same fact
364
+ * the same way: whichever issue's panel it wants the refusal read on, decided
365
+ * when it made the edit.
366
+ *
367
+ * NOT DERIVABLE FROM {@link WorkspaceRefusal.edgeId}, which is why it is a
368
+ * second field rather than a lookup. `unknown-issue` names an issue the
369
+ * document does not hold; `unknown-edge` names an edge it does not hold; and
370
+ * a together unit's panel is headed by a key the edge may never mention.
371
+ */
372
+ readonly carrier: string;
373
+ /**
374
+ * Whether the edge this refusal marks exists only because the refusal does.
375
+ *
376
+ * NOT EVERY REFUSAL IS ABOUT AN EDIT THAT LEFT NO TRACE, and reading them as
377
+ * if they were deleted a relationship the reader still has. Three of the
378
+ * store's codes mark an edge that is already LANDED — measured against the
379
+ * store rather than reasoned from the code list: `duplicate-edge` (a create
380
+ * of a relationship that exists; `project` folds the phantom onto the real
381
+ * edge), and `unchanged-kind` and `symmetric-edge` (a retype or flip whose
382
+ * result has the identity it started from, so `edgeChangeFor` marks the
383
+ * original rather than swapping it). `cardinality` looks like a fourth and is
384
+ * not: through a create it marks the edge it would have made, and through a
385
+ * retype it marks the edge it would have BECOME — a phantom either way, with
386
+ * the original hidden — which is exactly why this is asked of the document
387
+ * rather than keyed off a list of codes. With the capsule REPLACING the row
388
+ * for all of them, telling a reader "this relationship is already declared"
389
+ * removed the declared relationship's row from the panel, remove control
390
+ * included — so the one message they were given was contradicted by the list
391
+ * beside it, and nothing on the surface could undo the edge they had just
392
+ * been told about.
393
+ *
394
+ * TRUE MEANS THE ROW IS THE REFUSAL. A refused CREATE of a genuinely new
395
+ * relationship draws as a capsule in the row it would have been: there is no
396
+ * relationship to operate, and a `select-edge` and a `✕` on it would offer to
397
+ * remove something that was never added. False means the relationship is
398
+ * real, so the row stands and the reason is attached to it.
399
+ *
400
+ * REQUIRED, BECAUSE NEITHER DEFAULT IS SAFE. Defaulted to `true` the panel
401
+ * eats real rows; defaulted to `false` it draws a live remove control for an
402
+ * edge the document does not have. A host derives it in one line — the
403
+ * refused id is in `snapshot.landed` or it is not — and the store's own
404
+ * projection is where both halves already come from.
405
+ */
406
+ readonly phantom: boolean;
77
407
  }
78
408
  export interface WorkspaceOptions {
79
409
  /**
@@ -115,8 +445,79 @@ export interface WorkspaceOptions {
115
445
  * The store's projection, for the canvas to draw each edge's write states.
116
446
  * See `ScaleLadderOptions.projected`; the workspace forwards it and reads
117
447
  * none of it, because the rail and the inspector draw no line to overlay.
448
+ *
449
+ * AND IT IS NOT WHERE THE INSPECTOR'S REFUSALS COME FROM, which is worth
450
+ * saying here because it is the obvious place to look. A `ProjectedEdge`
451
+ * carries `states` and `writes` — so it says an edge is `invalid` and never
452
+ * says why. The code is on the write record, and the panel takes it through
453
+ * {@link WorkspaceOptions.refusals}.
118
454
  */
119
455
  readonly projected?: readonly ProjectedEdge[] | undefined;
456
+ /**
457
+ * The create draft in flight, so the panel can draw the step the reader is on.
458
+ *
459
+ * §17b's inspector path is `+ add` → kind → target, and `create/draft.ts`
460
+ * makes `begin` RESET the kind and the target on purpose. An always-visible
461
+ * kind list would therefore let a reader fill a slot that the next `+ add`
462
+ * silently discards, so the list is drawn only while a draft is live —
463
+ * which the panel cannot know without being told.
464
+ *
465
+ * Absent means idle, which is the state a host that has no create path is in.
466
+ */
467
+ readonly draft?: CreateDraft | undefined;
468
+ /**
469
+ * Where a canvas drop landed, or `null`. Read for its NULLNESS alone.
470
+ *
471
+ * THE PANEL SUPPRESSES ITS KIND LIST WHILE A DROP IS LIVE, which is the
472
+ * invariant that keeps the two choosers mutually exclusive. A drag that ends
473
+ * on the canvas opens a chooser AT THE DROP POINT; a panel list drawn at the
474
+ * same moment would be a second copy of the same step, one under the
475
+ * reader's pointer and one in the column beside it, both writing to the one
476
+ * draft. The shell held that rule alone while it drew both, and it cannot
477
+ * hold it any more now that one of them is package markup.
478
+ *
479
+ * The host's own value is forwarded rather than a boolean derived from it:
480
+ * a `dropInFlight: boolean` would be a second spelling of a fact the shell
481
+ * already holds, free to disagree with it on the render where it matters.
482
+ */
483
+ readonly drop?: Point | null | undefined;
484
+ /**
485
+ * The edits the store refused, so the panel can say so where the reader made
486
+ * them. See {@link WorkspaceRefusal}.
487
+ *
488
+ * IN THE ORDER THE STORE RECORDED THEM, and that is part of the contract
489
+ * rather than an accident of how a caller happened to build it. One row
490
+ * states one reason, so two refusals naming one edge collapse to the LAST —
491
+ * the one the reader just caused, rather than the one they have read and
492
+ * moved past. A caller assembling this from two passes and concatenating
493
+ * them loses that chronology at the join; `mountWorkspace` walks
494
+ * `snapshot.writes` once for exactly this reason.
495
+ *
496
+ * ABSENT MEANS "NOTHING REFUSED", which is unlike {@link WorkspaceOptions.audit}
497
+ * and is safe for the reason that one is not: a refusal is a fact the store
498
+ * produces, so a host with no refusals to report and a host that never asked
499
+ * are in the same position — there is nothing to draw either way. An audit
500
+ * count is a NUMBER, and inventing a zero for one nobody computed is the
501
+ * distinction that option exists to keep.
502
+ */
503
+ readonly refusals?: readonly WorkspaceRefusal[] | undefined;
504
+ /**
505
+ * The failed and conflicted writes the reader can still act on.
506
+ *
507
+ * OPTIONAL, on the same reasoning as {@link WorkspaceOptions.refusals}: an
508
+ * unsettled write is a fact the store produces, so "none" and "never asked"
509
+ * leave the panel with the same nothing to draw.
510
+ */
511
+ readonly recoveries?: readonly WorkspaceRecovery[] | undefined;
512
+ /**
513
+ * Which conflict has its difference open, if any.
514
+ *
515
+ * ONE AT A TIME, because the region is inside a panel rather than a dialog.
516
+ * The shell holds it for the reason `auditFiltered` is held: the card
517
+ * publishes a toggle, so something has to carry its state or every render
518
+ * answers "closed" and the control cannot complete what it advertises.
519
+ */
520
+ readonly diffOpen?: MutationId | null | undefined;
120
521
  }
121
522
  export interface WorkspaceView {
122
523
  readonly selection: WorkspaceSelection;
@@ -1 +1 @@
1
- {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/workspace/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAEH,OAAO,EAIL,KAAK,KAAK,EACV,KAAK,cAAc,EAUpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,sBAAsB,CAAC;AAGtE,OAAO,EAEL,KAAK,YAAY,EAKlB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,UAAU,EAAuB,MAAM,sBAAsB,CAAC;AAI5E,OAAO,EAA8B,KAAK,aAAa,EAAiB,MAAM,gBAAgB,CAAC;AAC/F,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,iBAAiB,EAAc,MAAM,WAAW,CAAC;AAChF,OAAO,EACL,KAAK,kBAAkB,EAIxB,MAAM,gBAAgB,CAAC;AAGxB,iFAAiF;AACjF,eAAO,MAAM,KAAK,oDAAoE,CAAC;AAEvF,MAAM,MAAM,IAAI,GAAG,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1C,MAAM,WAAW,cAAc;IAC7B,uDAAuD;IACvD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC;;;;;;;;OAQG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,oCAAoC;IACpC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;IACpD,qEAAqE;IACrE,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IACxC,uEAAuE;IACvE,QAAQ,CAAC,IAAI,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAC9C;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IACxC;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7C,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;IACnC,mEAAmE;IACnE,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,aAAa,EAAE,GAAG,SAAS,CAAC;CAC3D;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAClC,oFAAoF;IACpF,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC;;;;;;OAMG;IACH,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,yDAAyD;IACzD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,6DAA6D;IAC7D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAoRD,0EAA0E;AAC1E,wBAAgB,eAAe,CAC7B,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,gBAAgB,GACxB,eAAe,CAsKjB"}
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/workspace/render.ts"],"names":[],"mappings":"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"}