@bluepic/embed 0.4.0-next.138 → 0.4.0-next.142

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,52 @@
1
+ import type { BluepicField } from '@bluepic/types';
2
+ import type { HintPoint, HintQuad } from './probe';
3
+ /**
4
+ * Geometry helpers over probe output. Everything here works in SERIAL
5
+ * coordinates — the overlay maps to screen space via zoompinch's
6
+ * `composePoint`, so nothing in this file needs to know about pan, zoom or
7
+ * viewport size.
8
+ */
9
+ /** Shoelace area. Quads may be rotated or sheared, so no width×height shortcut. */
10
+ export declare function quadArea(quad: HintQuad): number;
11
+ export declare function quadCentroid(quad: HintQuad): HintPoint;
12
+ export declare function quadAABB(quad: HintQuad): {
13
+ x: number;
14
+ y: number;
15
+ width: number;
16
+ height: number;
17
+ };
18
+ /**
19
+ * Which frame a quad belongs to, in the 1-based numbering `field.frameIndex`
20
+ * already uses (`BxTemplateEditor` reads it as `field.frameIndex || 1` and
21
+ * derives `frameX = (frameIndex - 1) % frames.x`).
22
+ *
23
+ * Decided by the centroid, so an element straddling a frame boundary lands in
24
+ * the frame it mostly occupies rather than being duplicated or dropped.
25
+ */
26
+ export declare function frameIndexOfPoint(point: HintPoint, serialWidth: number, serialHeight: number, frames: {
27
+ x: number;
28
+ y: number;
29
+ } | undefined): number;
30
+ /**
31
+ * Stacking class for overlap resolution — lower sits ON TOP.
32
+ *
33
+ * Hitboxes overlap constantly (a logo inside a full-bleed background image), so
34
+ * the pointer has to go somewhere defensible. Content-bearing fields win over
35
+ * pickers, which win over the tweak-a-value controls.
36
+ */
37
+ export declare function hintPriorityClass(fieldType: BluepicField['type']): number;
38
+ export type OrderableHitbox = {
39
+ fieldType: BluepicField['type'];
40
+ area: number;
41
+ /** Position of the element in the serial's depth-first order; later paints later. */
42
+ elementOrder: number;
43
+ };
44
+ /**
45
+ * Sorts hitboxes back-to-front, so the caller can render in array order and let
46
+ * DOM order do the stacking. The LAST entry is the topmost / most reachable.
47
+ *
48
+ * Smaller area on top is what keeps a logo sitting inside a full-bleed image
49
+ * clickable — without it the background would swallow every pointer event
50
+ * inside its own bounds.
51
+ */
52
+ export declare function orderHitboxes<T extends OrderableHitbox>(hitboxes: T[]): T[];
@@ -0,0 +1,8 @@
1
+ export { fieldHitboxProbe } from './probe';
2
+ export type { HintPoint, HintQuad, ElementQuad, HitboxProbeResult } from './probe';
3
+ export { collectSerialElements, expandBindingClosure, fieldBindings, referencesIdentifier, resolveFieldElements, scoreElement, CANDIDATE_SCORE_RATIO, } from './resolve';
4
+ export type { FieldElementMatch } from './resolve';
5
+ export { frameIndexOfPoint, hintPriorityClass, orderHitboxes, quadAABB, quadArea, quadCentroid } from './geometry';
6
+ export type { OrderableHitbox } from './geometry';
7
+ export { useFieldHitboxes } from './useFieldHitboxes';
8
+ export type { FieldHitbox } from './useFieldHitboxes';
@@ -0,0 +1,73 @@
1
+ /**
2
+ * The sandbox-side geometry probe.
3
+ *
4
+ * Field hints need to know where each element ACTUALLY landed on the canvas —
5
+ * after every ancestor transform, layout-group flow shift, mask clip and text
6
+ * auto-fit. That composition already happened once, inside the sandbox iframe,
7
+ * and the browser is holding the result. So rather than re-deriving it in the
8
+ * parent (which would mean linking `@bluepic/core` into this package and
9
+ * pinning ONE engine version against sandboxes that deliberately freeze one
10
+ * engine per template), we ask the frame that drew the pixels.
11
+ *
12
+ * `getBBox()` gives an element's local geometry; `getScreenCTM()` gives its
13
+ * full transform chain — CSS `transform` included, which is how core applies
14
+ * per-element matrices. Composing the element's screen CTM with the INVERSE of
15
+ * the root SVG's screen CTM cancels out viewport sizing and `preserveAspectRatio`
16
+ * letterboxing, leaving a matrix straight into viewBox units — i.e. serial
17
+ * coordinates. Verified against a live sandbox (0.2.65) to 0.0000px for an
18
+ * identity rect, a 23°-rotated text element, a rotate+scale group, and a rect
19
+ * nested inside that group.
20
+ *
21
+ * Nothing here needs a core release or a sandbox redeploy: it travels as source
22
+ * text through the existing `EmbeddedController.evaluate()` channel.
23
+ */
24
+ export type HintPoint = {
25
+ x: number;
26
+ y: number;
27
+ };
28
+ /** Element corners in serial coordinates, clockwise from the local top-left. */
29
+ export type HintQuad = [HintPoint, HintPoint, HintPoint, HintPoint];
30
+ export type ElementQuad = {
31
+ /**
32
+ * The rendered instance's unique id (`data-id`). An element with an
33
+ * `iteration` renders once per item, so one serial element can yield several
34
+ * of these — `id__0`, `id__1`, … — all sharing one `data-orginal-id`.
35
+ */
36
+ dataId: string;
37
+ quad: HintQuad;
38
+ };
39
+ export type HitboxProbeResult = {
40
+ supported: false;
41
+ /**
42
+ * `no-root` — no `.serial-wrapper > svg` yet (not mounted, or a sandbox
43
+ * that structures its DOM differently).
44
+ * `no-element-identity` — rendered, but the engine predates per-element
45
+ * `data-id` attributes (roughly < core 0.1.400). Nothing to anchor to;
46
+ * hints stay off for this template forever, by design.
47
+ * `no-ctm` — the root SVG has no screen CTM (whole subtree hidden).
48
+ */
49
+ reason: 'no-root' | 'no-element-identity' | 'no-ctm';
50
+ } | {
51
+ supported: true;
52
+ quads: {
53
+ [originalId: string]: ElementQuad[];
54
+ };
55
+ };
56
+ /**
57
+ * ⚠️ THIS FUNCTION IS SHIPPED AS SOURCE TEXT, NOT AS A CLOSURE.
58
+ *
59
+ * `EmbeddedController.evaluate()` does `func.toString()` here and `new Function(...)`
60
+ * inside the iframe, so the body executes in a scope where NOTHING from this
61
+ * bundle exists. Any reference to an import, a module-scope const, or a helper
62
+ * defined outside these braces typechecks, builds green, and then throws
63
+ * `ReferenceError` in the sandbox under a minified name that won't resemble the
64
+ * original.
65
+ *
66
+ * Rules, all enforced by `probe.test.ts`:
67
+ * - one self-contained function; no free variables beyond `document`
68
+ * - plain ES (`var`, `function`, no optional chaining) so no transpiler
69
+ * helper can be hoisted out of the body
70
+ * - arguments in as a JSON string, return value JSON-serialisable — flatten
71
+ * every `DOMMatrix`/`DOMRect` before returning
72
+ */
73
+ export declare function fieldHitboxProbe(originalIdsJson: string): HitboxProbeResult;
@@ -0,0 +1,55 @@
1
+ import type { BluepicField, Template } from '@bluepic/types';
2
+ /**
3
+ * Candidates scoring below this share of the winner are dropped. Keeps the
4
+ * legitimate multi-element cases — a headline plus the background bar bound to
5
+ * it, or one binding echoed across two frames — while cutting the long tail of
6
+ * weak style/visibility couplings that would otherwise blanket the canvas.
7
+ */
8
+ export declare const CANDIDATE_SCORE_RATIO = 0.6;
9
+ export type FieldElementMatch = {
10
+ /** Serial element id — matches the rendered `data-orginal-id`. */
11
+ elementId: string;
12
+ element: Template.Element;
13
+ score: number;
14
+ /** Property keys that produced the match, for debugging a surprising hitbox. */
15
+ matchedProperties: string[];
16
+ };
17
+ /** Depth-first walk over the serial tree, descending into group/mask slots. */
18
+ export declare function collectSerialElements(elements: Template.Element[]): Generator<Template.Element>;
19
+ /**
20
+ * Does `expression` reference `name` as a standalone identifier?
21
+ *
22
+ * Hand-rolled rather than a regex with a lookbehind: `(?<!…)` still throws at
23
+ * compile time on Safari below 16.4, and a template that renders fine
24
+ * everywhere else must not blank its hints on an older iPad. A preceding `.`
25
+ * is excluded so `other.headline` doesn't match `headline`.
26
+ */
27
+ export declare function referencesIdentifier(expression: string, name: string): boolean;
28
+ /**
29
+ * Every binding that carries this field's value, mapped to how many
30
+ * `computedBindings` hops away it is. A field may write `headline`, while the
31
+ * element actually reads `headlineUpper = UPPER(headline)`.
32
+ *
33
+ * Iterated to a fixpoint and guarded on improvement only, so a cyclic
34
+ * computed-binding definition terminates instead of spinning.
35
+ */
36
+ export declare function expandBindingClosure(computedBindings: {
37
+ [k: string]: string;
38
+ } | undefined, roots: string[]): Map<string, number>;
39
+ /** The bindings a field writes. Group/Instruction fields have none. */
40
+ export declare function fieldBindings(field: BluepicField): string[];
41
+ /**
42
+ * Score one element against one field. Returns `null` when nothing matched.
43
+ * The score is the STRONGEST single property match, not a sum — an element
44
+ * whose text is bound to the field shouldn't outrank a sibling merely by also
45
+ * borrowing the same value for its opacity.
46
+ */
47
+ export declare function scoreElement(field: BluepicField, element: Template.Element, hopsByBinding: Map<string, number>): {
48
+ score: number;
49
+ matchedProperties: string[];
50
+ } | null;
51
+ /**
52
+ * The elements a field drives, best first, already cut to the candidates worth
53
+ * drawing a hitbox for.
54
+ */
55
+ export declare function resolveFieldElements(serial: Template.Serial, field: BluepicField): FieldElementMatch[];