@michaelmagan/dnumb 0.1.0 → 0.1.2
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/AGENTS.md +27 -17
- package/README.md +26 -10
- package/dist/cli.cjs +0 -0
- package/dist/digest/render.cjs +77 -1
- package/dist/digest/render.js +77 -1
- package/docs/digest.md +1600 -0
- package/package.json +20 -20
- package/skills/dnumb/SKILL.md +20 -10
package/AGENTS.md
CHANGED
|
@@ -8,13 +8,13 @@ in a browser. Every output is text a model reads.
|
|
|
8
8
|
`dnumb` turns a rendered page into measurements: element geometry, overflow, clipping, and the
|
|
9
9
|
CSS that says what those measurements mean. It renders them as a linear text digest — one line per
|
|
10
10
|
element that stands on its own, and one fold line standing in for a whole run of repeated siblings
|
|
11
|
-
at once, each still addressable by id (see [`docs/digest.md`](
|
|
11
|
+
at once, each still addressable by id (see [`docs/digest.md`](docs/digest.md#the-fold-line)) — and
|
|
12
12
|
writes them to a self-contained JSON bundle you can query later on a machine with no app and no
|
|
13
13
|
browser.
|
|
14
14
|
|
|
15
15
|
**Live example** (`fixture=test/fixtures/flex-min-width-ellipsis.html line=e2`) — one real line out
|
|
16
16
|
of a real capture, checked by `test/browser/doc-examples.test.ts`; the full capture is in
|
|
17
|
-
[`docs/digest.md`](
|
|
17
|
+
[`docs/digest.md`](docs/digest.md#a-truncated-heading).
|
|
18
18
|
|
|
19
19
|
```text
|
|
20
20
|
e2 h1.heading "Quarterly revenue review for t…" @0,0 668x28 [overflow-x +147px; clip, ellipsis]
|
|
@@ -40,7 +40,7 @@ Stay with the screenshot for *paint* — imagery, typography, and overall visual
|
|
|
40
40
|
anything painted rather than styled (a photo, a canvas, video, a gradient's true value at the
|
|
41
41
|
glyphs). Color is the one item that moved off this list: dnumb now measures the ink and the
|
|
42
42
|
composited backdrop text is actually drawn on, and reports a WCAG contrast ratio, never a
|
|
43
|
-
pass/fail — see [`docs/digest.md`](
|
|
43
|
+
pass/fail — see [`docs/digest.md`](docs/digest.md#color).
|
|
44
44
|
The two tools still compose well: use the image to form a suspicion and the numbers to check it.
|
|
45
45
|
|
|
46
46
|
Honest cost: on the predecessor to this tool, tool-grounded review cost **25–30% more tokens**
|
|
@@ -83,9 +83,9 @@ problem:
|
|
|
83
83
|
element with its own text gets an *effective backdrop* (the real, composited color behind it,
|
|
84
84
|
not its own usually-transparent `background-color`) and, where both ink and backdrop resolve, a
|
|
85
85
|
WCAG contrast ratio — never a pass/fail. Reachable via the digest's `# colors:` header line, `q
|
|
86
|
-
color`, and `q describe`, per [`docs/digest.md`](
|
|
86
|
+
color`, and `q describe`, per [`docs/digest.md`](docs/digest.md#color).
|
|
87
87
|
|
|
88
|
-
Full grammar, every token, in [`docs/digest.md`](
|
|
88
|
+
Full grammar, every token, in [`docs/digest.md`](docs/digest.md).
|
|
89
89
|
|
|
90
90
|
**Do not treat a fact as a verdict.** Do not report `[overflow-x +147px]` as "an overflow bug"
|
|
91
91
|
without the consequence tokens beside it, and do not treat the absence of facts as a clean bill of
|
|
@@ -96,8 +96,7 @@ can check the inference separately from the measurement.
|
|
|
96
96
|
|
|
97
97
|
The predecessor to this tool died of exactly this. It measured `scrollWidth: 815, clientWidth:
|
|
98
98
|
668` on a real truncated heading, then discarded the finding because the rule that owned the
|
|
99
|
-
measurement read `text-overflow: ellipsis` as absolution.
|
|
100
|
-
[`docs/01-POSTMORTEM.md`](https://github.com/michaelmagan/dnumb/blob/main/docs/01-POSTMORTEM.md).
|
|
99
|
+
measurement read `text-overflow: ellipsis` as absolution.
|
|
101
100
|
|
|
102
101
|
## Two things that will trip you up
|
|
103
102
|
|
|
@@ -111,7 +110,7 @@ space for the ellipsis glyph and counts it in `scrollWidth`. Measured at +10px a
|
|
|
111
110
|
+17px at font-size 20; the amount is that glyph's advance width, so it tracks font-size and varies
|
|
112
111
|
with font-family. When a line says `ellipsis`, treat the number as an upper bound on the hidden
|
|
113
112
|
content, not an exact width, and do not subtract a constant. Full detail in
|
|
114
|
-
[`docs/digest.md`](
|
|
113
|
+
[`docs/digest.md`](docs/digest.md).
|
|
115
114
|
|
|
116
115
|
## The library surface that exists today
|
|
117
116
|
|
|
@@ -136,7 +135,7 @@ import {
|
|
|
136
135
|
|
|
137
136
|
`diffCaptures` is the other core verb — what changed between two captures, and why, at the same
|
|
138
137
|
"report, do not adjudicate" register as everything else in this package. See the CLI's `diff` verb
|
|
139
|
-
and [Reading a diff](
|
|
138
|
+
and [Reading a diff](README.md#reading-a-diff) in
|
|
140
139
|
the README for the grammar and a worked example.
|
|
141
140
|
|
|
142
141
|
Plus the geometry primitives — `intersect`, `contains`, `gapAlong`, `area`, `center` — all pure
|
|
@@ -165,16 +164,27 @@ does not.
|
|
|
165
164
|
|
|
166
165
|
## Status
|
|
167
166
|
|
|
168
|
-
**
|
|
169
|
-
stable verb set — `capture`, `look`, `diff`, and
|
|
167
|
+
**Published.** `npm install --save-dev @michaelmagan/dnumb` — MIT, zero dependencies. The library
|
|
168
|
+
surface above is built and tested. The CLI has a stable verb set — `capture`, `look`, `diff`, and
|
|
169
|
+
`q describe|find|at|within|distance|nearest|facts|color`
|
|
170
170
|
— documented with worked examples in
|
|
171
|
-
[README § From the CLI](
|
|
172
|
-
[§ Reading a diff](
|
|
171
|
+
[README § From the CLI](README.md#from-the-cli) and
|
|
172
|
+
[§ Reading a diff](README.md#reading-a-diff). This
|
|
173
173
|
file still does not restate flags, on purpose: run `dnumb --help` for the exhaustive, versioned
|
|
174
174
|
list of what your installed copy actually supports, and treat it as authoritative over any prose
|
|
175
175
|
here or there.
|
|
176
176
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
177
|
+
**Invoking it — three forms, not one.** Nothing installed yet, any package manager: `npx
|
|
178
|
+
@michaelmagan/dnumb --help` (or `bunx @michaelmagan/dnumb --help`) — the zero-install form, works
|
|
179
|
+
everywhere. Already a dependency — installed via `npm install`, `pnpm add`, or `bun add`, all of
|
|
180
|
+
which populate `node_modules/.bin/dnumb` the same way: `npx dnumb --help` or `bunx dnumb --help`,
|
|
181
|
+
either resolves the local bin regardless of which tool installed it (verified: `npx` correctly
|
|
182
|
+
resolves a bun-installed bin, including under bun's isolated linker). **If a bare `npx dnumb ...` or
|
|
183
|
+
`bunx dnumb ...` 404s, that means nothing is installed locally, not that the package doesn't
|
|
184
|
+
exist** — both tools then resolve `dnumb` as a *package* name against the registry, and there is no
|
|
185
|
+
unscoped `dnumb` package on npm (permanently blocked by npm's similarity filter); retry with the
|
|
186
|
+
scoped form above, or install first: `npm install --save-dev @michaelmagan/dnumb` / `bun add -d
|
|
187
|
+
@michaelmagan/dnumb`.
|
|
188
|
+
|
|
189
|
+
The digest format is specified in [`docs/digest.md`](docs/digest.md), shipped alongside this file
|
|
190
|
+
in the installed package — no network access needed to read it.
|
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ e0 body @0,0 1024x181
|
|
|
20
20
|
e1 h1.heading "Account settings" @24,24 976x25
|
|
21
21
|
e2 div @24,65 976x44 [row] ↓16 [overflow-y +105px; clip]
|
|
22
22
|
e3 button.button "Actions" @39,77 60x21
|
|
23
|
-
e4 span "3 selected" @111,
|
|
23
|
+
e4 span "3 selected" @111,75 90x24 →12
|
|
24
24
|
e5 nav.menu "Actions" @39,104 182x109 →-110 [clipped by e2 div; overflow:hidden, not scrollable; 103.8px below, 182x5 visible]
|
|
25
25
|
e6 a.menuitem "Rename" @40,105 180x36 [clipped by e2 div; overflow:hidden, not scrollable; 31.6px below, 180x4 visible]
|
|
26
26
|
e7 a.menuitem "Duplicate" @40,141 180x36 ↓0 [clipped by e2 div; overflow:hidden, not scrollable; 67.2px below, nothing visible]
|
|
@@ -41,12 +41,13 @@ other four are, only in this one header line and the `q color` verb — naming e
|
|
|
41
41
|
pair on the page with a WCAG contrast ratio beside it, and never a pass/fail. See
|
|
42
42
|
[`docs/digest.md`](docs/digest.md#color) for the full grammar.
|
|
43
43
|
|
|
44
|
-
> **Status:
|
|
45
|
-
> analysis, bundles, the Playwright integration — is built
|
|
46
|
-
> `diff`, `q`) has a stable verb set, worked below in
|
|
47
|
-
> [Reading a diff](#reading-a-diff); `dnumb --help` stays the
|
|
48
|
-
> Build journal in [`LOG.md`](LOG.md); the plan is
|
|
49
|
-
> [`docs/00-PLAN-OF-RECORD.md`](docs/00-PLAN-OF-RECORD.md)
|
|
44
|
+
> **Status: published.** `npm install --save-dev @michaelmagan/dnumb` — MIT, zero dependencies. The
|
|
45
|
+
> library — capture, digest, facts, clip analysis, bundles, the Playwright integration — is built
|
|
46
|
+
> and tested. The CLI (`capture`, `look`, `diff`, `q`) has a stable verb set, worked below in
|
|
47
|
+
> [From the CLI](#from-the-cli) and [Reading a diff](#reading-a-diff); `dnumb --help` stays the
|
|
48
|
+
> exhaustive, versioned option list. Build journal in [`LOG.md`](LOG.md); the plan is
|
|
49
|
+
> [`docs/00-PLAN-OF-RECORD.md`](docs/00-PLAN-OF-RECORD.md); the release history is
|
|
50
|
+
> [`docs/06-RELEASE.md`](docs/06-RELEASE.md).
|
|
50
51
|
|
|
51
52
|
## The idea
|
|
52
53
|
|
|
@@ -174,8 +175,23 @@ No test suite yet, or just want a bundle right now? The shortest path from a URL
|
|
|
174
175
|
two commands:
|
|
175
176
|
|
|
176
177
|
```bash
|
|
177
|
-
dnumb capture https://example.com/settings --name settings-desktop
|
|
178
|
-
dnumb --from .dnumb/settings-desktop.json look
|
|
178
|
+
npx @michaelmagan/dnumb capture https://example.com/settings --name settings-desktop # no install
|
|
179
|
+
npx @michaelmagan/dnumb --from .dnumb/settings-desktop.json look
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
On a bun project, `bunx` is the equivalent zero-install form: `bunx @michaelmagan/dnumb --help`.
|
|
183
|
+
|
|
184
|
+
Already installed it (`npm install --save-dev @michaelmagan/dnumb`, or `bun add -d
|
|
185
|
+
@michaelmagan/dnumb`)? Drop the scope — `npx dnumb` and `bunx dnumb` both resolve the local `dnumb`
|
|
186
|
+
bin the same way, since npm, pnpm, and bun all populate `node_modules/.bin/dnumb` identically.
|
|
187
|
+
Without a local install, either tool takes `dnumb` as a *package* name, not a bin name, and there is
|
|
188
|
+
no unscoped `dnumb` package on npm (see [`docs/06-RELEASE.md` §0](docs/06-RELEASE.md)) — so a bare
|
|
189
|
+
`npx dnumb` or `bunx dnumb` with nothing installed 404s.
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
npx dnumb capture https://example.com/settings --name settings-desktop # after install
|
|
193
|
+
npx dnumb --from .dnumb/settings-desktop.json look
|
|
194
|
+
# or, in a bun project: bunx dnumb capture ... / bunx dnumb --from ... look
|
|
179
195
|
```
|
|
180
196
|
|
|
181
197
|
`capture` launches a browser, navigates, captures, and writes `.dnumb/settings-desktop.json` —
|
|
@@ -274,7 +290,7 @@ empty body and a page that did not change would otherwise look identical.
|
|
|
274
290
|
|
|
275
291
|
| Doc | What it is |
|
|
276
292
|
|---|---|
|
|
277
|
-
| [docs/digest.md](docs/digest.md) | **The digest format.** Every token, every fact form, the fold and collapse syntax, the parser contract |
|
|
293
|
+
| [docs/digest.md](docs/digest.md) | **The digest format.** Every token, every fact form, the fold and collapse syntax, the parser contract. Ships in the tarball |
|
|
278
294
|
| [AGENTS.md](AGENTS.md) | For the model that will use it. Ships in the tarball |
|
|
279
295
|
| [skills/dnumb/SKILL.md](skills/dnumb/SKILL.md) | The agent skill. Ships in the tarball, discoverable via `@tanstack/intent` |
|
|
280
296
|
| [LOG.md](LOG.md) | Build journal, one entry per step |
|
package/dist/cli.cjs
CHANGED
|
File without changes
|
package/dist/digest/render.cjs
CHANGED
|
@@ -151,10 +151,86 @@ function edgeDistance(nodeStart, nodeSize, prevStart, prevSize) {
|
|
|
151
151
|
return -(nodeEnd - prevStart);
|
|
152
152
|
return nodeStart - prevEnd;
|
|
153
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Which axis `gapToken` should measure and print, for one sibling pair.
|
|
156
|
+
*
|
|
157
|
+
* For `row` and `column` flex containers this is not a judgment call: flexbox
|
|
158
|
+
* has exactly one main axis, fixed by `flex-direction`, and every child is
|
|
159
|
+
* placed along it by the spec regardless of what the cross axis happens to
|
|
160
|
+
* measure between any two of them. `docs/digest.md`'s own pinned example
|
|
161
|
+
* (`clipped-menu.html`) is a real case where trusting the cross axis instead
|
|
162
|
+
* would have been wrong: `e5` (a dropdown flown out below the toolbar) and
|
|
163
|
+
* `e4` (a toolbar button) sit in a `[row]` container, genuinely 5px apart on
|
|
164
|
+
* y (the dropdown starts below the row, not overlapping it) and 110px
|
|
165
|
+
* overlapping on x -- the row's own axis. The declared axis wins there
|
|
166
|
+
* unconditionally, which is why this function never second-guesses `row` or
|
|
167
|
+
* `column`.
|
|
168
|
+
*
|
|
169
|
+
* `grid` is different in kind, not degree: CSS Grid is two-dimensional, and
|
|
170
|
+
* `analysis/neighbors.ts#siblingGaps` and `analysis/uniformity.ts#
|
|
171
|
+
* consecutiveGaps` already encode this ("`flow: 'grid'` has no single
|
|
172
|
+
* well-defined axis") by skipping grid containers outright for the analyses
|
|
173
|
+
* that need one global axis per container. The element line cannot skip --
|
|
174
|
+
* every non-first sibling gets a gap token -- so it asks the only question
|
|
175
|
+
* that has an honest answer for a *specific pair*: are these two boxes
|
|
176
|
+
* actually adjacent along x, along y, or is the relationship genuinely
|
|
177
|
+
* ambiguous? Two consecutive DOM-order children of a `grid-template-columns:
|
|
178
|
+
* repeat(N, ...)` row (the common case -- a stat-card row, a two-column
|
|
179
|
+
* article layout) sit at the same y with the same height and a real x gutter
|
|
180
|
+
* between them; before this function existed, `gapToken` always measured
|
|
181
|
+
* grid children vertically, so that real horizontal gutter (`ar.wikipedia.org`,
|
|
182
|
+
* real capture: a 24px column gutter between the TOC sidebar and the main
|
|
183
|
+
* content) came out as `↓-11195` and `↓-11167` -- values with the *shape* of
|
|
184
|
+
* a fabricated near-total vertical overlap, which is exactly the mechanism
|
|
185
|
+
* `docs/notes/07-refusal.md` §6 and a blind-read rater on `dashboard-
|
|
186
|
+
* defect.html`'s 4-column `stats-grid` (`↓-112`, one card's full height) both
|
|
187
|
+
* hit: a direction-agnostic negative number, printed on the one flow kind
|
|
188
|
+
* with no declared axis to check it against, read as a literal overlap
|
|
189
|
+
* claim.
|
|
190
|
+
*
|
|
191
|
+
* The rule, applied only when `flow === 'grid'`:
|
|
192
|
+
*
|
|
193
|
+
* - One axis shows a real separation (no overlap, i.e. `>= 0`) and the
|
|
194
|
+
* other shows overlap: the boxes are unambiguously adjacent along the
|
|
195
|
+
* separated axis (aligned/overlapping on the other is what "in the same
|
|
196
|
+
* row/column" looks like) -- print that axis. This is the fix for the
|
|
197
|
+
* stat-card and Wikipedia cases above.
|
|
198
|
+
* - Both axes show a real separation: a diagonal case with no overlap on
|
|
199
|
+
* either axis at all, the shape of a grid-auto-flow row-wrap boundary
|
|
200
|
+
* (last card of row N to first card of row N+1) -- the smaller of the two
|
|
201
|
+
* magnitudes is kept, since the wrap boundary's "distance" back across the
|
|
202
|
+
* whole row width is an artifact of printing one linear sequence for a
|
|
203
|
+
* 2-D layout, while the real row-to-row gap is usually the smaller number
|
|
204
|
+
* and is what a reader means by "the gap to the next one".
|
|
205
|
+
* - Both axes overlap: no separation anywhere to anchor a choice on (a
|
|
206
|
+
* decorative full-bleed overlay sibling, e.g., `linear-app`'s `e347`) --
|
|
207
|
+
* falls back to the same vertical default every other ambiguous flow kind
|
|
208
|
+
* (`column`, `none`) already uses, unchanged from before this fix.
|
|
209
|
+
*
|
|
210
|
+
* None of this touches `row`, `column`, or `none`: their gap arithmetic and
|
|
211
|
+
* axis were already correct (see `edgeDistance`'s own doc comment for the
|
|
212
|
+
* direction-agnostic RTL fix), and `07-refusal.md` §6 names the fabrication
|
|
213
|
+
* as specific to grid/row-flow siblings, not flex row/column.
|
|
214
|
+
*/
|
|
215
|
+
function resolveGapAxis(node, previous, flow) {
|
|
216
|
+
if (flow !== 'grid')
|
|
217
|
+
return flow !== 'row';
|
|
218
|
+
const h = edgeDistance(node.rect.x, node.rect.width, previous.rect.x, previous.rect.width);
|
|
219
|
+
const v = edgeDistance(node.rect.y, node.rect.height, previous.rect.y, previous.rect.height);
|
|
220
|
+
const hSeparated = Number.isFinite(h) && h >= 0;
|
|
221
|
+
const vSeparated = Number.isFinite(v) && v >= 0;
|
|
222
|
+
if (hSeparated && !vSeparated)
|
|
223
|
+
return false;
|
|
224
|
+
if (vSeparated && !hSeparated)
|
|
225
|
+
return true;
|
|
226
|
+
if (hSeparated && vSeparated)
|
|
227
|
+
return Math.abs(v) <= Math.abs(h);
|
|
228
|
+
return true; // both overlap: no separated axis to anchor on, keep the old default.
|
|
229
|
+
}
|
|
154
230
|
function gapToken(node, previous, flow) {
|
|
155
231
|
if (!previous)
|
|
156
232
|
return '';
|
|
157
|
-
const vertical =
|
|
233
|
+
const vertical = resolveGapAxis(node, previous, flow);
|
|
158
234
|
const value = vertical
|
|
159
235
|
? edgeDistance(node.rect.y, node.rect.height, previous.rect.y, previous.rect.height)
|
|
160
236
|
: edgeDistance(node.rect.x, node.rect.width, previous.rect.x, previous.rect.width);
|
package/dist/digest/render.js
CHANGED
|
@@ -147,10 +147,86 @@ function edgeDistance(nodeStart, nodeSize, prevStart, prevSize) {
|
|
|
147
147
|
return -(nodeEnd - prevStart);
|
|
148
148
|
return nodeStart - prevEnd;
|
|
149
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Which axis `gapToken` should measure and print, for one sibling pair.
|
|
152
|
+
*
|
|
153
|
+
* For `row` and `column` flex containers this is not a judgment call: flexbox
|
|
154
|
+
* has exactly one main axis, fixed by `flex-direction`, and every child is
|
|
155
|
+
* placed along it by the spec regardless of what the cross axis happens to
|
|
156
|
+
* measure between any two of them. `docs/digest.md`'s own pinned example
|
|
157
|
+
* (`clipped-menu.html`) is a real case where trusting the cross axis instead
|
|
158
|
+
* would have been wrong: `e5` (a dropdown flown out below the toolbar) and
|
|
159
|
+
* `e4` (a toolbar button) sit in a `[row]` container, genuinely 5px apart on
|
|
160
|
+
* y (the dropdown starts below the row, not overlapping it) and 110px
|
|
161
|
+
* overlapping on x -- the row's own axis. The declared axis wins there
|
|
162
|
+
* unconditionally, which is why this function never second-guesses `row` or
|
|
163
|
+
* `column`.
|
|
164
|
+
*
|
|
165
|
+
* `grid` is different in kind, not degree: CSS Grid is two-dimensional, and
|
|
166
|
+
* `analysis/neighbors.ts#siblingGaps` and `analysis/uniformity.ts#
|
|
167
|
+
* consecutiveGaps` already encode this ("`flow: 'grid'` has no single
|
|
168
|
+
* well-defined axis") by skipping grid containers outright for the analyses
|
|
169
|
+
* that need one global axis per container. The element line cannot skip --
|
|
170
|
+
* every non-first sibling gets a gap token -- so it asks the only question
|
|
171
|
+
* that has an honest answer for a *specific pair*: are these two boxes
|
|
172
|
+
* actually adjacent along x, along y, or is the relationship genuinely
|
|
173
|
+
* ambiguous? Two consecutive DOM-order children of a `grid-template-columns:
|
|
174
|
+
* repeat(N, ...)` row (the common case -- a stat-card row, a two-column
|
|
175
|
+
* article layout) sit at the same y with the same height and a real x gutter
|
|
176
|
+
* between them; before this function existed, `gapToken` always measured
|
|
177
|
+
* grid children vertically, so that real horizontal gutter (`ar.wikipedia.org`,
|
|
178
|
+
* real capture: a 24px column gutter between the TOC sidebar and the main
|
|
179
|
+
* content) came out as `↓-11195` and `↓-11167` -- values with the *shape* of
|
|
180
|
+
* a fabricated near-total vertical overlap, which is exactly the mechanism
|
|
181
|
+
* `docs/notes/07-refusal.md` §6 and a blind-read rater on `dashboard-
|
|
182
|
+
* defect.html`'s 4-column `stats-grid` (`↓-112`, one card's full height) both
|
|
183
|
+
* hit: a direction-agnostic negative number, printed on the one flow kind
|
|
184
|
+
* with no declared axis to check it against, read as a literal overlap
|
|
185
|
+
* claim.
|
|
186
|
+
*
|
|
187
|
+
* The rule, applied only when `flow === 'grid'`:
|
|
188
|
+
*
|
|
189
|
+
* - One axis shows a real separation (no overlap, i.e. `>= 0`) and the
|
|
190
|
+
* other shows overlap: the boxes are unambiguously adjacent along the
|
|
191
|
+
* separated axis (aligned/overlapping on the other is what "in the same
|
|
192
|
+
* row/column" looks like) -- print that axis. This is the fix for the
|
|
193
|
+
* stat-card and Wikipedia cases above.
|
|
194
|
+
* - Both axes show a real separation: a diagonal case with no overlap on
|
|
195
|
+
* either axis at all, the shape of a grid-auto-flow row-wrap boundary
|
|
196
|
+
* (last card of row N to first card of row N+1) -- the smaller of the two
|
|
197
|
+
* magnitudes is kept, since the wrap boundary's "distance" back across the
|
|
198
|
+
* whole row width is an artifact of printing one linear sequence for a
|
|
199
|
+
* 2-D layout, while the real row-to-row gap is usually the smaller number
|
|
200
|
+
* and is what a reader means by "the gap to the next one".
|
|
201
|
+
* - Both axes overlap: no separation anywhere to anchor a choice on (a
|
|
202
|
+
* decorative full-bleed overlay sibling, e.g., `linear-app`'s `e347`) --
|
|
203
|
+
* falls back to the same vertical default every other ambiguous flow kind
|
|
204
|
+
* (`column`, `none`) already uses, unchanged from before this fix.
|
|
205
|
+
*
|
|
206
|
+
* None of this touches `row`, `column`, or `none`: their gap arithmetic and
|
|
207
|
+
* axis were already correct (see `edgeDistance`'s own doc comment for the
|
|
208
|
+
* direction-agnostic RTL fix), and `07-refusal.md` §6 names the fabrication
|
|
209
|
+
* as specific to grid/row-flow siblings, not flex row/column.
|
|
210
|
+
*/
|
|
211
|
+
function resolveGapAxis(node, previous, flow) {
|
|
212
|
+
if (flow !== 'grid')
|
|
213
|
+
return flow !== 'row';
|
|
214
|
+
const h = edgeDistance(node.rect.x, node.rect.width, previous.rect.x, previous.rect.width);
|
|
215
|
+
const v = edgeDistance(node.rect.y, node.rect.height, previous.rect.y, previous.rect.height);
|
|
216
|
+
const hSeparated = Number.isFinite(h) && h >= 0;
|
|
217
|
+
const vSeparated = Number.isFinite(v) && v >= 0;
|
|
218
|
+
if (hSeparated && !vSeparated)
|
|
219
|
+
return false;
|
|
220
|
+
if (vSeparated && !hSeparated)
|
|
221
|
+
return true;
|
|
222
|
+
if (hSeparated && vSeparated)
|
|
223
|
+
return Math.abs(v) <= Math.abs(h);
|
|
224
|
+
return true; // both overlap: no separated axis to anchor on, keep the old default.
|
|
225
|
+
}
|
|
150
226
|
function gapToken(node, previous, flow) {
|
|
151
227
|
if (!previous)
|
|
152
228
|
return '';
|
|
153
|
-
const vertical =
|
|
229
|
+
const vertical = resolveGapAxis(node, previous, flow);
|
|
154
230
|
const value = vertical
|
|
155
231
|
? edgeDistance(node.rect.y, node.rect.height, previous.rect.y, previous.rect.height)
|
|
156
232
|
: edgeDistance(node.rect.x, node.rect.width, previous.rect.x, previous.rect.width);
|