@react-x11/components 0.14.0 → 0.16.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 (97) hide show
  1. package/dist/code-editor/node.d.ts +13 -0
  2. package/dist/code-editor/node.d.ts.map +1 -1
  3. package/dist/code-editor/node.js +23 -5
  4. package/dist/code-editor/node.js.map +1 -1
  5. package/dist/html/css/cascade.d.ts +209 -23
  6. package/dist/html/css/cascade.d.ts.map +1 -1
  7. package/dist/html/css/cascade.js +873 -175
  8. package/dist/html/css/cascade.js.map +1 -1
  9. package/dist/html/css/color.d.ts +25 -0
  10. package/dist/html/css/color.d.ts.map +1 -1
  11. package/dist/html/css/color.js +307 -19
  12. package/dist/html/css/color.js.map +1 -1
  13. package/dist/html/css/content.d.ts +11 -6
  14. package/dist/html/css/content.d.ts.map +1 -1
  15. package/dist/html/css/content.js +171 -12
  16. package/dist/html/css/content.js.map +1 -1
  17. package/dist/html/css/parse.d.ts.map +1 -1
  18. package/dist/html/css/parse.js +62 -9
  19. package/dist/html/css/parse.js.map +1 -1
  20. package/dist/html/css/shapes.d.ts +62 -0
  21. package/dist/html/css/shapes.d.ts.map +1 -0
  22. package/dist/html/css/shapes.js +214 -0
  23. package/dist/html/css/shapes.js.map +1 -0
  24. package/dist/html/css/style.d.ts +42 -5
  25. package/dist/html/css/style.d.ts.map +1 -1
  26. package/dist/html/css/style.js +75 -27
  27. package/dist/html/css/style.js.map +1 -1
  28. package/dist/html/css/ua.js +15 -0
  29. package/dist/html/css/ua.js.map +1 -1
  30. package/dist/html/css/values.d.ts +5 -0
  31. package/dist/html/css/values.d.ts.map +1 -1
  32. package/dist/html/css/values.js +18 -9
  33. package/dist/html/css/values.js.map +1 -1
  34. package/dist/html/index.d.ts.map +1 -1
  35. package/dist/html/index.js +7 -0
  36. package/dist/html/index.js.map +1 -1
  37. package/dist/html/layout/block.d.ts +38 -7
  38. package/dist/html/layout/block.d.ts.map +1 -1
  39. package/dist/html/layout/block.js +160 -50
  40. package/dist/html/layout/block.js.map +1 -1
  41. package/dist/html/layout/boxes.d.ts +29 -2
  42. package/dist/html/layout/boxes.d.ts.map +1 -1
  43. package/dist/html/layout/boxes.js +159 -20
  44. package/dist/html/layout/boxes.js.map +1 -1
  45. package/dist/html/layout/css-grid.d.ts.map +1 -1
  46. package/dist/html/layout/css-grid.js +69 -19
  47. package/dist/html/layout/css-grid.js.map +1 -1
  48. package/dist/html/layout/flex.d.ts.map +1 -1
  49. package/dist/html/layout/flex.js +1648 -225
  50. package/dist/html/layout/flex.js.map +1 -1
  51. package/dist/html/layout/inline.d.ts +15 -3
  52. package/dist/html/layout/inline.d.ts.map +1 -1
  53. package/dist/html/layout/inline.js +23 -8
  54. package/dist/html/layout/inline.js.map +1 -1
  55. package/dist/html/layout/table.d.ts +27 -1
  56. package/dist/html/layout/table.d.ts.map +1 -1
  57. package/dist/html/layout/table.js +907 -323
  58. package/dist/html/layout/table.js.map +1 -1
  59. package/dist/html/node.d.ts +36 -4
  60. package/dist/html/node.d.ts.map +1 -1
  61. package/dist/html/node.js +174 -37
  62. package/dist/html/node.js.map +1 -1
  63. package/dist/html/paint.d.ts +4 -1
  64. package/dist/html/paint.d.ts.map +1 -1
  65. package/dist/html/paint.js +769 -204
  66. package/dist/html/paint.js.map +1 -1
  67. package/dist/html/resources.js +14 -1
  68. package/dist/html/resources.js.map +1 -1
  69. package/dist/html/svg.d.ts +50 -17
  70. package/dist/html/svg.d.ts.map +1 -1
  71. package/dist/html/svg.js +501 -83
  72. package/dist/html/svg.js.map +1 -1
  73. package/dist/html/widgets.d.ts.map +1 -1
  74. package/dist/html/widgets.js +57 -4
  75. package/dist/html/widgets.js.map +1 -1
  76. package/package.json +3 -3
  77. package/src/code-editor/node.ts +24 -5
  78. package/src/html/css/cascade.ts +987 -176
  79. package/src/html/css/color.ts +345 -18
  80. package/src/html/css/content.ts +173 -15
  81. package/src/html/css/parse.ts +60 -8
  82. package/src/html/css/shapes.ts +273 -0
  83. package/src/html/css/style.ts +123 -30
  84. package/src/html/css/ua.ts +15 -0
  85. package/src/html/css/values.ts +23 -9
  86. package/src/html/index.ts +7 -0
  87. package/src/html/layout/block.ts +169 -56
  88. package/src/html/layout/boxes.ts +198 -24
  89. package/src/html/layout/css-grid.ts +89 -22
  90. package/src/html/layout/flex.ts +1863 -219
  91. package/src/html/layout/inline.ts +27 -7
  92. package/src/html/layout/table.ts +989 -333
  93. package/src/html/node.ts +204 -37
  94. package/src/html/paint.ts +999 -252
  95. package/src/html/resources.ts +13 -1
  96. package/src/html/svg.ts +600 -78
  97. package/src/html/widgets.ts +61 -4
