@react-x11/components 0.10.0 → 0.11.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 (90) hide show
  1. package/dist/code/index.d.ts.map +1 -1
  2. package/dist/code/index.js +25 -24
  3. package/dist/code/index.js.map +1 -1
  4. package/dist/code-language/index.d.ts +1 -1
  5. package/dist/code-language/index.d.ts.map +1 -1
  6. package/dist/code-language/index.js +1 -1
  7. package/dist/code-language/index.js.map +1 -1
  8. package/dist/code-language/runs.d.ts +31 -0
  9. package/dist/code-language/runs.d.ts.map +1 -1
  10. package/dist/code-language/runs.js +105 -0
  11. package/dist/code-language/runs.js.map +1 -1
  12. package/dist/codeblock/index.d.ts +13 -0
  13. package/dist/codeblock/index.d.ts.map +1 -1
  14. package/dist/codeblock/index.js +37 -1
  15. package/dist/codeblock/index.js.map +1 -1
  16. package/dist/html/css/cascade.d.ts +32 -10
  17. package/dist/html/css/cascade.d.ts.map +1 -1
  18. package/dist/html/css/cascade.js +72 -27
  19. package/dist/html/css/cascade.js.map +1 -1
  20. package/dist/html/css/style.d.ts +7 -4
  21. package/dist/html/css/style.d.ts.map +1 -1
  22. package/dist/html/css/style.js +37 -6
  23. package/dist/html/css/style.js.map +1 -1
  24. package/dist/html/css/ua.js +8 -5
  25. package/dist/html/css/ua.js.map +1 -1
  26. package/dist/html/layout/block.d.ts.map +1 -1
  27. package/dist/html/layout/block.js +127 -9
  28. package/dist/html/layout/block.js.map +1 -1
  29. package/dist/html/layout/boxes.d.ts +27 -0
  30. package/dist/html/layout/boxes.d.ts.map +1 -1
  31. package/dist/html/layout/boxes.js +30 -2
  32. package/dist/html/layout/boxes.js.map +1 -1
  33. package/dist/html/layout/inline.d.ts +3 -0
  34. package/dist/html/layout/inline.d.ts.map +1 -1
  35. package/dist/html/layout/inline.js +236 -14
  36. package/dist/html/layout/inline.js.map +1 -1
  37. package/dist/html/node.d.ts.map +1 -1
  38. package/dist/html/node.js +118 -57
  39. package/dist/html/node.js.map +1 -1
  40. package/dist/html/paint.d.ts.map +1 -1
  41. package/dist/html/paint.js +203 -37
  42. package/dist/html/paint.js.map +1 -1
  43. package/dist/html/svg.d.ts.map +1 -1
  44. package/dist/html/svg.js +27 -20
  45. package/dist/html/svg.js.map +1 -1
  46. package/dist/internal/codelines.d.ts +42 -0
  47. package/dist/internal/codelines.d.ts.map +1 -0
  48. package/dist/internal/codelines.js +170 -0
  49. package/dist/internal/codelines.js.map +1 -0
  50. package/dist/markdown/index.d.ts.map +1 -1
  51. package/dist/markdown/index.js +98 -26
  52. package/dist/markdown/index.js.map +1 -1
  53. package/dist/rich-text-editor/index.d.ts.map +1 -1
  54. package/dist/rich-text-editor/index.js +3 -8
  55. package/dist/rich-text-editor/index.js.map +1 -1
  56. package/dist/rich-text-editor/markdown.d.ts.map +1 -1
  57. package/dist/rich-text-editor/markdown.js +30 -1
  58. package/dist/rich-text-editor/markdown.js.map +1 -1
  59. package/dist/rich-text-editor/replace.d.ts +13 -0
  60. package/dist/rich-text-editor/replace.d.ts.map +1 -0
  61. package/dist/rich-text-editor/replace.js +64 -0
  62. package/dist/rich-text-editor/replace.js.map +1 -0
  63. package/dist/terminal-output/index.d.ts.map +1 -1
  64. package/dist/terminal-output/index.js +100 -25
  65. package/dist/terminal-output/index.js.map +1 -1
  66. package/dist/terminal-output/runs.d.ts +4 -1
  67. package/dist/terminal-output/runs.d.ts.map +1 -1
  68. package/dist/terminal-output/runs.js +5 -1
  69. package/dist/terminal-output/runs.js.map +1 -1
  70. package/package.json +1 -1
  71. package/src/code/index.ts +34 -32
  72. package/src/code-language/index.ts +1 -1
  73. package/src/code-language/runs.ts +111 -1
  74. package/src/codeblock/index.ts +48 -1
  75. package/src/html/css/cascade.ts +117 -39
  76. package/src/html/css/style.ts +43 -15
  77. package/src/html/css/ua.ts +8 -5
  78. package/src/html/layout/block.ts +134 -8
  79. package/src/html/layout/boxes.ts +43 -2
  80. package/src/html/layout/inline.ts +257 -13
  81. package/src/html/node.ts +145 -79
  82. package/src/html/paint.ts +236 -45
  83. package/src/html/svg.ts +30 -19
  84. package/src/internal/codelines.ts +200 -0
  85. package/src/markdown/index.ts +145 -26
  86. package/src/rich-text-editor/index.ts +3 -11
  87. package/src/rich-text-editor/markdown.ts +36 -1
  88. package/src/rich-text-editor/replace.ts +85 -0
  89. package/src/terminal-output/index.ts +127 -34
  90. package/src/terminal-output/runs.ts +16 -2
