@bluepic/embed 0.5.9 → 0.5.11

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.
@@ -0,0 +1,4 @@
1
+ export { imageStateProbe, imageStateProbeStop, IMAGE_STATE_MESSAGE } from './probe';
2
+ export type { ImageLoadState, ImageStateEntry, ImageStatePoint, ImageStateQuad, ImageStateSnapshot } from './probe';
3
+ export { useImageStates, createImageStateStore } from './useImageStates';
4
+ export type { ImageOverlay } from './useImageStates';
@@ -0,0 +1,83 @@
1
+ /**
2
+ * The sandbox-side image-state watcher.
3
+ *
4
+ * Everything else in the editor updates instantly: a binding changes, the
5
+ * engine recomposes, the pixels are already right. A picture is the exception —
6
+ * it has to travel the network first — and because an expression can drive
7
+ * `image.image` off something that *looks* instantaneous (a dropdown), there is
8
+ * no way to know from the outside whether a data change will land in one frame
9
+ * or in two seconds.
10
+ *
11
+ * `@bluepic/core` answers that on the element itself (`Image.vue`), publishing
12
+ * `data-bx-image-state` on each rendered image's `<g>`. The DOM is the channel
13
+ * because a sandboxed frame is all DOM to us: we cannot import from it, and its
14
+ * engine version is not ours to choose.
15
+ *
16
+ * ⚠️ SHIPPED AS SOURCE TEXT, NOT AS A CLOSURE — same rules as
17
+ * `fieldHints/probe.ts`: `EmbeddedController.evaluate()` does `func.toString()`
18
+ * and `new Function(...)` inside the frame, so the body runs in a scope where
19
+ * nothing from this bundle exists. No free variables beyond `document`/`window`,
20
+ * plain ES, JSON in and JSON out.
21
+ *
22
+ * AND NO NAMED INNER FUNCTION OF ANY KIND — a constraint that comes from the
23
+ * TEST pipeline, not from the shipped one. Vite's production build leaves probe
24
+ * bodies alone (verified against the built `dist/bluepic-embed.umd.js`), but
25
+ * `tsx`/esbuild runs with `keepNames`, which rewrites every function with an
26
+ * inferable name — `function read()`, `var read = function()`, `var read = () =>`,
27
+ * object methods alike — into `__name(fn, 'read')`, a helper that does not exist
28
+ * in the frame. A probe written with inner functions therefore cannot be
29
+ * EXERCISED by the `new Function` round trip that proves self-containment; the
30
+ * fieldHints probes only pass theirs because the stub returns before reaching
31
+ * any of their code. Two forms survive `keepNames`, and this probe uses both:
32
+ * an anonymous callback passed inline (`new MutationObserver(function () {…})`)
33
+ * and a function assigned to a MEMBER (`w.__bxImageStateRead = function () {…}`).
34
+ * The cost is a slightly odd shape; the gain is a test that runs the real body.
35
+ *
36
+ * Unlike the geometry probe this one INSTALLS: it returns the current snapshot
37
+ * and then keeps posting to the parent as things change. Polling at the latency
38
+ * a spinner needs would be a round trip every few frames.
39
+ */
40
+ export type ImageLoadState = 'loading' | 'ready' | 'error';
41
+ export type ImageStatePoint = {
42
+ x: number;
43
+ y: number;
44
+ };
45
+ export type ImageStateQuad = [ImageStatePoint, ImageStatePoint, ImageStatePoint, ImageStatePoint];
46
+ export type ImageStateEntry = {
47
+ /** Rendered instance id — an iterated element reports `id__0`, `id__1`, … */
48
+ id: string;
49
+ state: ImageLoadState;
50
+ /**
51
+ * Whether the ENGINE is currently painting its in-flight treatment (the blur)
52
+ * on this instance — its own timed decision, not the raw `state`.
53
+ *
54
+ * The spinner follows this rather than timing the wait itself. While the two
55
+ * decided independently they disagreed by construction: the blur stopped the
56
+ * instant `load` fired, the spinner served out its own minimum, and it was
57
+ * left turning over an unblurred picture for up to 420ms. One clock, one
58
+ * follower.
59
+ */
60
+ busy: boolean;
61
+ /**
62
+ * The element's AUTHORED frame in serial coordinates, not the painted
63
+ * picture: a contain-mode image paints smaller than its frame, and a busy
64
+ * overlay that shrank to the old picture's letterbox would jump the moment
65
+ * the new one landed. Absent when the frame has not painted yet — the host
66
+ * keeps whatever it measured last.
67
+ */
68
+ quad?: ImageStateQuad;
69
+ };
70
+ export type ImageStateSnapshot = {
71
+ /**
72
+ * `false` means this engine predates `data-bx-image-state`: it renders images
73
+ * but publishes no state for them. The host then treats every picture as
74
+ * loaded, which is exactly how the editor behaved before this feature.
75
+ */
76
+ supported: boolean;
77
+ entries: ImageStateEntry[];
78
+ };
79
+ /** What the installed watcher posts to the parent window. */
80
+ export declare const IMAGE_STATE_MESSAGE = "bx:image-states";
81
+ export declare function imageStateProbe(configJson: string): ImageStateSnapshot;
82
+ /** Tears the watcher down. Separate function because probes ship as source. */
83
+ export declare function imageStateProbeStop(): boolean;
@@ -0,0 +1,63 @@
1
+ import { type Ref } from 'vue';
2
+ import { type ImageLoadState, type ImageStateEntry, type ImageStateQuad } from './probe';
3
+ export type ImageOverlay = {
4
+ /** Rendered instance id: `id__0`, `id__1`, … for an iterated element. */
5
+ id: string;
6
+ state: ImageLoadState;
7
+ /** Element frame in SERIAL coordinates. */
8
+ quad: ImageStateQuad;
9
+ aabb: {
10
+ x: number;
11
+ y: number;
12
+ width: number;
13
+ height: number;
14
+ };
15
+ centroid: {
16
+ x: number;
17
+ y: number;
18
+ };
19
+ };
20
+ type EvaluateFn = (func: Function, ...args: string[]) => Promise<unknown>;
21
+ /**
22
+ * Snapshots in, "what is covered right now" out, keyed by RENDERED INSTANCE id —
23
+ * an iterated element contributes one entry per instance, so `logo__2` can be
24
+ * covered while `logo__0` and `logo__1` stay untouched.
25
+ *
26
+ * NO TIMERS LIVE HERE. Deciding when a wait is worth announcing used to be this
27
+ * file's job, and it was the right place while the host also drew the blur. Once
28
+ * the blur moved into the engine (only the engine knows an image's clip, radius
29
+ * and mask) two clocks were deciding one thing: the blur stopped the instant
30
+ * `load` fired while the spinner served out its own 420ms minimum, so on
31
+ * medium-speed loads the spinner was left turning over an unblurred picture.
32
+ * The engine now makes that call once and publishes it; this mirrors it. See
33
+ * `useInFlightTreatment` in @bluepic/core.
34
+ */
35
+ export declare function createImageStateStore(): {
36
+ apply: (list: ImageStateEntry[]) => void;
37
+ states: import("vue").ComputedRef<Record<string, ImageLoadState>>;
38
+ busy: import("vue").ComputedRef<boolean>;
39
+ overlays: import("vue").ComputedRef<ImageOverlay[]>;
40
+ coveredIds: import("vue").ComputedRef<Set<string>>;
41
+ };
42
+ /**
43
+ * Live load state for every rendered image in the sandbox.
44
+ *
45
+ * The store above is the level; the sandbox pushes the edges. See `probe.ts`
46
+ * for why this is a DOM watcher installed over `evaluate()` rather than a core
47
+ * event or a poll.
48
+ */
49
+ export declare function useImageStates(options: {
50
+ /** Resolved lazily — the sandbox controller only exists once the iframe mounts. */
51
+ evaluate: () => EvaluateFn | undefined;
52
+ /** The frame's own window, so a message from anywhere else can be ignored. */
53
+ frameWindow: () => Window | null | undefined;
54
+ ready: Ref<boolean>;
55
+ }): {
56
+ states: import("vue").ComputedRef<Record<string, ImageLoadState>>;
57
+ busy: import("vue").ComputedRef<boolean>;
58
+ overlays: import("vue").ComputedRef<ImageOverlay[]>;
59
+ coveredIds: import("vue").ComputedRef<Set<string>>;
60
+ supported: Ref<boolean | undefined, boolean | undefined>;
61
+ dispose: () => void;
62
+ };
63
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bluepic/embed",
3
- "version": "0.5.9",
3
+ "version": "0.5.11",
4
4
  "description": "Bluepic embed sdk",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -42,7 +42,8 @@
42
42
  "test:autocrop": "tsx src/util/autoCrop/solveAutoCrop.test.ts",
43
43
  "test:gallery": "tsx src/util/gallery.test.ts",
44
44
  "test:fieldhints": "tsx src/util/fieldHints/fieldHints.test.ts",
45
- "test:clone": "tsx src/util/cloneDeep.test.ts"
45
+ "test:clone": "tsx src/util/cloneDeep.test.ts",
46
+ "test:imagestates": "tsx src/util/imageStates/imageStates.test.ts"
46
47
  },
47
48
  "peerDependencies": {
48
49
  "vue": "^3.5.23"