@designfever/web-review-kit 0.8.1 → 0.8.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/README.md +4 -6
- package/dist/{chunk-4ZP7B7R6.js → chunk-FWN7RQCW.js} +190 -92
- package/dist/chunk-FWN7RQCW.js.map +1 -0
- package/dist/{chunk-UNDQZ4Y2.js → chunk-XP2IACXL.js} +80 -46
- package/dist/chunk-XP2IACXL.js.map +1 -0
- package/dist/index.cjs +268 -143
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -7
- package/dist/index.js.map +1 -1
- package/dist/react-shell.cjs +2627 -1736
- package/dist/react-shell.cjs.map +1 -1
- package/dist/react-shell.d.cts +2 -1
- package/dist/react-shell.d.ts +2 -1
- package/dist/react-shell.js +2247 -1447
- package/dist/react-shell.js.map +1 -1
- package/dist/{types-BM8E7BFV.d.cts → types-DKDtZjTH.d.cts} +8 -0
- package/dist/{types-BM8E7BFV.d.ts → types-DKDtZjTH.d.ts} +8 -0
- package/dist/vite.cjs +13 -5
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.d.cts +2 -0
- package/dist/vite.d.ts +2 -0
- package/dist/vite.js +14 -6
- package/dist/vite.js.map +1 -1
- package/docs/README.md +4 -2
- package/docs/architecture.md +33 -7
- package/docs/figma-overlay.md +24 -0
- package/docs/installation.md +3 -5
- package/docs/release-notes-0.8.2.md +50 -0
- package/docs/release-notes-0.8.3.md +48 -0
- package/docs/testing.md +12 -4
- package/package.json +1 -1
- package/dist/chunk-4ZP7B7R6.js.map +0 -1
- package/dist/chunk-UNDQZ4Y2.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -248,7 +248,7 @@ function normalizeItemForSupabaseCreate(item, source, options) {
|
|
|
248
248
|
const normalizedStatus = normalizeReviewItemStatus(item.status);
|
|
249
249
|
const routeKey = item.routeKey || item.normalizedPath || "/";
|
|
250
250
|
const viewport = item.viewport ?? { width: 390, height: 720 };
|
|
251
|
-
|
|
251
|
+
return {
|
|
252
252
|
...item,
|
|
253
253
|
id,
|
|
254
254
|
reviewNumber: void 0,
|
|
@@ -269,10 +269,6 @@ function normalizeItemForSupabaseCreate(item, source, options) {
|
|
|
269
269
|
createdAt: now,
|
|
270
270
|
updatedAt: now
|
|
271
271
|
};
|
|
272
|
-
return {
|
|
273
|
-
...nextItem,
|
|
274
|
-
externalIssueUrl: nextItem.externalIssueUrl ?? buildSupabaseReviewUrl(nextItem, source, options)
|
|
275
|
-
};
|
|
276
272
|
}
|
|
277
273
|
async function createItemWithRpc(item, source, options) {
|
|
278
274
|
const rpcName = options.createRpc ?? DEFAULT_SUPABASE_CREATE_REVIEW_ITEM_RPC;
|
|
@@ -625,38 +621,69 @@ async function createClientRenderedFigmaAsset(figmaUrl, token, options, fetchOpt
|
|
|
625
621
|
const requestFetch = fetchOption ?? globalThis.fetch;
|
|
626
622
|
if (!requestFetch) throw new Error("Figma client rendering requires fetch.");
|
|
627
623
|
const renderFormat = options.renderFormat ?? "png";
|
|
628
|
-
const
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
624
|
+
const timeoutMs = options.timeoutMs ?? 1e4;
|
|
625
|
+
const imageUrl = await withStageTimeout(
|
|
626
|
+
async (signal) => {
|
|
627
|
+
const response = await requestFetch(
|
|
628
|
+
createReviewFigmaImageApiUrl({
|
|
629
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
630
|
+
fileKey: ref.fileKey,
|
|
631
|
+
nodeId: ref.nodeId,
|
|
632
|
+
format: renderFormat,
|
|
633
|
+
scale: options.renderScale,
|
|
634
|
+
useAbsoluteBounds: options.useAbsoluteBounds
|
|
635
|
+
}),
|
|
636
|
+
{
|
|
637
|
+
headers: {
|
|
638
|
+
"X-Figma-Token": token
|
|
639
|
+
},
|
|
640
|
+
signal
|
|
641
|
+
}
|
|
642
|
+
);
|
|
643
|
+
const body = await response.json().catch(() => null);
|
|
644
|
+
if (!response.ok) {
|
|
645
|
+
throw new Error(
|
|
646
|
+
body?.err || `Figma image render failed with ${response.status}`
|
|
647
|
+
);
|
|
640
648
|
}
|
|
641
|
-
|
|
649
|
+
const nextImageUrl = body?.images?.[ref.nodeId];
|
|
650
|
+
if (!nextImageUrl) {
|
|
651
|
+
throw new Error(
|
|
652
|
+
`Figma image render returned no URL for ${ref.nodeId}.`
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
return nextImageUrl;
|
|
656
|
+
},
|
|
657
|
+
timeoutMs,
|
|
658
|
+
"Figma API request timed out."
|
|
642
659
|
);
|
|
643
|
-
const
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
660
|
+
const originalBlob = await withStageTimeout(
|
|
661
|
+
async (signal) => {
|
|
662
|
+
const imageResponse = await requestFetch(imageUrl, { signal });
|
|
663
|
+
if (!imageResponse.ok) {
|
|
664
|
+
throw new Error(
|
|
665
|
+
`Figma image download failed with ${imageResponse.status}`
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
return imageResponse.blob();
|
|
669
|
+
},
|
|
670
|
+
timeoutMs,
|
|
671
|
+
"Figma image download timed out."
|
|
672
|
+
);
|
|
673
|
+
return withStageTimeout(
|
|
674
|
+
() => createProcessedFigmaAsset(originalBlob, renderFormat, options),
|
|
675
|
+
timeoutMs,
|
|
676
|
+
"Figma image processing timed out."
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
async function createProcessedFigmaAsset(originalBlob, renderFormat, options) {
|
|
654
680
|
const originalMimeType = normalizeClientImageMimeType(originalBlob.type) ?? getReviewFigmaImageMimeType(renderFormat === "jpg" ? "jpg" : "png");
|
|
655
681
|
const originalFormat = getReviewFigmaImageFormatFromMimeType(originalMimeType) ?? (renderFormat === "jpg" ? "jpg" : "png");
|
|
656
|
-
const
|
|
682
|
+
const image = await loadImageBlob(originalBlob);
|
|
683
|
+
const dimensions = readImageDimensions(image);
|
|
657
684
|
const shouldConvertToWebp = options.convertToWebp ?? true;
|
|
658
|
-
const convertedBlob = shouldConvertToWebp ? await
|
|
659
|
-
|
|
685
|
+
const convertedBlob = shouldConvertToWebp ? await convertImageToWebp(
|
|
686
|
+
image,
|
|
660
687
|
options.webpQuality ?? 0.9,
|
|
661
688
|
dimensions
|
|
662
689
|
).catch(() => null) : null;
|
|
@@ -678,22 +705,17 @@ function createReviewFigmaClientRenderedAsset({
|
|
|
678
705
|
token,
|
|
679
706
|
...options
|
|
680
707
|
}) {
|
|
681
|
-
return
|
|
682
|
-
createClientRenderedFigmaAsset(figmaUrl, token, options, fetchOption),
|
|
683
|
-
options.timeoutMs ?? 1e4
|
|
684
|
-
);
|
|
708
|
+
return createClientRenderedFigmaAsset(figmaUrl, token, options, fetchOption);
|
|
685
709
|
}
|
|
686
|
-
|
|
687
|
-
const image = await loadImageBlob(blob);
|
|
710
|
+
function readImageDimensions(image) {
|
|
688
711
|
return {
|
|
689
712
|
width: image.naturalWidth || image.width,
|
|
690
713
|
height: image.naturalHeight || image.height
|
|
691
714
|
};
|
|
692
715
|
}
|
|
693
|
-
async function
|
|
716
|
+
async function convertImageToWebp(image, quality, dimensions) {
|
|
694
717
|
if (typeof document === "undefined") return null;
|
|
695
718
|
if (!dimensions.width || !dimensions.height) return null;
|
|
696
|
-
const image = await loadImageBlob(blob);
|
|
697
719
|
const canvas = document.createElement("canvas");
|
|
698
720
|
canvas.width = dimensions.width;
|
|
699
721
|
canvas.height = dimensions.height;
|
|
@@ -745,18 +767,26 @@ function normalizeClientImageMimeType(value) {
|
|
|
745
767
|
}
|
|
746
768
|
return null;
|
|
747
769
|
}
|
|
748
|
-
async function
|
|
770
|
+
async function withStageTimeout(run, timeoutMs, message) {
|
|
771
|
+
const controller = new AbortController();
|
|
772
|
+
let didTimeout = false;
|
|
749
773
|
let timeoutId;
|
|
750
774
|
try {
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
775
|
+
try {
|
|
776
|
+
return await Promise.race([
|
|
777
|
+
run(controller.signal),
|
|
778
|
+
new Promise((_, reject) => {
|
|
779
|
+
timeoutId = setTimeout(() => {
|
|
780
|
+
didTimeout = true;
|
|
781
|
+
controller.abort();
|
|
782
|
+
reject(new Error(message));
|
|
783
|
+
}, timeoutMs);
|
|
784
|
+
})
|
|
785
|
+
]);
|
|
786
|
+
} catch (error) {
|
|
787
|
+
if (didTimeout) throw new Error(message);
|
|
788
|
+
throw error;
|
|
789
|
+
}
|
|
760
790
|
} finally {
|
|
761
791
|
if (timeoutId) clearTimeout(timeoutId);
|
|
762
792
|
}
|
|
@@ -962,7 +992,7 @@ async function uploadFigmaAsset({
|
|
|
962
992
|
const requestFetch = fetchOption ?? globalThis.fetch;
|
|
963
993
|
if (!requestFetch) throw new Error("Figma asset upload requires fetch.");
|
|
964
994
|
const { blob, mimeType } = decodeAssetDataUrl(asset);
|
|
965
|
-
const response = await
|
|
995
|
+
const response = await withTimeout(
|
|
966
996
|
requestFetch(
|
|
967
997
|
createAssetUploadUrl(endpoint, {
|
|
968
998
|
imageFormat: asset.imageFormat,
|
|
@@ -1091,7 +1121,7 @@ function decodeAssetDataUrl(asset) {
|
|
|
1091
1121
|
mimeType
|
|
1092
1122
|
};
|
|
1093
1123
|
}
|
|
1094
|
-
async function
|
|
1124
|
+
async function withTimeout(promise, timeoutMs, message) {
|
|
1095
1125
|
let timeoutId;
|
|
1096
1126
|
try {
|
|
1097
1127
|
return await Promise.race([
|
|
@@ -1945,6 +1975,15 @@ function isMeaningfulClass(value) {
|
|
|
1945
1975
|
) && !normalized.startsWith("mq-");
|
|
1946
1976
|
}
|
|
1947
1977
|
|
|
1978
|
+
// src/core/error.ts
|
|
1979
|
+
function getErrorMessage(error, fallback) {
|
|
1980
|
+
if (error instanceof Error) return error.message;
|
|
1981
|
+
if (error && typeof error === "object" && "message" in error && typeof error.message === "string") {
|
|
1982
|
+
return error.message;
|
|
1983
|
+
}
|
|
1984
|
+
return fallback;
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1948
1987
|
// src/core/id.ts
|
|
1949
1988
|
function createId() {
|
|
1950
1989
|
if ("randomUUID" in crypto) return crypto.randomUUID();
|
|
@@ -1980,6 +2019,13 @@ var HOTKEY_KEY_ALIASES = {
|
|
|
1980
2019
|
n: ["\u315C"],
|
|
1981
2020
|
m: ["\u3161"]
|
|
1982
2021
|
};
|
|
2022
|
+
function isEditableEventTarget(event) {
|
|
2023
|
+
const path = event.composedPath?.() ?? [];
|
|
2024
|
+
const element = path[0] ?? event.target;
|
|
2025
|
+
if (!element || typeof element.tagName !== "string") return false;
|
|
2026
|
+
const tag = element.tagName;
|
|
2027
|
+
return tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || element.isContentEditable === true;
|
|
2028
|
+
}
|
|
1983
2029
|
function isHotkey(event, hotkey) {
|
|
1984
2030
|
const parts = hotkey.split("+").map((part) => part.trim().toLowerCase()).filter(Boolean);
|
|
1985
2031
|
const key = parts.find(
|
|
@@ -2426,6 +2472,7 @@ var DraftPreviewController = class {
|
|
|
2426
2472
|
};
|
|
2427
2473
|
element.style.visibility = "hidden";
|
|
2428
2474
|
}
|
|
2475
|
+
syncDraftPreviewCloneRect(this.snapshot.clone, element);
|
|
2429
2476
|
const metrics = this.config.getMetrics(draft);
|
|
2430
2477
|
const translate = `translate(${toCssNumber(metrics.cssX)}px, ${toCssNumber(
|
|
2431
2478
|
metrics.cssY
|
|
@@ -2450,16 +2497,11 @@ var DraftPreviewController = class {
|
|
|
2450
2497
|
}
|
|
2451
2498
|
};
|
|
2452
2499
|
function positionDraftPreviewClone(clone, element, computedStyle) {
|
|
2453
|
-
const rect = element.getBoundingClientRect();
|
|
2454
2500
|
clone.setAttribute("data-dfwr-adjust-preview", "true");
|
|
2455
2501
|
clone.setAttribute("aria-hidden", "true");
|
|
2456
2502
|
clone.style.position = "fixed";
|
|
2457
|
-
clone.style.left = `${toCssNumber(rect.left)}px`;
|
|
2458
|
-
clone.style.top = `${toCssNumber(rect.top)}px`;
|
|
2459
2503
|
clone.style.right = "auto";
|
|
2460
2504
|
clone.style.bottom = "auto";
|
|
2461
|
-
clone.style.width = `${toCssNumber(rect.width)}px`;
|
|
2462
|
-
clone.style.height = `${toCssNumber(rect.height)}px`;
|
|
2463
2505
|
clone.style.maxWidth = "none";
|
|
2464
2506
|
clone.style.maxHeight = "none";
|
|
2465
2507
|
clone.style.margin = "0";
|
|
@@ -2471,6 +2513,14 @@ function positionDraftPreviewClone(clone, element, computedStyle) {
|
|
|
2471
2513
|
clone.style.willChange = "transform";
|
|
2472
2514
|
clone.style.transformOrigin = "top left";
|
|
2473
2515
|
clone.style.transform = "none";
|
|
2516
|
+
syncDraftPreviewCloneRect(clone, element);
|
|
2517
|
+
}
|
|
2518
|
+
function syncDraftPreviewCloneRect(clone, element) {
|
|
2519
|
+
const rect = element.getBoundingClientRect();
|
|
2520
|
+
clone.style.left = `${toCssNumber(rect.left)}px`;
|
|
2521
|
+
clone.style.top = `${toCssNumber(rect.top)}px`;
|
|
2522
|
+
clone.style.width = `${toCssNumber(rect.width)}px`;
|
|
2523
|
+
clone.style.height = `${toCssNumber(rect.height)}px`;
|
|
2474
2524
|
}
|
|
2475
2525
|
function getDraftPreviewDisplay(display) {
|
|
2476
2526
|
if (display === "inline" || display === "contents") return "inline-block";
|
|
@@ -2546,11 +2596,11 @@ function createStyleElement() {
|
|
|
2546
2596
|
--df-review-color-text: #f7f7f2;
|
|
2547
2597
|
--df-review-color-text-muted: rgba(247, 247, 242, 0.62);
|
|
2548
2598
|
--df-review-color-text-subtle: rgba(247, 247, 242, 0.46);
|
|
2549
|
-
--df-review-color-accent: #
|
|
2599
|
+
--df-review-color-accent: #7cc7ff;
|
|
2550
2600
|
--df-review-color-accent-contrast: #171b1e;
|
|
2551
|
-
--df-review-color-accent-soft: rgba(
|
|
2552
|
-
--df-review-color-accent-ring: rgba(
|
|
2553
|
-
--df-review-color-area: #
|
|
2601
|
+
--df-review-color-accent-soft: rgba(124, 199, 255, 0.16);
|
|
2602
|
+
--df-review-color-accent-ring: rgba(124, 199, 255, 0.6);
|
|
2603
|
+
--df-review-color-area: #7cc7ff;
|
|
2554
2604
|
--df-review-color-error: #ffb7a7;
|
|
2555
2605
|
--df-review-shadow-panel: 0 18px 48px rgba(0, 0, 0, 0.34);
|
|
2556
2606
|
--df-review-shadow-popover: 0 16px 38px rgba(0, 0, 0, 0.32);
|
|
@@ -2736,9 +2786,9 @@ function createStyleElement() {
|
|
|
2736
2786
|
.dfwr-selection-highlight {
|
|
2737
2787
|
position: fixed;
|
|
2738
2788
|
z-index: 1;
|
|
2739
|
-
border: 2px solid #
|
|
2789
|
+
border: 2px solid #7cc7ff;
|
|
2740
2790
|
border-radius: var(--df-review-radius-xs);
|
|
2741
|
-
background: rgba(
|
|
2791
|
+
background: rgba(124, 199, 255, 0.08);
|
|
2742
2792
|
box-shadow:
|
|
2743
2793
|
0 0 0 1px rgba(31, 36, 40, 0.72),
|
|
2744
2794
|
0 0 0 9999px rgba(0, 0, 0, 0.12),
|
|
@@ -2747,8 +2797,8 @@ function createStyleElement() {
|
|
|
2747
2797
|
}
|
|
2748
2798
|
|
|
2749
2799
|
.dfwr-selection-highlight.is-draft {
|
|
2750
|
-
border-color: #
|
|
2751
|
-
background: rgba(
|
|
2800
|
+
border-color: #7cc7ff;
|
|
2801
|
+
background: rgba(124, 199, 255, 0.1);
|
|
2752
2802
|
box-shadow:
|
|
2753
2803
|
0 0 0 1px rgba(31, 36, 40, 0.72),
|
|
2754
2804
|
0 0 0 9999px rgba(0, 0, 0, 0.08),
|
|
@@ -2759,9 +2809,9 @@ function createStyleElement() {
|
|
|
2759
2809
|
.dfwr-dom-hover {
|
|
2760
2810
|
position: fixed;
|
|
2761
2811
|
z-index: 2;
|
|
2762
|
-
border: 1px solid #
|
|
2812
|
+
border: 1px solid #7cc7ff;
|
|
2763
2813
|
border-radius: var(--df-review-radius-xs);
|
|
2764
|
-
background: rgba(
|
|
2814
|
+
background: rgba(124, 199, 255, 0.1);
|
|
2765
2815
|
box-shadow:
|
|
2766
2816
|
0 0 0 1px rgba(31, 36, 40, 0.72),
|
|
2767
2817
|
0 0 0 9999px rgba(0, 0, 0, 0.08);
|
|
@@ -2942,12 +2992,12 @@ function createStyleElement() {
|
|
|
2942
2992
|
}
|
|
2943
2993
|
|
|
2944
2994
|
.dfwr-area-preview-layer .dfwr-bound-marker {
|
|
2945
|
-
border-color: #
|
|
2995
|
+
border-color: #7cc7ff;
|
|
2946
2996
|
background: var(--df-review-color-panel);
|
|
2947
2997
|
box-shadow:
|
|
2948
|
-
0 0 0 5px rgba(
|
|
2998
|
+
0 0 0 5px rgba(124, 199, 255, 0.2),
|
|
2949
2999
|
0 12px 26px rgba(0, 0, 0, 0.3);
|
|
2950
|
-
color: #
|
|
3000
|
+
color: #7cc7ff;
|
|
2951
3001
|
}
|
|
2952
3002
|
|
|
2953
3003
|
.dfwr-bound-marker-icon {
|
|
@@ -3030,7 +3080,7 @@ function createStyleElement() {
|
|
|
3030
3080
|
border-radius: var(--df-review-radius-pill);
|
|
3031
3081
|
background: var(--df-review-color-accent);
|
|
3032
3082
|
box-shadow:
|
|
3033
|
-
0 0 0 4px rgba(
|
|
3083
|
+
0 0 0 4px rgba(124, 199, 255, 0.22),
|
|
3034
3084
|
0 8px 18px rgba(0, 0, 0, 0.28);
|
|
3035
3085
|
cursor: grab;
|
|
3036
3086
|
pointer-events: auto;
|
|
@@ -3048,7 +3098,7 @@ function createStyleElement() {
|
|
|
3048
3098
|
pointer-events: auto;
|
|
3049
3099
|
color: var(--df-review-color-text);
|
|
3050
3100
|
background: var(--df-review-color-panel);
|
|
3051
|
-
border: 1px solid rgba(
|
|
3101
|
+
border: 1px solid rgba(124, 199, 255, 0.56);
|
|
3052
3102
|
border-radius: var(--df-review-radius-md);
|
|
3053
3103
|
box-shadow: var(--df-review-shadow-popover);
|
|
3054
3104
|
}
|
|
@@ -3057,7 +3107,7 @@ function createStyleElement() {
|
|
|
3057
3107
|
.dfwr-area-draft.is-composer {
|
|
3058
3108
|
max-height: min(360px, calc(100vh - 32px));
|
|
3059
3109
|
overflow: auto;
|
|
3060
|
-
border-color: rgba(
|
|
3110
|
+
border-color: rgba(124, 199, 255, 0.56);
|
|
3061
3111
|
}
|
|
3062
3112
|
|
|
3063
3113
|
.dfwr-shell.is-docked-composer .dfwr-dom-popover.is-docked-composer,
|
|
@@ -3094,7 +3144,7 @@ function createStyleElement() {
|
|
|
3094
3144
|
|
|
3095
3145
|
.dfwr-draft-drag-handle:hover,
|
|
3096
3146
|
.dfwr-draft-drag-handle:focus-visible {
|
|
3097
|
-
background: rgba(
|
|
3147
|
+
background: rgba(124, 199, 255, 0.62);
|
|
3098
3148
|
}
|
|
3099
3149
|
|
|
3100
3150
|
.dfwr-draft-drag-handle:active {
|
|
@@ -3113,7 +3163,7 @@ function createStyleElement() {
|
|
|
3113
3163
|
pointer-events: auto;
|
|
3114
3164
|
color: var(--df-review-color-text);
|
|
3115
3165
|
background: var(--df-review-color-panel);
|
|
3116
|
-
border: 1px solid rgba(
|
|
3166
|
+
border: 1px solid rgba(124, 199, 255, 0.56);
|
|
3117
3167
|
border-radius: var(--df-review-radius-md);
|
|
3118
3168
|
box-shadow: var(--df-review-shadow-popover);
|
|
3119
3169
|
}
|
|
@@ -3332,7 +3382,7 @@ function createStyleElement() {
|
|
|
3332
3382
|
}
|
|
3333
3383
|
|
|
3334
3384
|
.dfwr-adjust-panel.is-active {
|
|
3335
|
-
border-color: rgba(
|
|
3385
|
+
border-color: rgba(124, 199, 255, 0.5);
|
|
3336
3386
|
background: var(--df-review-color-accent-soft);
|
|
3337
3387
|
}
|
|
3338
3388
|
|
|
@@ -3371,7 +3421,7 @@ function createStyleElement() {
|
|
|
3371
3421
|
.dfwr-adjust-toggle:hover,
|
|
3372
3422
|
.dfwr-adjust-toggle:focus-visible,
|
|
3373
3423
|
.dfwr-adjust-toggle.is-active {
|
|
3374
|
-
border-color: rgba(
|
|
3424
|
+
border-color: rgba(124, 199, 255, 0.68);
|
|
3375
3425
|
background: var(--df-review-color-accent-soft);
|
|
3376
3426
|
outline: none;
|
|
3377
3427
|
}
|
|
@@ -3434,7 +3484,7 @@ function createStyleElement() {
|
|
|
3434
3484
|
}
|
|
3435
3485
|
|
|
3436
3486
|
.dfwr-item:focus-visible {
|
|
3437
|
-
outline: 2px solid rgba(
|
|
3487
|
+
outline: 2px solid rgba(124, 199, 255, 0.72);
|
|
3438
3488
|
outline-offset: 4px;
|
|
3439
3489
|
}
|
|
3440
3490
|
|
|
@@ -3544,8 +3594,8 @@ function createStyleElement() {
|
|
|
3544
3594
|
z-index: 2;
|
|
3545
3595
|
width: 0;
|
|
3546
3596
|
height: 0;
|
|
3547
|
-
border: 1px solid #
|
|
3548
|
-
background: rgba(
|
|
3597
|
+
border: 1px solid #7cc7ff;
|
|
3598
|
+
background: rgba(124, 199, 255, 0.16);
|
|
3549
3599
|
box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.18);
|
|
3550
3600
|
}
|
|
3551
3601
|
|
|
@@ -4622,6 +4672,12 @@ function createDomDraftLayer(context, draft, options = {}) {
|
|
|
4622
4672
|
pin.setAttribute("aria-label", "Move DOM point");
|
|
4623
4673
|
pin.style.left = `${hostPoint.x}px`;
|
|
4624
4674
|
pin.style.top = `${hostPoint.y}px`;
|
|
4675
|
+
if (draft.isSelectionOnly) {
|
|
4676
|
+
pin.classList.add("is-selection-only");
|
|
4677
|
+
pin.tabIndex = -1;
|
|
4678
|
+
group.append(pin);
|
|
4679
|
+
return { layer: group, composer: void 0 };
|
|
4680
|
+
}
|
|
4625
4681
|
const popover = document.createElement("div");
|
|
4626
4682
|
const position = getPopoverPosition(hostPoint, environment);
|
|
4627
4683
|
popover.className = [
|
|
@@ -4744,7 +4800,7 @@ function createDomDraftLayer(context, draft, options = {}) {
|
|
|
4744
4800
|
attachments: currentDraft.attachments
|
|
4745
4801
|
});
|
|
4746
4802
|
};
|
|
4747
|
-
const adjustmentControls = isElementDraft ? createAdjustmentControls(context, {
|
|
4803
|
+
const adjustmentControls = isElementDraft && !options.dockComposer ? createAdjustmentControls(context, {
|
|
4748
4804
|
draft,
|
|
4749
4805
|
pin,
|
|
4750
4806
|
popover,
|
|
@@ -5390,7 +5446,7 @@ var WebReviewKitView = class {
|
|
|
5390
5446
|
this.draftPreview = new DraftPreviewController({
|
|
5391
5447
|
getEnvironment: () => this.config.getEnvironment(),
|
|
5392
5448
|
getMetrics: (draft) => getDraftAdjustmentMetrics(draft, presets()),
|
|
5393
|
-
hasAdjustment: (draft) => hasDraftAdjustment(draft, presets())
|
|
5449
|
+
hasAdjustment: (draft) => draft.adjustment?.preview !== false && hasDraftAdjustment(draft, presets())
|
|
5394
5450
|
});
|
|
5395
5451
|
this.draftContext = {
|
|
5396
5452
|
config: this.config,
|
|
@@ -5411,8 +5467,11 @@ var WebReviewKitView = class {
|
|
|
5411
5467
|
shadow.replaceChildren();
|
|
5412
5468
|
shadow.append(createStyleElement());
|
|
5413
5469
|
shadow.append(hiddenItemsStyle);
|
|
5414
|
-
const
|
|
5415
|
-
const
|
|
5470
|
+
const hasSelectionOnlyDraft = state.domDraft?.isSelectionOnly === true;
|
|
5471
|
+
const hasDismissableDraft = Boolean(
|
|
5472
|
+
state.domDraft && !hasSelectionOnlyDraft || state.areaDraft
|
|
5473
|
+
);
|
|
5474
|
+
const shouldDockComposer = this.config.options.ui?.panel === false && hasDismissableDraft && !hasSelectionOnlyDraft && Boolean(this.getShellComposerHost());
|
|
5416
5475
|
let dockedComposer;
|
|
5417
5476
|
const shell = document.createElement("div");
|
|
5418
5477
|
shell.className = [
|
|
@@ -5534,13 +5593,7 @@ var WebReviewKitView = class {
|
|
|
5534
5593
|
|
|
5535
5594
|
// src/core/web.review.kit.app.ts
|
|
5536
5595
|
var ROOT_ID = "df-web-review-kit-root";
|
|
5537
|
-
|
|
5538
|
-
const path = event.composedPath?.() ?? [];
|
|
5539
|
-
const element = path[0] ?? event.target;
|
|
5540
|
-
if (!element || typeof element.tagName !== "string") return false;
|
|
5541
|
-
const tag = element.tagName;
|
|
5542
|
-
return tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || element.isContentEditable === true;
|
|
5543
|
-
}
|
|
5596
|
+
var VIEWPORT_SCROLL_OPTIONS = { capture: true, passive: true };
|
|
5544
5597
|
function createWebReviewKit(options) {
|
|
5545
5598
|
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
5546
5599
|
return createNoopController();
|
|
@@ -5553,6 +5606,8 @@ function createWebReviewKit(options) {
|
|
|
5553
5606
|
toggle: () => app.toggle(),
|
|
5554
5607
|
setMode: (mode) => app.setMode(mode),
|
|
5555
5608
|
startElementReview: (element, comment) => app.startElementReview(element, comment),
|
|
5609
|
+
selectElement: (element) => app.selectElement(element),
|
|
5610
|
+
adjustElementSelection: (delta, adjustmentOptions) => app.adjustElementSelection(delta, adjustmentOptions),
|
|
5556
5611
|
getMode: () => app.getMode(),
|
|
5557
5612
|
highlightItem: (itemId) => app.highlightItem(itemId),
|
|
5558
5613
|
setHiddenItemIds: (itemIds) => app.setHiddenItemIds(itemIds),
|
|
@@ -5573,6 +5628,12 @@ var WebReviewKitApp = class {
|
|
|
5573
5628
|
this.isSelectingArea = false;
|
|
5574
5629
|
this.handleKeyDown = (event) => {
|
|
5575
5630
|
if (event.key === "Escape" && this.cancelMode()) {
|
|
5631
|
+
const activeElement = document.activeElement;
|
|
5632
|
+
if (activeElement instanceof HTMLButtonElement && activeElement.matches(
|
|
5633
|
+
".df-review-mode-button, .df-review-section-outline-link.is-dom-select"
|
|
5634
|
+
)) {
|
|
5635
|
+
activeElement.blur();
|
|
5636
|
+
}
|
|
5576
5637
|
event.preventDefault();
|
|
5577
5638
|
event.stopPropagation();
|
|
5578
5639
|
return;
|
|
@@ -5587,6 +5648,7 @@ var WebReviewKitApp = class {
|
|
|
5587
5648
|
this.renderFrame = window.requestAnimationFrame(() => {
|
|
5588
5649
|
this.renderFrame = void 0;
|
|
5589
5650
|
if (this.isDraftComposerFocused()) return;
|
|
5651
|
+
this.syncDomDraftViewportGeometry();
|
|
5590
5652
|
this.render();
|
|
5591
5653
|
});
|
|
5592
5654
|
};
|
|
@@ -5644,16 +5706,48 @@ var WebReviewKitApp = class {
|
|
|
5644
5706
|
this.shadow = this.root.attachShadow({ mode: "open" });
|
|
5645
5707
|
document.body.appendChild(this.root);
|
|
5646
5708
|
document.addEventListener("keydown", this.handleKeyDown, true);
|
|
5647
|
-
window.addEventListener(
|
|
5709
|
+
window.addEventListener(
|
|
5710
|
+
"scroll",
|
|
5711
|
+
this.handleViewportChange,
|
|
5712
|
+
VIEWPORT_SCROLL_OPTIONS
|
|
5713
|
+
);
|
|
5648
5714
|
window.addEventListener("resize", this.handleViewportChange);
|
|
5715
|
+
this.targetViewportWindow = this.getEnvironment()?.window;
|
|
5716
|
+
if (this.targetViewportWindow && this.targetViewportWindow !== window) {
|
|
5717
|
+
this.targetViewportWindow.addEventListener(
|
|
5718
|
+
"scroll",
|
|
5719
|
+
this.handleViewportChange,
|
|
5720
|
+
VIEWPORT_SCROLL_OPTIONS
|
|
5721
|
+
);
|
|
5722
|
+
this.targetViewportWindow.addEventListener(
|
|
5723
|
+
"resize",
|
|
5724
|
+
this.handleViewportChange
|
|
5725
|
+
);
|
|
5726
|
+
}
|
|
5649
5727
|
this.render();
|
|
5650
5728
|
}
|
|
5651
5729
|
destroy() {
|
|
5652
5730
|
this.view.clearDraftPreview();
|
|
5653
5731
|
this.clearDrafts();
|
|
5654
5732
|
document.removeEventListener("keydown", this.handleKeyDown, true);
|
|
5655
|
-
window.removeEventListener(
|
|
5733
|
+
window.removeEventListener(
|
|
5734
|
+
"scroll",
|
|
5735
|
+
this.handleViewportChange,
|
|
5736
|
+
VIEWPORT_SCROLL_OPTIONS
|
|
5737
|
+
);
|
|
5656
5738
|
window.removeEventListener("resize", this.handleViewportChange);
|
|
5739
|
+
if (this.targetViewportWindow && this.targetViewportWindow !== window) {
|
|
5740
|
+
this.targetViewportWindow.removeEventListener(
|
|
5741
|
+
"scroll",
|
|
5742
|
+
this.handleViewportChange,
|
|
5743
|
+
VIEWPORT_SCROLL_OPTIONS
|
|
5744
|
+
);
|
|
5745
|
+
this.targetViewportWindow.removeEventListener(
|
|
5746
|
+
"resize",
|
|
5747
|
+
this.handleViewportChange
|
|
5748
|
+
);
|
|
5749
|
+
}
|
|
5750
|
+
this.targetViewportWindow = void 0;
|
|
5657
5751
|
if (this.renderFrame) {
|
|
5658
5752
|
window.cancelAnimationFrame(this.renderFrame);
|
|
5659
5753
|
this.renderFrame = void 0;
|
|
@@ -5698,6 +5792,33 @@ var WebReviewKitApp = class {
|
|
|
5698
5792
|
this.isSelectingArea = false;
|
|
5699
5793
|
await this.bindElementDraftToElement(element, { comment });
|
|
5700
5794
|
}
|
|
5795
|
+
async selectElement(element) {
|
|
5796
|
+
if (!this.isOpen) {
|
|
5797
|
+
this.isOpen = true;
|
|
5798
|
+
}
|
|
5799
|
+
this.setModeState("element");
|
|
5800
|
+
this.clearDrafts();
|
|
5801
|
+
this.isSelectingArea = false;
|
|
5802
|
+
await this.bindElementDraftToElement(element, {
|
|
5803
|
+
isSelectionOnly: true
|
|
5804
|
+
});
|
|
5805
|
+
}
|
|
5806
|
+
adjustElementSelection(delta, options) {
|
|
5807
|
+
if (!this.domDraft?.selection) return false;
|
|
5808
|
+
const current = this.domDraft.adjustment;
|
|
5809
|
+
this.domDraft = {
|
|
5810
|
+
...this.domDraft,
|
|
5811
|
+
adjustment: {
|
|
5812
|
+
x: (current?.x ?? 0) + (delta.x ?? 0),
|
|
5813
|
+
y: (current?.y ?? 0) + (delta.y ?? 0),
|
|
5814
|
+
scale: (current?.scale ?? 0) + (delta.scale ?? 0),
|
|
5815
|
+
isActive: true,
|
|
5816
|
+
preview: options?.preview ?? current?.preview
|
|
5817
|
+
}
|
|
5818
|
+
};
|
|
5819
|
+
this.render();
|
|
5820
|
+
return true;
|
|
5821
|
+
}
|
|
5701
5822
|
getMode() {
|
|
5702
5823
|
return this.mode;
|
|
5703
5824
|
}
|
|
@@ -5777,6 +5898,33 @@ var WebReviewKitApp = class {
|
|
|
5777
5898
|
this.render();
|
|
5778
5899
|
return true;
|
|
5779
5900
|
}
|
|
5901
|
+
syncDomDraftViewportGeometry() {
|
|
5902
|
+
const draft = this.domDraft;
|
|
5903
|
+
const element = draft?.previewElement;
|
|
5904
|
+
const environment = this.getEnvironment();
|
|
5905
|
+
if (!draft?.selection || !element?.isConnected || !environment || element.ownerDocument !== environment.document) {
|
|
5906
|
+
return;
|
|
5907
|
+
}
|
|
5908
|
+
const rect = element.getBoundingClientRect();
|
|
5909
|
+
if (rect.width <= 0 || rect.height <= 0) return;
|
|
5910
|
+
const selection = {
|
|
5911
|
+
left: rect.left,
|
|
5912
|
+
top: rect.top,
|
|
5913
|
+
width: rect.width,
|
|
5914
|
+
height: rect.height
|
|
5915
|
+
};
|
|
5916
|
+
this.domDraft = {
|
|
5917
|
+
...draft,
|
|
5918
|
+
marker: {
|
|
5919
|
+
...draft.marker,
|
|
5920
|
+
viewport: roundPoint({ x: rect.left, y: rect.top })
|
|
5921
|
+
},
|
|
5922
|
+
selection: {
|
|
5923
|
+
...draft.selection,
|
|
5924
|
+
viewport: toPublicSelection(selection)
|
|
5925
|
+
}
|
|
5926
|
+
};
|
|
5927
|
+
}
|
|
5780
5928
|
isDraftComposerFocused() {
|
|
5781
5929
|
if (!this.domDraft && !this.areaDraft) return false;
|
|
5782
5930
|
const composerHost = this.getEnvironment()?.composerHost;
|
|
@@ -6000,46 +6148,28 @@ var WebReviewKitApp = class {
|
|
|
6000
6148
|
this.root.style.display = previousDisplay;
|
|
6001
6149
|
}
|
|
6002
6150
|
}
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
const environment = this.getEnvironment();
|
|
6006
|
-
const draft = this.domDraft;
|
|
6007
|
-
if (!draft) return;
|
|
6008
|
-
if (!environment?.captureViewport) {
|
|
6009
|
-
this.draftError = "Viewport capture helper is not available.";
|
|
6010
|
-
this.render();
|
|
6011
|
-
return;
|
|
6012
|
-
}
|
|
6013
|
-
const captureInput = this.createViewportCaptureInput(
|
|
6014
|
-
environment,
|
|
6151
|
+
captureDomDraft(input) {
|
|
6152
|
+
return this.captureDraft(
|
|
6015
6153
|
input,
|
|
6016
|
-
|
|
6154
|
+
() => this.domDraft,
|
|
6155
|
+
(draft) => {
|
|
6156
|
+
this.domDraft = draft;
|
|
6157
|
+
}
|
|
6158
|
+
);
|
|
6159
|
+
}
|
|
6160
|
+
captureAreaDraft(input) {
|
|
6161
|
+
return this.captureDraft(
|
|
6162
|
+
input,
|
|
6163
|
+
() => this.areaDraft,
|
|
6164
|
+
(draft) => {
|
|
6165
|
+
this.areaDraft = draft;
|
|
6166
|
+
}
|
|
6017
6167
|
);
|
|
6018
|
-
this.draftError = "";
|
|
6019
|
-
this.isCapturingViewport = true;
|
|
6020
|
-
this.render();
|
|
6021
|
-
try {
|
|
6022
|
-
const result = await environment.captureViewport(captureInput);
|
|
6023
|
-
const attachment = this.createCaptureDraftAttachment(result, captureInput);
|
|
6024
|
-
const currentDraft = this.domDraft ?? draft;
|
|
6025
|
-
this.domDraft = {
|
|
6026
|
-
...currentDraft,
|
|
6027
|
-
attachments: [...currentDraft.attachments ?? [], attachment]
|
|
6028
|
-
};
|
|
6029
|
-
} catch (error) {
|
|
6030
|
-
this.draftError = this.getErrorMessage(
|
|
6031
|
-
error,
|
|
6032
|
-
"Failed to capture viewport."
|
|
6033
|
-
);
|
|
6034
|
-
} finally {
|
|
6035
|
-
this.isCapturingViewport = false;
|
|
6036
|
-
this.render();
|
|
6037
|
-
}
|
|
6038
6168
|
}
|
|
6039
|
-
async
|
|
6169
|
+
async captureDraft(input, getDraft, setDraft) {
|
|
6040
6170
|
if (this.isCapturingViewport) return;
|
|
6041
6171
|
const environment = this.getEnvironment();
|
|
6042
|
-
const draft =
|
|
6172
|
+
const draft = getDraft();
|
|
6043
6173
|
if (!draft) return;
|
|
6044
6174
|
if (!environment?.captureViewport) {
|
|
6045
6175
|
this.draftError = "Viewport capture helper is not available.";
|
|
@@ -6057,16 +6187,13 @@ var WebReviewKitApp = class {
|
|
|
6057
6187
|
try {
|
|
6058
6188
|
const result = await environment.captureViewport(captureInput);
|
|
6059
6189
|
const attachment = this.createCaptureDraftAttachment(result, captureInput);
|
|
6060
|
-
const currentDraft =
|
|
6061
|
-
|
|
6190
|
+
const currentDraft = getDraft() ?? draft;
|
|
6191
|
+
setDraft({
|
|
6062
6192
|
...currentDraft,
|
|
6063
6193
|
attachments: [...currentDraft.attachments ?? [], attachment]
|
|
6064
|
-
};
|
|
6194
|
+
});
|
|
6065
6195
|
} catch (error) {
|
|
6066
|
-
this.draftError =
|
|
6067
|
-
error,
|
|
6068
|
-
"Failed to capture viewport."
|
|
6069
|
-
);
|
|
6196
|
+
this.draftError = getErrorMessage(error, "Failed to capture viewport.");
|
|
6070
6197
|
} finally {
|
|
6071
6198
|
this.isCapturingViewport = false;
|
|
6072
6199
|
this.render();
|
|
@@ -6205,17 +6332,10 @@ var WebReviewKitApp = class {
|
|
|
6205
6332
|
);
|
|
6206
6333
|
}
|
|
6207
6334
|
getCreateItemErrorMessage(error, wasUploadingAttachments) {
|
|
6208
|
-
const message =
|
|
6335
|
+
const message = getErrorMessage(error, "Failed to save QA.");
|
|
6209
6336
|
const reason = error && typeof error === "object" && "reason" in error && typeof error.reason === "string" ? ` (${error.reason})` : "";
|
|
6210
6337
|
return wasUploadingAttachments && reason ? `Attachment upload failed${reason}: ${message}` : message;
|
|
6211
6338
|
}
|
|
6212
|
-
getErrorMessage(error, fallback) {
|
|
6213
|
-
if (error instanceof Error) return error.message;
|
|
6214
|
-
if (error && typeof error === "object" && "message" in error && typeof error.message === "string") {
|
|
6215
|
-
return error.message;
|
|
6216
|
-
}
|
|
6217
|
-
return fallback;
|
|
6218
|
-
}
|
|
6219
6339
|
async restoreItem(item) {
|
|
6220
6340
|
this.setModeState("idle");
|
|
6221
6341
|
this.clearDrafts();
|
|
@@ -6248,6 +6368,11 @@ function createNoopController() {
|
|
|
6248
6368
|
},
|
|
6249
6369
|
async startElementReview() {
|
|
6250
6370
|
},
|
|
6371
|
+
async selectElement() {
|
|
6372
|
+
},
|
|
6373
|
+
adjustElementSelection() {
|
|
6374
|
+
return false;
|
|
6375
|
+
},
|
|
6251
6376
|
getMode() {
|
|
6252
6377
|
return "idle";
|
|
6253
6378
|
},
|