@neuroplastio/xterm-addon-hotty 0.1.0-next.3 → 0.1.0-next.4

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.
@@ -2607,17 +2607,21 @@ var Surface = class {
2607
2607
  /**
2608
2608
  * The cells an element's border box covers as the user sees it, scrolled
2609
2609
  * included (SPEC §9: `area`), counted from the surface's top left cell:
2610
- * whole, where it is clipped or scrolled away. An edge within half a
2611
- * device pixel of a cell's is on it: layout rounds positions to fractions
2612
- * of a pixel, and the user sees no less than a device pixel.
2610
+ * whole, where it is clipped or scrolled away. An edge close enough to a
2611
+ * cell's is on it: within half a device pixel, which the user cannot see,
2612
+ * plus what layout loses on the way there. Layout keeps lengths in fixed
2613
+ * units (1/64 px in Chromium, 1/60 in Firefox) and cuts each box's to
2614
+ * them, so with a cell a fraction of a pixel, a row 29 boxes of one cell
2615
+ * down starts up to 29/64 px short of its cell.
2613
2616
  */
2614
2617
  areaOf(el) {
2615
2618
  const b = el.getBoundingClientRect();
2616
- const tol = 0.5 / (this.frame.ownerDocument.defaultView?.devicePixelRatio || 1);
2617
- const c = Math.floor((b.left + tol) / this.cellW);
2618
- const r = Math.floor((b.top + tol) / this.cellH);
2619
- const right = Math.ceil((b.right - tol) / this.cellW);
2620
- const bottom = Math.ceil((b.bottom - tol) / this.cellH);
2619
+ const half = 0.5 / (this.frame.ownerDocument.defaultView?.devicePixelRatio || 1);
2620
+ const slack = (edge, cell) => half + Math.abs(edge / cell) / 60;
2621
+ const c = Math.floor((b.left + slack(b.left, this.cellW)) / this.cellW);
2622
+ const r = Math.floor((b.top + slack(b.top, this.cellH)) / this.cellH);
2623
+ const right = Math.ceil((b.right - slack(b.right, this.cellW)) / this.cellW);
2624
+ const bottom = Math.ceil((b.bottom - slack(b.bottom, this.cellH)) / this.cellH);
2621
2625
  return { c, r, w: Math.max(0, right - c), h: Math.max(0, bottom - r) };
2622
2626
  }
2623
2627
  // --- Events for the program (PROTOCOL §8) --------------------------------
@@ -3318,6 +3322,9 @@ var HottyAddon = class {
3318
3322
  css = "";
3319
3323
  policy;
3320
3324
  metrics = { cellW: 0, cellH: 0, key: "" };
3325
+ /** The cell (CSS px) and font the program last heard of, or saw at the
3326
+ * start: a `resize` is owed when both have changed since (SPEC §5.3). */
3327
+ told = { cellW: 0, cellH: 0, font: "" };
3321
3328
  resizeTimer = null;
3322
3329
  /** A surface's key is being forwarded to the terminal (`key`), and the
3323
3330
  * terminal's own focus is held off meanwhile. A program in the page may
@@ -3972,23 +3979,40 @@ var HottyAddon = class {
3972
3979
  const o = this.term.options;
3973
3980
  const key = `${cellW}x${cellH}|${o.fontFamily}|${o.fontSize}|${JSON.stringify(o.theme ?? {})}`;
3974
3981
  if (key === this.metrics.key) return;
3975
- const sizeChanged = this.metrics.cellW !== 0 && (cellW !== this.metrics.cellW || cellH !== this.metrics.cellH);
3982
+ const first = this.metrics.cellW === 0;
3976
3983
  this.metrics = { cellW, cellH, key };
3984
+ if (first) this.told = { cellW, cellH, font: this.font() };
3985
+ else this.settle();
3977
3986
  if (this.surfaces.size === 0) return;
3978
3987
  const css = this.hostCss();
3979
3988
  const scheme = this.scheme();
3980
3989
  for (const s of this.surfaces.values()) s.setHostCss(css, scheme);
3981
3990
  this.reposition();
3982
- if (sizeChanged) {
3983
- if (this.resizeTimer) clearTimeout(this.resizeTimer);
3984
- this.resizeTimer = setTimeout(() => {
3985
- for (const p of this.placements.values()) {
3986
- const s = p.surface;
3987
- if (s.detached) continue;
3988
- this.host().event(s.name, "resize", "", { w: Math.round(s.cols * cellW), h: s.rows * cellH });
3989
- }
3990
- }, 100);
3991
- }
3991
+ }
3992
+ font() {
3993
+ return `${this.term.options.fontFamily}|${this.term.options.fontSize}`;
3994
+ }
3995
+ /** Once the cell and font settle: a new font that changed the cell is
3996
+ * re-rendered with no program involvement, so the program hears its new
3997
+ * size (SPEC §5.3, §9). A browser's zoom moves the cell only by device
3998
+ * pixel rounding and keeps the font, and is not told. xterm.js may take
3999
+ * the font and its cell in two renders, so this waits for both. */
4000
+ settle() {
4001
+ if (this.resizeTimer) clearTimeout(this.resizeTimer);
4002
+ this.resizeTimer = setTimeout(() => {
4003
+ this.resizeTimer = null;
4004
+ const { cellW, cellH } = this.cell();
4005
+ const font = this.font();
4006
+ const t = this.told;
4007
+ const owed = font !== t.font && (cellW !== t.cellW || cellH !== t.cellH);
4008
+ this.told = { cellW, cellH, font };
4009
+ if (!owed) return;
4010
+ for (const p of this.placements.values()) {
4011
+ const s = p.surface;
4012
+ if (s.detached) continue;
4013
+ this.host().event(s.name, "resize", "", { w: Math.round(s.cols * cellW), h: s.rows * cellH });
4014
+ }
4015
+ }, 100);
3992
4016
  }
3993
4017
  };
3994
4018
  function sequence(chain, f) {