@weasel-js/hud 0.8.0 → 1.0.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.
- package/README.md +12 -0
- package/dist/attach.d.ts +7 -1
- package/dist/attach.d.ts.map +1 -1
- package/dist/hud.d.ts +3 -0
- package/dist/hud.d.ts.map +1 -1
- package/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +142 -2
- package/dist/index.js.map +1 -0
- package/dist/loupe/createLoupe.d.ts +56 -0
- package/dist/loupe/createLoupe.d.ts.map +1 -0
- package/dist/loupe/innerView.d.ts +12 -0
- package/dist/loupe/innerView.d.ts.map +1 -0
- package/dist/loupe/readback.d.ts +15 -0
- package/dist/loupe/readback.d.ts.map +1 -0
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +15 -11
- package/dist/react/index.js.map +1 -1
- package/dist/react/useHud.d.ts +5 -5
- package/dist/react/useHud.d.ts.map +1 -1
- package/dist/{tool-D2HHqsdo.js → tool-Cvy1MwFJ.js} +498 -164
- package/dist/tool-Cvy1MwFJ.js.map +1 -0
- package/dist/tool.d.ts +12 -14
- package/dist/tool.d.ts.map +1 -1
- package/dist/widget.d.ts +77 -12
- package/dist/widget.d.ts.map +1 -1
- 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/rect.d.ts.map +1 -1
- package/dist/widgets/text.d.ts.map +1 -1
- package/dist/widgets/window/window.d.ts +38 -0
- package/dist/widgets/window/window.d.ts.map +1 -0
- package/dist/widgets/window/zones.d.ts +24 -0
- package/dist/widgets/window/zones.d.ts.map +1 -0
- package/package.json +3 -3
- package/dist/theme.d.ts +0 -30
- package/dist/theme.d.ts.map +0 -1
- package/dist/tool-D2HHqsdo.js.map +0 -1
package/README.md
CHANGED
|
@@ -19,6 +19,18 @@ import { /* … */ } from '@weasel-js/hud';
|
|
|
19
19
|
import { /* … */ } from '@weasel-js/hud/react';
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
## Widgets
|
|
23
|
+
|
|
24
|
+
`rect`, `text`, `image`, `label`, `button`, and `window` — a draggable,
|
|
25
|
+
resizable frame whose interior is painted by an opt-in `content` callback. See
|
|
26
|
+
[`src/widgets/window`](src/widgets/window/README.md) for how content composes
|
|
27
|
+
with the frame, and `createLoupe` in [`src/loupe`](src/loupe) for the first
|
|
28
|
+
consumer.
|
|
29
|
+
|
|
30
|
+
Widgets draw from a data-free context (`{ dims, defaultFont, tokens }`), which
|
|
31
|
+
is what lets a HUD render headlessly and identically. A window's `content`
|
|
32
|
+
painter is the single, explicit exception.
|
|
33
|
+
|
|
22
34
|
## License
|
|
23
35
|
|
|
24
36
|
MIT
|
package/dist/attach.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type { Hud } from './hud';
|
|
2
2
|
import type { CanvasExtensionApi } from '@weasel-js/core';
|
|
3
|
-
|
|
3
|
+
import { type ResolvedTheme } from '@weasel-js/theme';
|
|
4
|
+
export interface AttachHudOptions {
|
|
5
|
+
/** Resolved theme the widgets draw with. Defaults to the built-in theme's
|
|
6
|
+
* default mode; pass `useTheme().resolved` to follow a live theme. */
|
|
7
|
+
readonly theme?: ResolvedTheme;
|
|
8
|
+
}
|
|
9
|
+
export declare function attachHud(api: CanvasExtensionApi, hud: Hud, options?: AttachHudOptions): () => void;
|
|
4
10
|
//# 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,
|
|
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,CAuHZ"}
|
package/dist/hud.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { type TextOptions, type TextWidget } from './widgets/text';
|
|
|
5
5
|
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
|
+
import { type WindowOptions, type WindowWidget } from './widgets/window/window';
|
|
8
9
|
export interface Hud {
|
|
9
10
|
add(widget: Widget): void;
|
|
10
11
|
remove(widget: Widget): void;
|
|
@@ -24,6 +25,8 @@ export interface Hud {
|
|
|
24
25
|
label(opts: LabelOptions): LabelWidget;
|
|
25
26
|
/** Create a button widget, add it to the HUD, and wire onChange → markDirty. */
|
|
26
27
|
button(opts: ButtonOptions): ButtonWidget;
|
|
28
|
+
/** Create a window widget, add it to the HUD, and wire onChange → markDirty. */
|
|
29
|
+
window(opts: WindowOptions): WindowWidget;
|
|
27
30
|
}
|
|
28
31
|
export declare function createHud(): Hud;
|
|
29
32
|
//# 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;
|
|
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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export { createHud, type Hud } from './hud';
|
|
2
2
|
export { attachHud } from './attach';
|
|
3
|
-
export {
|
|
4
|
-
export type { Widget, WidgetBounds, HudDrawCtx, HudPointerEvent,
|
|
3
|
+
export { createHudContribution, HUD_AFFORDANCE_KIND, type HudHitPayload } from './tool';
|
|
4
|
+
export type { Widget, WidgetBounds, HudDrawCtx, HudPointerEvent, } from './widget';
|
|
5
|
+
export { DEFAULT_WIDGET_CLAIMS, claimsOf } from './widget';
|
|
5
6
|
export type { HudHost } from './host';
|
|
6
7
|
export { DEFAULT_FONT_FAMILY, registerDefaultFont } from './fonts/registerDefaultFont';
|
|
7
8
|
export type { RectOptions, RectWidget } from './widgets/rect';
|
|
@@ -9,5 +10,10 @@ export type { TextOptions, TextWidget } from './widgets/text';
|
|
|
9
10
|
export type { ImageOptions, ImageWidget } from './widgets/image';
|
|
10
11
|
export type { LabelOptions, LabelWidget } from './widgets/label';
|
|
11
12
|
export type { ButtonOptions, ButtonWidget, ButtonEvent, ButtonHandler } from './widgets/button';
|
|
12
|
-
export {
|
|
13
|
+
export type { WindowOptions, WindowWidget } from './widgets/window/window';
|
|
14
|
+
export { DEFAULT_WINDOW_METRICS, cursorForZone, type WindowZone, type WindowMetrics, } from './widgets/window/zones';
|
|
15
|
+
export type { HudContentCtx } from './widget';
|
|
16
|
+
export { createLoupe, type LoupeOptions, type LoupeHandle, type LoupeMode } from './loupe/createLoupe';
|
|
17
|
+
export { loupeInnerView } from './loupe/innerView';
|
|
18
|
+
export type { ResolvedTheme } from '@weasel-js/theme';
|
|
13
19
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;AACxF,YAAY,EACV,MAAM,EACN,YAAY,EACZ,UAAU,EACV,eAAe,GAChB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAC3D,YAAY,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvF,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC9D,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC9D,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACjE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACjE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAChG,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EACL,sBAAsB,EAAE,aAAa,EACrC,KAAK,UAAU,EAAE,KAAK,aAAa,GACpC,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACvG,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,142 @@
|
|
|
1
|
-
import { a as e,
|
|
2
|
-
|
|
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-Cvy1MwFJ.js";
|
|
2
|
+
import { createViewportLayer as u, screenToWorld as d, viewToTransform as f } from "@weasel-js/core";
|
|
3
|
+
//#region src/loupe/innerView.ts
|
|
4
|
+
function p(e, t, n, r) {
|
|
5
|
+
let i = {
|
|
6
|
+
x: t.scale.x * r,
|
|
7
|
+
y: t.scale.y * r
|
|
8
|
+
};
|
|
9
|
+
return {
|
|
10
|
+
x: e.x - n.w / 2 / i.x,
|
|
11
|
+
y: e.y - n.h / 2 / i.y,
|
|
12
|
+
scale: i
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/loupe/readback.ts
|
|
17
|
+
function m(e, t, n, r, i, a) {
|
|
18
|
+
let o = Math.round(n.x * r), s = Math.round(n.y * r), c = h(o - Math.floor(i / 2), 0, Math.max(0, t.width - i)), l = h(s - Math.floor(a / 2), 0, Math.max(0, t.height - a)), u = t.height - l - a, d = new Uint8Array(i * a * 4);
|
|
19
|
+
e.readPixels(c, u, i, a, e.RGBA, e.UNSIGNED_BYTE, d);
|
|
20
|
+
let f = new Uint8ClampedArray(i * a * 4), p = i * 4;
|
|
21
|
+
for (let e = 0; e < a; e++) {
|
|
22
|
+
let t = (a - 1 - e) * p;
|
|
23
|
+
f.set(d.subarray(t, t + p), e * p);
|
|
24
|
+
}
|
|
25
|
+
return new ImageData(f, i, a);
|
|
26
|
+
}
|
|
27
|
+
function h(e, t, n) {
|
|
28
|
+
return e < t ? t : e > n ? n : e;
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/loupe/createLoupe.ts
|
|
32
|
+
function g(e) {
|
|
33
|
+
let { hud: t, element: n, source: r, requestRedraw: i } = e, a = e.mode ?? "vector", o = e.factor ?? 8, s = {
|
|
34
|
+
x: 0,
|
|
35
|
+
y: 0
|
|
36
|
+
}, c = null, l = null, h = !1, g = e.bounds ?? {
|
|
37
|
+
x: 24,
|
|
38
|
+
y: 24,
|
|
39
|
+
w: 220,
|
|
40
|
+
h: 200
|
|
41
|
+
}, _ = (e) => a === "vector" ? v(e) : y(e), v = (t) => {
|
|
42
|
+
let n = d(s.x, s.y, f(t.view)), i = p({
|
|
43
|
+
x: n[0],
|
|
44
|
+
y: n[1]
|
|
45
|
+
}, t.view, t.rect, o);
|
|
46
|
+
return u({
|
|
47
|
+
id: `${S.id}:lens`,
|
|
48
|
+
label: "Loupe content",
|
|
49
|
+
source: r,
|
|
50
|
+
view: i,
|
|
51
|
+
bounds: () => t.rect,
|
|
52
|
+
background: e.background ?? t.tokens["--wzl-surface"]
|
|
53
|
+
}).draw(t.data, t.view, t.dims);
|
|
54
|
+
}, y = (e) => l ? [{
|
|
55
|
+
kind: "image",
|
|
56
|
+
image: l,
|
|
57
|
+
x: e.rect.x,
|
|
58
|
+
y: e.rect.y,
|
|
59
|
+
w: e.rect.w,
|
|
60
|
+
h: e.rect.h,
|
|
61
|
+
sampling: "nearest"
|
|
62
|
+
}] : [], b = () => {
|
|
63
|
+
if (a !== "pixel" || h) return;
|
|
64
|
+
let e = n.getContext("webgl2");
|
|
65
|
+
if (!e) return;
|
|
66
|
+
let t = n.getBoundingClientRect();
|
|
67
|
+
if (t.width === 0) return;
|
|
68
|
+
let r = n.width / t.width, c = S.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
|
+
width: n.width,
|
|
70
|
+
height: n.height
|
|
71
|
+
}, s, r, u, d);
|
|
72
|
+
h = !0, createImageBitmap(f).then((e) => {
|
|
73
|
+
l?.close(), l = e, h = !1, i();
|
|
74
|
+
}).catch(() => {
|
|
75
|
+
h = !1;
|
|
76
|
+
});
|
|
77
|
+
}, x = () => {
|
|
78
|
+
let t = n.getContext("webgl2");
|
|
79
|
+
if (!t) return;
|
|
80
|
+
let r = n.getBoundingClientRect();
|
|
81
|
+
if (r.width === 0) return;
|
|
82
|
+
let i = n.width / r.width, a = m(t, {
|
|
83
|
+
width: n.width,
|
|
84
|
+
height: n.height
|
|
85
|
+
}, s, i, 1, 1), o = "#" + [
|
|
86
|
+
a.data[0],
|
|
87
|
+
a.data[1],
|
|
88
|
+
a.data[2]
|
|
89
|
+
].map((e) => e.toString(16).padStart(2, "0")).join("");
|
|
90
|
+
o !== c && (c = o, e.onColorChange?.(o));
|
|
91
|
+
}, S = t.window({
|
|
92
|
+
id: "weasel-loupe",
|
|
93
|
+
x: g.x,
|
|
94
|
+
y: g.y,
|
|
95
|
+
w: g.w,
|
|
96
|
+
h: g.h,
|
|
97
|
+
title: e.title ?? "Loupe",
|
|
98
|
+
titlebar: e.titlebar,
|
|
99
|
+
content: _,
|
|
100
|
+
onResize: () => {
|
|
101
|
+
b();
|
|
102
|
+
},
|
|
103
|
+
...e.onClose ? { onClose: e.onClose } : {}
|
|
104
|
+
}), C = (e) => {
|
|
105
|
+
let t = n.getBoundingClientRect();
|
|
106
|
+
w({
|
|
107
|
+
x: e.clientX - t.left,
|
|
108
|
+
y: e.clientY - t.top
|
|
109
|
+
});
|
|
110
|
+
}, w = (e) => {
|
|
111
|
+
S.hidden || S.hitTest(e.x, e.y) || (s = e, x(), a === "pixel" && b(), i());
|
|
112
|
+
};
|
|
113
|
+
return n.addEventListener("pointermove", C), {
|
|
114
|
+
window: S,
|
|
115
|
+
get mode() {
|
|
116
|
+
return a;
|
|
117
|
+
},
|
|
118
|
+
get factor() {
|
|
119
|
+
return o;
|
|
120
|
+
},
|
|
121
|
+
get aim() {
|
|
122
|
+
return s;
|
|
123
|
+
},
|
|
124
|
+
get color() {
|
|
125
|
+
return c;
|
|
126
|
+
},
|
|
127
|
+
setMode(e) {
|
|
128
|
+
e !== a && (a = e, a === "pixel" && b(), i());
|
|
129
|
+
},
|
|
130
|
+
setFactor(e) {
|
|
131
|
+
o = e, a === "pixel" && b(), i();
|
|
132
|
+
},
|
|
133
|
+
aimAt: w,
|
|
134
|
+
dispose() {
|
|
135
|
+
n.removeEventListener("pointermove", C), l?.close(), l = null, S.dispose();
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
//#endregion
|
|
140
|
+
export { a as DEFAULT_FONT_FAMILY, n as DEFAULT_WIDGET_CLAIMS, r as DEFAULT_WINDOW_METRICS, c as HUD_AFFORDANCE_KIND, o as attachHud, e as claimsOf, t as createHud, i as createHudContribution, g as createLoupe, l as cursorForZone, p as loupeInnerView, s as registerDefaultFont };
|
|
141
|
+
|
|
142
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type RenderLayer } from '@weasel-js/core';
|
|
2
|
+
import type { Hud } from '../hud';
|
|
3
|
+
import type { WindowWidget } from '../widgets/window/window';
|
|
4
|
+
import type { WidgetBounds } from '../widget';
|
|
5
|
+
export type LoupeMode = 'vector' | 'pixel';
|
|
6
|
+
export interface LoupeOptions {
|
|
7
|
+
hud: Hud;
|
|
8
|
+
/** The live canvas. Supplies the GL context for pixel mode and the pointer
|
|
9
|
+
* feed for aiming. */
|
|
10
|
+
element: HTMLCanvasElement;
|
|
11
|
+
/** Layers re-rendered through the magnified inner view in vector mode. */
|
|
12
|
+
source: RenderLayer<unknown>[];
|
|
13
|
+
requestRedraw: () => void;
|
|
14
|
+
mode?: LoupeMode;
|
|
15
|
+
factor?: number;
|
|
16
|
+
bounds?: WidgetBounds;
|
|
17
|
+
title?: string;
|
|
18
|
+
/** Draw the window's titlebar and close box. Default `true`. Pass `false`
|
|
19
|
+
* for a bare lens; the interior then doubles as the move handle. */
|
|
20
|
+
titlebar?: boolean;
|
|
21
|
+
/** Called with the hex color under the aim point whenever it changes.
|
|
22
|
+
* Sampled from the framebuffer in both modes — vector content is a
|
|
23
|
+
* re-render whose edge colors differ from the screen's, so reading the
|
|
24
|
+
* color off it would report something the user cannot see. */
|
|
25
|
+
onColorChange?: (hex: string) => void;
|
|
26
|
+
/** Fired by the window's close box. The window does not hide itself —
|
|
27
|
+
* whoever owns the loupe's visibility decides what closing means. */
|
|
28
|
+
onClose?: () => void;
|
|
29
|
+
/** Opaque backdrop behind vector content, so the outer canvas does not
|
|
30
|
+
* show through where the inner view is empty. */
|
|
31
|
+
background?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface LoupeHandle {
|
|
34
|
+
readonly window: WindowWidget;
|
|
35
|
+
readonly mode: LoupeMode;
|
|
36
|
+
readonly factor: number;
|
|
37
|
+
/** Last aimed point, in screen-space CSS px relative to the canvas. */
|
|
38
|
+
readonly aim: {
|
|
39
|
+
x: number;
|
|
40
|
+
y: number;
|
|
41
|
+
};
|
|
42
|
+
/** Hex color under the aim point, read off the framebuffer. `null` before
|
|
43
|
+
* the first aim, or when the GL context is unavailable. */
|
|
44
|
+
readonly color: string | null;
|
|
45
|
+
setMode(mode: LoupeMode): void;
|
|
46
|
+
setFactor(factor: number): void;
|
|
47
|
+
/** Aim at a screen point. Ignored while the point is over the window —
|
|
48
|
+
* that is the freeze rule that makes the borders reachable. */
|
|
49
|
+
aimAt(p: {
|
|
50
|
+
x: number;
|
|
51
|
+
y: number;
|
|
52
|
+
}): void;
|
|
53
|
+
dispose(): void;
|
|
54
|
+
}
|
|
55
|
+
export declare function createLoupe(opts: LoupeOptions): LoupeHandle;
|
|
56
|
+
//# sourceMappingURL=createLoupe.d.ts.map
|
|
@@ -0,0 +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,CAsI3D"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { View } from '@weasel-js/core';
|
|
2
|
+
import type { WidgetBounds } from '../widget';
|
|
3
|
+
/**
|
|
4
|
+
* The inner `View` a loupe renders its content through: the outer view's
|
|
5
|
+
* magnification times `factor`, positioned so `target` sits at the center of
|
|
6
|
+
* a viewport the size of `rect`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function loupeInnerView(target: {
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
}, outer: View, rect: WidgetBounds, factor: number): View;
|
|
12
|
+
//# sourceMappingURL=innerView.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"innerView.d.ts","sourceRoot":"","sources":["../../src/loupe/innerView.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EAChC,KAAK,EAAE,IAAI,EACX,IAAI,EAAE,YAAY,EAClB,MAAM,EAAE,MAAM,GACb,IAAI,CAON"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read a `rw × rh` device-pixel region of the drawing buffer centered on
|
|
3
|
+
* `pointer` (CSS px relative to the canvas), returned top-down.
|
|
4
|
+
*
|
|
5
|
+
* GL reports rows bottom-up; every consumer here wants top-down, so both the
|
|
6
|
+
* read origin and the returned rows are flipped.
|
|
7
|
+
*/
|
|
8
|
+
export declare function readbackRegion(gl: WebGL2RenderingContext, buffer: {
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
}, pointer: {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
}, dpr: number, rw: number, rh: number): ImageData;
|
|
15
|
+
//# sourceMappingURL=readback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readback.d.ts","sourceRoot":"","sources":["../../src/loupe/readback.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,EAAE,EAAE,sBAAsB,EAC1B,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EACzC,OAAO,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EACjC,GAAG,EAAE,MAAM,EACX,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,GACT,SAAS,CAiBX"}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { useHud,
|
|
1
|
+
export { useHud, useHudContribution } from './useHud';
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/react/index.js
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { c as e, n as t, r as n } from "../tool-Cvy1MwFJ.js";
|
|
2
|
+
import { resolveTheme as r, weaselTheme as i } from "@weasel-js/theme";
|
|
3
|
+
import { useEffect as a, useMemo as o, useState as s } from "react";
|
|
4
|
+
import { useThemeOptional as c } from "@weasel-js/theme/react";
|
|
3
5
|
//#region src/react/useHud.ts
|
|
4
|
-
function
|
|
5
|
-
let [
|
|
6
|
-
return
|
|
7
|
-
let
|
|
8
|
-
if (
|
|
9
|
-
|
|
6
|
+
function l(t) {
|
|
7
|
+
let [l] = s(() => e()), u = c(), d = o(() => u?.resolved ?? r(i, i.defaultMode), [u]);
|
|
8
|
+
return a(() => {
|
|
9
|
+
let e = t.current;
|
|
10
|
+
if (!e) return;
|
|
11
|
+
let r = n(e, l, { theme: d });
|
|
12
|
+
return e.requestRedraw(), r;
|
|
13
|
+
}, [l, d]), l;
|
|
10
14
|
}
|
|
11
|
-
function
|
|
12
|
-
return
|
|
15
|
+
function u() {
|
|
16
|
+
return o(() => t(), []);
|
|
13
17
|
}
|
|
14
18
|
//#endregion
|
|
15
|
-
export {
|
|
19
|
+
export { l as useHud, u as useHudContribution };
|
|
16
20
|
|
|
17
21
|
//# sourceMappingURL=index.js.map
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/react/useHud.ts"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { createHud, type Hud } from '../hud';\nimport { attachHud } from '../attach';\nimport type { CanvasExtensionApi,
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/react/useHud.ts"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { useThemeOptional } from '@weasel-js/theme/react';\nimport { resolveTheme, weaselTheme } from '@weasel-js/theme';\nimport { createHud, type Hud } from '../hud';\nimport { attachHud } from '../attach';\nimport type { CanvasExtensionApi, Contribution } from '@weasel-js/core';\nimport { createHudContribution } from '../tool';\n\n/**\n * Create a HUD and attach it to a Canvas via its imperative ref handle.\n * Returns the Hud immediately (unbound); attaches in an effect after the\n * canvas ref populates. Cleans up on unmount.\n *\n * Usage:\n * const ref = useRef<CanvasExtensionApi>(null);\n * const hud = useHud(ref);\n * // ... later\n * const btn = hud.button({ ... });\n */\nexport function useHud(canvasRef: { current: CanvasExtensionApi | null }): Hud {\n const [hud] = useState(() => createHud());\n\n // Follow the app's theme when there is one. Widgets are drawn into the\n // canvas, so they can't inherit the cascade — handing them the resolved\n // record is what keeps HUD chrome in step with a mode switch.\n const provided = useThemeOptional();\n const theme = useMemo(\n () => provided?.resolved ?? resolveTheme(weaselTheme, weaselTheme.defaultMode),\n [provided],\n );\n\n useEffect(() => {\n const api = canvasRef.current;\n if (!api) return;\n const detach = attachHud(api, hud, { theme });\n api.requestRedraw();\n return detach;\n // canvasRef.current changing during component lifetime is unusual for\n // canvas refs; treat as effectively-stable in v1. The dep on `hud` is\n // also stable (it comes from useState's initializer, never changes).\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [hud, theme]);\n\n return hud;\n}\n\n/**\n * Memoize the HUD's input contribution — see {@link createHudContribution}\n * for how the routing works.\n *\n * It is stateless (its actions resolve the hit widget from the gesture's\n * affordance payload), so one instance serves any number of HUDs on the page.\n */\nexport function useHudContribution(): Contribution {\n return useMemo(() => createHudContribution(), []);\n}\n"],"mappings":";;;;;AAmBA,SAAgB,EAAO,GAAwD;CAC7E,IAAM,CAAC,KAAO,QAAe,GAAW,CAAC,EAKnC,IAAW,GAAkB,EAC7B,IAAQ,QACN,GAAU,YAAY,EAAa,GAAa,EAAY,YAAY,EAC9E,CAAC,EAAS,CACX;AAcD,QAZA,QAAgB;EACd,IAAM,IAAM,EAAU;AACtB,MAAI,CAAC,EAAK;EACV,IAAM,IAAS,EAAU,GAAK,GAAK,EAAE,UAAO,CAAC;AAE7C,SADA,EAAI,eAAe,EACZ;IAKN,CAAC,GAAK,EAAM,CAAC,EAET;;AAUT,SAAgB,IAAmC;AACjD,QAAO,QAAc,GAAuB,EAAE,EAAE,CAAC"}
|
package/dist/react/useHud.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Hud } from '../hud';
|
|
2
|
-
import type { CanvasExtensionApi,
|
|
2
|
+
import type { CanvasExtensionApi, Contribution } from '@weasel-js/core';
|
|
3
3
|
/**
|
|
4
4
|
* Create a HUD and attach it to a Canvas via its imperative ref handle.
|
|
5
5
|
* Returns the Hud immediately (unbound); attaches in an effect after the
|
|
@@ -15,11 +15,11 @@ export declare function useHud(canvasRef: {
|
|
|
15
15
|
current: CanvasExtensionApi | null;
|
|
16
16
|
}): Hud;
|
|
17
17
|
/**
|
|
18
|
-
* Memoize the HUD's input
|
|
19
|
-
*
|
|
18
|
+
* Memoize the HUD's input contribution — see {@link createHudContribution}
|
|
19
|
+
* for how the routing works.
|
|
20
20
|
*
|
|
21
|
-
*
|
|
21
|
+
* It is stateless (its actions resolve the hit widget from the gesture's
|
|
22
22
|
* affordance payload), so one instance serves any number of HUDs on the page.
|
|
23
23
|
*/
|
|
24
|
-
export declare function
|
|
24
|
+
export declare function useHudContribution(): Contribution;
|
|
25
25
|
//# sourceMappingURL=useHud.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useHud.d.ts","sourceRoot":"","sources":["../../src/react/useHud.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useHud.d.ts","sourceRoot":"","sources":["../../src/react/useHud.ts"],"names":[],"mappings":"AAGA,OAAO,EAAa,KAAK,GAAG,EAAE,MAAM,QAAQ,CAAC;AAE7C,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGxE;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CAAC,SAAS,EAAE;IAAE,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAAA;CAAE,GAAG,GAAG,CAyB7E;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,IAAI,YAAY,CAEjD"}
|