@@ -24,7 +24,7 @@ import { alphaOf, inkColor, isTransparent, resolve } from './css/values.js';
24
24
  import { blend, borderShades } from './css/color.js';
25
25
  import { CONTAIN_LAYOUT, CONTAIN_PAINT, CONTAIN_SIZE, copyStyle, scrolls, } from './css/style.js';
26
26
  import { contained } from './layout/block.js';
27
- import { BOX_RAISES, LINE_BOX_RAISES, Box, CLAMPED, CLIPPED_CELLS, COLLAPSED_CELLS, FADED_BLOCKS, PAINT_ORDER, INLINE_OFFSETS, SHADOWED_TEXT, SHIFTED_LINES, TEXT_RAISES, TEXT_SHIFTS, } from './layout/boxes.js';
27
+ import { BOX_RAISES, LINE_BOX_RAISES, Box, CLAMPED, CLIPPED_CELLS, COLLAPSED_CELLS, FADED_BLOCKS, PAINT_ORDER, INLINE_OFFSETS, MOVED_OFF_LINES, SHADOWED_TEXT, SHIFTED_LINES, TEXT_RAISES, TEXT_SHIFTS, } from './layout/boxes.js';
28
28
  import { CLIPPED_TEXT, clipBoxOf, depthOf, inklessLayout, layoutOffsetOf, layoutOffsets, } from './layout/inline.js';
29
29
  import { tableGrid } from './layout/grid.js';
30
30
  import { SvgDrawing, inlineDrawing } from './svg.js';
