@neuroplastio/xterm-addon-hotty 0.1.0-next.3 → 0.1.0-next.5
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/dist/hotty-xterm.js +68 -26
- package/dist/hotty-xterm.js.map +2 -2
- package/dist/types/addon.d.ts +10 -0
- package/dist/types/surface.d.ts +11 -4
- package/package.json +1 -1
package/dist/hotty-xterm.js
CHANGED
|
@@ -1384,6 +1384,9 @@ var Surface = class {
|
|
|
1384
1384
|
/** The document's base URL (SPEC §7.3). */
|
|
1385
1385
|
base = NO_BASE;
|
|
1386
1386
|
keyboard = false;
|
|
1387
|
+
/** The element the program knows has focus: the last `focus` named it,
|
|
1388
|
+
* or the program's `a=focus` gave it (SPEC §10.1). */
|
|
1389
|
+
told = null;
|
|
1387
1390
|
/** Detached (SPEC §5.5): nothing in the surface reaches the program. */
|
|
1388
1391
|
detachedState = false;
|
|
1389
1392
|
/** A press is on something that takes no focus (SPEC §10.1): the focus
|
|
@@ -1560,6 +1563,7 @@ var Surface = class {
|
|
|
1560
1563
|
this.dropDrag();
|
|
1561
1564
|
const had = this.keyboard || this.frameFocused();
|
|
1562
1565
|
this.keyboard = false;
|
|
1566
|
+
this.told = null;
|
|
1563
1567
|
this.focusedControl()?.blur();
|
|
1564
1568
|
if (had) this.giveBack();
|
|
1565
1569
|
}
|
|
@@ -1751,17 +1755,21 @@ var Surface = class {
|
|
|
1751
1755
|
if (target && !el) throw new Error(target);
|
|
1752
1756
|
this.programFocus = true;
|
|
1753
1757
|
try {
|
|
1754
|
-
this.
|
|
1755
|
-
|
|
1758
|
+
if (!this.frameFocused()) {
|
|
1759
|
+
this.frame.focus();
|
|
1760
|
+
this.frame.contentWindow?.focus();
|
|
1761
|
+
}
|
|
1756
1762
|
if (el) el.focus();
|
|
1757
1763
|
else if (!this.focusedControl()) this.tabbable()[0]?.focus();
|
|
1758
1764
|
this.keyboard = true;
|
|
1765
|
+
this.told = this.focusedControl();
|
|
1759
1766
|
} finally {
|
|
1760
1767
|
setTimeout(() => this.programFocus = false, 0);
|
|
1761
1768
|
}
|
|
1762
1769
|
}
|
|
1763
1770
|
/** `a=blur`, or the keyboard leaving: the edited control commits first. */
|
|
1764
1771
|
blur() {
|
|
1772
|
+
this.pressed = null;
|
|
1765
1773
|
this.commit();
|
|
1766
1774
|
if (this.keyboard) this.giveBack();
|
|
1767
1775
|
}
|
|
@@ -1812,6 +1820,7 @@ var Surface = class {
|
|
|
1812
1820
|
if (this.keyboard) {
|
|
1813
1821
|
this.commit();
|
|
1814
1822
|
this.keyboard = false;
|
|
1823
|
+
this.told = null;
|
|
1815
1824
|
this.emit("blur", "");
|
|
1816
1825
|
}
|
|
1817
1826
|
this.settle();
|
|
@@ -1839,16 +1848,24 @@ var Surface = class {
|
|
|
1839
1848
|
}
|
|
1840
1849
|
/** An element got focus: the surface takes the keyboard if the element is
|
|
1841
1850
|
* one that takes focus, and the focus is neither the program's (no echo)
|
|
1842
|
-
* nor a detached surface's.
|
|
1851
|
+
* nor a detached surface's. The program hears where the user put focus,
|
|
1852
|
+
* each time it is another element than the one it knows (SPEC §10.1). */
|
|
1843
1853
|
onFocusIn(e) {
|
|
1844
|
-
|
|
1845
|
-
if (this.
|
|
1854
|
+
const el = isElement(e.target) ? e.target : null;
|
|
1855
|
+
if (this.programFocus) {
|
|
1856
|
+
this.told = el;
|
|
1857
|
+
return;
|
|
1858
|
+
}
|
|
1859
|
+
if (this.auxPress) return;
|
|
1860
|
+
if (this.detachedState || this.pressNothing || !el || !takesFocus(el, this.hyper)) {
|
|
1846
1861
|
this.settle();
|
|
1847
1862
|
return;
|
|
1848
1863
|
}
|
|
1849
|
-
|
|
1864
|
+
const gained = !this.keyboard;
|
|
1850
1865
|
this.keyboard = true;
|
|
1851
|
-
this.
|
|
1866
|
+
if (!gained && el === this.told) return;
|
|
1867
|
+
this.told = el;
|
|
1868
|
+
this.emit("focus", el.closest('[id]:not([id=""])')?.id ?? "");
|
|
1852
1869
|
}
|
|
1853
1870
|
tabbable() {
|
|
1854
1871
|
const all = this.doc.querySelectorAll(
|
|
@@ -2607,17 +2624,21 @@ var Surface = class {
|
|
|
2607
2624
|
/**
|
|
2608
2625
|
* The cells an element's border box covers as the user sees it, scrolled
|
|
2609
2626
|
* included (SPEC §9: `area`), counted from the surface's top left cell:
|
|
2610
|
-
* whole, where it is clipped or scrolled away. An edge
|
|
2611
|
-
*
|
|
2612
|
-
*
|
|
2627
|
+
* whole, where it is clipped or scrolled away. An edge close enough to a
|
|
2628
|
+
* cell's is on it: within half a device pixel, which the user cannot see,
|
|
2629
|
+
* plus what layout loses on the way there. Layout keeps lengths in fixed
|
|
2630
|
+
* units (1/64 px in Chromium, 1/60 in Firefox) and cuts each box's to
|
|
2631
|
+
* them, so with a cell a fraction of a pixel, a row 29 boxes of one cell
|
|
2632
|
+
* down starts up to 29/64 px short of its cell.
|
|
2613
2633
|
*/
|
|
2614
2634
|
areaOf(el) {
|
|
2615
2635
|
const b = el.getBoundingClientRect();
|
|
2616
|
-
const
|
|
2617
|
-
const
|
|
2618
|
-
const
|
|
2619
|
-
const
|
|
2620
|
-
const
|
|
2636
|
+
const half = 0.5 / (this.frame.ownerDocument.defaultView?.devicePixelRatio || 1);
|
|
2637
|
+
const slack = (edge, cell) => half + Math.abs(edge / cell) / 60;
|
|
2638
|
+
const c = Math.floor((b.left + slack(b.left, this.cellW)) / this.cellW);
|
|
2639
|
+
const r = Math.floor((b.top + slack(b.top, this.cellH)) / this.cellH);
|
|
2640
|
+
const right = Math.ceil((b.right - slack(b.right, this.cellW)) / this.cellW);
|
|
2641
|
+
const bottom = Math.ceil((b.bottom - slack(b.bottom, this.cellH)) / this.cellH);
|
|
2621
2642
|
return { c, r, w: Math.max(0, right - c), h: Math.max(0, bottom - r) };
|
|
2622
2643
|
}
|
|
2623
2644
|
// --- Events for the program (PROTOCOL §8) --------------------------------
|
|
@@ -2708,6 +2729,7 @@ var Surface = class {
|
|
|
2708
2729
|
this.commit();
|
|
2709
2730
|
if (this.keyboard) {
|
|
2710
2731
|
this.keyboard = false;
|
|
2732
|
+
this.told = null;
|
|
2711
2733
|
this.emit("blur", "");
|
|
2712
2734
|
}
|
|
2713
2735
|
});
|
|
@@ -3318,6 +3340,9 @@ var HottyAddon = class {
|
|
|
3318
3340
|
css = "";
|
|
3319
3341
|
policy;
|
|
3320
3342
|
metrics = { cellW: 0, cellH: 0, key: "" };
|
|
3343
|
+
/** The cell (CSS px) and font the program last heard of, or saw at the
|
|
3344
|
+
* start: a `resize` is owed when both have changed since (SPEC §5.3). */
|
|
3345
|
+
told = { cellW: 0, cellH: 0, font: "" };
|
|
3321
3346
|
resizeTimer = null;
|
|
3322
3347
|
/** A surface's key is being forwarded to the terminal (`key`), and the
|
|
3323
3348
|
* terminal's own focus is held off meanwhile. A program in the page may
|
|
@@ -3972,23 +3997,40 @@ var HottyAddon = class {
|
|
|
3972
3997
|
const o = this.term.options;
|
|
3973
3998
|
const key = `${cellW}x${cellH}|${o.fontFamily}|${o.fontSize}|${JSON.stringify(o.theme ?? {})}`;
|
|
3974
3999
|
if (key === this.metrics.key) return;
|
|
3975
|
-
const
|
|
4000
|
+
const first = this.metrics.cellW === 0;
|
|
3976
4001
|
this.metrics = { cellW, cellH, key };
|
|
4002
|
+
if (first) this.told = { cellW, cellH, font: this.font() };
|
|
4003
|
+
else this.settle();
|
|
3977
4004
|
if (this.surfaces.size === 0) return;
|
|
3978
4005
|
const css = this.hostCss();
|
|
3979
4006
|
const scheme = this.scheme();
|
|
3980
4007
|
for (const s of this.surfaces.values()) s.setHostCss(css, scheme);
|
|
3981
4008
|
this.reposition();
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
4009
|
+
}
|
|
4010
|
+
font() {
|
|
4011
|
+
return `${this.term.options.fontFamily}|${this.term.options.fontSize}`;
|
|
4012
|
+
}
|
|
4013
|
+
/** Once the cell and font settle: a new font that changed the cell is
|
|
4014
|
+
* re-rendered with no program involvement, so the program hears its new
|
|
4015
|
+
* size (SPEC §5.3, §9). A browser's zoom moves the cell only by device
|
|
4016
|
+
* pixel rounding and keeps the font, and is not told. xterm.js may take
|
|
4017
|
+
* the font and its cell in two renders, so this waits for both. */
|
|
4018
|
+
settle() {
|
|
4019
|
+
if (this.resizeTimer) clearTimeout(this.resizeTimer);
|
|
4020
|
+
this.resizeTimer = setTimeout(() => {
|
|
4021
|
+
this.resizeTimer = null;
|
|
4022
|
+
const { cellW, cellH } = this.cell();
|
|
4023
|
+
const font = this.font();
|
|
4024
|
+
const t = this.told;
|
|
4025
|
+
const owed = font !== t.font && (cellW !== t.cellW || cellH !== t.cellH);
|
|
4026
|
+
this.told = { cellW, cellH, font };
|
|
4027
|
+
if (!owed) return;
|
|
4028
|
+
for (const p of this.placements.values()) {
|
|
4029
|
+
const s = p.surface;
|
|
4030
|
+
if (s.detached) continue;
|
|
4031
|
+
this.host().event(s.name, "resize", "", { w: Math.round(s.cols * cellW), h: s.rows * cellH });
|
|
4032
|
+
}
|
|
4033
|
+
}, 100);
|
|
3992
4034
|
}
|
|
3993
4035
|
};
|
|
3994
4036
|
function sequence(chain, f) {
|