package/dist/html/node.js CHANGED
@@ -40,10 +40,10 @@ import { registerElement, registeredElements } from 'react-x11/host';
40
40
  import { Node } from 'react-x11/node';
41
41
  import { codePointAtOffset, codeUnitOffsets } from '../internal/text.js';
42
42
  import { attr, HtmlSource, imageUrlOf, isElement, tagOf } from './dom.js';
43
- import { Cascade, HOVER_FOLLOWED, HOVER_PSEUDO_NONE, HOVER_PSEUDO_OTHER, HOVER_UNTOUCHED, } from './css/cascade.js';
43
+ import { Cascade, HOVER_FOLLOWED, HOVER_PSEUDO_NONE, HOVER_PSEUDO_OTHER, HOVER_UNTOUCHED, NO_FOCUS, } from './css/cascade.js';
44
44
  import { mediaMatches, parseStylesheet } from './css/parse.js';
45
45
  import { uaStylesheet } from './css/ua.js';
46
- import { BOX_RAISES, buildBoxes, CONTENT_IMAGES, CLEARED_FROM, COLUMN_LINES, COLUMN_PIECES, COLUMN_ROWS, CUT_BLOCKS, GENERATED_FROM, INLINE_OFFSETS, LINE_BOX_RAISES, SHIFTED_LINES, TEXT_SHIFTS, columned, } from './layout/boxes.js';
46
+ import { BOX_RAISES, buildBoxes, CONTENT_IMAGES, CLEARED_FROM, COLUMN_LINES, COLUMN_PIECES, COLUMN_ROWS, CUT_BLOCKS, GENERATED_FROM, INLINE_OFFSETS, LINE_BOX_RAISES, SHAPE_STYLES, SHIFTED_LINES, TEXT_SHIFTS, columned, } from './layout/boxes.js';
47
47
  import { layoutDocument, placedMatrix, retranslate } from './layout/block.js';
48
48
  import { invert, mapPoint, mapRect, transformed } from './css/transform.js';
