hop2 0.11.60 → 0.11.61
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/hay/apps/web/src/App.tsx
CHANGED
|
@@ -21,7 +21,7 @@ import { enableMathHover } from "./utils/mathLinkProvider";
|
|
|
21
21
|
import { getMathTip } from "./utils/mathTooltip";
|
|
22
22
|
import { scanKeyboardProtocol } from "./utils/keyboardProtocol";
|
|
23
23
|
import { originalPathHint, pasteableUploadPaths } from "./utils/fileDrop";
|
|
24
|
-
import { claimOnAttach, claimOnClick } from "./utils/sizeClaim";
|
|
24
|
+
import { claimOnAttach, claimOnClick, isPlainClick } from "./utils/sizeClaim";
|
|
25
25
|
import { MobileKeyboard } from "./components/MobileKeyboard";
|
|
26
26
|
import { SessionSwitcher } from "./components/SessionSwitcher";
|
|
27
27
|
import { SecondaryPane } from "./components/SecondaryPane";
|
|
@@ -1356,6 +1356,9 @@ const App = () => {
|
|
|
1356
1356
|
// switch, closing the wall into the session, a click, the Fit button) —
|
|
1357
1357
|
// a reconnect is the socket healing itself, not a person.
|
|
1358
1358
|
const deliberateAttachRef = useRef(true);
|
|
1359
|
+
// Where the last press on the terminal landed, so a click can be told
|
|
1360
|
+
// apart from the release of a drag.
|
|
1361
|
+
const clickDownRef = useRef<{ x: number; y: number } | null>(null);
|
|
1359
1362
|
// A canvas that was HIDDEN can come back showing a stale frame: the wall
|
|
1360
1363
|
// covers the terminal with visibility:hidden, browsers throttle or drop
|
|
1361
1364
|
// WebGL contexts for hidden canvases, and xterm only repaints rows it
|
|
@@ -2170,6 +2173,8 @@ const App = () => {
|
|
|
2170
2173
|
|
|
2171
2174
|
termRef.current = terminal;
|
|
2172
2175
|
fitRef.current = fitAddon;
|
|
2176
|
+
// Debug handle for probes and the console (selection, buffer, size).
|
|
2177
|
+
(window as any).__hopTerm = terminal;
|
|
2173
2178
|
fitWhenReady();
|
|
2174
2179
|
|
|
2175
2180
|
// Register OSC handlers for sequences xterm.js doesn't fully support
|
|
@@ -4603,15 +4608,20 @@ const App = () => {
|
|
|
4603
4608
|
<section inert={switcherOpen} style={switcherOpen ? { visibility: "hidden" } : undefined} className={`terminal${visibleStatus === "disconnected" || visibleStatus === "ended" ? " degraded" : ""}`}>
|
|
4604
4609
|
<div
|
|
4605
4610
|
className="terminal-frame"
|
|
4606
|
-
|
|
4611
|
+
onMouseDown={(e) => { clickDownRef.current = { x: e.clientX, y: e.clientY }; }}
|
|
4612
|
+
onClick={(e) => {
|
|
4607
4613
|
// On mobile, don't focus terminal to prevent system keyboard
|
|
4608
4614
|
if (!isMobile) {
|
|
4609
4615
|
termRef.current?.focus();
|
|
4610
4616
|
}
|
|
4611
4617
|
// A click here is a human act on this surface, so it may
|
|
4612
4618
|
// take a size some other window left behind. No-op when
|
|
4613
|
-
// the session already fits this viewport
|
|
4614
|
-
|
|
4619
|
+
// the session already fits this viewport — and never for
|
|
4620
|
+
// the click that ends a drag: that was a text selection,
|
|
4621
|
+
// and the resize a claim brings would wipe it.
|
|
4622
|
+
if (isPlainClick(clickDownRef.current, { x: e.clientX, y: e.clientY }, !!termRef.current?.hasSelection())) {
|
|
4623
|
+
claimSizeHere();
|
|
4624
|
+
}
|
|
4615
4625
|
}}
|
|
4616
4626
|
onDragEnter={(e) => {
|
|
4617
4627
|
if (Array.from(e.dataTransfer?.types || []).includes("Files")) {
|
|
@@ -39,3 +39,20 @@ export const claimOnClick = (o: {
|
|
|
39
39
|
if (!o.active) return true;
|
|
40
40
|
return o.active.cols !== o.natural.cols || o.active.rows !== o.natural.rows;
|
|
41
41
|
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A click is a click only when the pointer stayed put between press and
|
|
45
|
+
* release and nothing is selected. A drag that ends over the terminal also
|
|
46
|
+
* fires `click` — and that drag was a text selection, which a size claim
|
|
47
|
+
* (a resize) would wipe the moment it finished.
|
|
48
|
+
*/
|
|
49
|
+
export const isPlainClick = (
|
|
50
|
+
down: { x: number; y: number } | null,
|
|
51
|
+
up: { x: number; y: number },
|
|
52
|
+
hasSelection: boolean,
|
|
53
|
+
slopPx = 4
|
|
54
|
+
): boolean => {
|
|
55
|
+
if (hasSelection) return false;
|
|
56
|
+
if (!down) return true;
|
|
57
|
+
return Math.hypot(up.x - down.x, up.y - down.y) <= slopPx;
|
|
58
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { claimOnAttach, claimOnClick } from "../src/utils/sizeClaim";
|
|
2
|
+
import { claimOnAttach, claimOnClick, isPlainClick } from "../src/utils/sizeClaim";
|
|
3
3
|
|
|
4
4
|
describe("claimOnAttach — only a deliberate open takes the size", () => {
|
|
5
5
|
const base = { viewMode: "fit" as const, visible: true, deliberate: true };
|
|
@@ -29,3 +29,14 @@ describe("claimOnClick — a click takes the size only when it changes something
|
|
|
29
29
|
expect(claimOnClick({ viewMode: "fit", natural: null, active: null, owned: false })).toBe(false);
|
|
30
30
|
});
|
|
31
31
|
});
|
|
32
|
+
|
|
33
|
+
describe("isPlainClick — the release of a drag is not a click", () => {
|
|
34
|
+
it("a press and release in the same place, nothing selected, is a click", () => {
|
|
35
|
+
expect(isPlainClick({ x: 100, y: 100 }, { x: 102, y: 101 }, false)).toBe(true);
|
|
36
|
+
expect(isPlainClick(null, { x: 5, y: 5 }, false)).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
it("a drag, or any release with a selection standing, is not", () => {
|
|
39
|
+
expect(isPlainClick({ x: 100, y: 100 }, { x: 260, y: 100 }, false)).toBe(false);
|
|
40
|
+
expect(isPlainClick({ x: 100, y: 100 }, { x: 100, y: 100 }, true)).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
});
|
package/package.json
CHANGED