arcy.js 0.1.5 → 0.1.6
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/arcy.flow.46ba8a96f99e.js +3 -0
- package/dist/{arcy.legacy.ba7330cb1ce9.js → arcy.legacy.98a48482777b.js} +2 -2
- package/dist/arcy.loader.js +1 -1
- package/dist/{arcy.modern.9d6d23d21370.js → arcy.modern.2c3fe0b458df.js} +2 -2
- package/dist/arcy.picker.777b2d4d1127.js +95 -0
- package/dist/{chunk-GDGE3HLE.js → chunk-7S5DA67F.js} +2 -2
- package/dist/{chunk-NY3NXM2V.js → chunk-AW7DHYSA.js} +1 -1
- package/dist/{chunk-NIHUDSWK.js → chunk-QDCO2MRA.js} +1 -1
- package/dist/design-mode.cjs +1 -1
- package/dist/design-mode.d.ts +9 -1
- package/dist/design-mode.js +2 -2
- package/dist/{flow-GATJGDCR.js → flow-FGTIZOXU.js} +37 -12
- package/dist/index.cjs +465 -87
- package/dist/index.d.ts +21 -6
- package/dist/index.js +15 -11
- package/dist/{picker-GPZ2GHB4.js → picker-6REZLJW3.js} +417 -69
- package/package.json +1 -1
- package/dist/arcy.flow.bc28949c2a72.js +0 -3
- package/dist/arcy.picker.b7e531503cf2.js +0 -59
package/dist/index.cjs
CHANGED
|
@@ -361,7 +361,7 @@ var init_message = __esm({
|
|
|
361
361
|
DESIGN_MODE_PICK = "arcy:design-mode:pick";
|
|
362
362
|
DESIGN_MODE_EXIT = "arcy:design-mode:exit";
|
|
363
363
|
DESIGN_MODE_SCREENSHOT = "arcy:design-mode:screenshot";
|
|
364
|
-
SCREENSHOT_RENDER_BUDGET_MS =
|
|
364
|
+
SCREENSHOT_RENDER_BUDGET_MS = 5e3;
|
|
365
365
|
}
|
|
366
366
|
});
|
|
367
367
|
|
|
@@ -5636,6 +5636,25 @@ function evaluateCurrentPage(node, href) {
|
|
|
5636
5636
|
if (node.matches.length === 0) return true;
|
|
5637
5637
|
return node.matches.some((pattern) => globMatches(pattern, href));
|
|
5638
5638
|
}
|
|
5639
|
+
function parseInstant(value) {
|
|
5640
|
+
if (!DATE_SHAPED.test(value)) return null;
|
|
5641
|
+
const ms = new Date(value).getTime();
|
|
5642
|
+
return Number.isFinite(ms) ? ms : null;
|
|
5643
|
+
}
|
|
5644
|
+
function compareOrdered(live, expected) {
|
|
5645
|
+
if (live.trim() === "" || expected.trim() === "") return null;
|
|
5646
|
+
const liveNumber = Number(live);
|
|
5647
|
+
const expectedNumber = Number(expected);
|
|
5648
|
+
if (Number.isFinite(liveNumber) && Number.isFinite(expectedNumber)) {
|
|
5649
|
+
return liveNumber - expectedNumber;
|
|
5650
|
+
}
|
|
5651
|
+
const liveInstant = parseInstant(live);
|
|
5652
|
+
const expectedInstant = parseInstant(expected);
|
|
5653
|
+
if (liveInstant !== null && expectedInstant !== null) {
|
|
5654
|
+
return liveInstant - expectedInstant;
|
|
5655
|
+
}
|
|
5656
|
+
return null;
|
|
5657
|
+
}
|
|
5639
5658
|
function compareText(operator, live, expected) {
|
|
5640
5659
|
if (live === void 0) return false;
|
|
5641
5660
|
switch (operator) {
|
|
@@ -5657,16 +5676,14 @@ function compareText(operator, live, expected) {
|
|
|
5657
5676
|
return live === "";
|
|
5658
5677
|
case "is_not_empty":
|
|
5659
5678
|
return live !== "";
|
|
5660
|
-
//
|
|
5661
|
-
// condition cannot hold (never throw, never
|
|
5679
|
+
// Ordered compares read both sides as numbers, then as instants; either
|
|
5680
|
+
// side unreadable means the condition cannot hold (never throw, never
|
|
5681
|
+
// coerce NaN into an answer).
|
|
5662
5682
|
case "greater_than":
|
|
5663
5683
|
case "less_than": {
|
|
5664
|
-
const
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
return false;
|
|
5668
|
-
}
|
|
5669
|
-
return operator === "greater_than" ? liveNumber > expectedNumber : liveNumber < expectedNumber;
|
|
5684
|
+
const ordered = compareOrdered(live, expected);
|
|
5685
|
+
if (ordered === null) return false;
|
|
5686
|
+
return operator === "greater_than" ? ordered > 0 : ordered < 0;
|
|
5670
5687
|
}
|
|
5671
5688
|
// The same `*` glob `current_page` uses, applied to a field value.
|
|
5672
5689
|
case "matches_pattern":
|
|
@@ -5735,15 +5752,22 @@ function evaluateElement(node, ctx) {
|
|
|
5735
5752
|
return false;
|
|
5736
5753
|
}
|
|
5737
5754
|
}
|
|
5755
|
+
function isEmptyValue(live) {
|
|
5756
|
+
if (live === void 0 || live === null || live === "") return true;
|
|
5757
|
+
if (Array.isArray(live)) return live.length === 0;
|
|
5758
|
+
if (typeof live === "object") return Object.keys(live).length === 0;
|
|
5759
|
+
return false;
|
|
5760
|
+
}
|
|
5738
5761
|
function evaluateAttribute(node, ctx) {
|
|
5739
5762
|
const live = ctx.getTrait?.(node.codeName);
|
|
5740
5763
|
switch (node.operator) {
|
|
5741
5764
|
case "has_any_value":
|
|
5742
|
-
return live
|
|
5765
|
+
return !isEmptyValue(live);
|
|
5743
5766
|
case "is_empty":
|
|
5744
|
-
return live
|
|
5767
|
+
return isEmptyValue(live);
|
|
5745
5768
|
}
|
|
5746
5769
|
if (live === void 0) return false;
|
|
5770
|
+
if (typeof live === "object") return false;
|
|
5747
5771
|
const liveText = String(live);
|
|
5748
5772
|
const expected = node.value === void 0 ? "" : String(node.value);
|
|
5749
5773
|
return compareText(node.operator, liveText, expected);
|
|
@@ -5796,7 +5820,7 @@ function needsIdleCheck(node) {
|
|
|
5796
5820
|
if (node.type === "group") return node.children.some(needsIdleCheck);
|
|
5797
5821
|
return false;
|
|
5798
5822
|
}
|
|
5799
|
-
var MAX_CONDITION_DEPTH, CONDITION_TYPES;
|
|
5823
|
+
var MAX_CONDITION_DEPTH, CONDITION_TYPES, DATE_SHAPED;
|
|
5800
5824
|
var init_condition = __esm({
|
|
5801
5825
|
"src/flow/condition.ts"() {
|
|
5802
5826
|
init_matcher();
|
|
@@ -5813,6 +5837,7 @@ var init_condition = __esm({
|
|
|
5813
5837
|
"always_true",
|
|
5814
5838
|
"group"
|
|
5815
5839
|
];
|
|
5840
|
+
DATE_SHAPED = /^\d{4}-\d{2}-\d{2}/;
|
|
5816
5841
|
}
|
|
5817
5842
|
});
|
|
5818
5843
|
|
|
@@ -8647,6 +8672,208 @@ function computeCropRect(target, viewportWidth, viewportHeight) {
|
|
|
8647
8672
|
if (bottom - top > height) top += (bottom - top - height) / 2;
|
|
8648
8673
|
return { left, top, width: Math.max(0, width), height: Math.max(0, height) };
|
|
8649
8674
|
}
|
|
8675
|
+
function effectiveOpacity(el, view) {
|
|
8676
|
+
let opacity = 1;
|
|
8677
|
+
let current = el;
|
|
8678
|
+
let depth = 0;
|
|
8679
|
+
while (current !== null && depth < 64) {
|
|
8680
|
+
try {
|
|
8681
|
+
const value = Number.parseFloat(view.getComputedStyle(current).opacity);
|
|
8682
|
+
if (!Number.isNaN(value)) opacity *= value;
|
|
8683
|
+
} catch {
|
|
8684
|
+
break;
|
|
8685
|
+
}
|
|
8686
|
+
if (opacity <= 1e-3) return 0;
|
|
8687
|
+
current = parentOrHost(current);
|
|
8688
|
+
depth++;
|
|
8689
|
+
}
|
|
8690
|
+
return opacity;
|
|
8691
|
+
}
|
|
8692
|
+
function waitUntilSettled(el, view) {
|
|
8693
|
+
return new Promise((resolve) => {
|
|
8694
|
+
const raf = view.requestAnimationFrame;
|
|
8695
|
+
if (typeof raf !== "function") {
|
|
8696
|
+
resolve();
|
|
8697
|
+
return;
|
|
8698
|
+
}
|
|
8699
|
+
const start = Date.now();
|
|
8700
|
+
let previous = null;
|
|
8701
|
+
let stable = 0;
|
|
8702
|
+
const check = () => {
|
|
8703
|
+
try {
|
|
8704
|
+
if (Date.now() - start >= SETTLE_MAX_MS) {
|
|
8705
|
+
resolve();
|
|
8706
|
+
return;
|
|
8707
|
+
}
|
|
8708
|
+
const rect = el.getBoundingClientRect();
|
|
8709
|
+
const now = `${Math.round(rect.left)},${Math.round(rect.top)},${Math.round(rect.width)},${Math.round(rect.height)}`;
|
|
8710
|
+
const opaque = effectiveOpacity(el, view) >= 0.99;
|
|
8711
|
+
stable = opaque && now === previous ? stable + 1 : 0;
|
|
8712
|
+
previous = now;
|
|
8713
|
+
if (stable >= SETTLE_STABLE_FRAMES) {
|
|
8714
|
+
resolve();
|
|
8715
|
+
return;
|
|
8716
|
+
}
|
|
8717
|
+
raf.call(view, check);
|
|
8718
|
+
} catch {
|
|
8719
|
+
resolve();
|
|
8720
|
+
}
|
|
8721
|
+
};
|
|
8722
|
+
raf.call(view, check);
|
|
8723
|
+
});
|
|
8724
|
+
}
|
|
8725
|
+
function findRenderRoot(el, crop, root, view) {
|
|
8726
|
+
let covering = null;
|
|
8727
|
+
let cheapest = null;
|
|
8728
|
+
let current = el;
|
|
8729
|
+
let depth = 0;
|
|
8730
|
+
while (current !== null && current !== root && depth < 64) {
|
|
8731
|
+
if (isTransformed(current, view)) {
|
|
8732
|
+
covering = null;
|
|
8733
|
+
cheapest = null;
|
|
8734
|
+
} else {
|
|
8735
|
+
const rect = current.getBoundingClientRect();
|
|
8736
|
+
if (covering === null && covers(rect, crop)) covering = current;
|
|
8737
|
+
if (cheapest === null && keptRatio(rect, crop) >= MIN_CROP_KEPT) {
|
|
8738
|
+
cheapest = current;
|
|
8739
|
+
}
|
|
8740
|
+
}
|
|
8741
|
+
current = parentOrHost(current);
|
|
8742
|
+
depth++;
|
|
8743
|
+
}
|
|
8744
|
+
if (covering !== null && isAffordable(covering, view)) return covering;
|
|
8745
|
+
return cheapest ?? covering ?? root;
|
|
8746
|
+
}
|
|
8747
|
+
function isAffordable(el, view) {
|
|
8748
|
+
if (countElements(el) > NODE_BUDGET) return false;
|
|
8749
|
+
const rect = el.getBoundingClientRect();
|
|
8750
|
+
const viewport = Math.max(1, view.innerWidth * view.innerHeight);
|
|
8751
|
+
return rect.width * rect.height / viewport <= AREA_BUDGET_SCREENS;
|
|
8752
|
+
}
|
|
8753
|
+
function keptRatio(bounds, crop) {
|
|
8754
|
+
const whole = crop.width * crop.height;
|
|
8755
|
+
if (whole <= 0) return 0;
|
|
8756
|
+
const kept = clipCrop(crop, {
|
|
8757
|
+
left: bounds.left,
|
|
8758
|
+
top: bounds.top,
|
|
8759
|
+
width: bounds.width,
|
|
8760
|
+
height: bounds.height
|
|
8761
|
+
});
|
|
8762
|
+
return kept.width * kept.height / whole;
|
|
8763
|
+
}
|
|
8764
|
+
function countElements(el) {
|
|
8765
|
+
try {
|
|
8766
|
+
return el.getElementsByTagName("*").length;
|
|
8767
|
+
} catch {
|
|
8768
|
+
return Number.MAX_SAFE_INTEGER;
|
|
8769
|
+
}
|
|
8770
|
+
}
|
|
8771
|
+
function clipCrop(crop, bounds) {
|
|
8772
|
+
const left = Math.max(crop.left, bounds.left);
|
|
8773
|
+
const top = Math.max(crop.top, bounds.top);
|
|
8774
|
+
const right = Math.min(crop.left + crop.width, bounds.left + bounds.width);
|
|
8775
|
+
const bottom = Math.min(crop.top + crop.height, bounds.top + bounds.height);
|
|
8776
|
+
return {
|
|
8777
|
+
left,
|
|
8778
|
+
top,
|
|
8779
|
+
width: Math.max(0, right - left),
|
|
8780
|
+
height: Math.max(0, bottom - top)
|
|
8781
|
+
};
|
|
8782
|
+
}
|
|
8783
|
+
function isTransformed(el, view) {
|
|
8784
|
+
try {
|
|
8785
|
+
const style2 = view.getComputedStyle(el);
|
|
8786
|
+
return style2.transform !== "" && style2.transform !== "none" || style2.perspective !== "" && style2.perspective !== "none" || style2.translate !== "" && style2.translate !== "none" || style2.rotate !== "" && style2.rotate !== "none" || style2.scale !== "" && style2.scale !== "none";
|
|
8787
|
+
} catch {
|
|
8788
|
+
return false;
|
|
8789
|
+
}
|
|
8790
|
+
}
|
|
8791
|
+
function covers(rect, crop) {
|
|
8792
|
+
return rect.left <= crop.left + 0.5 && rect.top <= crop.top + 0.5 && rect.right >= crop.left + crop.width - 0.5 && rect.bottom >= crop.top + crop.height - 0.5;
|
|
8793
|
+
}
|
|
8794
|
+
function backgroundUnder(root, crop, doc, view) {
|
|
8795
|
+
const points = [
|
|
8796
|
+
[crop.left + crop.width / 2, crop.top + crop.height / 2],
|
|
8797
|
+
[crop.left + 1, crop.top + 1],
|
|
8798
|
+
[crop.left + crop.width - 1, crop.top + crop.height - 1]
|
|
8799
|
+
];
|
|
8800
|
+
for (const [x, y] of points) {
|
|
8801
|
+
let stack = [];
|
|
8802
|
+
try {
|
|
8803
|
+
stack = doc.elementsFromPoint(x, y) ?? [];
|
|
8804
|
+
} catch {
|
|
8805
|
+
break;
|
|
8806
|
+
}
|
|
8807
|
+
for (const candidate of stack) {
|
|
8808
|
+
if (candidate === root || root.contains(candidate)) continue;
|
|
8809
|
+
if (candidate.hasAttribute(HOST_ATTRIBUTE)) continue;
|
|
8810
|
+
const color = opaqueBackground(candidate, view);
|
|
8811
|
+
if (color) return color;
|
|
8812
|
+
}
|
|
8813
|
+
}
|
|
8814
|
+
let current = root;
|
|
8815
|
+
let depth = 0;
|
|
8816
|
+
while (current !== null && depth < 64) {
|
|
8817
|
+
const color = opaqueBackground(current, view);
|
|
8818
|
+
if (color) return color;
|
|
8819
|
+
current = parentOrHost(current);
|
|
8820
|
+
depth++;
|
|
8821
|
+
}
|
|
8822
|
+
return "#ffffff";
|
|
8823
|
+
}
|
|
8824
|
+
function opaqueBackground(el, view) {
|
|
8825
|
+
try {
|
|
8826
|
+
const color = view.getComputedStyle(el).backgroundColor;
|
|
8827
|
+
if (!color || color === "transparent") return null;
|
|
8828
|
+
if (color.replace(/\s/g, "").startsWith("rgba(0,0,0,0)")) return null;
|
|
8829
|
+
return color;
|
|
8830
|
+
} catch {
|
|
8831
|
+
return null;
|
|
8832
|
+
}
|
|
8833
|
+
}
|
|
8834
|
+
function renderRootStyle(root, rootRect, doc, view, shiftX, shiftY) {
|
|
8835
|
+
const placement = {
|
|
8836
|
+
transform: `translate(${shiftX}px, ${shiftY}px)`,
|
|
8837
|
+
transformOrigin: "top left",
|
|
8838
|
+
margin: "0"
|
|
8839
|
+
};
|
|
8840
|
+
if (root === doc.documentElement) {
|
|
8841
|
+
return {
|
|
8842
|
+
...placement,
|
|
8843
|
+
width: `${view.innerWidth}px`,
|
|
8844
|
+
minHeight: `${view.innerHeight}px`,
|
|
8845
|
+
overflow: "visible"
|
|
8846
|
+
};
|
|
8847
|
+
}
|
|
8848
|
+
return {
|
|
8849
|
+
...placement,
|
|
8850
|
+
position: "static",
|
|
8851
|
+
inset: "auto",
|
|
8852
|
+
float: "none",
|
|
8853
|
+
// The size has to be stated, and this is the line the whole subtree render
|
|
8854
|
+
// stands on. The clone is laid out inside a `foreignObject` the size of
|
|
8855
|
+
// the CROP, and a block element with no width of its own fills its
|
|
8856
|
+
// parent: a 1440px section became 163px wide and the page reflowed into a
|
|
8857
|
+
// column, which is why the hero came back as an empty panel rather than a
|
|
8858
|
+
// misplaced one. `documentElement` was pinned the same way from the start,
|
|
8859
|
+
// which is why nobody met this until the root became something else.
|
|
8860
|
+
// Border-box, because a measured rect is a border box.
|
|
8861
|
+
boxSizing: "border-box",
|
|
8862
|
+
width: `${rootRect.width}px`,
|
|
8863
|
+
minHeight: `${rootRect.height}px`
|
|
8864
|
+
};
|
|
8865
|
+
}
|
|
8866
|
+
function withDeadline(work, ms, view) {
|
|
8867
|
+
return Promise.race([
|
|
8868
|
+
work,
|
|
8869
|
+
new Promise((resolve) => {
|
|
8870
|
+
try {
|
|
8871
|
+
view.setTimeout(() => resolve(null), ms);
|
|
8872
|
+
} catch {
|
|
8873
|
+
}
|
|
8874
|
+
})
|
|
8875
|
+
]);
|
|
8876
|
+
}
|
|
8650
8877
|
function isTextualField(el) {
|
|
8651
8878
|
if (el instanceof HTMLTextAreaElement) return true;
|
|
8652
8879
|
if (!(el instanceof HTMLInputElement)) return false;
|
|
@@ -8656,6 +8883,56 @@ function isTextualField(el) {
|
|
|
8656
8883
|
function isCapturable(node) {
|
|
8657
8884
|
return !(node instanceof Element && node.hasAttribute(HOST_ATTRIBUTE));
|
|
8658
8885
|
}
|
|
8886
|
+
function repairAutoMargins(live, clone, view) {
|
|
8887
|
+
let repaired = 0;
|
|
8888
|
+
const walk = (liveNode, cloneNode2, depth) => {
|
|
8889
|
+
if (depth > 64 || repaired > 2e3) return;
|
|
8890
|
+
const liveKids = liveNode.children;
|
|
8891
|
+
const cloneKids = cloneNode2.children;
|
|
8892
|
+
let l = 0;
|
|
8893
|
+
let c = 0;
|
|
8894
|
+
while (l < liveKids.length && c < cloneKids.length) {
|
|
8895
|
+
const liveKid = liveKids[l];
|
|
8896
|
+
const cloneKid = cloneKids[c];
|
|
8897
|
+
if (liveKid.tagName !== cloneKid.tagName) {
|
|
8898
|
+
l++;
|
|
8899
|
+
continue;
|
|
8900
|
+
}
|
|
8901
|
+
if (applyUsedMargins(liveNode, liveKid, cloneKid, view)) repaired++;
|
|
8902
|
+
walk(liveKid, cloneKid, depth + 1);
|
|
8903
|
+
l++;
|
|
8904
|
+
c++;
|
|
8905
|
+
}
|
|
8906
|
+
};
|
|
8907
|
+
try {
|
|
8908
|
+
walk(live, clone, 0);
|
|
8909
|
+
} catch {
|
|
8910
|
+
}
|
|
8911
|
+
}
|
|
8912
|
+
function applyUsedMargins(liveParent, live, clone, view) {
|
|
8913
|
+
try {
|
|
8914
|
+
const style2 = view.getComputedStyle(live);
|
|
8915
|
+
if (style2.marginLeft !== "0px" || style2.marginRight !== "0px") return false;
|
|
8916
|
+
if (style2.position !== "static" && style2.position !== "relative") return false;
|
|
8917
|
+
if (style2.float !== "none") return false;
|
|
8918
|
+
const parentStyle = view.getComputedStyle(liveParent);
|
|
8919
|
+
if (!BLOCK_DISPLAYS.has(parentStyle.display)) return false;
|
|
8920
|
+
const rect = live.getBoundingClientRect();
|
|
8921
|
+
const parentRect = liveParent.getBoundingClientRect();
|
|
8922
|
+
const contentLeft = parentRect.left + parseFloat(parentStyle.borderLeftWidth || "0") + parseFloat(parentStyle.paddingLeft || "0");
|
|
8923
|
+
const contentRight = parentRect.right - parseFloat(parentStyle.borderRightWidth || "0") - parseFloat(parentStyle.paddingRight || "0");
|
|
8924
|
+
const left = rect.left - contentLeft;
|
|
8925
|
+
const right = contentRight - rect.right;
|
|
8926
|
+
if (left < 0.5 || right < 0.5 || Math.abs(left - right) > 1) return false;
|
|
8927
|
+
const target = clone;
|
|
8928
|
+
if (!target.style) return false;
|
|
8929
|
+
target.style.setProperty("margin-left", `${left}px`);
|
|
8930
|
+
target.style.setProperty("margin-right", `${right}px`);
|
|
8931
|
+
return true;
|
|
8932
|
+
} catch {
|
|
8933
|
+
return false;
|
|
8934
|
+
}
|
|
8935
|
+
}
|
|
8659
8936
|
function maskClone(root) {
|
|
8660
8937
|
const doc = root.ownerDocument ?? root;
|
|
8661
8938
|
const view = doc.defaultView;
|
|
@@ -8688,7 +8965,8 @@ function maskClone(root) {
|
|
|
8688
8965
|
el.value = masked;
|
|
8689
8966
|
}
|
|
8690
8967
|
const placeholder = el.getAttribute("placeholder");
|
|
8691
|
-
if (placeholder)
|
|
8968
|
+
if (placeholder)
|
|
8969
|
+
el.setAttribute("placeholder", maskPiiText(placeholder));
|
|
8692
8970
|
}
|
|
8693
8971
|
}
|
|
8694
8972
|
node = walker.nextNode();
|
|
@@ -8728,6 +9006,8 @@ async function captureElementScreenshot(el) {
|
|
|
8728
9006
|
const doc = el.ownerDocument;
|
|
8729
9007
|
const view = doc.defaultView;
|
|
8730
9008
|
if (!view || !doc.documentElement || !el.isConnected) return null;
|
|
9009
|
+
await waitUntilSettled(el, view);
|
|
9010
|
+
if (!el.isConnected) return null;
|
|
8731
9011
|
const targetRectRaw = el.getBoundingClientRect();
|
|
8732
9012
|
const targetRect = {
|
|
8733
9013
|
left: targetRectRaw.left,
|
|
@@ -8736,31 +9016,54 @@ async function captureElementScreenshot(el) {
|
|
|
8736
9016
|
height: targetRectRaw.height
|
|
8737
9017
|
};
|
|
8738
9018
|
if (targetRect.width <= 0 || targetRect.height <= 0) return null;
|
|
8739
|
-
const
|
|
9019
|
+
const idealCrop = computeCropRect(
|
|
9020
|
+
targetRect,
|
|
9021
|
+
view.innerWidth,
|
|
9022
|
+
view.innerHeight
|
|
9023
|
+
);
|
|
9024
|
+
if (idealCrop.width <= 0 || idealCrop.height <= 0) return null;
|
|
9025
|
+
const renderRoot = findRenderRoot(el, idealCrop, doc.documentElement, view);
|
|
9026
|
+
const rootRect = renderRoot.getBoundingClientRect();
|
|
9027
|
+
const rootBox = {
|
|
9028
|
+
left: rootRect.left,
|
|
9029
|
+
top: rootRect.top,
|
|
9030
|
+
width: rootRect.width,
|
|
9031
|
+
height: rootRect.height
|
|
9032
|
+
};
|
|
9033
|
+
const cropRect = clipCrop(idealCrop, rootBox);
|
|
8740
9034
|
if (cropRect.width <= 0 || cropRect.height <= 0) return null;
|
|
8741
9035
|
const scale = outputScale(view, cropRect);
|
|
8742
|
-
const shiftX = -(cropRect.left
|
|
8743
|
-
const shiftY = -(cropRect.top
|
|
8744
|
-
const canvas = await
|
|
8745
|
-
|
|
8746
|
-
|
|
8747
|
-
|
|
8748
|
-
|
|
8749
|
-
|
|
8750
|
-
|
|
8751
|
-
|
|
8752
|
-
|
|
8753
|
-
|
|
8754
|
-
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
|
|
8758
|
-
|
|
8759
|
-
|
|
8760
|
-
|
|
8761
|
-
|
|
8762
|
-
|
|
8763
|
-
|
|
9036
|
+
const shiftX = -(cropRect.left - rootRect.left);
|
|
9037
|
+
const shiftY = -(cropRect.top - rootRect.top);
|
|
9038
|
+
const canvas = await withDeadline(
|
|
9039
|
+
domToCanvas(renderRoot, {
|
|
9040
|
+
width: cropRect.width,
|
|
9041
|
+
height: cropRect.height,
|
|
9042
|
+
scale,
|
|
9043
|
+
backgroundColor: backgroundUnder(renderRoot, cropRect, doc, view),
|
|
9044
|
+
// Per resource, not per capture. `RENDER_TIMEOUT_MS` is the total,
|
|
9045
|
+
// and it is the race around this call that enforces it.
|
|
9046
|
+
timeout: RESOURCE_TIMEOUT_MS,
|
|
9047
|
+
filter: isCapturable,
|
|
9048
|
+
onCloneNode: (cloned) => {
|
|
9049
|
+
if (cloned instanceof Element) {
|
|
9050
|
+
repairAutoMargins(renderRoot, cloned, view);
|
|
9051
|
+
}
|
|
9052
|
+
maskClone(cloned);
|
|
9053
|
+
},
|
|
9054
|
+
// No `preferredFormat`. Asking for woff2 sounds like asking for the
|
|
9055
|
+
// smaller file and is really a filter: a face whose chosen source is
|
|
9056
|
+
// not woff2 is dropped rather than downgraded, and the text renders in
|
|
9057
|
+
// the browser's serif fallback. On our own hero that turned the button
|
|
9058
|
+
// label into Times (D1258). Let the library take whatever each face
|
|
9059
|
+
// actually offers.
|
|
9060
|
+
font: {},
|
|
9061
|
+
style: renderRootStyle(renderRoot, rootBox, doc, view, shiftX, shiftY)
|
|
9062
|
+
}),
|
|
9063
|
+
RENDER_TIMEOUT_MS,
|
|
9064
|
+
view
|
|
9065
|
+
);
|
|
9066
|
+
if (!canvas) return null;
|
|
8764
9067
|
const ctx = canvas.getContext("2d");
|
|
8765
9068
|
if (!ctx) return null;
|
|
8766
9069
|
drawOutline(ctx, targetRect, cropRect, scale);
|
|
@@ -8775,10 +9078,11 @@ async function captureElementScreenshot(el) {
|
|
|
8775
9078
|
return null;
|
|
8776
9079
|
}
|
|
8777
9080
|
}
|
|
8778
|
-
var CROP_MARGIN_RATIO, CROP_MARGIN_MAX_PX, CROP_MARGIN_MIN_PX, CROP_MAX_WIDTH, CROP_MAX_HEIGHT, OUTPUT_MAX_DPR, OUTPUT_MAX_PIXELS, DATA_URL_SOFT_MAX, RENDER_TIMEOUT_MS, PII_PATTERNS2, MASK2, OUTLINE_COLOR;
|
|
9081
|
+
var CROP_MARGIN_RATIO, CROP_MARGIN_MAX_PX, CROP_MARGIN_MIN_PX, CROP_MAX_WIDTH, CROP_MAX_HEIGHT, OUTPUT_MAX_DPR, OUTPUT_MAX_PIXELS, DATA_URL_SOFT_MAX, RENDER_TIMEOUT_MS, RESOURCE_TIMEOUT_MS, PII_PATTERNS2, MASK2, OUTLINE_COLOR, SETTLE_MAX_MS, SETTLE_STABLE_FRAMES, NODE_BUDGET, AREA_BUDGET_SCREENS, MIN_CROP_KEPT, BLOCK_DISPLAYS;
|
|
8779
9082
|
var init_screenshot = __esm({
|
|
8780
9083
|
"src/picker/screenshot.ts"() {
|
|
8781
9084
|
init_dist();
|
|
9085
|
+
init_shadow();
|
|
8782
9086
|
init_host();
|
|
8783
9087
|
init_message();
|
|
8784
9088
|
CROP_MARGIN_RATIO = 0.25;
|
|
@@ -8790,6 +9094,7 @@ var init_screenshot = __esm({
|
|
|
8790
9094
|
OUTPUT_MAX_PIXELS = 26e5;
|
|
8791
9095
|
DATA_URL_SOFT_MAX = 36e5;
|
|
8792
9096
|
RENDER_TIMEOUT_MS = SCREENSHOT_RENDER_BUDGET_MS;
|
|
9097
|
+
RESOURCE_TIMEOUT_MS = 1500;
|
|
8793
9098
|
PII_PATTERNS2 = [
|
|
8794
9099
|
/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
|
|
8795
9100
|
/\b\d{3}-\d{2}-\d{4}\b/g,
|
|
@@ -8797,6 +9102,12 @@ var init_screenshot = __esm({
|
|
|
8797
9102
|
];
|
|
8798
9103
|
MASK2 = "[masked]";
|
|
8799
9104
|
OUTLINE_COLOR = "#f04e23";
|
|
9105
|
+
SETTLE_MAX_MS = 900;
|
|
9106
|
+
SETTLE_STABLE_FRAMES = 2;
|
|
9107
|
+
NODE_BUDGET = 800;
|
|
9108
|
+
AREA_BUDGET_SCREENS = 3;
|
|
9109
|
+
MIN_CROP_KEPT = 0.4;
|
|
9110
|
+
BLOCK_DISPLAYS = /* @__PURE__ */ new Set(["block", "flow-root", "list-item", "table-cell"]);
|
|
8800
9111
|
}
|
|
8801
9112
|
});
|
|
8802
9113
|
|
|
@@ -8808,10 +9119,6 @@ function applySurvivalStyle(el, pointerEvents, offsets = []) {
|
|
|
8808
9119
|
el.style.setProperty(property, value, "important");
|
|
8809
9120
|
}
|
|
8810
9121
|
}
|
|
8811
|
-
function statusLabel(target) {
|
|
8812
|
-
const { text, tag } = target.core;
|
|
8813
|
-
return text ? `Sent "${text}" to ARCY` : `Sent <${tag}> to ARCY`;
|
|
8814
|
-
}
|
|
8815
9122
|
function createPickerOverlay(context) {
|
|
8816
9123
|
let shell = createShell({ id: PICKER_HOST_ID });
|
|
8817
9124
|
if (!shell) return null;
|
|
@@ -8834,28 +9141,36 @@ function createPickerOverlay(context) {
|
|
|
8834
9141
|
for (const [property, value] of BANNER_STYLE) {
|
|
8835
9142
|
banner.style.setProperty(property, value);
|
|
8836
9143
|
}
|
|
8837
|
-
const
|
|
8838
|
-
|
|
8839
|
-
|
|
9144
|
+
const mark = doc.createElement("span");
|
|
9145
|
+
mark.className = `${BANNER_CLASS}__mark`;
|
|
9146
|
+
const brand = doc.createElement("span");
|
|
9147
|
+
brand.className = `${BANNER_CLASS}__brand`;
|
|
9148
|
+
brand.textContent = "ARCY";
|
|
9149
|
+
const label = doc.createElement("span");
|
|
9150
|
+
label.className = `${BANNER_CLASS}__label`;
|
|
9151
|
+
label.textContent = "Element picker";
|
|
9152
|
+
mark.append(brand, label);
|
|
9153
|
+
const modes = doc.createElement("span");
|
|
9154
|
+
modes.className = `${BANNER_CLASS}__modes`;
|
|
9155
|
+
applySurvivalStyle(modes, "auto");
|
|
9156
|
+
modes.style.setProperty("position", "relative", "important");
|
|
8840
9157
|
const pickButton = doc.createElement("button");
|
|
8841
9158
|
pickButton.type = "button";
|
|
8842
9159
|
pickButton.textContent = "Pick";
|
|
8843
9160
|
const browseButton = doc.createElement("button");
|
|
8844
9161
|
browseButton.type = "button";
|
|
8845
9162
|
browseButton.textContent = "Browse";
|
|
8846
|
-
|
|
8847
|
-
status.className = `${BANNER_CLASS}__status`;
|
|
8848
|
-
status.textContent = "Click the element you want this step to target.";
|
|
9163
|
+
modes.append(pickButton, browseButton);
|
|
8849
9164
|
const exitButton = doc.createElement("button");
|
|
8850
9165
|
exitButton.type = "button";
|
|
9166
|
+
exitButton.className = `${BANNER_CLASS}__exit`;
|
|
8851
9167
|
exitButton.textContent = "Exit";
|
|
8852
|
-
banner.append(
|
|
9168
|
+
banner.append(mark, modes, exitButton);
|
|
8853
9169
|
shell.root.append(highlight, banner);
|
|
8854
9170
|
function setMode(next) {
|
|
8855
9171
|
mode = next;
|
|
8856
9172
|
pickButton.classList.toggle("is-active", next === "pick");
|
|
8857
9173
|
browseButton.classList.toggle("is-active", next === "browse");
|
|
8858
|
-
status.textContent = next === "pick" ? "Click the element you want this step to target." : "Browsing. Clicks work normally; switch back to pick.";
|
|
8859
9174
|
if (next === "browse") hideHighlight();
|
|
8860
9175
|
}
|
|
8861
9176
|
function hideHighlight() {
|
|
@@ -8938,7 +9253,6 @@ function createPickerOverlay(context) {
|
|
|
8938
9253
|
const ranked = rankSelectors(el);
|
|
8939
9254
|
fingerprint.selector = ranked.autoSelector;
|
|
8940
9255
|
fingerprint.selectorCandidates = ranked.candidates;
|
|
8941
|
-
status.textContent = statusLabel(fingerprint);
|
|
8942
9256
|
hovered = el;
|
|
8943
9257
|
moveHighlight(el);
|
|
8944
9258
|
let matchCount = 0;
|
|
@@ -8949,17 +9263,29 @@ function createPickerOverlay(context) {
|
|
|
8949
9263
|
context.onPick(fingerprint, matchCount);
|
|
8950
9264
|
if (context.screenshotsEnabled === false) return;
|
|
8951
9265
|
const token = ++screenshotToken;
|
|
8952
|
-
|
|
8953
|
-
if (
|
|
8954
|
-
|
|
8955
|
-
|
|
8956
|
-
|
|
8957
|
-
|
|
9266
|
+
afterNextPaint(() => {
|
|
9267
|
+
if (destroyed || token !== screenshotToken) return;
|
|
9268
|
+
void captureElementScreenshot(el).then((screenshot) => {
|
|
9269
|
+
if (!screenshot || destroyed || token !== screenshotToken) return;
|
|
9270
|
+
try {
|
|
9271
|
+
context.onScreenshot?.(screenshot);
|
|
9272
|
+
} catch {
|
|
9273
|
+
}
|
|
9274
|
+
});
|
|
8958
9275
|
});
|
|
8959
9276
|
} catch (error) {
|
|
8960
9277
|
warn(`The element picker could not read that element. ${String(error)}`);
|
|
8961
9278
|
}
|
|
8962
9279
|
};
|
|
9280
|
+
function afterNextPaint(fn) {
|
|
9281
|
+
const view = doc.defaultView;
|
|
9282
|
+
const raf = view?.requestAnimationFrame;
|
|
9283
|
+
if (typeof raf !== "function") {
|
|
9284
|
+
setTimeout(fn, 0);
|
|
9285
|
+
return;
|
|
9286
|
+
}
|
|
9287
|
+
raf.call(view, () => raf.call(view, fn));
|
|
9288
|
+
}
|
|
8963
9289
|
function isEditable(target) {
|
|
8964
9290
|
if (!(target instanceof Element)) return false;
|
|
8965
9291
|
const tag = target.tagName;
|
|
@@ -9025,7 +9351,7 @@ function createPickerOverlay(context) {
|
|
|
9025
9351
|
setMode("pick");
|
|
9026
9352
|
return { destroy };
|
|
9027
9353
|
}
|
|
9028
|
-
var PICKER_HOST_ID, BANNER_CLASS, HIGHLIGHT_CLASS, PICKER_CSS, HIGHLIGHT_STYLE, BANNER_OFFSET, BANNER_STYLE;
|
|
9354
|
+
var PICKER_HOST_ID, BANNER_CLASS, HIGHLIGHT_CLASS, INK, SURFACE, HAIRLINE, TEXT, MUTED, ACCENT, ON_ACCENT, PICKER_CSS, HIGHLIGHT_STYLE, BANNER_OFFSET, BANNER_STYLE;
|
|
9029
9355
|
var init_overlay = __esm({
|
|
9030
9356
|
"src/picker/overlay.ts"() {
|
|
9031
9357
|
init_capture();
|
|
@@ -9039,54 +9365,102 @@ var init_overlay = __esm({
|
|
|
9039
9365
|
PICKER_HOST_ID = "arcy-picker";
|
|
9040
9366
|
BANNER_CLASS = "arcy-picker-banner";
|
|
9041
9367
|
HIGHLIGHT_CLASS = "arcy-picker-highlight";
|
|
9368
|
+
INK = "#17131A";
|
|
9369
|
+
SURFACE = "#201B23";
|
|
9370
|
+
HAIRLINE = "#332B33";
|
|
9371
|
+
TEXT = "#F3EEE8";
|
|
9372
|
+
MUTED = "#9E959A";
|
|
9373
|
+
ACCENT = "#FF5A3C";
|
|
9374
|
+
ON_ACCENT = "#17131A";
|
|
9042
9375
|
PICKER_CSS = `
|
|
9043
9376
|
.${BANNER_CLASS} {
|
|
9044
9377
|
display: flex;
|
|
9045
9378
|
align-items: center;
|
|
9046
|
-
gap:
|
|
9047
|
-
padding:
|
|
9379
|
+
gap: 12px;
|
|
9380
|
+
padding: 7px 8px 7px 16px;
|
|
9048
9381
|
border-radius: 999px;
|
|
9049
|
-
background:
|
|
9050
|
-
|
|
9051
|
-
|
|
9052
|
-
|
|
9382
|
+
background: ${INK};
|
|
9383
|
+
border: 1px solid ${HAIRLINE};
|
|
9384
|
+
color: ${TEXT};
|
|
9385
|
+
font: 400 14px/1.2 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
9386
|
+
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.38);
|
|
9053
9387
|
white-space: nowrap;
|
|
9054
9388
|
}
|
|
9055
|
-
.${BANNER_CLASS}
|
|
9389
|
+
.${BANNER_CLASS}__mark {
|
|
9390
|
+
display: flex;
|
|
9391
|
+
align-items: baseline;
|
|
9392
|
+
gap: 7px;
|
|
9393
|
+
}
|
|
9394
|
+
.${BANNER_CLASS}__brand {
|
|
9056
9395
|
font-weight: 600;
|
|
9057
|
-
|
|
9396
|
+
letter-spacing: 0.02em;
|
|
9058
9397
|
}
|
|
9059
|
-
|
|
9060
|
-
|
|
9061
|
-
|
|
9062
|
-
|
|
9063
|
-
|
|
9398
|
+
/* The label role from DESIGN.md: 600, 12px, uppercase, tracked wide. */
|
|
9399
|
+
.${BANNER_CLASS}__label {
|
|
9400
|
+
font-size: 12px;
|
|
9401
|
+
font-weight: 600;
|
|
9402
|
+
letter-spacing: 0.14em;
|
|
9403
|
+
text-transform: uppercase;
|
|
9404
|
+
color: ${MUTED};
|
|
9405
|
+
}
|
|
9406
|
+
/* Pick and Browse are one control with two states, not two buttons that
|
|
9407
|
+
* happen to disagree. The old bar drew both as outlines and coloured whichever
|
|
9408
|
+
* was on, which reads as two things you can press rather than as a switch. */
|
|
9409
|
+
.${BANNER_CLASS}__modes {
|
|
9410
|
+
display: flex;
|
|
9411
|
+
align-items: center;
|
|
9412
|
+
gap: 2px;
|
|
9413
|
+
padding: 3px;
|
|
9414
|
+
border-radius: 999px;
|
|
9415
|
+
background: ${SURFACE};
|
|
9064
9416
|
}
|
|
9065
9417
|
.${BANNER_CLASS} button {
|
|
9066
9418
|
appearance: none;
|
|
9067
|
-
border: 1px solid
|
|
9419
|
+
border: 1px solid transparent;
|
|
9068
9420
|
border-radius: 999px;
|
|
9069
9421
|
background: transparent;
|
|
9070
|
-
color:
|
|
9422
|
+
color: ${MUTED};
|
|
9071
9423
|
font: inherit;
|
|
9072
|
-
|
|
9424
|
+
font-weight: 500;
|
|
9425
|
+
padding: 5px 14px;
|
|
9073
9426
|
cursor: pointer;
|
|
9427
|
+
transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
|
|
9074
9428
|
}
|
|
9075
9429
|
.${BANNER_CLASS} button:hover {
|
|
9076
|
-
|
|
9430
|
+
color: ${TEXT};
|
|
9431
|
+
}
|
|
9432
|
+
.${BANNER_CLASS} button:focus-visible {
|
|
9433
|
+
outline: 2px solid ${ACCENT};
|
|
9434
|
+
outline-offset: 2px;
|
|
9077
9435
|
}
|
|
9078
|
-
|
|
9079
|
-
|
|
9080
|
-
|
|
9436
|
+
/* The one filled control on the bar (DESIGN.md, one accent fill per surface). */
|
|
9437
|
+
.${BANNER_CLASS}__modes button.is-active {
|
|
9438
|
+
background: ${ACCENT};
|
|
9439
|
+
color: ${ON_ACCENT};
|
|
9440
|
+
font-weight: 600;
|
|
9441
|
+
}
|
|
9442
|
+
.${BANNER_CLASS}__modes button.is-active:hover {
|
|
9443
|
+
color: ${ON_ACCENT};
|
|
9444
|
+
}
|
|
9445
|
+
.${BANNER_CLASS}__exit {
|
|
9446
|
+
border-color: ${HAIRLINE} !important;
|
|
9447
|
+
}
|
|
9448
|
+
.${BANNER_CLASS}__exit:hover {
|
|
9449
|
+
background: ${SURFACE};
|
|
9081
9450
|
}
|
|
9082
9451
|
.${HIGHLIGHT_CLASS} {
|
|
9083
|
-
border-radius:
|
|
9452
|
+
border-radius: 4px;
|
|
9084
9453
|
}
|
|
9085
9454
|
`;
|
|
9086
9455
|
HIGHLIGHT_STYLE = [
|
|
9087
9456
|
["box-sizing", "border-box"],
|
|
9088
|
-
[
|
|
9089
|
-
|
|
9457
|
+
[
|
|
9458
|
+
"border",
|
|
9459
|
+
// ADR 0171's accent border rule: an edge, not a fill. The tint behind it
|
|
9460
|
+
// is what keeps a thin outline readable on a busy page.
|
|
9461
|
+
`2px solid ${ACCENT}`
|
|
9462
|
+
],
|
|
9463
|
+
["background", "rgba(255, 90, 60, 0.1)"]
|
|
9090
9464
|
];
|
|
9091
9465
|
BANNER_OFFSET = [
|
|
9092
9466
|
["left", "50%"],
|
|
@@ -13502,15 +13876,19 @@ function createArcyInternals(internalOptions = {}) {
|
|
|
13502
13876
|
token,
|
|
13503
13877
|
sessionToken,
|
|
13504
13878
|
sessionId: telemetry?.sessionId(),
|
|
13505
|
-
// Slice 18.1's trigger conditions read
|
|
13506
|
-
//
|
|
13507
|
-
//
|
|
13508
|
-
//
|
|
13879
|
+
// Slice 18.1's trigger conditions read attributes set via identify().
|
|
13880
|
+
// Every present, non-null value is reported as it is and judged here
|
|
13881
|
+
// not at all (ADR 0205). Filtering lists out used to report a written
|
|
13882
|
+
// key as absent, which made `has_any_value` false for a list the
|
|
13883
|
+
// customer had sent; what a value *means* is `condition.ts`'s call and
|
|
13884
|
+
// is made in one place there.
|
|
13885
|
+
//
|
|
13886
|
+
// Nothing becomes comparable by arriving: `condition.ts` refuses every
|
|
13887
|
+
// comparison on an object or an array (ADR 0198's opaque rule), so no
|
|
13888
|
+
// value here can become `is "[object Object]"`.
|
|
13509
13889
|
getTrait: (codeName) => {
|
|
13510
13890
|
const value = identity.attributes[codeName];
|
|
13511
|
-
if (value === void 0 || value === null
|
|
13512
|
-
return void 0;
|
|
13513
|
-
}
|
|
13891
|
+
if (value === void 0 || value === null) return void 0;
|
|
13514
13892
|
return value;
|
|
13515
13893
|
},
|
|
13516
13894
|
// D1299: absent entirely (rather than answering false) when storage
|