49
49
  import { TextLayoutCache } from './layout/cache.js';
@@ -56,7 +56,7 @@ import { faceExtentOf, inlineDecoration, runFor } from './layout/inline.js';
56
56
  import { documentOffsetOf, layoutOffsetOf, layoutOffsets as layoutOffsetsOf, } from './layout/inline.js';
57
57
  import { lineBands as bandsFor } from '../richtext/runs.js';
58
58
  import { clipsOverflow, computePaintBounds, containingBlockOf, FIXED_BOXES, fixedToViewport, forgetDecoratedAncestors, hasRect, hoistNegative, holds, holdsAbsolute, inClip, inClipPath, onLine, ownBounds, paintDocument, pathClips, queryChildIndex, selectionRows, stackLayers, stacksLayers, } from './paint.js';
59
- import { controlRectsOf, measureControl } from './controls.js';
59
+ import { controlRectsOf, measureControl, styledField } from './controls.js';
60
60
  import { ResourceStore } from './resources.js';
61
61
  import { WebFonts } from './fonts.js';
62
62
  import { resolveUrl, UrlResolver } from './url.js';
@@ -156,6 +156,8 @@ export class HtmlViewNode extends Node {
156
156
  * the text carries surrogate pairs. null until checked. */
157
157
  _pointsAreUnits = null;
158
158
  _hovered = [];
159
+ /** Which element's widget holds the focus (`setFocus`). */
160
+ _focus = NO_FOCUS;
159
161
  /** Where the pointer last moved to, in logical window pixels, while it
160
162
  * is over the element (`defaultMouseMove`). */
161
163
  _pointerAt = null;
@@ -249,6 +251,12 @@ export class HtmlViewNode extends Node {
249
251
  controlBorder: look.controlBorder * s,
250
252
  controlRadius: look.controlRadius * s,
251
253
  controlFontSize: (look.controlFontSize ?? look.fontSize) * s,
254
+ ...(look.focusRingWidth !== undefined && {
255
+ focusRingWidth: look.focusRingWidth * s,
256
+ }),
257
+ ...(look.focusRingOffset !== undefined && {
258
+ focusRingOffset: look.focusRingOffset * s,
259
+ }),
252
260
  };
253
261
  this._deviceLookFor = look;
254
262
  this._deviceLookAt = s;
@@ -538,6 +546,7 @@ export class HtmlViewNode extends Node {
538
546
  hovered: new Set(this._hovered),
539
547
  active: EMPTY_SET,
540
548
  });
549
+ this._cascade.setFocus(this._focus);
541
550
  this._mediaBand = this._cascade.mediaBand(width);
542
551
  }
543
552
  /**
@@ -828,6 +837,10 @@ export class HtmlViewNode extends Node {
828
837
  scale: this._scale,
829
838
  imageSize: (el) => this._resources.imageSize(imageUrlOf(el) ?? ''),
830
839
  urlSize: (url) => this._resources.imageSize(url),
840
+ faceAscent: (style) => {
841
+ const fonts = this._fonts();
842
+ return fonts ? faceExtentOf(fonts, style).ascent : undefined;
843
+ },
831
844
  controlSize: (el, kind, style) => measureControl(el, kind, style, this._fonts(), look),
832
845
  });
833
846
  this._tree = build();
@@ -1309,37 +1322,97 @@ export class HtmlViewNode extends Node {
1309
1322
  * with the styles of every element the move did not reach kept.
1310
1323
  */
