@issuegraph/editor 0.10.0 → 0.23.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/README.md +38 -7
- package/dist/a11y/baseline.d.ts.map +1 -1
- package/dist/a11y/baseline.js +4 -0
- package/dist/a11y/baseline.js.map +1 -1
- package/dist/audit/findings.d.ts +107 -1
- package/dist/audit/findings.d.ts.map +1 -1
- package/dist/audit/findings.js +114 -7
- 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 +428 -0
- 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 +54 -14
- package/dist/audit/surface.js.map +1 -1
- package/dist/create/draft.d.ts +19 -0
- package/dist/create/draft.d.ts.map +1 -1
- package/dist/create/draft.js +21 -0
- package/dist/create/draft.js.map +1 -1
- package/dist/create/keys.d.ts +68 -8
- package/dist/create/keys.d.ts.map +1 -1
- package/dist/create/keys.js +43 -5
- package/dist/create/keys.js.map +1 -1
- package/dist/firstpass/render.d.ts.map +1 -1
- package/dist/firstpass/render.js +10 -9
- package/dist/firstpass/render.js.map +1 -1
- package/dist/index.d.ts +40 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -13
- 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 +24 -7
- 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 +38 -7
- package/dist/overlay/styles.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 +127 -16
- 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 +223 -30
- 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 +185 -4
- package/dist/scale/styles.js.map +1 -1
- package/dist/workspace/host.d.ts +83 -1
- package/dist/workspace/host.d.ts.map +1 -1
- package/dist/workspace/host.js +197 -4
- package/dist/workspace/host.js.map +1 -1
- package/dist/workspace/inspector.d.ts +33 -7
- package/dist/workspace/inspector.d.ts.map +1 -1
- package/dist/workspace/inspector.js +15 -0
- package/dist/workspace/inspector.js.map +1 -1
- package/dist/workspace/mount.d.ts +23 -1
- package/dist/workspace/mount.d.ts.map +1 -1
- package/dist/workspace/mount.js +605 -20
- 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/render.d.ts +390 -3
- package/dist/workspace/render.d.ts.map +1 -1
- package/dist/workspace/render.js +924 -133
- 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/skeleton.d.ts +33 -0
- package/dist/workspace/skeleton.d.ts.map +1 -0
- package/dist/workspace/skeleton.js +56 -0
- package/dist/workspace/skeleton.js.map +1 -0
- package/dist/workspace/styles.d.ts +1 -1
- package/dist/workspace/styles.d.ts.map +1 -1
- package/dist/workspace/styles.js +931 -19
- package/dist/workspace/styles.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §17d's findings panel: the three RELATIONSHIP findings, as a list.
|
|
3
|
+
*
|
|
4
|
+
* `./surface.ts` draws the ambient half — the persistent header count and the
|
|
5
|
+
* left-bar on an affected rail row. Neither says what was found. This module
|
|
6
|
+
* draws the list, so a reader who sees a count has somewhere to read it.
|
|
7
|
+
*
|
|
8
|
+
* ## Three classes, not four — the fourth is `./refused.ts`'
|
|
9
|
+
*
|
|
10
|
+
* `encoding-refused` is drawn OUTSIDE this panel, and SPEC is why: a refusal is
|
|
11
|
+
* *"surfaced on the issue itself, because until it parses the issue has no
|
|
12
|
+
* edges at all and would otherwise look simply unencoded"*. The other three
|
|
13
|
+
* say something about a relationship that exists; that one says the
|
|
14
|
+
* relationships cannot be read.
|
|
15
|
+
*
|
|
16
|
+
* SO THERE ARE TWO COUNTS NOW, AND THEY ARE DIFFERENT NUMBERS ON PURPOSE.
|
|
17
|
+
* `./surface.ts`'s ambient count stays every finding — it is the persistent
|
|
18
|
+
* control, it sits in the workspace chrome beside both surfaces, and a reader
|
|
19
|
+
* works through all four classes. The count in THIS panel's head is this
|
|
20
|
+
* panel's own cards, because it is enclosed by the list it counts. See
|
|
21
|
+
* {@link renderAuditPanel}.
|
|
22
|
+
*
|
|
23
|
+
* ## It is the module `./surface.ts` predicted
|
|
24
|
+
*
|
|
25
|
+
* That file's rule is *"findings travel as data; whatever lists them owns their
|
|
26
|
+
* escaping"*, and it is still true of it: `renderAuditHeader` concatenates by
|
|
27
|
+
* hand and is safe because the only value reaching it is a COUNT. A `detail` is
|
|
28
|
+
* not a number — it is prose about issues a host supplied — so this module is
|
|
29
|
+
* the lister that owns the escaping, and it owns it the way every other leaf in
|
|
30
|
+
* this package does: it builds an {@link ElementSpec} tree and the caller renders
|
|
31
|
+
* it through the viewer's `renderMarkup`. Nothing here writes a tag.
|
|
32
|
+
*
|
|
33
|
+
* ## The card is THREE lines, or FOUR when the finding carries a walk
|
|
34
|
+
*
|
|
35
|
+
* The frame draws a chip, a title, a mono line of the refs, and a prose
|
|
36
|
+
* paragraph. This module long drew three, on this argument: `AuditFinding.detail`
|
|
37
|
+
* ALREADY OPENS WITH THE MEMBERS — `${members.join(' · ')} form a blocked-by
|
|
38
|
+
* cycle; …` — so *"drawing a members line beside it prints every reference
|
|
39
|
+
* twice"*.
|
|
40
|
+
*
|
|
41
|
+
* THAT ARGUMENT WAS RIGHT ABOUT A LINE THE FRAME DOES NOT DRAW. The frame's
|
|
42
|
+
* third line is `#544 → #551 → #560 → #544` — a WALK, not the `·`-joined set
|
|
43
|
+
* the sentence opens with. Calling it "a mono line of the refs" was the slip:
|
|
44
|
+
* the line that was declined and the line drawn below are not the same line, and
|
|
45
|
+
* the refs are the only thing they have in common.
|
|
46
|
+
*
|
|
47
|
+
* SO THE COST IS CONCEDED RATHER THAN ARGUED AWAY. On a card that carries a
|
|
48
|
+
* walk the refs really do appear twice, and the head three times. What buys it
|
|
49
|
+
* is the one thing the sentence cannot say: `#544 · #551 · #560` does not say
|
|
50
|
+
* who waits on whom, and a stuck group's ordering is the first thing a groomer
|
|
51
|
+
* needs in order to pick an edge to cut. Direction appears nowhere else on the
|
|
52
|
+
* card.
|
|
53
|
+
*
|
|
54
|
+
* `detail` IS LEFT BYTE-IDENTICAL, and composing the walk INTO it was the
|
|
55
|
+
* alternative. It is rejected on §17d rather than on the accessible name — a
|
|
56
|
+
* walk-composed sentence would be collision-free by the same construction the
|
|
57
|
+
* current one is, so that cost is not real. The reason is that the frame draws a
|
|
58
|
+
* SEPARATE line and this section is delivered as a frame/implementation pair, so
|
|
59
|
+
* folding it into the sentence deviates from the artifact the work is measured
|
|
60
|
+
* against; and it would give `detail` two shapes, since a component §6.6
|
|
61
|
+
* declines to order has no walk to compose.
|
|
62
|
+
*
|
|
63
|
+
* THE ARROW GLYPH IS `aria-hidden`, AND THAT IS A GAP RATHER THAN A WIN. The
|
|
64
|
+
* only thing the line adds is direction, and the glyph does not carry it: `→`
|
|
65
|
+
* announces as "right arrow", not "waits on". Announcing the line anyway would
|
|
66
|
+
* repeat three refs `detail` has just named, in a form that has lost the reason
|
|
67
|
+
* it was drawn — the same trade `renderAuditPanel`'s `◆` makes below. The
|
|
68
|
+
* remaining options were a visually-hidden utility this package does not have,
|
|
69
|
+
* or an `aria-label` on a `p` with no role, which is not reliably announced. So
|
|
70
|
+
* a screen reader hears exactly what it heard before this line existed, and the
|
|
71
|
+
* ordering is genuinely lost to it. Filed, not resolved here.
|
|
72
|
+
*
|
|
73
|
+
* THE TITLE IS PER CLASS, AND THE FRAME'S ARE PER FINDING. `AuditFinding` has no
|
|
74
|
+
* title field, and this package appends numbers rather than interpolating them
|
|
75
|
+
* (`WorkspaceWords.whyRank` records why), so a per-finding title would need a
|
|
76
|
+
* template language this package has twice refused to ship. Four fixed titles
|
|
77
|
+
* keep the frame's type hierarchy at no cost to the data model; the difference
|
|
78
|
+
* is a stated deviation rather than a silent one.
|
|
79
|
+
*
|
|
80
|
+
* ## The chip carries `data-ig-audit-kind`, NEVER `data-ig-audit`
|
|
81
|
+
*
|
|
82
|
+
* `./styles.ts` ends with an UNQUALIFIED `[data-ig-audit]` rule — deliberately,
|
|
83
|
+
* because it lands on a row the viewer rendered and this layer may not rewrite
|
|
84
|
+
* that row's `class`. So an element out here carrying the same attribute picks
|
|
85
|
+
* up the rail's 2px gold left-bar whatever hue it was given, and the four-hue
|
|
86
|
+
* chip mapping would draw one gold bar on all four. The severity attribute stays
|
|
87
|
+
* reserved for the rail rows it was scoped for.
|
|
88
|
+
*
|
|
89
|
+
* ## There is no filter control here, and that is SPEC's call rather than a cut
|
|
90
|
+
*
|
|
91
|
+
* The frame's panel header carries `filter the rail to these`. SPEC §17a gives
|
|
92
|
+
* the workspace *"an audit **filter** … for focus; an audit **mode** does not"*,
|
|
93
|
+
* and §17d gives the header count one job — *"always the same click"*. That is
|
|
94
|
+
* one control, which `./surface.ts` already draws and which is on screen beside
|
|
95
|
+
* this panel. A second element on `data-ig-audit-filter` would be a second
|
|
96
|
+
* TOGGLE for one boolean (`workspace/host.ts` flips rather than sets), one
|
|
97
|
+
* announcing `aria-pressed` and one not.
|
|
98
|
+
*
|
|
99
|
+
* The frame needs its own because it draws the panel STANDALONE, with no
|
|
100
|
+
* workspace header in the artboard. Composed, it has one.
|
|
101
|
+
*
|
|
102
|
+
* ## `Show the loop` is in, because §17d's rule is navigation
|
|
103
|
+
*
|
|
104
|
+
* `./surface.ts` states the section's own doctrine: *"every finding is a
|
|
105
|
+
* judgment call, so the surface offers navigation and never a remedy."* The
|
|
106
|
+
* frame's other three buttons are remedies and are deferred. Without this one
|
|
107
|
+
* the panel is a paragraph you cannot act on or navigate from, sitting at the
|
|
108
|
+
* top of a column whose whole job is the selection.
|
|
109
|
+
*
|
|
110
|
+
* IT PUBLISHES `reveal-issue`, NOT the `select-issue` the inspector's hold rows
|
|
111
|
+
* use, and the difference is the whole of the control's safety. `select-issue`
|
|
112
|
+
* is a POINTER: with a relationship draft awaiting its target, `workspace/host
|
|
113
|
+
* .ts` reads a pointer as CHOOSING that target and emits the create proposal.
|
|
114
|
+
* So "go and look" would have declared a relationship — a write, from the one
|
|
115
|
+
* surface whose stated rule is that it never offers one. `reveal-issue` moves
|
|
116
|
+
* the selection and abandons the draft, and can produce no edit at all.
|
|
117
|
+
*
|
|
118
|
+
* AND IT IS DRAWN ONLY FOR A LOADED MEMBER — see {@link navigableMember}.
|
|
119
|
+
*
|
|
120
|
+
* @see https://github.com/autnmy/issuegraph/blob/main/SPEC.md
|
|
121
|
+
*/
|
|
122
|
+
import { element } from '@issuegraph/viewer';
|
|
123
|
+
import { AUDIT_FILTER_ATTRIBUTE } from "./surface.js";
|
|
124
|
+
/**
|
|
125
|
+
* The attribute a severity chip carries.
|
|
126
|
+
*
|
|
127
|
+
* ITS OWN NAME RATHER THAN `AUDIT_SEVERITY_ATTRIBUTE`, for the reason in this
|
|
128
|
+
* module's header: that one is matched by an unqualified rule meant for rail
|
|
129
|
+
* rows, and a chip wearing it would draw the ambient bar.
|
|
130
|
+
*/
|
|
131
|
+
export const AUDIT_KIND_ATTRIBUTE = 'data-ig-audit-kind';
|
|
132
|
+
/**
|
|
133
|
+
* The navigable member of a finding, or `undefined` when it has none.
|
|
134
|
+
*
|
|
135
|
+
* THE FIRST MEMBER THE DRAWN SURFACE CARRIES, and the filter is not defensive
|
|
136
|
+
* tidying. An encoding refusal is a fact the HOST asserted about an issue it
|
|
137
|
+
* read, and `findings.ts` keeps one whose ref lies outside the loaded document
|
|
138
|
+
* ON PURPOSE — filtering findings to the loaded set would drop them on exactly
|
|
139
|
+
* the issues a paging boundary has not reached. So a finding's members are not
|
|
140
|
+
* all rows, and a control targeting one that is not advertises a move it cannot
|
|
141
|
+
* make: the mount reconciles the unknown selection straight back away.
|
|
142
|
+
*
|
|
143
|
+
* IT ASKS THE CALLER, NOT THE OVERLAY, AND THE DIFFERENCE IS NOT PEDANTRY. An
|
|
144
|
+
* earlier revision used `overlay.rowFor`, which answers "does the AUDIT's
|
|
145
|
+
* document carry this ref" — a different question, and the two come apart
|
|
146
|
+
* exactly where a host audits more than it draws. The demo does: it audits the
|
|
147
|
+
* repository it holds and renders a page of it, so a ref can be audited, have
|
|
148
|
+
* an overlay row, and still reach a view with nothing in it.
|
|
149
|
+
*
|
|
150
|
+
* FIRST RATHER THAN "MOST IMPORTANT": a finding names a component, no member of
|
|
151
|
+
* it leads, and a rule invented here would be a second ranking nothing else in
|
|
152
|
+
* the package agrees with.
|
|
153
|
+
*/
|
|
154
|
+
function navigableMember(finding, known) {
|
|
155
|
+
return finding.members.find((ref) => known.has(ref));
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* The card for one finding.
|
|
159
|
+
*
|
|
160
|
+
* A card whose finding names no loaded issue draws no control — see
|
|
161
|
+
* {@link navigableMember}. It keeps its chip, title and sentence, because the
|
|
162
|
+
* FINDING is still true and still worth reading; what it loses is a move that
|
|
163
|
+
* would not have moved anything.
|
|
164
|
+
*/
|
|
165
|
+
function cardSpec(finding, known, edges, words) {
|
|
166
|
+
const target = navigableMember(finding, known);
|
|
167
|
+
return element('li', {
|
|
168
|
+
class: 'ig-audit-card',
|
|
169
|
+
[AUDIT_KIND_ATTRIBUTE]: finding.kind,
|
|
170
|
+
// THE SEVERITY IS PUBLISHED, NOT DRAWN FROM. A host styling the four
|
|
171
|
+
// differently needs it, and on the card — which this package owns — it
|
|
172
|
+
// carries no rule of its own, so it cannot collide the way it would on a
|
|
173
|
+
// chip. See the header.
|
|
174
|
+
'data-ig-audit-severity': finding.severity,
|
|
175
|
+
}, [
|
|
176
|
+
element('span', { class: 'ig-audit-chip', [AUDIT_KIND_ATTRIBUTE]: finding.kind }, [
|
|
177
|
+
words.classes[finding.kind],
|
|
178
|
+
]),
|
|
179
|
+
element('p', { class: 'ig-audit-title' }, [words.titles[finding.kind]]),
|
|
180
|
+
// THE FRAME'S THIRD LINE, and only where the reader named an ordering —
|
|
181
|
+
// see this module's header for the amendment that admits it, the cost it
|
|
182
|
+
// concedes, and why it is hidden from the accessibility tree.
|
|
183
|
+
//
|
|
184
|
+
// THE HEAD REPEATS HERE AND NOWHERE ELSE. `AuditFinding.walk` names each
|
|
185
|
+
// member once and implies the return edge, because closing the loop is a
|
|
186
|
+
// drawing decision — this is the drawing.
|
|
187
|
+
//
|
|
188
|
+
// NO `AuditWords` ENTRY. The arrow is a glyph the package draws, the same
|
|
189
|
+
// call `refusedOpen`'s `↗` makes; a host supplies words, not punctuation.
|
|
190
|
+
// GATED ON THE CLASS AS WELL AS THE FIELD. `AuditFinding` is a public
|
|
191
|
+
// export and so is this renderer, so a host can hand it a hand-built
|
|
192
|
+
// finding — and a walk drawn under a `stale blocker` chip would be an
|
|
193
|
+
// ordering of an edge that has none. The field's own doc says "only ever
|
|
194
|
+
// on a cycle finding"; this is what makes that a fact here rather than a
|
|
195
|
+
// hope about callers.
|
|
196
|
+
finding.kind !== 'cycle' || finding.walk === undefined || finding.walk.length === 0
|
|
197
|
+
? null
|
|
198
|
+
: element('p', { class: 'ig-audit-walk', 'aria-hidden': 'true' }, [
|
|
199
|
+
[...finding.walk, finding.walk[0]].join(' → '),
|
|
200
|
+
]),
|
|
201
|
+
element('p', { class: 'ig-audit-detail' }, [finding.detail]),
|
|
202
|
+
target === undefined
|
|
203
|
+
? null
|
|
204
|
+
: element('button', {
|
|
205
|
+
type: 'button',
|
|
206
|
+
class: 'ig-audit-show',
|
|
207
|
+
// `reveal-issue`, NEVER `select-issue`. The latter is a POINTER,
|
|
208
|
+
// and with a draft awaiting its target `workspace/host.ts` reads a
|
|
209
|
+
// pointer as choosing that target and emits the create proposal —
|
|
210
|
+
// so this button would declare a relationship. §17d's surface
|
|
211
|
+
// "offers navigation and never a remedy", and a control that can
|
|
212
|
+
// write is a remedy whatever its label says.
|
|
213
|
+
'data-ig-command': 'reveal-issue',
|
|
214
|
+
// THE NAME IS THE FINDING'S OWN SENTENCE, AND THAT IS A CLASS
|
|
215
|
+
// REMOVED RATHER THAN A THIRD ATTEMPT AT IT.
|
|
216
|
+
//
|
|
217
|
+
// Several findings mean several of these buttons, and in the one
|
|
218
|
+
// navigation mode that strips the surrounding card — a screen
|
|
219
|
+
// reader's BUTTON LIST — the name is all a reader has. Three
|
|
220
|
+
// revisions tried to SUMMARISE a finding into that name, and each
|
|
221
|
+
// summary collided on a shape the last had not met: the host's
|
|
222
|
+
// word alone collided everywhere; adding the ref collided when
|
|
223
|
+
// one issue carried two findings; adding the class collided when
|
|
224
|
+
// two findings shared a class AND a target — `a blocked-by y` and
|
|
225
|
+
// `a blocked-by z`, two closed blockers on one issue.
|
|
226
|
+
//
|
|
227
|
+
// The PATTERN is the defect. Any hand-picked subset of a
|
|
228
|
+
// finding's attributes has two findings that agree on it, so
|
|
229
|
+
// counting the collisions was never going to end. `detail` is the
|
|
230
|
+
// one value that cannot collide by construction: `findings.ts`
|
|
231
|
+
// composes it from the specific edge or members the finding is
|
|
232
|
+
// about, and documents it as "what the reader can be told, in one
|
|
233
|
+
// sentence" — so two findings with one sentence are one finding.
|
|
234
|
+
//
|
|
235
|
+
// IT ADDS NO ENGLISH. `detail` is already this card's visible
|
|
236
|
+
// prose, so nothing is announced that is not on screen, and the
|
|
237
|
+
// host's word still leads. Appended, never interpolated, the
|
|
238
|
+
// `WorkspaceWords.whyRank` idiom.
|
|
239
|
+
//
|
|
240
|
+
// A LONG NAME IS THE RIGHT TRADE. In a button list a name is an
|
|
241
|
+
// identifier before it is a label, and a distinct sentence beats
|
|
242
|
+
// a short ambiguous phrase; the VISIBLE label stays the short
|
|
243
|
+
// word.
|
|
244
|
+
//
|
|
245
|
+
// `WorkspaceWords.remove` has the same shape one leaf over and is
|
|
246
|
+
// deliberately untouched — filed as #176 rather than widened.
|
|
247
|
+
'data-ig-target': target,
|
|
248
|
+
'aria-label': `${words.show} ${finding.detail}`,
|
|
249
|
+
}, [words.show]),
|
|
250
|
+
// §17d's REMEDIES. Drawn after the navigation control, which is the
|
|
251
|
+
// frame's order: `Show the loop` then `pick one edge to drop`.
|
|
252
|
+
remedySpec(finding, known, edges, words),
|
|
253
|
+
]);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* The remedy controls for one finding, or `null` where the frame draws none.
|
|
257
|
+
*
|
|
258
|
+
* NOT ONE OF THEM WRITES. `RULINGS.md` §3 requires remedies and forbids
|
|
259
|
+
* auto-fix in one breath, and the mechanism it names is the same for all of
|
|
260
|
+
* them: *"Taking a remedy selects the finding's issue or edge and hands the
|
|
261
|
+
* work to the inspector, which already owns every edit affordance."* So every
|
|
262
|
+
* button here publishes a SELECTION, and the label says what the reader has
|
|
263
|
+
* decided to do once they are there.
|
|
264
|
+
*
|
|
265
|
+
* THE SUBJECT IS THE FINDING'S EDGE WHERE IT HAS ONE. {@link AuditFinding.edge}
|
|
266
|
+
* carries it for the two edge classes; a cycle names no single edge on purpose
|
|
267
|
+
* — choosing which to drop is the judgment §17d keeps with the owner — so its
|
|
268
|
+
* remedy selects the walk's head instead and the reader picks from there.
|
|
269
|
+
*
|
|
270
|
+
* WITHHELD RATHER THAN DRAWN INERT when the subject is missing. A finding whose
|
|
271
|
+
* edge the drawn document does not carry would give a button that selects
|
|
272
|
+
* nothing: §17d's own `Show the loop` already declines for exactly that reason
|
|
273
|
+
* one function up, and this follows it rather than inventing a second rule.
|
|
274
|
+
*/
|
|
275
|
+
function remedySpec(finding, known, edges, words) {
|
|
276
|
+
const { remedies } = words;
|
|
277
|
+
// A SELECT-EDGE BUTTON, BUILT ONCE. The two edge classes differ only in their
|
|
278
|
+
// label, so the shape is written once and the difference stays a word.
|
|
279
|
+
// THE EDGE ITSELF MUST BE DRAWN — not merely its two ends.
|
|
280
|
+
//
|
|
281
|
+
// A remedy publishes `select-edge`, and the host resolves that against the
|
|
282
|
+
// document IT is drawing, which is not always the one the audit read. A
|
|
283
|
+
// remedy naming an edge that document does not hold is a button that clears
|
|
284
|
+
// the reader's selection and opens an inspector with no subject.
|
|
285
|
+
//
|
|
286
|
+
// THE FIRST VERSION OF THIS GUARD ASKED WHETHER BOTH ENDS WERE KNOWN, and a
|
|
287
|
+
// review round reproduced the hole: two issues drawn, the edge between them
|
|
288
|
+
// omitted — which `mount.ts` does on purpose — and both remedies rendered
|
|
289
|
+
// anyway. Endpoint presence is a PROXY for edge presence and they come apart
|
|
290
|
+
// exactly where it matters.
|
|
291
|
+
//
|
|
292
|
+
// `Show the loop` follows the same rule one function up, asking `known` about
|
|
293
|
+
// the ref it will actually navigate to. This asks `edges` about the id it
|
|
294
|
+
// will actually select.
|
|
295
|
+
const reachable = (edge) => edges.has(edge.id);
|
|
296
|
+
const onEdge = (label, tag) => finding.edge === undefined || !reachable(finding.edge)
|
|
297
|
+
? null
|
|
298
|
+
: element('button', {
|
|
299
|
+
type: 'button',
|
|
300
|
+
class: 'ig-audit-remedy',
|
|
301
|
+
'data-ig-remedy': tag,
|
|
302
|
+
// `select-edge`, WHICH SELECTS AND DOES NOT WRITE. The inspector
|
|
303
|
+
// draws the edge's own controls once it is selected; this only
|
|
304
|
+
// says which edge the reader is judging.
|
|
305
|
+
'data-ig-command': 'select-edge',
|
|
306
|
+
'data-ig-target': finding.edge.id,
|
|
307
|
+
// THE FINDING'S OWN SENTENCE, for the reason `Show the loop`
|
|
308
|
+
// records above: in a screen reader's button list the name is all
|
|
309
|
+
// a reader has, and any hand-picked subset of a finding's
|
|
310
|
+
// attributes has two findings that agree on it.
|
|
311
|
+
'aria-label': `${label} ${finding.detail}`,
|
|
312
|
+
}, [label]);
|
|
313
|
+
switch (finding.kind) {
|
|
314
|
+
case 'stale-blocker': {
|
|
315
|
+
// TWO CONTROLS, AND THE SECOND IS GATED ON THE FINDING'S OWN FIELD rather
|
|
316
|
+
// than on the class. `keepAsHistory` is read from `AUDIT_CLASS_SPECS` and
|
|
317
|
+
// is `true` for this class alone — it predates this control and was
|
|
318
|
+
// waiting for it — so asking the finding keeps the two in step instead of
|
|
319
|
+
// restating the table here.
|
|
320
|
+
const remove = onEdge(remedies.removeEdge, 'remove-edge');
|
|
321
|
+
const keep = finding.keepAsHistory ? onEdge(remedies.keepAsHistory, 'keep-as-history') : null;
|
|
322
|
+
const both = [remove, keep].filter((spec) => spec !== null);
|
|
323
|
+
return both.length === 0 ? null : element('div', { class: 'ig-audit-remedies' }, both);
|
|
324
|
+
}
|
|
325
|
+
case 'dead-duplicate-ref': {
|
|
326
|
+
const repoint = onEdge(remedies.repoint, 'repoint');
|
|
327
|
+
return repoint === null ? null : element('div', { class: 'ig-audit-remedies' }, [repoint]);
|
|
328
|
+
}
|
|
329
|
+
case 'cycle': {
|
|
330
|
+
// THE WALK'S HEAD, NOT AN EDGE. See this function's header: the cycle is
|
|
331
|
+
// about a component, and the reader chooses the edge in the inspector.
|
|
332
|
+
// `walk` is absent whenever the reader declined to order the component,
|
|
333
|
+
// and then there is no defensible head to name — so no button.
|
|
334
|
+
const head = finding.walk?.[0];
|
|
335
|
+
// AND THE HEAD HAS TO BE DRAWN, for the reason `reachable` records above.
|
|
336
|
+
if (head === undefined || !known.has(head))
|
|
337
|
+
return null;
|
|
338
|
+
return element('div', { class: 'ig-audit-remedies' }, [
|
|
339
|
+
element('button', {
|
|
340
|
+
type: 'button',
|
|
341
|
+
class: 'ig-audit-remedy',
|
|
342
|
+
'data-ig-remedy': 'pick-edge',
|
|
343
|
+
// `reveal-issue`, NEVER `select-issue`, for the reason the
|
|
344
|
+
// navigation control above records: with a draft awaiting its
|
|
345
|
+
// target, `workspace/host.ts` reads a POINTER as choosing that
|
|
346
|
+
// target and emits a create proposal — so `select-issue` here
|
|
347
|
+
// would declare a relationship while claiming to remove one.
|
|
348
|
+
'data-ig-command': 'reveal-issue',
|
|
349
|
+
'data-ig-target': head,
|
|
350
|
+
'aria-label': `${remedies.pickEdge} ${finding.detail}`,
|
|
351
|
+
}, [remedies.pickEdge]),
|
|
352
|
+
]);
|
|
353
|
+
}
|
|
354
|
+
// NO REMEDY. The refusal's controls are `refusedOpen` and `refusedRewrite`
|
|
355
|
+
// in `./refused.ts`, and this panel does not list that class at all.
|
|
356
|
+
default:
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* The panel, or `null` when there is nothing to list.
|
|
362
|
+
*
|
|
363
|
+
* `null` RATHER THAN AN EMPTY PANEL, and it is not the same decision as the
|
|
364
|
+
* header count's. The count is drawn at zero on purpose — `./surface.ts`:
|
|
365
|
+
* *"a control that appears when there is bad news is a control the eye has to
|
|
366
|
+
* re-find"* — because it IS the control. A list of nothing is not a control; it
|
|
367
|
+
* is a heading over an empty region in a column whose space belongs to the
|
|
368
|
+
* selection. So the ambient half stays put at zero and the list goes away.
|
|
369
|
+
*
|
|
370
|
+
* IT IS THE LISTED FINDINGS THAT DECIDE, NOT `overlay.findings`. A document
|
|
371
|
+
* whose only findings are encoding refusals has a non-zero count, a bar on
|
|
372
|
+
* every affected row, and NOTHING for this panel to list — so it draws the
|
|
373
|
+
* refused block and no panel at all. Testing the overlay instead would have
|
|
374
|
+
* drawn a panel whose head said `2` above an empty list.
|
|
375
|
+
*/
|
|
376
|
+
export function renderAuditPanel(overlay, options) {
|
|
377
|
+
const listed = overlay.findings.filter((finding) => finding.kind !== 'encoding-refused');
|
|
378
|
+
if (listed.length === 0)
|
|
379
|
+
return null;
|
|
380
|
+
const { words } = options;
|
|
381
|
+
return element('section', {
|
|
382
|
+
class: 'ig-audit-panel',
|
|
383
|
+
// A `section` WITH NO NAME IS NOT A LANDMARK AT ALL — the implicit `region`
|
|
384
|
+
// role is conditional on an accessible name, so an unnamed one is exposed
|
|
385
|
+
// as nothing and a reader navigating by landmark cannot find the panel.
|
|
386
|
+
// `firstpass/render.ts` already names its own section from its words; this
|
|
387
|
+
// is the same rule one leaf over.
|
|
388
|
+
'aria-label': words.heading,
|
|
389
|
+
// A TAB STOP, AND THE REASON IS NOW THE SECOND ONE ALONE. It was the
|
|
390
|
+
// scroll container itself while #177's half-column bound sat on this
|
|
391
|
+
// element; since §17d's fourth class moved to `./refused.ts` the bound and
|
|
392
|
+
// the scrolling belong to the REGION holding both surfaces, and this panel
|
|
393
|
+
// does not shrink inside it. So "there is content only scrolling reaches"
|
|
394
|
+
// is no longer a fact about the panel — but the stop stays, because:
|
|
395
|
+
//
|
|
396
|
+
// ITS CARDS CANNOT BE RELIED ON TO CARRY THE KEYBOARD. `cardSpec` draws
|
|
397
|
+
// its navigation control only for a member the drawn document actually
|
|
398
|
+
// holds, so an audit whose findings all name issues outside the loaded
|
|
399
|
+
// page renders no button at all. That panel has no focusable descendant,
|
|
400
|
+
// and browsers disagree about putting a generic scroll container in the
|
|
401
|
+
// tab order — some do, some never have. Declaring it removes the
|
|
402
|
+
// disagreement rather than depending on which engine is running.
|
|
403
|
+
//
|
|
404
|
+
// It is already named, so this makes a NAMED region focusable rather than
|
|
405
|
+
// adding an anonymous stop. `styles.ts` draws the ring it now needs.
|
|
406
|
+
tabindex: '0',
|
|
407
|
+
}, [
|
|
408
|
+
element('div', { class: 'ig-audit-panel-head' }, [
|
|
409
|
+
// THE MARK IS THE FRAME'S, AND IT IS NOT A WORD. `◆` is the same glyph
|
|
410
|
+
// §17a puts before the ambient count, so the panel and the chip that
|
|
411
|
+
// counts it read as one thing. `aria-hidden`, because it says nothing a
|
|
412
|
+
// reader needs said twice — the count and the heading beside it do.
|
|
413
|
+
element('span', { class: 'ig-audit-mark', 'aria-hidden': 'true' }, ['◆']),
|
|
414
|
+
// THIS PANEL'S OWN CARDS, NOT `overlay.count`, AND THE TWO ARE NOW
|
|
415
|
+
// DIFFERENT NUMBERS. `./surface.ts`'s header count is the persistent
|
|
416
|
+
// control §17d gives one job — "always the same click" — and it counts
|
|
417
|
+
// every finding, because the reader works through all four classes. This
|
|
418
|
+
// number is ENCLOSED IN THE SAME BOUNDED, SCROLLING SECTION AS THE LIST
|
|
419
|
+
// UNDER IT, so once `encoding-refused` moved to `./refused.ts` it would
|
|
420
|
+
// have printed `4` directly above three cards.
|
|
421
|
+
//
|
|
422
|
+
// THE FRAME AGREES: §17d's panel head reads `3 encoding problems` over
|
|
423
|
+
// exactly its own three cards, with the refusal a separate card outside.
|
|
424
|
+
// A count enclosed by the thing it counts is a count OF that thing.
|
|
425
|
+
element('span', { class: 'ig-audit-panel-count' }, [String(listed.length)]),
|
|
426
|
+
// A REAL HEADING, AT THE LEVEL ITS PLACE IN THE ZONE EARNS. A `span` left
|
|
427
|
+
// the panel invisible to heading navigation and put a gap in the column's
|
|
428
|
+
// outline; the caps are the stylesheet's, as they are for every other
|
|
429
|
+
// heading here, so the host's word stays a word.
|
|
430
|
+
//
|
|
431
|
+
// `h2`, NOT `h3`, AND THE LEVEL FOLLOWS THE COMPOSITION. It was an `h3`
|
|
432
|
+
// while the panel was drawn INSIDE `.ig-inspector`, under that column's
|
|
433
|
+
// own `h2`. The panel is a SIBLING of it now, and `renderWorkspace`
|
|
434
|
+
// concatenates the panel first — so an `h3` announced a level three
|
|
435
|
+
// before the level two it claimed to sit under, describing a nesting the
|
|
436
|
+
// markup does not have. Two peers, two `h2`s.
|
|
437
|
+
element('h2', { class: 'ig-audit-panel-heading' }, [words.heading]),
|
|
438
|
+
// §17d's OWN ENTRY POINT, drawn where the frame draws it. `RULINGS.md` §2
|
|
439
|
+
// already settled what it does — *"The filter hides ROWS. It never
|
|
440
|
+
// touches the ORDER"* — and §3 moved it here from the header count so
|
|
441
|
+
// that count could become this panel's anchor.
|
|
442
|
+
//
|
|
443
|
+
// `aria-pressed` IS RIGHT HERE AND WRONG ON THE HEADER. This one really
|
|
444
|
+
// is a toggle with an ON state; the header's is a disclosure.
|
|
445
|
+
element('button', {
|
|
446
|
+
type: 'button',
|
|
447
|
+
class: 'ig-audit-filter',
|
|
448
|
+
'aria-pressed': options.filtered === true ? 'true' : 'false',
|
|
449
|
+
// ONE CHANNEL, NOT TWO. The mount routes this on the published
|
|
450
|
+
// attribute alone, and a control carrying `data-ig-command` as well
|
|
451
|
+
// would be two entries in the a11y baseline for one button.
|
|
452
|
+
[AUDIT_FILTER_ATTRIBUTE]: '',
|
|
453
|
+
}, [words.filter]),
|
|
454
|
+
]),
|
|
455
|
+
// ORDERED BY THE CLASS TABLE, WHICH `auditOverlay` ALREADY DID. `findings`
|
|
456
|
+
// arrives in `AUDIT_CLASSES` order because `auditDocument` concatenates its
|
|
457
|
+
// four detectors in that order; re-sorting here would be a second opinion
|
|
458
|
+
// about severity next to `AUDIT_CLASS_SPECS`, which is the one that ranks.
|
|
459
|
+
element('ul', { class: 'ig-audit-list' }, listed.map((finding) => cardSpec(finding, options.known, options.edges, words))),
|
|
460
|
+
]);
|
|
461
|
+
}
|
|
462
|
+
//# sourceMappingURL=panel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panel.js","sourceRoot":"","sources":["../../src/audit/panel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwHG;AAGH,OAAO,EAAoB,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAGtD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,oBAAoB,CAAC;AAoIzD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,SAAS,eAAe,CACtB,OAAqB,EACrB,KAA0B;IAE1B,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;AACvD,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,QAAQ,CACf,OAAqB,EACrB,KAA0B,EAC1B,KAA0B,EAC1B,KAAiB;IAEjB,MAAM,MAAM,GAAG,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC/C,OAAO,OAAO,CACZ,IAAI,EACJ;QACE,KAAK,EAAE,eAAe;QACtB,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC,IAAI;QACpC,qEAAqE;QACrE,uEAAuE;QACvE,yEAAyE;QACzE,wBAAwB;QACxB,wBAAwB,EAAE,OAAO,CAAC,QAAQ;KAC3C,EACD;QACE,OAAO,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE;YAChF,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;SAC5B,CAAC;QACF,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACvE,wEAAwE;QACxE,yEAAyE;QACzE,8DAA8D;QAC9D,EAAE;QACF,yEAAyE;QACzE,yEAAyE;QACzE,0CAA0C;QAC1C,EAAE;QACF,0EAA0E;QAC1E,0EAA0E;QAC1E,sEAAsE;QACtE,qEAAqE;QACrE,sEAAsE;QACtE,yEAAyE;QACzE,yEAAyE;QACzE,sBAAsB;QACtB,OAAO,CAAC,IAAI,KAAK,OAAO,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;YACjF,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,EAAE,EAAE;gBAC9D,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAa,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;aAC3D,CAAC;QACN,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC5D,MAAM,KAAK,SAAS;YAClB,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,OAAO,CACL,QAAQ,EACR;gBACE,IAAI,EAAE,QAAQ;gBACd,KAAK,EAAE,eAAe;gBACtB,iEAAiE;gBACjE,mEAAmE;gBACnE,kEAAkE;gBAClE,8DAA8D;gBAC9D,iEAAiE;gBACjE,6CAA6C;gBAC7C,iBAAiB,EAAE,cAAc;gBACjC,8DAA8D;gBAC9D,6CAA6C;gBAC7C,EAAE;gBACF,iEAAiE;gBACjE,8DAA8D;gBAC9D,6DAA6D;gBAC7D,kEAAkE;gBAClE,+DAA+D;gBAC/D,+DAA+D;gBAC/D,iEAAiE;gBACjE,kEAAkE;gBAClE,sDAAsD;gBACtD,EAAE;gBACF,yDAAyD;gBACzD,6DAA6D;gBAC7D,kEAAkE;gBAClE,+DAA+D;gBAC/D,+DAA+D;gBAC/D,kEAAkE;gBAClE,iEAAiE;gBACjE,EAAE;gBACF,8DAA8D;gBAC9D,gEAAgE;gBAChE,6DAA6D;gBAC7D,kCAAkC;gBAClC,EAAE;gBACF,gEAAgE;gBAChE,iEAAiE;gBACjE,8DAA8D;gBAC9D,QAAQ;gBACR,EAAE;gBACF,kEAAkE;gBAClE,8DAA8D;gBAC9D,gBAAgB,EAAE,MAAM;gBACxB,YAAY,EAAE,GAAG,KAAK,CAAC,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE;aAChD,EACD,CAAC,KAAK,CAAC,IAAI,CAAC,CACb;QACL,oEAAoE;QACpE,+DAA+D;QAC/D,UAAU,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC;KACzC,CACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAS,UAAU,CACjB,OAAqB,EACrB,KAA0B,EAC1B,KAA0B,EAC1B,KAAiB;IAEjB,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IAC3B,8EAA8E;IAC9E,uEAAuE;IACvE,2DAA2D;IAC3D,EAAE;IACF,2EAA2E;IAC3E,wEAAwE;IACxE,4EAA4E;IAC5E,iEAAiE;IACjE,EAAE;IACF,4EAA4E;IAC5E,4EAA4E;IAC5E,0EAA0E;IAC1E,6EAA6E;IAC7E,4BAA4B;IAC5B,EAAE;IACF,8EAA8E;IAC9E,0EAA0E;IAC1E,wBAAwB;IACxB,MAAM,SAAS,GAAG,CAAC,IAAe,EAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,CAAC,KAAa,EAAE,GAAW,EAAsB,EAAE,CAChE,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;QACpD,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,OAAO,CACL,QAAQ,EACR;YACE,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,iBAAiB;YACxB,gBAAgB,EAAE,GAAG;YACrB,iEAAiE;YACjE,+DAA+D;YAC/D,yCAAyC;YACzC,iBAAiB,EAAE,aAAa;YAChC,gBAAgB,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE;YACjC,6DAA6D;YAC7D,kEAAkE;YAClE,0DAA0D;YAC1D,gDAAgD;YAChD,YAAY,EAAE,GAAG,KAAK,IAAI,OAAO,CAAC,MAAM,EAAE;SAC3C,EACD,CAAC,KAAK,CAAC,CACR,CAAC;IAER,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;QACrB,KAAK,eAAe,CAAC,CAAC,CAAC;YACrB,0EAA0E;YAC1E,0EAA0E;YAC1E,oEAAoE;YACpE,0EAA0E;YAC1E,4BAA4B;YAC5B,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;YAC1D,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC9F,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAuB,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;YACjF,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAAE,IAAI,CAAC,CAAC;QACzF,CAAC;QACD,KAAK,oBAAoB,CAAC,CAAC,CAAC;YAC1B,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YACpD,OAAO,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7F,CAAC;QACD,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,yEAAyE;YACzE,uEAAuE;YACvE,wEAAwE;YACxE,+DAA+D;YAC/D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC/B,0EAA0E;YAC1E,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC;YACxD,OAAO,OAAO,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAAE;gBACpD,OAAO,CACL,QAAQ,EACR;oBACE,IAAI,EAAE,QAAQ;oBACd,KAAK,EAAE,iBAAiB;oBACxB,gBAAgB,EAAE,WAAW;oBAC7B,2DAA2D;oBAC3D,8DAA8D;oBAC9D,+DAA+D;oBAC/D,8DAA8D;oBAC9D,6DAA6D;oBAC7D,iBAAiB,EAAE,cAAc;oBACjC,gBAAgB,EAAE,IAAI;oBACtB,YAAY,EAAE,GAAG,QAAQ,CAAC,QAAQ,IAAI,OAAO,CAAC,MAAM,EAAE;iBACvD,EACD,CAAC,QAAQ,CAAC,QAAQ,CAAC,CACpB;aACF,CAAC,CAAC;QACL,CAAC;QACD,2EAA2E;QAC3E,qEAAqE;QACrE;YACE,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAqB,EACrB,OAA0B;IAE1B,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,kBAAkB,CAAC,CAAC;IACzF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAC1B,OAAO,OAAO,CACZ,SAAS,EACT;QACE,KAAK,EAAE,gBAAgB;QACvB,4EAA4E;QAC5E,0EAA0E;QAC1E,wEAAwE;QACxE,2EAA2E;QAC3E,kCAAkC;QAClC,YAAY,EAAE,KAAK,CAAC,OAAO;QAC3B,qEAAqE;QACrE,qEAAqE;QACrE,2EAA2E;QAC3E,2EAA2E;QAC3E,0EAA0E;QAC1E,qEAAqE;QACrE,EAAE;QACF,wEAAwE;QACxE,uEAAuE;QACvE,uEAAuE;QACvE,yEAAyE;QACzE,wEAAwE;QACxE,iEAAiE;QACjE,iEAAiE;QACjE,EAAE;QACF,0EAA0E;QAC1E,qEAAqE;QACrE,QAAQ,EAAE,GAAG;KACd,EACD;QACA,OAAO,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,qBAAqB,EAAE,EAAE;YAC/C,uEAAuE;YACvE,qEAAqE;YACrE,wEAAwE;YACxE,oEAAoE;YACpE,OAAO,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;YACzE,mEAAmE;YACnE,qEAAqE;YACrE,uEAAuE;YACvE,yEAAyE;YACzE,wEAAwE;YACxE,wEAAwE;YACxE,+CAA+C;YAC/C,EAAE;YACF,uEAAuE;YACvE,yEAAyE;YACzE,oEAAoE;YACpE,OAAO,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;YAC3E,0EAA0E;YAC1E,0EAA0E;YAC1E,sEAAsE;YACtE,iDAAiD;YACjD,EAAE;YACF,wEAAwE;YACxE,wEAAwE;YACxE,oEAAoE;YACpE,oEAAoE;YACpE,yEAAyE;YACzE,8CAA8C;YAC9C,OAAO,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,wBAAwB,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACnE,0EAA0E;YAC1E,mEAAmE;YACnE,sEAAsE;YACtE,+CAA+C;YAC/C,EAAE;YACF,wEAAwE;YACxE,8DAA8D;YAC9D,OAAO,CACL,QAAQ,EACR;gBACE,IAAI,EAAE,QAAQ;gBACd,KAAK,EAAE,iBAAiB;gBACxB,cAAc,EAAE,OAAO,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;gBAC5D,+DAA+D;gBAC/D,oEAAoE;gBACpE,4DAA4D;gBAC5D,CAAC,sBAAsB,CAAC,EAAE,EAAE;aAC7B,EACD,CAAC,KAAK,CAAC,MAAM,CAAC,CACf;SACF,CAAC;QACF,2EAA2E;QAC3E,4EAA4E;QAC5E,0EAA0E;QAC1E,2EAA2E;QAC3E,OAAO,CACL,IAAI,EACJ,EAAE,KAAK,EAAE,eAAe,EAAE,EAC1B,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAChF;KACA,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §17d's fourth class, drawn on its own rather than in the findings list.
|
|
3
|
+
*
|
|
4
|
+
* `./panel.ts` lists the three RELATIONSHIP findings — a cycle, a stale
|
|
5
|
+
* blocker, a dead duplicate ref. This module draws the fourth, and the
|
|
6
|
+
* separation is SPEC's rather than a layout preference: an encoding refusal is
|
|
7
|
+
* *"surfaced on the issue itself, because until it parses the issue has no
|
|
8
|
+
* edges at all and would otherwise look simply unencoded"*. The other three say
|
|
9
|
+
* something about a relationship that exists; this one says the relationships
|
|
10
|
+
* cannot be read, which is a statement about an ISSUE.
|
|
11
|
+
*
|
|
12
|
+
* ## It reads the host's refusals, not the audit's findings
|
|
13
|
+
*
|
|
14
|
+
* The two are the same set — `auditDocument` turns every refusal into a finding
|
|
15
|
+
* — but only the refusal carries `diagnostic` and `sourceLine`, and only the
|
|
16
|
+
* refusal is meant to. `AuditFinding` is one shape over four classes, so
|
|
17
|
+
* declaring two fields three of them can never hold would render an absence as
|
|
18
|
+
* a value in the module whose fourth class exists to refuse that; and
|
|
19
|
+
* `AuditFinding.detail` is "one sentence, never parsed, never a code", so
|
|
20
|
+
* recovering a source line out of it would make a sentence a wire format. See
|
|
21
|
+
* {@link EncodingRefusal}.
|
|
22
|
+
*
|
|
23
|
+
* THE COST IS ONE DUPLICATED RULE AND IT IS STATED RATHER THAN HIDDEN.
|
|
24
|
+
* `encodingRefusedFindings` deduplicates refusals by ref, first occurrence
|
|
25
|
+
* winning, and {@link refusalsToDraw} applies the same rule here — so a host
|
|
26
|
+
* that reports one issue twice gets one finding and one card rather than one
|
|
27
|
+
* and two. `findings.ts` warns against exactly this shape ("a renderer doing it
|
|
28
|
+
* would leave the HEADER COUNT wrong"), and the warning is answered rather than
|
|
29
|
+
* ignored: the count is not computed here, the rule is one line rather than a
|
|
30
|
+
* detector, and `./panel.test.ts` pins the arithmetic across both surfaces.
|
|
31
|
+
*
|
|
32
|
+
* The alternative was to drive this off `overlay.findings` filtered to the
|
|
33
|
+
* class, using the refusals only as a `ref -> fields` map. That makes the two
|
|
34
|
+
* surfaces a partition of one list and cannot drift by construction — a real
|
|
35
|
+
* advantage — but it makes the block read a finding whose only role at that
|
|
36
|
+
* point is to be a key, in order to learn a ref it was already handed.
|
|
37
|
+
*
|
|
38
|
+
* ## Nothing here decides that a refusal is worth drawing
|
|
39
|
+
*
|
|
40
|
+
* A refusal naming an issue the drawn page does not carry STILL DRAWS ITS CARD.
|
|
41
|
+
* `findings.ts` keeps such a refusal on purpose — filtering to the loaded set
|
|
42
|
+
* would drop findings on exactly the issues a paging boundary has not reached —
|
|
43
|
+
* and `./surface.ts` deliberately gives it no rail row. What it loses is the
|
|
44
|
+
* move that needs a row: see {@link revealControl}. It keeps the outward link,
|
|
45
|
+
* because leaving the application does not need a local row, and it keeps its
|
|
46
|
+
* prose, because the finding is true.
|
|
47
|
+
*
|
|
48
|
+
* ## The block is a tab stop for the same reason the panel is
|
|
49
|
+
*
|
|
50
|
+
* Every control here is conditional, so a block whose refusals all name
|
|
51
|
+
* unloaded issues, with no URL resolver supplied, has NO focusable descendant —
|
|
52
|
+
* and browsers disagree about putting a generic container in the tab order.
|
|
53
|
+
* `./panel.ts` met this and settled it; this is the same answer one leaf over.
|
|
54
|
+
*
|
|
55
|
+
* @see https://github.com/autnmy/issuegraph/blob/main/SPEC.md
|
|
56
|
+
*/
|
|
57
|
+
import type { IssueRef } from '@issuegraph/store';
|
|
58
|
+
import { type ElementSpec } from '@issuegraph/viewer';
|
|
59
|
+
import type { EncodingRefusal } from './findings.ts';
|
|
60
|
+
import { type AuditWords } from './panel.ts';
|
|
61
|
+
export interface EncodingRefusedBlockOptions {
|
|
62
|
+
readonly words: AuditWords;
|
|
63
|
+
/**
|
|
64
|
+
* The keys the DRAWN surface carries.
|
|
65
|
+
*
|
|
66
|
+
* THE SAME SET AND THE SAME RULE `./panel.ts` AND `holdRow` USE, and it is
|
|
67
|
+
* the caller's answer rather than one derived here: a host audits the whole
|
|
68
|
+
* repository it holds and renders a page of it, so a ref can be audited and
|
|
69
|
+
* still have no row to arrive at. Neither this module nor the overlay can see
|
|
70
|
+
* that difference.
|
|
71
|
+
*/
|
|
72
|
+
readonly known: ReadonlySet<string>;
|
|
73
|
+
/**
|
|
74
|
+
* Where this issue lives, in the host's own world.
|
|
75
|
+
*
|
|
76
|
+
* THE HOST'S ANSWER BECAUSE IT IS THE ONLY PARTY THAT HAS ONE. An
|
|
77
|
+
* {@link IssueRef} is opaque here and this package holds no repository
|
|
78
|
+
* identity — `check:isolation` is what keeps it that way — so a URL is not
|
|
79
|
+
* derivable, only supplied.
|
|
80
|
+
*
|
|
81
|
+
* ABSENT, `null`, OR NOT LINKABLE ALL MEAN THE SAME THING: no link is drawn.
|
|
82
|
+
* The last of those is not tidiness. An `href` runs in the host's origin, so
|
|
83
|
+
* `javascript:` and `data:` are script-execution sinks that escaping does not
|
|
84
|
+
* close — `renderMarkup` escapes attribute TEXT and has no opinion about
|
|
85
|
+
* schemes. `@issuegraph/viewer` already owns that allowlist and canonicalizes
|
|
86
|
+
* the way a URL parser does, which is what a naive scan gets wrong; a second
|
|
87
|
+
* copy out here would be the mirror whose input space drifts.
|
|
88
|
+
*/
|
|
89
|
+
readonly issueUrl?: ((ref: IssueRef) => string | null) | undefined;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The refused block, or `null` when the host reported no refusals.
|
|
93
|
+
*
|
|
94
|
+
* `null` RATHER THAN AN EMPTY BLOCK, the same call `renderAuditPanel` makes: a
|
|
95
|
+
* heading over an empty region is not a control, and this zone's space belongs
|
|
96
|
+
* to the selection.
|
|
97
|
+
*/
|
|
98
|
+
export declare function renderEncodingRefusedBlock(encodingRefused: readonly EncodingRefusal[], options: EncodingRefusedBlockOptions): ElementSpec | null;
|
|
99
|
+
//# sourceMappingURL=refused.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refused.d.ts","sourceRoot":"","sources":["../../src/audit/refused.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,KAAK,WAAW,EAAuB,MAAM,oBAAoB,CAAC;AAE3E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAwB,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAanE,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B;;;;;;;;OAQG;IACH,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACpC;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,QAAQ,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,SAAS,CAAC;CACpE;AAwMD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,SAAS,eAAe,EAAE,EAC3C,OAAO,EAAE,2BAA2B,GACnC,WAAW,GAAG,IAAI,CA+BpB"}
|