@@ -280,7 +280,7 @@ export function paintDocument(ctx, tree, options) {
280
280
  if (!canFill(ctx))
281
281
  return;
282
282
  ctx.save();
283
- const canvas = canvasBackground(tree.root);
283
+ const canvas = canvasBackground(tree);
284
284
  if (canvas)
285
285
  paintCanvas(ctx, canvas, tree.root, options);
286
286
  paintBox(ctx, tree.root, {
@@ -296,22 +296,41 @@ export function paintDocument(ctx, tree, options) {
296
296
  * `<body>`'s, which then paints no background of its own. A document with
297
297
  * no `<html>` tag has the root box standing in for its root element, and a
298
298
  * `<body>` in it is still the body: mail often starts at `<body style>`.
299
- * `anchor` is the box the image is positioned against: the root element's,
300
- * whichever box it came from.
299
+ * With neither tag, the root box stands in for the body, and the `<html>`
300
+ * around it is implied: an `html { … }` or a `:root { … }` background, which
301
+ * a test's reference sets on a document with no tags at all, has no box
302
+ * but the canvas to be painted on. `anchor` is the box the image is
303
+ * positioned against: the root element's, whichever box it came from.
301
304
  */
302
- function canvasBackground(root) {
303
- const has = (b) => !isTransparent(b.style.backgroundColor) ||
304
- !!b.style.backgroundImage ||
305
- !!b.style.backgroundGradient ||
306
- !!b.style.backgroundImages;
305
+ function canvasBackground(tree) {
306
+ const root = tree.root;
307
+ const has = (s) => !isTransparent(s.backgroundColor) ||
308
+ !!s.backgroundImage ||
309
+ !!s.backgroundGradient ||
310
+ !!s.backgroundImages;
311
+ const implied = tree.impliedHtml;
312
+ if (implied) {
313
+ // an `<html>` that is not displayed has no background to give
314
+ if (implied.display === 'none')
315
+ return null;
316
+ if (has(implied))
317
+ return { style: implied, source: null, anchor: root };
318
+ // containment on either keeps the body's to the body (CSS Containment 2)
319
+ if (implied.contain || root.style.contain)
320
+ return null;
321
+ return has(root.style)
322
+ ? { style: root.style, source: root, anchor: root }
323
+ : null;
324
+ }
307
325
  const top = childNamed(root, 'html') ?? root;
308
- if (has(top))
309
- return { source: top, anchor: top };
326
+ if (has(top.style))
327
+ return { style: top.style, source: top, anchor: top };
310
328
  const body = childNamed(top, 'body');
311
- // containment on either keeps the body's to the body (CSS Containment 2)
312
329
  if (!body || top.style.contain || body.style.contain)
313
330
  return null;
314
- return has(body) ? { source: body, anchor: top } : null;
331
+ return has(body.style)
332
+ ? { style: body.style, source: body, anchor: top }
333
+ : null;
315
334
  }
316
335
  /** A box's child element of that name, or one inside the anonymous boxes
317
336
  * it is wrapped in: a `<body>` in an `<html>` set `display: table` is in
@@ -328,7 +347,7 @@ function childNamed(box, name) {
328
347
  }
329
348
  return null;
330
349
  }
331
- function paintCanvas(ctx, { source, anchor }, root, options) {
350
+ function paintCanvas(ctx, { style: source, anchor }, root, options) {
332
351
  const whole = options.canvas ?? {
333
352
  x: root.x + options.originX,
334
353
  y: root.y + options.originY,
@@ -338,8 +357,8 @@ function paintCanvas(ctx, { source, anchor }, root, options) {
338
357
  const area = clampRect(options, Math.round(whole.x), Math.round(whole.y), Math.ceil(whole.width), Math.ceil(whole.height));
339
358
  if (!area)
340
359
  return;
341
- const layers = layersOf(source.style) ?? [source.style];
342
- const visible = source.style.visibility === 'visible';
360
+ const layers = layersOf(source) ?? [source];
361
+ const visible = source.visibility === 'visible';
343
362
  for (let i = layers.length - 1; i >= 0; i -= 1) {
344
363
  const style = layers[i];
345
364
  if (!isTransparent(style.backgroundColor) && visible) {
@@ -403,22 +422,32 @@ function layersOf(style) {
403
422
  */
404
423
  function paintLayers(ctx, box, options, image) {
405
424
  // a background painted through the text is painted with it
406
- // (`paintClippedText`), and not as a box
407
- if (box.style.backgroundClipText)
425
+ // (`paintClippedText`), and not as a box — but for what `border-area
426
+ // text` paints in the border as well
427
+ const style = box.style;
428
+ if (style.backgroundClipText && style.backgroundClip !== 'border-area') {
408
429
  return;
409
- const layers = layersOf(box.style);
430
+ }
431
+ const layers = layersOf(style);
410
432
  if (!layers) {
411
- paintBackground(ctx, box, options, box.style);
412
- if (image && box.style.backgroundImage)
413
- image(box.style);
433
+ paintLayer(ctx, box, options, style, image);
414
434
  return;
415
435
  }
416
436
  for (let i = layers.length - 1; i >= 0; i -= 1) {
417
- paintBackground(ctx, box, options, layers[i]);
418
- if (image && layers[i].backgroundImage)
419
- image(layers[i]);
437
+ paintLayer(ctx, box, options, layers[i], image);
420
438
  }
421
439
  }
440
+ function paintLayer(ctx, box, options, layer, image) {
441
+ // painted over the border box, inside what the border paints
442
+ const area = layer.backgroundClip === 'border-area';
443
+ if (area && !pushBorderArea(ctx, box, options))
444
+ return;
445
+ paintBackground(ctx, box, options, layer);
446
+ if (image && layer.backgroundImage)
447
+ image(layer);
448
+ if (area)
449
+ ctx.restore();
450
+ }
422
451
  /** A box's padding box in window coordinates. */
423
452
  function paddingBox(box, options) {
424
453
  return {
@@ -627,14 +656,7 @@ function paintOwnBackground(ctx, box, options) {
627
656
  if (style.boxShadow)
628
657
  paintShadows(ctx, box, options, false);
629
658
  if (box !== options.canvasSource) {
630
- paintLayers(ctx, box, options, (layer) => {
631
- const painted = clipArea(box, options, layer, true);
632
- const area = painted &&
633
- clampRect(options, painted.x, painted.y, painted.w, painted.h);
634
- if (area) {
635
- paintBackgroundImage(ctx, layer, area, originBox(box, options, layer), options, painted.corners);
636
- }
637
- });
659
+ paintLayers(ctx, box, options, frameImages(ctx, box, options));
638
660
  }
639
661
  if (style.boxShadow)
640
662
  paintShadows(ctx, box, options, true);
@@ -644,6 +666,21 @@ function paintOwnBackground(ctx, box, options) {
644
666
  if (box.kind === 'table')
645
667
  paintPartBackgrounds(ctx, box, options);
646
668
  }
669
+ /**
670
+ * How `paintLayers` draws a frame's image layers: each in the area its
671
+ * `background-clip` names, from the box its `background-origin` names
672
+ * (CSS Backgrounds 3, 3.7 and 3.8) — a block's, or an inline box's
673
+ * fragment's, whose background is its images as much as its colour.
674
+ */
675
+ function frameImages(ctx, frame, options) {
676
+ return (layer) => {
677
+ const painted = clipArea(frame, options, layer, true);
678
+ const area = painted && clampRect(options, painted.x, painted.y, painted.w, painted.h);
679
+ if (area) {
680
+ paintBackgroundImage(ctx, layer, area, originBox(frame, options, layer), options, painted.corners);
681
+ }
682
+ };
683
+ }
647
684
  /** How far past its shape a shadow's blur shows: three standard deviations,
648
685
  * the blur being two (CSS Backgrounds 3, 7.1.1). */
649
686
  function shadowReach(blur) {
@@ -1215,9 +1252,10 @@ function spreadCorners(c, width, height, top, right, bottom, left) {
1215
1252
  return rounded ? { x, y } : null;
1216
1253
  }
1217
1254
  /** How far in from a box's border box the box a `<visual-box>` names is:
1218
- * top, right, bottom and left. */
1255
+ * top, right, bottom and left — none for `border-area`, whose layer is
1256
+ * painted over the border box inside what the border paints. */
1219
1257
  function edgeInsets(box, which) {
1220
- if (which === 'border-box')
1258
+ if (which === 'border-box' || which === 'border-area')
1221
1259
  return NO_INSETS;
1222
1260
  const t = box.borderTop;
1223
1261
  const r = box.borderRight;
@@ -2821,6 +2859,88 @@ function roundedRing(ctx, box, x, y, w, h, options) {
2821
2859
  }, insetCorners(corners, top, right, bottom, left));
2822
2860
  return true;
2823
2861
  }
2862
+ /**
2863
+ * Clip to what a box's border paints, for a layer of `background-clip:
2864
+ * border-area` (CSS Backgrounds 4, 2.1): each side's width and style, and
2865
+ * not its colour — a transparent border still has an area, which is what
2866
+ * the value is for. The shapes are the border painter's own, so that the
2867
+ * background shows exactly where that border would: the ring `roundedRing`
2868
+ * fills where a rounded border is solid, the ring the trapezoids of a 3D
2869
+ * style make, and otherwise the rectangles `fillEdge` fills a side at a
2870
+ * time — the dots, the dashes, the two lines of a double border. All of
2871
+ * them run clockwise and a ring's inside the other way, so the clip is the
2872
+ * union by the non-zero rule, the one every context's `clip` takes. False
2873
+ * where the border paints nothing or the context cannot clip, and nothing
2874
+ * is pushed.
2875
+ */
2876
+ function pushBorderArea(ctx, box, options) {
2877
+ const t = box.borderTop;
2878
+ const r = box.borderRight;
2879
+ const b = box.borderBottom;
2880
+ const l = box.borderLeft;
2881
+ if (!(t || r || b || l) || !ctx.beginPath || !ctx.rect || !ctx.clip) {
2882
+ return false;
2883
+ }
2884
+ const s = box.style;
2885
+ const left = box.x + options.originX;
2886
+ const top = frameY(box) + options.originY;
2887
+ const x = Math.round(left);
2888
+ const y = Math.round(top);
2889
+ const w = Math.round(left + box.width) - x;
2890
+ const h = Math.round(top + frameHeight(box)) - y;
2891
+ if (w <= 0 || h <= 0)
2892
+ return false;
2893
+ const area = clampRect(options, x, y, w, h);
2894
+ if (!area)
2895
+ return false;
2896
+ const corners = cornersOf(s, w, h);
2897
+ ctx.save();
2898
+ ctx.beginPath();
2899
+ if (corners && canCurve(ctx) && solidBorder(box)) {
2900
+ roundedRect(ctx, area.x, area.y, area.w, area.h, corners, true);
2901
+ const iw = area.w - l - r;
2902
+ const ih = area.h - t - b;
2903
+ if (iw > 0 && ih > 0) {
2904
+ const inner = insetCorners(corners, t, r, b, l);
2905
+ roundedRect(ctx, area.x + l, area.y + t, iw, ih, inner, true, true);
2906
+ }
2907
+ }
2908
+ else if (sculpted(s)) {
2909
+ // a band a side, which the trapezoids cover between them
2910
+ ctx.rect(area.x, area.y, area.w, t);
2911
+ ctx.rect(area.x, area.y + area.h - b, area.w, b);
2912
+ ctx.rect(area.x, area.y + t, l, area.h - t - b);
2913
+ ctx.rect(area.x + area.w - r, area.y + t, r, area.h - t - b);
2914
+ }
2915
+ else {
2916
+ const path = { fillRect: ctx.rect.bind(ctx) };
2917
+ const edge = (ex, ey, ew, eh, style, horizontal) => {
2918
+ const rect = clampRect(options, ex, ey, ew, eh);
2919
+ if (rect)
2920
+ fillEdge(path, rect, horizontal ? ex : ey, style, horizontal);
2921
+ };
2922
+ const between = h - t - b;
2923
+ if (t > 0)
2924
+ edge(x, y, w, t, s.borderTopStyle, true);
2925
+ if (b > 0)
2926
+ edge(x, y + h - b, w, b, s.borderBottomStyle, true);
2927
+ if (l > 0)
2928
+ edge(x, y + t, l, between, s.borderLeftStyle, false);
2929
+ if (r > 0)
2930
+ edge(x + w - r, y + t, r, between, s.borderRightStyle, false);
2931
+ }
2932
+ ctx.clip();
2933
+ return true;
2934
+ }
2935
+ /** Whether every side a box has a border on is solid, which a rounded box
2936
+ * draws as one ring (`roundedRing`). */
2937
+ function solidBorder(box) {
2938
+ const s = box.style;
2939
+ return ((!box.borderTop || s.borderTopStyle === 'solid') &&
2940
+ (!box.borderRight || s.borderRightStyle === 'solid') &&
2941
+ (!box.borderBottom || s.borderBottomStyle === 'solid') &&
2942
+ (!box.borderLeft || s.borderLeftStyle === 'solid'));
2943
+ }
2824
2944
  /** The border styles drawn in two shades, as though lit from the top left. */
2825
2945
  const SCULPTED = new Set(['groove', 'ridge', 'inset', 'outset']);
2826
2946
  function sculpted(s) {
@@ -3215,6 +3335,20 @@ function paintLines(ctx, box, options) {
3215
3335
  if (line.y + line.height > top)
3216
3336
  visible.push(line);
3217
3337
  }
3338
+ // and a line the damage misses whose text or inline-block `position:
3339
+ // relative` moved into it: drawn by its line, it went undrawn with it
3340
+ const moved = MOVED_OFF_LINES.get(lines);
3341
+ if (moved && reachedOff(visible, moved, top, bottom)) {
3342
+ const drawn = new Set(visible);
3343
+ for (const line of moved) {
3344
+ if (movedInto(line, top, bottom))
3345
+ drawn.add(line);
3346
+ }
3347
+ visible.length = 0;
3348
+ for (const line of lines)
3349
+ if (drawn.has(line))
3350
+ visible.push(line);
3351
+ }
3218
3352
  }
3219
3353
  else {
3220
3354
  visible.push(...lines);
@@ -3276,6 +3410,35 @@ function paintLines(ctx, box, options) {
3276
3410
  }
3277
3411
  }
3278
3412
  }
3413
+ /** Whether any of the lines moved content was taken off, and the damage
3414
+ * does not already draw, has it within the damage's rows. */
3415
+ function reachedOff(visible, moved, top, bottom) {
3416
+ for (const line of moved) {
3417
+ if (!visible.includes(line) && movedInto(line, top, bottom))
3418
+ return true;
3419
+ }
3420
+ return false;
3421
+ }
3422
+ /** Whether what was moved off a line — its inline-blocks and images, where
3423
+ * their ink is, and its texts, where they are drawn — reaches the rows
3424
+ * between `top` and `bottom`. */
3425
+ function movedInto(line, top, bottom) {
3426
+ for (const placed of line.atomics) {
3427
+ const box = placed.box;
3428
+ if (box.boundsY < bottom && box.boundsY + box.boundsHeight > top) {
3429
+ return true;
3430
+ }
3431
+ }
3432
+ for (const text of line.texts) {
3433
+ const natural = text.layout.lines[text.layoutLine];
3434
+ if (!natural)
3435
+ continue;
3436
+ const y = text.drawY + natural.y;
3437
+ if (y < bottom && y + natural.height > top)
3438
+ return true;
3439
+ }
3440
+ return false;
3441
+ }
3279
3442
  /**
3280
3443
  * The part of each such fragment above its line, drawn again over the
3281
3444
  * text there. CSS 2.1 Appendix E paints a block's inline content a line at
@@ -3297,7 +3460,7 @@ function paintBleeds(ctx, bleeds, options) {
3297
3460
  ctx.beginPath();
3298
3461
  ctx.rect(left, top, Math.ceil(fragment.width) + 2, bottom - top);
3299
3462
  ctx.clip();
3300
- paintLayers(ctx, fragment, options);
3463
+ paintLayers(ctx, fragment, options, frameImages(ctx, fragment, options));
3301
3464
  paintBorders(ctx, fragment, options);
3302
3465
  ctx.restore();
3303
3466
  }
@@ -3755,7 +3918,10 @@ bleeds = null) {
3755
3918
  };
3756
3919
  if (fragment.width <= 0)
3757
3920
  continue;
3758
- paintLayers(ctx, fragment, options);
3921
+ // its images from its own padding box, as its gradients are, each
3922
+ // fragment alike: `slice` would lay them out as though the fragments
3923
+ // were one box end to end, which a box on one line is
3924
+ paintLayers(ctx, fragment, options, frameImages(ctx, fragment, options));
3759
3925
  paintBorders(ctx, fragment, options);
3760
3926
  if (box.style.outlineStyle !== 'none')
3761
3927
  paintOutline(ctx, fragment, options);