@weasel-js/hud 1.0.1 → 1.0.3
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/attach.d.ts +8 -0
- package/dist/attach.d.ts.map +1 -1
- package/dist/fonts/registerDefaultFont.d.ts +4 -0
- package/dist/fonts/registerDefaultFont.d.ts.map +1 -1
- package/dist/host.d.ts +3 -0
- package/dist/host.d.ts.map +1 -1
- package/dist/hud.d.ts +10 -0
- package/dist/hud.d.ts.map +1 -1
- package/dist/index.js +37 -29
- package/dist/index.js.map +1 -1
- package/dist/loupe/createLoupe.d.ts +11 -0
- package/dist/loupe/createLoupe.d.ts.map +1 -1
- package/dist/react/index.js +1 -1
- package/dist/{tool-Cvy1MwFJ.js → tool-DyPy2h0T.js} +57 -49
- package/dist/tool-DyPy2h0T.js.map +1 -0
- package/dist/widget.d.ts +16 -0
- package/dist/widget.d.ts.map +1 -1
- package/dist/widgets/button.d.ts +8 -0
- package/dist/widgets/button.d.ts.map +1 -1
- package/dist/widgets/image.d.ts +2 -0
- package/dist/widgets/image.d.ts.map +1 -1
- package/dist/widgets/label.d.ts +3 -0
- package/dist/widgets/label.d.ts.map +1 -1
- package/dist/widgets/rect.d.ts +3 -0
- package/dist/widgets/rect.d.ts.map +1 -1
- package/dist/widgets/text.d.ts +3 -0
- package/dist/widgets/text.d.ts.map +1 -1
- package/dist/widgets/window/window.d.ts +6 -0
- package/dist/widgets/window/window.d.ts.map +1 -1
- package/dist/widgets/window/zones.d.ts +5 -0
- package/dist/widgets/window/zones.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/tool-Cvy1MwFJ.js.map +0 -1
package/dist/attach.d.ts
CHANGED
|
@@ -6,5 +6,13 @@ export interface AttachHudOptions {
|
|
|
6
6
|
* default mode; pass `useTheme().resolved` to follow a live theme. */
|
|
7
7
|
readonly theme?: ResolvedTheme;
|
|
8
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* Bind a HUD to a canvas: register its render layer, route pointer input to
|
|
11
|
+
* the widget under the cursor, and kick off default-font registration. Returns
|
|
12
|
+
* the detach function.
|
|
13
|
+
*
|
|
14
|
+
* Throws if the HUD is already attached — a HUD belongs to one canvas at a
|
|
15
|
+
* time.
|
|
16
|
+
*/
|
|
9
17
|
export declare function attachHud(api: CanvasExtensionApi, hud: Hud, options?: AttachHudOptions): () => void;
|
|
10
18
|
//# sourceMappingURL=attach.d.ts.map
|
package/dist/attach.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attach.d.ts","sourceRoot":"","sources":["../src/attach.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,KAAK,EAAE,kBAAkB,EAA+B,MAAM,iBAAiB,CAAC;AAOvF,OAAO,EAA6B,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjF,MAAM,WAAW,gBAAgB;IAC/B;2EACuE;IACvE,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;CAChC;AAED,wBAAgB,SAAS,CACvB,GAAG,EAAE,kBAAkB,EACvB,GAAG,EAAE,GAAG,EACR,OAAO,GAAE,gBAAqB,GAC7B,MAAM,IAAI,
|
|
1
|
+
{"version":3,"file":"attach.d.ts","sourceRoot":"","sources":["../src/attach.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,KAAK,EAAE,kBAAkB,EAA+B,MAAM,iBAAiB,CAAC;AAOvF,OAAO,EAA6B,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjF,MAAM,WAAW,gBAAgB;IAC/B;2EACuE;IACvE,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;CAChC;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,kBAAkB,EACvB,GAAG,EAAE,GAAG,EACR,OAAO,GAAE,gBAAqB,GAC7B,MAAM,IAAI,CAwHZ"}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/** Family name the bundled HUD font registers under. Widgets that name no
|
|
2
|
+
* family draw with it. */
|
|
1
3
|
export declare const DEFAULT_FONT_FAMILY = "weasel-hud-default";
|
|
4
|
+
/** Register the bundled Inter atlas under `DEFAULT_FONT_FAMILY`. `attachHud`
|
|
5
|
+
* calls this; calling it again is a no-op. */
|
|
2
6
|
export declare function registerDefaultFont(): Promise<void>;
|
|
3
7
|
//# sourceMappingURL=registerDefaultFont.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registerDefaultFont.d.ts","sourceRoot":"","sources":["../../src/fonts/registerDefaultFont.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,mBAAmB,uBAAuB,CAAC;AAExD,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEzD"}
|
|
1
|
+
{"version":3,"file":"registerDefaultFont.d.ts","sourceRoot":"","sources":["../../src/fonts/registerDefaultFont.ts"],"names":[],"mappings":"AAOA;2BAC2B;AAC3B,eAAO,MAAM,mBAAmB,uBAAuB,CAAC;AAExD;+CAC+C;AAC/C,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEzD"}
|
package/dist/host.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { RenderLayer } from '@weasel-js/core';
|
|
2
|
+
/** What a HUD needs from whatever is hosting it: somewhere to register its
|
|
3
|
+
* render layer, and a way to ask for a repaint. `attachHud` supplies this
|
|
4
|
+
* from a canvas; a test or a headless renderer can supply its own. */
|
|
2
5
|
export interface HudHost {
|
|
3
6
|
requestRedraw(): void;
|
|
4
7
|
registerLayer(layer: RenderLayer<unknown>): () => void;
|
package/dist/host.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,MAAM,WAAW,OAAO;IACtB,aAAa,IAAI,IAAI,CAAC;IACtB,aAAa,CAAC,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,MAAM,IAAI,CAAC;CACxD"}
|
|
1
|
+
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD;;uEAEuE;AACvE,MAAM,WAAW,OAAO;IACtB,aAAa,IAAI,IAAI,CAAC;IACtB,aAAa,CAAC,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,MAAM,IAAI,CAAC;CACxD"}
|
package/dist/hud.d.ts
CHANGED
|
@@ -6,6 +6,15 @@ import { type ImageOptions, type ImageWidget } from './widgets/image';
|
|
|
6
6
|
import { type LabelOptions, type LabelWidget } from './widgets/label';
|
|
7
7
|
import { type ButtonOptions, type ButtonWidget } from './widgets/button';
|
|
8
8
|
import { type WindowOptions, type WindowWidget } from './widgets/window/window';
|
|
9
|
+
/**
|
|
10
|
+
* A HUD: an ordered set of widgets drawn in screen space over the canvas, and
|
|
11
|
+
* the factories that create them. The factory methods (`rect`, `text`, …) also
|
|
12
|
+
* add the widget and wire its mutations to a redraw; the bare `createRect`-style
|
|
13
|
+
* factories do neither, leaving redraw scheduling to the caller.
|
|
14
|
+
*
|
|
15
|
+
* A HUD holds no GL state of its own. It becomes visible by binding to a host
|
|
16
|
+
* — normally through `attachHud`.
|
|
17
|
+
*/
|
|
9
18
|
export interface Hud {
|
|
10
19
|
add(widget: Widget): void;
|
|
11
20
|
remove(widget: Widget): void;
|
|
@@ -28,5 +37,6 @@ export interface Hud {
|
|
|
28
37
|
/** Create a window widget, add it to the HUD, and wire onChange → markDirty. */
|
|
29
38
|
window(opts: WindowOptions): WindowWidget;
|
|
30
39
|
}
|
|
40
|
+
/** Create an empty, unbound HUD. */
|
|
31
41
|
export declare function createHud(): Hud;
|
|
32
42
|
//# sourceMappingURL=hud.d.ts.map
|
package/dist/hud.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hud.d.ts","sourceRoot":"","sources":["../src/hud.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAc,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAc,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAe,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnF,OAAO,EAAe,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnF,OAAO,EAAgB,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACvF,OAAO,EAAgB,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE9F,MAAM,WAAW,GAAG;IAClB,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,IAAI,SAAS,MAAM,EAAE,CAAC;IAC7B,SAAS,IAAI,IAAI,CAAC;IAClB,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,MAAM,IAAI,IAAI,CAAC;IACf,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,8EAA8E;IAC9E,IAAI,CAAC,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC;IACpC,8EAA8E;IAC9E,IAAI,CAAC,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC;IACpC,gFAAgF;IAChF,KAAK,CAAC,IAAI,EAAE,YAAY,GAAG,WAAW,CAAC;IACvC,+EAA+E;IAC/E,KAAK,CAAC,IAAI,EAAE,YAAY,GAAG,WAAW,CAAC;IACvC,gFAAgF;IAChF,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;IAC1C,gFAAgF;IAChF,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;CAC3C;AAED,wBAAgB,SAAS,IAAI,GAAG,CA0G/B"}
|
|
1
|
+
{"version":3,"file":"hud.d.ts","sourceRoot":"","sources":["../src/hud.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAc,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAc,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAe,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnF,OAAO,EAAe,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnF,OAAO,EAAgB,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACvF,OAAO,EAAgB,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE9F;;;;;;;;GAQG;AACH,MAAM,WAAW,GAAG;IAClB,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,IAAI,SAAS,MAAM,EAAE,CAAC;IAC7B,SAAS,IAAI,IAAI,CAAC;IAClB,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,MAAM,IAAI,IAAI,CAAC;IACf,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,8EAA8E;IAC9E,IAAI,CAAC,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC;IACpC,8EAA8E;IAC9E,IAAI,CAAC,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC;IACpC,gFAAgF;IAChF,KAAK,CAAC,IAAI,EAAE,YAAY,GAAG,WAAW,CAAC;IACvC,+EAA+E;IAC/E,KAAK,CAAC,IAAI,EAAE,YAAY,GAAG,WAAW,CAAC;IACvC,gFAAgF;IAChF,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;IAC1C,gFAAgF;IAChF,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;CAC3C;AAED,oCAAoC;AACpC,wBAAgB,SAAS,IAAI,GAAG,CA0G/B"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as e, c as t, i as n, l as r, n as i, o as a, r as o, s, t as c, u as l } from "./tool-
|
|
1
|
+
import { a as e, c as t, i as n, l as r, n as i, o as a, r as o, s, t as c, u as l } from "./tool-DyPy2h0T.js";
|
|
2
2
|
import { createViewportLayer as u, screenToWorld as d, viewToTransform as f } from "@weasel-js/core";
|
|
3
3
|
//#region src/loupe/innerView.ts
|
|
4
4
|
function p(e, t, n, r) {
|
|
@@ -33,25 +33,25 @@ function g(e) {
|
|
|
33
33
|
let { hud: t, element: n, source: r, requestRedraw: i } = e, a = e.mode ?? "vector", o = e.factor ?? 8, s = {
|
|
34
34
|
x: 0,
|
|
35
35
|
y: 0
|
|
36
|
-
}, c = null, l = null, h = !1, g = e.bounds ?? {
|
|
36
|
+
}, c = null, l = null, h = !1, g = !1, _ = !1, v = e.bounds ?? {
|
|
37
37
|
x: 24,
|
|
38
38
|
y: 24,
|
|
39
39
|
w: 220,
|
|
40
40
|
h: 200
|
|
41
|
-
},
|
|
41
|
+
}, y = (e) => a === "vector" ? b(e) : x(e), b = (t) => {
|
|
42
42
|
let n = d(s.x, s.y, f(t.view)), i = p({
|
|
43
43
|
x: n[0],
|
|
44
44
|
y: n[1]
|
|
45
45
|
}, t.view, t.rect, o);
|
|
46
46
|
return u({
|
|
47
|
-
id: `${
|
|
47
|
+
id: `${w.id}:lens`,
|
|
48
48
|
label: "Loupe content",
|
|
49
49
|
source: r,
|
|
50
50
|
view: i,
|
|
51
51
|
bounds: () => t.rect,
|
|
52
52
|
background: e.background ?? t.tokens["--wzl-surface"]
|
|
53
53
|
}).draw(t.data, t.view, t.dims);
|
|
54
|
-
},
|
|
54
|
+
}, x = (e) => l ? [{
|
|
55
55
|
kind: "image",
|
|
56
56
|
image: l,
|
|
57
57
|
x: e.rect.x,
|
|
@@ -59,22 +59,30 @@ function g(e) {
|
|
|
59
59
|
w: e.rect.w,
|
|
60
60
|
h: e.rect.h,
|
|
61
61
|
sampling: "nearest"
|
|
62
|
-
}] : [],
|
|
63
|
-
if (a !== "pixel"
|
|
62
|
+
}] : [], S = () => {
|
|
63
|
+
if (_ || a !== "pixel") return;
|
|
64
|
+
if (h) {
|
|
65
|
+
g = !0;
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
64
68
|
let e = n.getContext("webgl2");
|
|
65
69
|
if (!e) return;
|
|
66
70
|
let t = n.getBoundingClientRect();
|
|
67
71
|
if (t.width === 0) return;
|
|
68
|
-
let r = n.width / t.width, c =
|
|
72
|
+
let r = n.width / t.width, c = w.contentRect, u = Math.max(1, Math.round(c.w * r / o)), d = Math.max(1, Math.round(c.h * r / o)), f = m(e, {
|
|
69
73
|
width: n.width,
|
|
70
74
|
height: n.height
|
|
71
75
|
}, s, r, u, d);
|
|
72
|
-
h = !0, createImageBitmap(f).then((e) => {
|
|
73
|
-
|
|
76
|
+
h = !0, g = !1, createImageBitmap(f).then((e) => {
|
|
77
|
+
if (_) {
|
|
78
|
+
e.close();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
l?.close(), l = e, h = !1, i(), g && S();
|
|
74
82
|
}).catch(() => {
|
|
75
|
-
h = !1;
|
|
83
|
+
h = !1, g && S();
|
|
76
84
|
});
|
|
77
|
-
},
|
|
85
|
+
}, C = () => {
|
|
78
86
|
let t = n.getContext("webgl2");
|
|
79
87
|
if (!t) return;
|
|
80
88
|
let r = n.getBoundingClientRect();
|
|
@@ -88,30 +96,30 @@ function g(e) {
|
|
|
88
96
|
a.data[2]
|
|
89
97
|
].map((e) => e.toString(16).padStart(2, "0")).join("");
|
|
90
98
|
o !== c && (c = o, e.onColorChange?.(o));
|
|
91
|
-
},
|
|
99
|
+
}, w = t.window({
|
|
92
100
|
id: "weasel-loupe",
|
|
93
|
-
x:
|
|
94
|
-
y:
|
|
95
|
-
w:
|
|
96
|
-
h:
|
|
101
|
+
x: v.x,
|
|
102
|
+
y: v.y,
|
|
103
|
+
w: v.w,
|
|
104
|
+
h: v.h,
|
|
97
105
|
title: e.title ?? "Loupe",
|
|
98
106
|
titlebar: e.titlebar,
|
|
99
|
-
content:
|
|
107
|
+
content: y,
|
|
100
108
|
onResize: () => {
|
|
101
|
-
|
|
109
|
+
S();
|
|
102
110
|
},
|
|
103
111
|
...e.onClose ? { onClose: e.onClose } : {}
|
|
104
|
-
}),
|
|
112
|
+
}), T = (e) => {
|
|
105
113
|
let t = n.getBoundingClientRect();
|
|
106
|
-
|
|
114
|
+
E({
|
|
107
115
|
x: e.clientX - t.left,
|
|
108
116
|
y: e.clientY - t.top
|
|
109
117
|
});
|
|
110
|
-
},
|
|
111
|
-
|
|
118
|
+
}, E = (e) => {
|
|
119
|
+
w.hidden || w.hitTest(e.x, e.y) || (s = e, C(), a === "pixel" && S(), i());
|
|
112
120
|
};
|
|
113
|
-
return n.addEventListener("pointermove",
|
|
114
|
-
window:
|
|
121
|
+
return n.addEventListener("pointermove", T), {
|
|
122
|
+
window: w,
|
|
115
123
|
get mode() {
|
|
116
124
|
return a;
|
|
117
125
|
},
|
|
@@ -125,14 +133,14 @@ function g(e) {
|
|
|
125
133
|
return c;
|
|
126
134
|
},
|
|
127
135
|
setMode(e) {
|
|
128
|
-
e !== a && (a = e, a === "pixel" &&
|
|
136
|
+
e !== a && (a = e, a === "pixel" && S(), i());
|
|
129
137
|
},
|
|
130
138
|
setFactor(e) {
|
|
131
|
-
o = e, a === "pixel" &&
|
|
139
|
+
o = e, a === "pixel" && S(), i();
|
|
132
140
|
},
|
|
133
|
-
aimAt:
|
|
141
|
+
aimAt: E,
|
|
134
142
|
dispose() {
|
|
135
|
-
n.removeEventListener("pointermove",
|
|
143
|
+
_ = !0, n.removeEventListener("pointermove", T), l?.close(), l = null, w.dispose();
|
|
136
144
|
}
|
|
137
145
|
};
|
|
138
146
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/loupe/innerView.ts","../src/loupe/readback.ts","../src/loupe/createLoupe.ts"],"sourcesContent":["import type { View } from '@weasel-js/core';\nimport type { WidgetBounds } from '../widget';\n\n/**\n * The inner `View` a loupe renders its content through: the outer view's\n * magnification times `factor`, positioned so `target` sits at the center of\n * a viewport the size of `rect`.\n */\nexport function loupeInnerView(\n target: { x: number; y: number },\n outer: View,\n rect: WidgetBounds,\n factor: number,\n): View {\n const scale = { x: outer.scale.x * factor, y: outer.scale.y * factor };\n return {\n x: target.x - rect.w / 2 / scale.x,\n y: target.y - rect.h / 2 / scale.y,\n scale,\n };\n}\n","/**\n * Read a `rw × rh` device-pixel region of the drawing buffer centered on\n * `pointer` (CSS px relative to the canvas), returned top-down.\n *\n * GL reports rows bottom-up; every consumer here wants top-down, so both the\n * read origin and the returned rows are flipped.\n */\nexport function readbackRegion(\n gl: WebGL2RenderingContext,\n buffer: { width: number; height: number },\n pointer: { x: number; y: number },\n dpr: number,\n rw: number,\n rh: number,\n): ImageData {\n const cx = Math.round(pointer.x * dpr);\n const cy = Math.round(pointer.y * dpr);\n const gx = clamp(cx - Math.floor(rw / 2), 0, Math.max(0, buffer.width - rw));\n const gyTop = clamp(cy - Math.floor(rh / 2), 0, Math.max(0, buffer.height - rh));\n const gy = buffer.height - gyTop - rh;\n\n const raw = new Uint8Array(rw * rh * 4);\n gl.readPixels(gx, gy, rw, rh, gl.RGBA, gl.UNSIGNED_BYTE, raw);\n\n const flipped = new Uint8ClampedArray(rw * rh * 4);\n const stride = rw * 4;\n for (let row = 0; row < rh; row++) {\n const src = (rh - 1 - row) * stride;\n flipped.set(raw.subarray(src, src + stride), row * stride);\n }\n return new ImageData(flipped, rw, rh);\n}\n\nfunction clamp(v: number, lo: number, hi: number): number {\n return v < lo ? lo : v > hi ? hi : v;\n}\n","import {\n createViewportLayer, screenToWorld, viewToTransform,\n type RenderLayer, type View,\n} from '@weasel-js/core';\nimport type { DrawCommand } from '@weasel-js/core/renderer';\nimport type { Hud } from '../hud';\nimport type { WindowWidget } from '../widgets/window/window';\nimport type { HudContentCtx, WidgetBounds } from '../widget';\nimport { loupeInnerView } from './innerView';\nimport { readbackRegion } from './readback';\n\nexport type LoupeMode = 'vector' | 'pixel';\n\nexport interface LoupeOptions {\n hud: Hud;\n /** The live canvas. Supplies the GL context for pixel mode and the pointer\n * feed for aiming. */\n element: HTMLCanvasElement;\n /** Layers re-rendered through the magnified inner view in vector mode. */\n source: RenderLayer<unknown>[];\n requestRedraw: () => void;\n mode?: LoupeMode;\n factor?: number;\n bounds?: WidgetBounds;\n title?: string;\n /** Draw the window's titlebar and close box. Default `true`. Pass `false`\n * for a bare lens; the interior then doubles as the move handle. */\n titlebar?: boolean;\n /** Called with the hex color under the aim point whenever it changes.\n * Sampled from the framebuffer in both modes — vector content is a\n * re-render whose edge colors differ from the screen's, so reading the\n * color off it would report something the user cannot see. */\n onColorChange?: (hex: string) => void;\n /** Fired by the window's close box. The window does not hide itself —\n * whoever owns the loupe's visibility decides what closing means. */\n onClose?: () => void;\n /** Opaque backdrop behind vector content, so the outer canvas does not\n * show through where the inner view is empty. */\n background?: string;\n}\n\nexport interface LoupeHandle {\n readonly window: WindowWidget;\n readonly mode: LoupeMode;\n readonly factor: number;\n /** Last aimed point, in screen-space CSS px relative to the canvas. */\n readonly aim: { x: number; y: number };\n /** Hex color under the aim point, read off the framebuffer. `null` before\n * the first aim, or when the GL context is unavailable. */\n readonly color: string | null;\n setMode(mode: LoupeMode): void;\n setFactor(factor: number): void;\n /** Aim at a screen point. Ignored while the point is over the window —\n * that is the freeze rule that makes the borders reachable. */\n aimAt(p: { x: number; y: number }): void;\n dispose(): void;\n}\n\nexport function createLoupe(opts: LoupeOptions): LoupeHandle {\n const { hud, element, source, requestRedraw } = opts;\n let mode: LoupeMode = opts.mode ?? 'vector';\n let factor = opts.factor ?? 8;\n let aim = { x: 0, y: 0 };\n let color: string | null = null;\n let pixels: ImageBitmap | null = null;\n let pixelsPending = false;\n\n const b = opts.bounds ?? { x: 24, y: 24, w: 220, h: 200 };\n\n const content = (ctx: HudContentCtx): DrawCommand[] =>\n mode === 'vector' ? drawVector(ctx) : drawPixels(ctx);\n\n const drawVector = (ctx: HudContentCtx): DrawCommand[] => {\n const world = screenToWorld(aim.x, aim.y, viewToTransform(ctx.view));\n const inner: View = loupeInnerView(\n { x: world[0], y: world[1] }, ctx.view, ctx.rect, factor,\n );\n // A fresh lens per frame: CreateViewportLayerOpts.view is static, and the\n // inner view moves with the pointer.\n const lens = createViewportLayer<unknown>({\n id: `${win.id}:lens`,\n label: 'Loupe content',\n source,\n view: inner,\n bounds: () => ctx.rect,\n background: opts.background ?? ctx.tokens['--wzl-surface'],\n });\n return lens.draw(ctx.data, ctx.view, ctx.dims);\n };\n\n const drawPixels = (ctx: HudContentCtx): DrawCommand[] => {\n if (!pixels) return [];\n return [{\n kind: 'image',\n image: pixels,\n x: ctx.rect.x, y: ctx.rect.y, w: ctx.rect.w, h: ctx.rect.h,\n sampling: 'nearest',\n }];\n };\n\n const refreshPixels = () => {\n if (mode !== 'pixel' || pixelsPending) return;\n const gl = element.getContext('webgl2');\n if (!gl) return;\n const cssRect = element.getBoundingClientRect();\n if (cssRect.width === 0) return;\n const dpr = element.width / cssRect.width;\n const rect = win.contentRect;\n const rw = Math.max(1, Math.round((rect.w * dpr) / factor));\n const rh = Math.max(1, Math.round((rect.h * dpr) / factor));\n const data = readbackRegion(\n gl, { width: element.width, height: element.height }, aim, dpr, rw, rh,\n );\n pixelsPending = true;\n createImageBitmap(data)\n .then((bmp) => {\n pixels?.close();\n pixels = bmp;\n pixelsPending = false;\n requestRedraw();\n })\n .catch(() => { pixelsPending = false; });\n };\n\n const sampleColor = () => {\n const gl = element.getContext('webgl2');\n if (!gl) return;\n const cssRect = element.getBoundingClientRect();\n if (cssRect.width === 0) return;\n const dpr = element.width / cssRect.width;\n const px = readbackRegion(\n gl, { width: element.width, height: element.height }, aim, dpr, 1, 1,\n );\n const hex = '#' + [px.data[0], px.data[1], px.data[2]]\n .map((c) => c.toString(16).padStart(2, '0')).join('');\n if (hex !== color) {\n color = hex;\n opts.onColorChange?.(hex);\n }\n };\n\n const win = hud.window({\n id: 'weasel-loupe',\n x: b.x, y: b.y, w: b.w, h: b.h,\n title: opts.title ?? 'Loupe',\n titlebar: opts.titlebar,\n content,\n onResize: () => { refreshPixels(); },\n ...(opts.onClose ? { onClose: opts.onClose } : {}),\n });\n\n const onPointerMove = (evt: PointerEvent) => {\n const r = element.getBoundingClientRect();\n handleAim({ x: evt.clientX - r.left, y: evt.clientY - r.top });\n };\n\n const handleAim = (p: { x: number; y: number }) => {\n if (win.hidden) return;\n if (win.hitTest(p.x, p.y)) return; // freeze rule\n aim = p;\n sampleColor();\n if (mode === 'pixel') refreshPixels();\n requestRedraw();\n };\n\n element.addEventListener('pointermove', onPointerMove);\n\n return {\n window: win,\n get mode() { return mode; },\n get factor() { return factor; },\n get aim() { return aim; },\n get color() { return color; },\n setMode(next) {\n if (next === mode) return;\n mode = next;\n if (mode === 'pixel') refreshPixels();\n requestRedraw();\n },\n setFactor(next) {\n factor = next;\n if (mode === 'pixel') refreshPixels();\n requestRedraw();\n },\n aimAt: handleAim,\n dispose() {\n element.removeEventListener('pointermove', onPointerMove);\n pixels?.close();\n pixels = null;\n win.dispose();\n },\n };\n}\n"],"mappings":";;;AAQA,SAAgB,EACd,GACA,GACA,GACA,GACM;CACN,IAAM,IAAQ;EAAE,GAAG,EAAM,MAAM,IAAI;EAAQ,GAAG,EAAM,MAAM,IAAI;EAAQ;AACtE,QAAO;EACL,GAAG,EAAO,IAAI,EAAK,IAAI,IAAI,EAAM;EACjC,GAAG,EAAO,IAAI,EAAK,IAAI,IAAI,EAAM;EACjC;EACD;;;;ACZH,SAAgB,EACd,GACA,GACA,GACA,GACA,GACA,GACW;CACX,IAAM,IAAK,KAAK,MAAM,EAAQ,IAAI,EAAI,EAChC,IAAK,KAAK,MAAM,EAAQ,IAAI,EAAI,EAChC,IAAK,EAAM,IAAK,KAAK,MAAM,IAAK,EAAE,EAAE,GAAG,KAAK,IAAI,GAAG,EAAO,QAAQ,EAAG,CAAC,EACtE,IAAQ,EAAM,IAAK,KAAK,MAAM,IAAK,EAAE,EAAE,GAAG,KAAK,IAAI,GAAG,EAAO,SAAS,EAAG,CAAC,EAC1E,IAAK,EAAO,SAAS,IAAQ,GAE7B,IAAM,IAAI,WAAW,IAAK,IAAK,EAAE;AACvC,GAAG,WAAW,GAAI,GAAI,GAAI,GAAI,EAAG,MAAM,EAAG,eAAe,EAAI;CAE7D,IAAM,IAAU,IAAI,kBAAkB,IAAK,IAAK,EAAE,EAC5C,IAAS,IAAK;AACpB,MAAK,IAAI,IAAM,GAAG,IAAM,GAAI,KAAO;EACjC,IAAM,KAAO,IAAK,IAAI,KAAO;AAC7B,IAAQ,IAAI,EAAI,SAAS,GAAK,IAAM,EAAO,EAAE,IAAM,EAAO;;AAE5D,QAAO,IAAI,UAAU,GAAS,GAAI,EAAG;;AAGvC,SAAS,EAAM,GAAW,GAAY,GAAoB;AACxD,QAAO,IAAI,IAAK,IAAK,IAAI,IAAK,IAAK;;;;ACwBrC,SAAgB,EAAY,GAAiC;CAC3D,IAAM,EAAE,QAAK,YAAS,WAAQ,qBAAkB,GAC5C,IAAkB,EAAK,QAAQ,UAC/B,IAAS,EAAK,UAAU,GACxB,IAAM;EAAE,GAAG;EAAG,GAAG;EAAG,EACpB,IAAuB,MACvB,IAA6B,MAC7B,IAAgB,IAEd,IAAI,EAAK,UAAU;EAAE,GAAG;EAAI,GAAG;EAAI,GAAG;EAAK,GAAG;EAAK,EAEnD,KAAW,MACf,MAAS,WAAW,EAAW,EAAI,GAAG,EAAW,EAAI,EAEjD,KAAc,MAAsC;EACxD,IAAM,IAAQ,EAAc,EAAI,GAAG,EAAI,GAAG,EAAgB,EAAI,KAAK,CAAC,EAC9D,IAAc,EAClB;GAAE,GAAG,EAAM;GAAI,GAAG,EAAM;GAAI,EAAE,EAAI,MAAM,EAAI,MAAM,EACnD;AAWD,SARa,EAA6B;GACxC,IAAI,GAAG,EAAI,GAAG;GACd,OAAO;GACP;GACA,MAAM;GACN,cAAc,EAAI;GAClB,YAAY,EAAK,cAAc,EAAI,OAAO;GAC3C,CACM,CAAK,KAAK,EAAI,MAAM,EAAI,MAAM,EAAI,KAAK;IAG1C,KAAc,MACb,IACE,CAAC;EACN,MAAM;EACN,OAAO;EACP,GAAG,EAAI,KAAK;EAAG,GAAG,EAAI,KAAK;EAAG,GAAG,EAAI,KAAK;EAAG,GAAG,EAAI,KAAK;EACzD,UAAU;EACX,CAAC,GANkB,EAAE,EASlB,UAAsB;AAC1B,MAAI,MAAS,WAAW,EAAe;EACvC,IAAM,IAAK,EAAQ,WAAW,SAAS;AACvC,MAAI,CAAC,EAAI;EACT,IAAM,IAAU,EAAQ,uBAAuB;AAC/C,MAAI,EAAQ,UAAU,EAAG;EACzB,IAAM,IAAM,EAAQ,QAAQ,EAAQ,OAC9B,IAAO,EAAI,aACX,IAAK,KAAK,IAAI,GAAG,KAAK,MAAO,EAAK,IAAI,IAAO,EAAO,CAAC,EACrD,IAAK,KAAK,IAAI,GAAG,KAAK,MAAO,EAAK,IAAI,IAAO,EAAO,CAAC,EACrD,IAAO,EACX,GAAI;GAAE,OAAO,EAAQ;GAAO,QAAQ,EAAQ;GAAQ,EAAE,GAAK,GAAK,GAAI,EACrE;AAED,EADA,IAAgB,IAChB,kBAAkB,EAAK,CACpB,MAAM,MAAQ;AAIb,GAHA,GAAQ,OAAO,EACf,IAAS,GACT,IAAgB,IAChB,GAAe;IACf,CACD,YAAY;AAAE,OAAgB;IAAS;IAGtC,UAAoB;EACxB,IAAM,IAAK,EAAQ,WAAW,SAAS;AACvC,MAAI,CAAC,EAAI;EACT,IAAM,IAAU,EAAQ,uBAAuB;AAC/C,MAAI,EAAQ,UAAU,EAAG;EACzB,IAAM,IAAM,EAAQ,QAAQ,EAAQ,OAC9B,IAAK,EACT,GAAI;GAAE,OAAO,EAAQ;GAAO,QAAQ,EAAQ;GAAQ,EAAE,GAAK,GAAK,GAAG,EACpE,EACK,IAAM,MAAM;GAAC,EAAG,KAAK;GAAI,EAAG,KAAK;GAAI,EAAG,KAAK;GAAG,CACnD,KAAK,MAAM,EAAE,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,KAAK,GAAG;AACvD,EAAI,MAAQ,MACV,IAAQ,GACR,EAAK,gBAAgB,EAAI;IAIvB,IAAM,EAAI,OAAO;EACrB,IAAI;EACJ,GAAG,EAAE;EAAG,GAAG,EAAE;EAAG,GAAG,EAAE;EAAG,GAAG,EAAE;EAC7B,OAAO,EAAK,SAAS;EACrB,UAAU,EAAK;EACf;EACA,gBAAgB;AAAE,MAAe;;EACjC,GAAI,EAAK,UAAU,EAAE,SAAS,EAAK,SAAS,GAAG,EAAE;EAClD,CAAC,EAEI,KAAiB,MAAsB;EAC3C,IAAM,IAAI,EAAQ,uBAAuB;AACzC,IAAU;GAAE,GAAG,EAAI,UAAU,EAAE;GAAM,GAAG,EAAI,UAAU,EAAE;GAAK,CAAC;IAG1D,KAAa,MAAgC;AAC7C,IAAI,UACJ,EAAI,QAAQ,EAAE,GAAG,EAAE,EAAE,KACzB,IAAM,GACN,GAAa,EACT,MAAS,WAAS,GAAe,EACrC,GAAe;;AAKjB,QAFA,EAAQ,iBAAiB,eAAe,EAAc,EAE/C;EACL,QAAQ;EACR,IAAI,OAAO;AAAE,UAAO;;EACpB,IAAI,SAAS;AAAE,UAAO;;EACtB,IAAI,MAAM;AAAE,UAAO;;EACnB,IAAI,QAAQ;AAAE,UAAO;;EACrB,QAAQ,GAAM;AACR,SAAS,MACb,IAAO,GACH,MAAS,WAAS,GAAe,EACrC,GAAe;;EAEjB,UAAU,GAAM;AAGd,GAFA,IAAS,GACL,MAAS,WAAS,GAAe,EACrC,GAAe;;EAEjB,OAAO;EACP,UAAU;AAIR,GAHA,EAAQ,oBAAoB,eAAe,EAAc,EACzD,GAAQ,OAAO,EACf,IAAS,MACT,EAAI,SAAS;;EAEhB"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/loupe/innerView.ts","../src/loupe/readback.ts","../src/loupe/createLoupe.ts"],"sourcesContent":["import type { View } from '@weasel-js/core';\nimport type { WidgetBounds } from '../widget';\n\n/**\n * The inner `View` a loupe renders its content through: the outer view's\n * magnification times `factor`, positioned so `target` sits at the center of\n * a viewport the size of `rect`.\n */\nexport function loupeInnerView(\n target: { x: number; y: number },\n outer: View,\n rect: WidgetBounds,\n factor: number,\n): View {\n const scale = { x: outer.scale.x * factor, y: outer.scale.y * factor };\n return {\n x: target.x - rect.w / 2 / scale.x,\n y: target.y - rect.h / 2 / scale.y,\n scale,\n };\n}\n","/**\n * Read a `rw × rh` device-pixel region of the drawing buffer centered on\n * `pointer` (CSS px relative to the canvas), returned top-down.\n *\n * GL reports rows bottom-up; every consumer here wants top-down, so both the\n * read origin and the returned rows are flipped.\n */\nexport function readbackRegion(\n gl: WebGL2RenderingContext,\n buffer: { width: number; height: number },\n pointer: { x: number; y: number },\n dpr: number,\n rw: number,\n rh: number,\n): ImageData {\n const cx = Math.round(pointer.x * dpr);\n const cy = Math.round(pointer.y * dpr);\n const gx = clamp(cx - Math.floor(rw / 2), 0, Math.max(0, buffer.width - rw));\n const gyTop = clamp(cy - Math.floor(rh / 2), 0, Math.max(0, buffer.height - rh));\n const gy = buffer.height - gyTop - rh;\n\n const raw = new Uint8Array(rw * rh * 4);\n gl.readPixels(gx, gy, rw, rh, gl.RGBA, gl.UNSIGNED_BYTE, raw);\n\n const flipped = new Uint8ClampedArray(rw * rh * 4);\n const stride = rw * 4;\n for (let row = 0; row < rh; row++) {\n const src = (rh - 1 - row) * stride;\n flipped.set(raw.subarray(src, src + stride), row * stride);\n }\n return new ImageData(flipped, rw, rh);\n}\n\nfunction clamp(v: number, lo: number, hi: number): number {\n return v < lo ? lo : v > hi ? hi : v;\n}\n","import {\n createViewportLayer, screenToWorld, viewToTransform,\n type RenderLayer, type View,\n} from '@weasel-js/core';\nimport type { DrawCommand } from '@weasel-js/core/renderer';\nimport type { Hud } from '../hud';\nimport type { WindowWidget } from '../widgets/window/window';\nimport type { HudContentCtx, WidgetBounds } from '../widget';\nimport { loupeInnerView } from './innerView';\nimport { readbackRegion } from './readback';\n\n/** How a loupe magnifies. `'vector'` re-renders the source layers through a\n * zoomed-in view, so content stays sharp at any factor; `'pixel'` reads the\n * framebuffer back and blows up the actual pixels. */\nexport type LoupeMode = 'vector' | 'pixel';\n\n/** Options for `createLoupe`. */\nexport interface LoupeOptions {\n hud: Hud;\n /** The live canvas. Supplies the GL context for pixel mode and the pointer\n * feed for aiming. */\n element: HTMLCanvasElement;\n /** Layers re-rendered through the magnified inner view in vector mode. */\n source: RenderLayer<unknown>[];\n requestRedraw: () => void;\n mode?: LoupeMode;\n factor?: number;\n bounds?: WidgetBounds;\n title?: string;\n /** Draw the window's titlebar and close box. Default `true`. Pass `false`\n * for a bare lens; the interior then doubles as the move handle. */\n titlebar?: boolean;\n /** Called with the hex color under the aim point whenever it changes.\n * Sampled from the framebuffer in both modes — vector content is a\n * re-render whose edge colors differ from the screen's, so reading the\n * color off it would report something the user cannot see. */\n onColorChange?: (hex: string) => void;\n /** Fired by the window's close box. The window does not hide itself —\n * whoever owns the loupe's visibility decides what closing means. */\n onClose?: () => void;\n /** Opaque backdrop behind vector content, so the outer canvas does not\n * show through where the inner view is empty. */\n background?: string;\n}\n\n/** Control surface for a live loupe: where it is aimed, how it magnifies,\n * what color it is over, and the window it lives in. */\nexport interface LoupeHandle {\n readonly window: WindowWidget;\n readonly mode: LoupeMode;\n readonly factor: number;\n /** Last aimed point, in screen-space CSS px relative to the canvas. */\n readonly aim: { x: number; y: number };\n /** Hex color under the aim point, read off the framebuffer. `null` before\n * the first aim, or when the GL context is unavailable. */\n readonly color: string | null;\n setMode(mode: LoupeMode): void;\n setFactor(factor: number): void;\n /** Aim at a screen point. Ignored while the point is over the window —\n * that is the freeze rule that makes the borders reachable. */\n aimAt(p: { x: number; y: number }): void;\n dispose(): void;\n}\n\n/**\n * Build a magnifier: a HUD window whose interior shows the canvas around an\n * aim point, enlarged. Adds the window to the HUD and starts following the\n * pointer; `dispose` removes it again.\n */\nexport function createLoupe(opts: LoupeOptions): LoupeHandle {\n const { hud, element, source, requestRedraw } = opts;\n let mode: LoupeMode = opts.mode ?? 'vector';\n let factor = opts.factor ?? 8;\n let aim = { x: 0, y: 0 };\n let color: string | null = null;\n let pixels: ImageBitmap | null = null;\n let pixelsPending = false;\n let pixelsStale = false;\n let disposed = false;\n\n const b = opts.bounds ?? { x: 24, y: 24, w: 220, h: 200 };\n\n const content = (ctx: HudContentCtx): DrawCommand[] =>\n mode === 'vector' ? drawVector(ctx) : drawPixels(ctx);\n\n const drawVector = (ctx: HudContentCtx): DrawCommand[] => {\n const world = screenToWorld(aim.x, aim.y, viewToTransform(ctx.view));\n const inner: View = loupeInnerView(\n { x: world[0], y: world[1] }, ctx.view, ctx.rect, factor,\n );\n // A fresh lens per frame: CreateViewportLayerOpts.view is static, and the\n // inner view moves with the pointer.\n const lens = createViewportLayer<unknown>({\n id: `${win.id}:lens`,\n label: 'Loupe content',\n source,\n view: inner,\n bounds: () => ctx.rect,\n background: opts.background ?? ctx.tokens['--wzl-surface'],\n });\n return lens.draw(ctx.data, ctx.view, ctx.dims);\n };\n\n const drawPixels = (ctx: HudContentCtx): DrawCommand[] => {\n if (!pixels) return [];\n return [{\n kind: 'image',\n image: pixels,\n x: ctx.rect.x, y: ctx.rect.y, w: ctx.rect.w, h: ctx.rect.h,\n sampling: 'nearest',\n }];\n };\n\n const refreshPixels = () => {\n if (disposed || mode !== 'pixel') return;\n // A readback requested mid-flight is remembered rather than dropped: the\n // aim that arrives during a fast drag is the one the user ends on.\n if (pixelsPending) { pixelsStale = true; return; }\n const gl = element.getContext('webgl2');\n if (!gl) return;\n const cssRect = element.getBoundingClientRect();\n if (cssRect.width === 0) return;\n const dpr = element.width / cssRect.width;\n const rect = win.contentRect;\n const rw = Math.max(1, Math.round((rect.w * dpr) / factor));\n const rh = Math.max(1, Math.round((rect.h * dpr) / factor));\n const data = readbackRegion(\n gl, { width: element.width, height: element.height }, aim, dpr, rw, rh,\n );\n pixelsPending = true;\n pixelsStale = false;\n createImageBitmap(data)\n .then((bmp) => {\n if (disposed) { bmp.close(); return; }\n pixels?.close();\n pixels = bmp;\n pixelsPending = false;\n requestRedraw();\n if (pixelsStale) refreshPixels();\n })\n .catch(() => {\n pixelsPending = false;\n if (pixelsStale) refreshPixels();\n });\n };\n\n const sampleColor = () => {\n const gl = element.getContext('webgl2');\n if (!gl) return;\n const cssRect = element.getBoundingClientRect();\n if (cssRect.width === 0) return;\n const dpr = element.width / cssRect.width;\n const px = readbackRegion(\n gl, { width: element.width, height: element.height }, aim, dpr, 1, 1,\n );\n const hex = '#' + [px.data[0], px.data[1], px.data[2]]\n .map((c) => c.toString(16).padStart(2, '0')).join('');\n if (hex !== color) {\n color = hex;\n opts.onColorChange?.(hex);\n }\n };\n\n const win = hud.window({\n id: 'weasel-loupe',\n x: b.x, y: b.y, w: b.w, h: b.h,\n title: opts.title ?? 'Loupe',\n titlebar: opts.titlebar,\n content,\n onResize: () => { refreshPixels(); },\n ...(opts.onClose ? { onClose: opts.onClose } : {}),\n });\n\n const onPointerMove = (evt: PointerEvent) => {\n const r = element.getBoundingClientRect();\n handleAim({ x: evt.clientX - r.left, y: evt.clientY - r.top });\n };\n\n const handleAim = (p: { x: number; y: number }) => {\n if (win.hidden) return;\n if (win.hitTest(p.x, p.y)) return; // freeze rule\n aim = p;\n sampleColor();\n if (mode === 'pixel') refreshPixels();\n requestRedraw();\n };\n\n element.addEventListener('pointermove', onPointerMove);\n\n return {\n window: win,\n get mode() { return mode; },\n get factor() { return factor; },\n get aim() { return aim; },\n get color() { return color; },\n setMode(next) {\n if (next === mode) return;\n mode = next;\n if (mode === 'pixel') refreshPixels();\n requestRedraw();\n },\n setFactor(next) {\n factor = next;\n if (mode === 'pixel') refreshPixels();\n requestRedraw();\n },\n aimAt: handleAim,\n dispose() {\n disposed = true;\n element.removeEventListener('pointermove', onPointerMove);\n pixels?.close();\n pixels = null;\n win.dispose();\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -2,7 +2,11 @@ import { type RenderLayer } from '@weasel-js/core';
|
|
|
2
2
|
import type { Hud } from '../hud';
|
|
3
3
|
import type { WindowWidget } from '../widgets/window/window';
|
|
4
4
|
import type { WidgetBounds } from '../widget';
|
|
5
|
+
/** How a loupe magnifies. `'vector'` re-renders the source layers through a
|
|
6
|
+
* zoomed-in view, so content stays sharp at any factor; `'pixel'` reads the
|
|
7
|
+
* framebuffer back and blows up the actual pixels. */
|
|
5
8
|
export type LoupeMode = 'vector' | 'pixel';
|
|
9
|
+
/** Options for `createLoupe`. */
|
|
6
10
|
export interface LoupeOptions {
|
|
7
11
|
hud: Hud;
|
|
8
12
|
/** The live canvas. Supplies the GL context for pixel mode and the pointer
|
|
@@ -30,6 +34,8 @@ export interface LoupeOptions {
|
|
|
30
34
|
* show through where the inner view is empty. */
|
|
31
35
|
background?: string;
|
|
32
36
|
}
|
|
37
|
+
/** Control surface for a live loupe: where it is aimed, how it magnifies,
|
|
38
|
+
* what color it is over, and the window it lives in. */
|
|
33
39
|
export interface LoupeHandle {
|
|
34
40
|
readonly window: WindowWidget;
|
|
35
41
|
readonly mode: LoupeMode;
|
|
@@ -52,5 +58,10 @@ export interface LoupeHandle {
|
|
|
52
58
|
}): void;
|
|
53
59
|
dispose(): void;
|
|
54
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Build a magnifier: a HUD window whose interior shows the canvas around an
|
|
63
|
+
* aim point, enlarged. Adds the window to the HUD and starts following the
|
|
64
|
+
* pointer; `dispose` removes it again.
|
|
65
|
+
*/
|
|
55
66
|
export declare function createLoupe(opts: LoupeOptions): LoupeHandle;
|
|
56
67
|
//# sourceMappingURL=createLoupe.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createLoupe.d.ts","sourceRoot":"","sources":["../../src/loupe/createLoupe.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,WAAW,EACjB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,WAAW,CAAC;AAI7D,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE3C,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,GAAG,CAAC;IACT;2BACuB;IACvB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,0EAA0E;IAC1E,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;IAC/B,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;yEACqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;mEAG+D;IAC/D,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC;0EACsE;IACtE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;sDACkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,uEAAuE;IACvE,QAAQ,CAAC,GAAG,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC;gEAC4D;IAC5D,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;oEACgE;IAChE,KAAK,CAAC,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzC,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"createLoupe.d.ts","sourceRoot":"","sources":["../../src/loupe/createLoupe.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,WAAW,EACjB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,WAAW,CAAC;AAI7D;;uDAEuD;AACvD,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE3C,iCAAiC;AACjC,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,GAAG,CAAC;IACT;2BACuB;IACvB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,0EAA0E;IAC1E,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;IAC/B,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;yEACqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;mEAG+D;IAC/D,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC;0EACsE;IACtE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;sDACkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;yDACyD;AACzD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,uEAAuE;IACvE,QAAQ,CAAC,GAAG,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC;gEAC4D;IAC5D,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;oEACgE;IAChE,KAAK,CAAC,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzC,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,WAAW,CAkJ3D"}
|
package/dist/react/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as e, n as t, r as n } from "../tool-
|
|
1
|
+
import { c as e, n as t, r as n } from "../tool-DyPy2h0T.js";
|
|
2
2
|
import { resolveTheme as r, weaselTheme as i } from "@weasel-js/theme";
|
|
3
3
|
import { useEffect as a, useMemo as o, useState as s } from "react";
|
|
4
4
|
import { useThemeOptional as c } from "@weasel-js/theme/react";
|
|
@@ -238,6 +238,9 @@ function d(e) {
|
|
|
238
238
|
hitTest(e, t) {
|
|
239
239
|
return i ? !1 : d(e, t);
|
|
240
240
|
},
|
|
241
|
+
cursorAt(t, n) {
|
|
242
|
+
return i || !d(t, n) ? "default" : e.cursor ?? "pointer";
|
|
243
|
+
},
|
|
241
244
|
onPointer(t) {
|
|
242
245
|
switch (t.type) {
|
|
243
246
|
case "down":
|
|
@@ -598,9 +601,12 @@ var C = [
|
|
|
598
601
|
function w(e) {
|
|
599
602
|
return e.claims ?? C;
|
|
600
603
|
}
|
|
604
|
+
function T(e, t, n) {
|
|
605
|
+
return e.cursorAt?.(t, n) ?? (w(e).includes("pointer") ? "pointer" : void 0);
|
|
606
|
+
}
|
|
601
607
|
//#endregion
|
|
602
608
|
//#region src/attach.ts
|
|
603
|
-
function
|
|
609
|
+
function E(e, t, i = {}) {
|
|
604
610
|
let s = i.theme ?? a(o, o.defaultMode);
|
|
605
611
|
if (t.attached) throw Error("weasel-hud: this HUD is already attached to a canvas.");
|
|
606
612
|
S().then(() => e.requestRedraw()).catch((e) => {
|
|
@@ -652,12 +658,14 @@ function T(e, t, i = {}) {
|
|
|
652
658
|
},
|
|
653
659
|
hitTest: (e, t, i, a, o) => {
|
|
654
660
|
let [s, c] = r(e, t, n(a)), u = l(s, c);
|
|
655
|
-
|
|
661
|
+
if (!u) return null;
|
|
662
|
+
let d = T(u, s, c);
|
|
663
|
+
return {
|
|
656
664
|
initialScratch: { widget: u },
|
|
657
665
|
strength: "exclusive",
|
|
658
666
|
claimedKinds: w(u),
|
|
659
|
-
...
|
|
660
|
-
}
|
|
667
|
+
...d === void 0 ? {} : { cursor: d }
|
|
668
|
+
};
|
|
661
669
|
},
|
|
662
670
|
onUncapturedMove: (t, i, a, o) => {
|
|
663
671
|
let [s, u] = r(t, i, n(o)), d = l(s, u);
|
|
@@ -687,30 +695,30 @@ function T(e, t, i = {}) {
|
|
|
687
695
|
}
|
|
688
696
|
//#endregion
|
|
689
697
|
//#region src/tool.ts
|
|
690
|
-
var
|
|
698
|
+
var D = "layer:weasel-hud", O = {
|
|
691
699
|
shift: "optional",
|
|
692
700
|
alt: "optional",
|
|
693
701
|
meta: "optional",
|
|
694
702
|
ctrl: "optional"
|
|
695
703
|
};
|
|
696
|
-
function O(e) {
|
|
697
|
-
return e?.kind === E;
|
|
698
|
-
}
|
|
699
704
|
function k(e) {
|
|
700
|
-
return e?.
|
|
705
|
+
return e?.kind === D;
|
|
701
706
|
}
|
|
702
707
|
function A(e) {
|
|
708
|
+
return e?.payload?.widget ?? null;
|
|
709
|
+
}
|
|
710
|
+
function j(e) {
|
|
703
711
|
return Object.assign((t) => {
|
|
704
|
-
if (!
|
|
705
|
-
let n =
|
|
712
|
+
if (!k(t)) return !1;
|
|
713
|
+
let n = A(t);
|
|
706
714
|
return n !== null && w(n).includes(e);
|
|
707
715
|
}, { readsAffordance: !0 });
|
|
708
716
|
}
|
|
709
|
-
function
|
|
717
|
+
function M(e, t, i) {
|
|
710
718
|
let a = e.view?.get();
|
|
711
719
|
return a ? r(t, i, n(a)) : [t, i];
|
|
712
720
|
}
|
|
713
|
-
function
|
|
721
|
+
function N() {
|
|
714
722
|
return {
|
|
715
723
|
id: "hud.press",
|
|
716
724
|
label: "HUD — press widget",
|
|
@@ -718,9 +726,9 @@ function M() {
|
|
|
718
726
|
invoker: {
|
|
719
727
|
timing: "immediate",
|
|
720
728
|
run: (e, t) => {
|
|
721
|
-
let n = t, r =
|
|
729
|
+
let n = t, r = A(n?.affordance);
|
|
722
730
|
if (!r || n?.worldX === void 0 || n.worldY === void 0) return;
|
|
723
|
-
let [i, a] =
|
|
731
|
+
let [i, a] = M(e, n.worldX, n.worldY);
|
|
724
732
|
r.onPointer({
|
|
725
733
|
type: "down",
|
|
726
734
|
x: i,
|
|
@@ -731,7 +739,7 @@ function M() {
|
|
|
731
739
|
}
|
|
732
740
|
};
|
|
733
741
|
}
|
|
734
|
-
function
|
|
742
|
+
function P() {
|
|
735
743
|
return {
|
|
736
744
|
id: "hud.release",
|
|
737
745
|
label: "HUD — release widget",
|
|
@@ -739,9 +747,9 @@ function N() {
|
|
|
739
747
|
invoker: {
|
|
740
748
|
timing: "immediate",
|
|
741
749
|
run: (e, t) => {
|
|
742
|
-
let n = t, r =
|
|
750
|
+
let n = t, r = A(n?.affordance);
|
|
743
751
|
if (!r || n?.worldX === void 0 || n.worldY === void 0) return;
|
|
744
|
-
let [i, a] =
|
|
752
|
+
let [i, a] = M(e, n.worldX, n.worldY);
|
|
745
753
|
r.onPointer({
|
|
746
754
|
type: "up",
|
|
747
755
|
x: i,
|
|
@@ -752,7 +760,7 @@ function N() {
|
|
|
752
760
|
}
|
|
753
761
|
};
|
|
754
762
|
}
|
|
755
|
-
function
|
|
763
|
+
function F() {
|
|
756
764
|
return {
|
|
757
765
|
id: "hud.drag",
|
|
758
766
|
label: "HUD — drag widget",
|
|
@@ -760,12 +768,12 @@ function P() {
|
|
|
760
768
|
invoker: {
|
|
761
769
|
timing: "ongoing",
|
|
762
770
|
start: (e) => {
|
|
763
|
-
let t =
|
|
771
|
+
let t = A(e.drag?.affordance);
|
|
764
772
|
if (!t) return {};
|
|
765
773
|
let n = e.deps;
|
|
766
774
|
return {
|
|
767
775
|
onMove: (e) => {
|
|
768
|
-
let [r, i] =
|
|
776
|
+
let [r, i] = M(n, e.world.x, e.world.y);
|
|
769
777
|
t.onPointer({
|
|
770
778
|
type: "move",
|
|
771
779
|
x: r,
|
|
@@ -781,7 +789,7 @@ function P() {
|
|
|
781
789
|
});
|
|
782
790
|
return;
|
|
783
791
|
}
|
|
784
|
-
let [i, a] =
|
|
792
|
+
let [i, a] = M(n, e.world.x, e.world.y);
|
|
785
793
|
t.onPointer({
|
|
786
794
|
type: "up",
|
|
787
795
|
x: i,
|
|
@@ -794,7 +802,7 @@ function P() {
|
|
|
794
802
|
}
|
|
795
803
|
};
|
|
796
804
|
}
|
|
797
|
-
function
|
|
805
|
+
function I(e, t, n) {
|
|
798
806
|
return {
|
|
799
807
|
id: e,
|
|
800
808
|
label: t,
|
|
@@ -802,9 +810,9 @@ function F(e, t, n) {
|
|
|
802
810
|
invoker: {
|
|
803
811
|
timing: "immediate",
|
|
804
812
|
run: (e, t) => {
|
|
805
|
-
let r = t, i =
|
|
813
|
+
let r = t, i = A(r?.affordance);
|
|
806
814
|
if (!i || r?.worldX === void 0 || r.worldY === void 0) return;
|
|
807
|
-
let [a, o] =
|
|
815
|
+
let [a, o] = M(e, r.worldX, r.worldY);
|
|
808
816
|
i.onPointer({
|
|
809
817
|
type: n,
|
|
810
818
|
x: a,
|
|
@@ -815,14 +823,14 @@ function F(e, t, n) {
|
|
|
815
823
|
}
|
|
816
824
|
};
|
|
817
825
|
}
|
|
818
|
-
function
|
|
826
|
+
function L() {
|
|
819
827
|
return {
|
|
820
828
|
id: "hud.wheel",
|
|
821
829
|
label: "HUD — wheel over widget",
|
|
822
830
|
invoker: {
|
|
823
831
|
timing: "immediate",
|
|
824
832
|
run: (e, t) => {
|
|
825
|
-
let n = t, r =
|
|
833
|
+
let n = t, r = A(n?.affordance);
|
|
826
834
|
!r || n?.clientX === void 0 || n.clientY === void 0 || r.onPointer({
|
|
827
835
|
type: "wheel",
|
|
828
836
|
x: n.clientX,
|
|
@@ -835,73 +843,73 @@ function I() {
|
|
|
835
843
|
}
|
|
836
844
|
};
|
|
837
845
|
}
|
|
838
|
-
function
|
|
846
|
+
function R() {
|
|
839
847
|
return {
|
|
840
848
|
id: "weasel-hud",
|
|
841
849
|
eligibility: { claimed: !0 },
|
|
842
850
|
actions: [
|
|
843
|
-
M(),
|
|
844
851
|
N(),
|
|
845
852
|
P(),
|
|
846
|
-
F(
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
I()
|
|
853
|
+
F(),
|
|
854
|
+
I("hud.doubleClick", "HUD — double-click widget", "doubleclick"),
|
|
855
|
+
I("hud.contextMenu", "HUD — right-click widget", "contextmenu"),
|
|
856
|
+
I("hud.longPress", "HUD — long-press widget", "longpress"),
|
|
857
|
+
L()
|
|
850
858
|
],
|
|
851
859
|
bindings: [
|
|
852
860
|
{
|
|
853
861
|
spec: {
|
|
854
862
|
kind: "pointerDown",
|
|
855
|
-
target: { kindOf:
|
|
856
|
-
mods:
|
|
863
|
+
target: { kindOf: j("pointer") },
|
|
864
|
+
mods: O
|
|
857
865
|
},
|
|
858
866
|
actionId: "hud.press"
|
|
859
867
|
},
|
|
860
868
|
{
|
|
861
869
|
spec: {
|
|
862
870
|
kind: "click",
|
|
863
|
-
target: { kindOf:
|
|
864
|
-
mods:
|
|
871
|
+
target: { kindOf: j("pointer") },
|
|
872
|
+
mods: O
|
|
865
873
|
},
|
|
866
874
|
actionId: "hud.release"
|
|
867
875
|
},
|
|
868
876
|
{
|
|
869
877
|
spec: {
|
|
870
878
|
kind: "drag",
|
|
871
|
-
target: { kindOf:
|
|
872
|
-
mods:
|
|
879
|
+
target: { kindOf: j("pointer") },
|
|
880
|
+
mods: O
|
|
873
881
|
},
|
|
874
882
|
actionId: "hud.drag"
|
|
875
883
|
},
|
|
876
884
|
{
|
|
877
885
|
spec: {
|
|
878
886
|
kind: "doubleClick",
|
|
879
|
-
target: { kindOf:
|
|
880
|
-
mods:
|
|
887
|
+
target: { kindOf: j("doubleClick") },
|
|
888
|
+
mods: O
|
|
881
889
|
},
|
|
882
890
|
actionId: "hud.doubleClick"
|
|
883
891
|
},
|
|
884
892
|
{
|
|
885
893
|
spec: {
|
|
886
894
|
kind: "contextMenu",
|
|
887
|
-
target: { kindOf:
|
|
888
|
-
mods:
|
|
895
|
+
target: { kindOf: j("contextMenu") },
|
|
896
|
+
mods: O
|
|
889
897
|
},
|
|
890
898
|
actionId: "hud.contextMenu"
|
|
891
899
|
},
|
|
892
900
|
{
|
|
893
901
|
spec: {
|
|
894
902
|
kind: "longPress",
|
|
895
|
-
target: { kindOf:
|
|
896
|
-
mods:
|
|
903
|
+
target: { kindOf: j("longPress") },
|
|
904
|
+
mods: O
|
|
897
905
|
},
|
|
898
906
|
actionId: "hud.longPress"
|
|
899
907
|
},
|
|
900
908
|
{
|
|
901
909
|
spec: {
|
|
902
910
|
kind: "wheel",
|
|
903
|
-
target: { kindOf:
|
|
904
|
-
mods:
|
|
911
|
+
target: { kindOf: j("wheel") },
|
|
912
|
+
mods: O
|
|
905
913
|
},
|
|
906
914
|
actionId: "hud.wheel"
|
|
907
915
|
}
|
|
@@ -909,6 +917,6 @@ function L() {
|
|
|
909
917
|
};
|
|
910
918
|
}
|
|
911
919
|
//#endregion
|
|
912
|
-
export { w as a, v as c, C as i, f as l,
|
|
920
|
+
export { w as a, v as c, C as i, f as l, R as n, x as o, E as r, S as s, D as t, g as u };
|
|
913
921
|
|
|
914
|
-
//# sourceMappingURL=tool-
|
|
922
|
+
//# sourceMappingURL=tool-DyPy2h0T.js.map
|