1311
1324
  _restyleHover(was, now) {
1325
+ const cascade = this._cascade;
1312
1326
  const reach = this._hoverReach(was, now);
1313
- if (reach !== null) {
1327
+ if (reach !== null && cascade) {
1314
1328
  if (!reach.size)
1315
1329
  return;
1316
- if (this._hoverInPlace(reach, was))
1330
+ const before = { hovered: new Set(was), active: EMPTY_SET };
1331
+ if (this._hoverInPlace(reach, before, this._focus, cascade.hoverPseudo)) {
1317
1332
  return;
1333
+ }
1318
1334
  }
1319
1335
  this._invalidate(Stale.Boxes, reach);
1320
1336
  }
1337
+ /** The element whose widget holds the focus, or null (`setFocus`). */
1338
+ get focusedElement() {
1339
+ return this._focus.element;
1340
+ }
1341
+ /**
1342
+ * The focus moved: `element`'s widget took it, or, given null, no widget
1343
+ * of this document holds it now — it went to something else in the
1344
+ * window, or nowhere. `visible` is whether it shows, `:focus-visible`,
1345
+ * which it always does in a text field. Restyles what `:focus` and its
1346
+ * kin reach as a hover does (`_restyleHover`), and returns whether the
1347
+ * cascade's answer could have changed.
1348
+ *
1349
+ * No element of a document takes the focus: a control's widget, mounted
1350
+ * beside it, does, and says so here. Only a text field's does, so far —
1351
+ * the widgets core draws as components keep their own focus to
1352
+ * themselves.
1353
+ */
1354
+ setFocus(element, visible = true) {
1355
+ const was = this._focus;
1356
+ if (was.element === element && (!element || was.visible === visible)) {
1357
+ return false;
1358
+ }
1359
+ const within = new Set();
1360
+ for (let at = element; at; at = isElement(at.parent) ? at.parent : null) {
1361
+ within.add(at);
1362
+ }
1363
+ const now = element ? { element, visible, within } : NO_FOCUS;
1364
+ this._focus = now;
1365
+ const cascade = this._cascade;
1366
+ cascade?.setFocus(now);
1367
+ if (!cascade || !cascade.focusSensitive)
1368
+ return false;
1369
+ const reach = cascade.focusLocal
1370
+ ? this._stateReach([...was.within], [...now.within],
1371
+ // one element, whose ring came or went
1372
+ was.element === element ? element : null, (el) => cascade.focusTouches(el), (el, into) => cascade.focusAnchors(el, into))
1373
+ : null;
1374
+ if (reach !== null) {
1375
+ if (!reach.size)
1376
+ return true;
1377
+ const pointer = { hovered: new Set(this._hovered), active: EMPTY_SET };
1378
+ if (this._hoverInPlace(reach, pointer, was, cascade.focusPseudo)) {
1379
+ return true;
1380
+ }
1381
+ }
1382
+ this._invalidate(Stale.Boxes, reach);
1383
+ return true;
1384
+ }
1321
1385
  /**
1322
1386
  * The elements whose styles a move of the hovered chain can change, or
1323
1387
  * null where that cannot be said (`Cascade.hoverLocal`).
1388
+ */
1389
+ _hoverReach(was, now) {
1390
+ const cascade = this._cascade;
1391
+ if (!cascade || !cascade.hoverLocal)
1392
+ return null;
1393
+ return this._stateReach(was, now, null, (el) => cascade.hoverTouches(el), (el, into) => cascade.hoverAnchors(el, into));
1394
+ }
1395
+ /**
1396
+ * The elements whose styles a move of the hovered chain can change, or
1397
+ * of the focus's chain.
1324
1398
  *
1325
1399
  * Only an element whose hover state flipped and that a compound testing
1326
1400
  * the pointer could match can change (`Cascade.hoverTouches`), with its
1327
1401
  * subtree, and its later siblings' where a sibling combinator is in play;
1328
1402
  * and the elements a `:has()` testing the pointer may flip, which are
1329
1403
  * around the ones that changed. A move between two paragraphs under
1330
- * `a:hover` reaches nothing.
1404
+ * `a:hover` reaches nothing. The focus's the same way, its chain the
1405
+ * focused element and every element around it, and `also` one whose
1406
+ * state flipped though it is in both — the ring coming or going.
1331
1407
  */
1332
- _hoverReach(was, now) {
1333
- const cascade = this._cascade;
1334
- if (!cascade || !cascade.hoverLocal)
1335
- return null;
1408
+ _stateReach(was, now, also, touches, anchors) {
1336
1409
  const before = new Set(was);
1337
1410
  const after = new Set(now);
1338
1411
  const roots = new Map();
1339
1412
  const flipped = (el, other) => {
1340
- if (other.has(el))
1413
+ if (other?.has(el))
1341
1414
  return;
1342
- const touch = cascade.hoverTouches(el);
1415
+ const touch = touches(el);
1343
1416
  if (touch > (roots.get(el) ?? HOVER_UNTOUCHED))
1344
1417
  roots.set(el, touch);
1345
1418
  };
@@ -1352,10 +1425,14 @@ export class HtmlViewNode extends Node {
1352
1425
  const deepest = (chain, other) => {
1353
1426
  const el = chain[0];
1354
1427
  if (el && !other.has(el))
1355
- cascade.hoverAnchors(el, roots);
1428
+ anchors(el, roots);
1356
1429
  };
1357
1430
  deepest(was, after);
1358
1431
  deepest(now, before);
1432
+ if (also) {
1433
+ flipped(also, null);
1434
+ anchors(also, roots);
1435
+ }
1359
1436
  // the roots' subtrees, which inherit from them and a descendant
1360
1437
  // combinator reaches, and their later siblings' too where a sibling
1361
1438
  // combinator follows the compound that tests the pointer
@@ -1402,8 +1479,10 @@ export class HtmlViewNode extends Node {
1402
1479
  * - The boxes take their new styles: an element's own, the text in it,
1403
1480
  * the anonymous boxes the fix-up made around what is in it, and its
1404
1481
  * `::before` and `::after`, styled again by their own rules. A box
1405
- * whose style is derived some other way — a marker, a first letter, a
1406
- * control — is not one to restyle here.
1482
+ * whose style is derived some other way — a marker, a first letter —
1483
+ * is not one to restyle here. A control's is, and its widget is told
1484
+ * (`_reportControls`), unless the page took to drawing the field or
1485
+ * stopped.
1407
1486
  * - Each layout of their text is made again from the runs it was made
1408
1487
  * from (`TextLayoutCache.inputsOf`) with the new ink (`runFor`), where
1409
1488
  * its geometry comes out the same.
@@ -1413,8 +1492,12 @@ export class HtmlViewNode extends Node {
1413
1492
  * costs what it changed, in a document of any length. True where it was
1414
1493
  * restyled here, or had nothing to restyle; false where the boxes have to
1415
1494
  * be built again.
1495
+ *
1496
+ * A focus change is answered the same way (`setFocus`): `before` and
1497
+ * `beforeFocus` are the states the change was from, and `pseudoRules`
1498
+ * which pseudo-elements' rules test the state that changed.
1416
1499
  */
1417
- _hoverInPlace(reach, was) {
1500
+ _hoverInPlace(reach, before, beforeFocus, pseudoRules) {
1418
1501
  const cascade = this._cascade;
1419
1502
  const tree = this._tree;
1420
1503
  const layouts = this._layouts;
@@ -1425,17 +1508,15 @@ export class HtmlViewNode extends Node {
1425
1508
  if (reach.size > HOVER_RESTYLE_LIMIT)
1426
1509
  return false;
1427
1510
  // a first line's, a first letter's and a marker's styles are layout's
1428
- const pseudoRules = cascade.hoverPseudo;
1429
1511
  if (pseudoRules === HOVER_PSEUDO_OTHER)
1430
1512
  return false;
1431
- // whether the rules that test the pointer answer differently for an
1432
- // element than they did before the move
1433
- const before = { hovered: new Set(was), active: EMPTY_SET };
1513
+ // whether the rules that test the pointer or the focus answer
1514
+ // differently for an element than they did before the change
1434
1515
  const flips = new Map();
1435
1516
  const flipped = (el) => {
1436
1517
  let flip = flips.get(el);
1437
1518
  if (flip === undefined) {
1438
- flip = cascade.pointerChanged(el, before);
1519
+ flip = cascade.pointerChanged(el, before, beforeFocus);
1439
1520
  flips.set(el, flip);
1440
1521
  }
1441
1522
  return flip;
@@ -1452,6 +1533,8 @@ export class HtmlViewNode extends Node {
1452
1533
  let reaches = false;
1453
1534
  let reorders = false;
1454
1535
  const moving = new Set();
1536
+ /** Whether a control's box was restyled: its widget is told. */
1537
+ let widgets = false;
1455
1538
  const styleOf = (el) => {
1456
1539
  const kept = tree.styles.get(el);
1457
1540
  if (!kept)
@@ -1497,8 +1580,45 @@ export class HtmlViewNode extends Node {
1497
1580
  if (refused)
1498
1581
  return false;
1499
1582
  }
1500
- if (!changed.size && pseudoRules === HOVER_PSEUDO_NONE)
1583
+ // The drawings the move reached. What the rules give the shapes in one
1584
+ // is no box's style (`BoxTree.shapeStyler`), so it is asked for again:
1585
+ // of a drawing whose own style changed — its colour, a custom property
1586
+ // — and of each where a rule that reaches into a drawing tests the
1587
+ // pointer, `a:hover svg path`. One with another answer is painted
1588
+ // again; one never painted has none yet, and asks when it is.
1589
+ const redrawn = [];
1590
+ if (tree.shapeStyler) {
1591
+ for (const el of reach) {
1592
+ if (tagOf(el) !== 'svg')
1593
+ continue;
1594
+ const style = fresh.get(el);
1595
+ if (!style)
1596
+ continue;
1597
+ if (!changed.has(el) && !cascade.shapesFollowPointer)
1598
+ continue;
1599
+ const box = this._firstBoxesOf(tree).get(el);
1600
+ const was = box && SHAPE_STYLES.get(box);
1601
+ if (!box || was === undefined)
1602
+ continue;
1603
+ const shapes = cascade.shapeStyles(el, style, tree.shapeCopies);
1604
+ if (shapes?.key !== was?.key)
1605
+ redrawn.push([box, shapes]);
1606
+ }
1607
+ }
1608
+ const redraw = () => {
1609
+ const inks = [];
1610
+ for (const [box, shapes] of redrawn) {
1611
+ SHAPE_STYLES.set(box, shapes);
1612
+ const ink = inkOf(box);
1613
+ if (ink)
1614
+ inks.push(ink);
1615
+ }
1616
+ return inks;
1617
+ };
1618
+ if (!changed.size && pseudoRules === HOVER_PSEUDO_NONE) {
1619
+ this._repaintInk(redraw());
1501
1620
  return true;
1621
+ }
1502
1622
  for (const el of changed.keys()) {
1503
1623
  const tag = tagOf(el);
1504
1624
  // their backgrounds are the canvas's
@@ -1585,13 +1705,22 @@ export class HtmlViewNode extends Node {
1585
1705
  continue;
1586
1706
  const kept = tree.styles.get(el).style;
1587
1707
  // a style derived from the element's, or drawn from it somewhere
1588
- // else — a widget mounted beside the document takes its look from
1589
- // it. An image, a drawing or a rule is drawn here, from its box,
1590
- // like any.
1708
+ // else. A widget mounted beside the document takes its look from
1709
+ // it, and is told again once it is restyled — unless the page took
1710
+ // to drawing the field, or stopped, which is another size of box.
1711
+ // An image, a drawing or a rule is drawn here, from its box, like
1712
+ // any.
1591
1713
  if (box.style !== kept)
1592
1714
  return false;
1593
- if (box.marker || WIDGETS.has(box.replaced))
1715
+ if (box.marker)
1594
1716
  return false;
1717
+ if (WIDGETS.has(box.replaced)) {
1718
+ const kind = box.replaced;
1719
+ if (styledField(kind, kept) !== styledField(kind, style)) {
1720
+ return false;
1721
+ }
1722
+ widgets = true;
1723
+ }
1595
1724
  if (moving.has(el) && !movable(box, kept, style))
1596
1725
  return false;
1597
1726
  ink = !quiet.has(el);
@@ -1643,8 +1772,10 @@ export class HtmlViewNode extends Node {
1643
1772
  }
1644
1773
  }
1645
1774
  }
1646
- if (!restyled.length)
1775
+ if (!restyled.length) {
1776
+ this._repaintInk(redraw());
1647
1777
  return true;
1778
+ }
1648
1779
  // the text: each layout holding a run of a restyled box's, made again
1649
1780
  // with its new ink, where it comes out the same shape
1650
1781
  const relaid = new Map();
@@ -1702,6 +1833,8 @@ export class HtmlViewNode extends Node {
1702
1833
  for (const [el, style] of changed) {
1703
1834
  tree.styles.set(el, { style, inFlex: tree.styles.get(el).inFlex });
1704
1835
  }
1836
+ for (const ink of redraw())
1837
+ inks.push(ink);
1705
1838
  // A box that moved moves what is in it, as layout moved it the first
1706
1839
  // time (`applyRelativeOffsets`), and a later layout moves it the same
1707
1840
  // way; where it reached the document's end, before or after, the
@@ -1727,7 +1860,7 @@ export class HtmlViewNode extends Node {
1727
1860
  relayout = true;
1728
1861
  }
1729
1862
  }
1730
- if (moved.length)
1863
+ if (moved.length || widgets)
1731
1864
  this._reportControls();
1732
1865
  if (relayout) {
1733
1866
  this._invalidate(Stale.Layout);
@@ -1744,14 +1877,7 @@ export class HtmlViewNode extends Node {
1744
1877
  /** An element's first box in document order, for the tree it was asked
1745
1878
  * of: made on the first move over a tree, which is one walk of it. */
1746
1879
  _firstBoxes = null;
1747
- /**
1748
- * The blocks a restyle of `changed` has to look in: for each element, the
1749
- * box its text is set in the lines of — the nearest box around its first
1750
- * that is not an inline one — which holds every box that takes its style
1751
- * from it, the pieces a block in it broke it into among them
1752
- * (`breakAround`). One that is inside another is left to it.
1753
- */
1754
- _blocksOf(tree, changed) {
1880
+ _firstBoxesOf(tree) {
1755
1881
  let index = this._firstBoxes;
1756
1882
  if (index?.tree !== tree) {
1757
1883
  const of = new Map();
@@ -1766,9 +1892,20 @@ export class HtmlViewNode extends Node {
1766
1892
  }
1767
1893
  index = this._firstBoxes = { tree, of };
1768
1894
  }
1895
+ return index.of;
1896
+ }
1897
+ /**
1898
+ * The blocks a restyle of `changed` has to look in: for each element, the
1899
+ * box its text is set in the lines of — the nearest box around its first
1900
+ * that is not an inline one — which holds every box that takes its style
1901
+ * from it, the pieces a block in it broke it into among them
1902
+ * (`breakAround`). One that is inside another is left to it.
1903
+ */
1904
+ _blocksOf(tree, changed) {
1905
+ const first = this._firstBoxesOf(tree);
1769
1906
  const blocks = new Set();
1770
1907
  for (const el of changed) {
1771
- let box = index.of.get(el) ?? null;
1908
+ let box = first.get(el) ?? null;
1772
1909
  while (box?.parent &&
1773
1910
  (box.kind === 'inline' || box.kind === 'text' || box.kind === 'break')) {
1774
1911
  box = box.parent;