@designfever/web-review-kit 0.6.0 → 0.7.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/.env.sample +29 -0
- package/README.md +35 -10
- package/dist/chunk-AB5B6O77.js +584 -0
- package/dist/chunk-AB5B6O77.js.map +1 -0
- package/dist/{chunk-IN36JHEU.js → chunk-RPVLRULC.js} +504 -143
- package/dist/chunk-RPVLRULC.js.map +1 -0
- package/dist/image.types-BmzkFSPX.d.cts +71 -0
- package/dist/image.types-BmzkFSPX.d.ts +71 -0
- package/dist/index.cjs +1019 -144
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -3
- package/dist/index.d.ts +27 -3
- package/dist/index.js +50 -3
- package/dist/index.js.map +1 -1
- package/dist/react-shell.cjs +10293 -5185
- package/dist/react-shell.cjs.map +1 -1
- package/dist/react-shell.d.cts +37 -4
- package/dist/react-shell.d.ts +37 -4
- package/dist/react-shell.js +9624 -4856
- package/dist/react-shell.js.map +1 -1
- package/dist/token-Dt-ZH-YO.d.cts +88 -0
- package/dist/token-nJXPPdYX.d.ts +88 -0
- package/dist/{types-DFHHVRBc.d.cts → types-DT9Z66mV.d.cts} +13 -1
- package/dist/{types-DFHHVRBc.d.ts → types-DT9Z66mV.d.ts} +13 -1
- package/dist/vite.cjs +1116 -5
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.d.cts +45 -1
- package/dist/vite.d.ts +45 -1
- package/dist/vite.js +800 -5
- package/dist/vite.js.map +1 -1
- package/docs/README.md +11 -7
- package/docs/adapters.md +126 -0
- package/docs/adaptor.sample.ts +13 -1
- package/docs/architecture.md +2 -1
- package/docs/code-review-0.6.0.md +232 -0
- package/docs/db-setup.md +4 -2
- package/docs/figma-image-mvp-0.7.0.md +327 -0
- package/docs/installation.md +39 -7
- package/docs/release-notes-0.7.0.md +128 -0
- package/package.json +6 -2
- package/dist/chunk-IN36JHEU.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -20,16 +20,35 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
DEFAULT_REVIEW_FIGMA_IMAGE_FORMAT: () => DEFAULT_REVIEW_FIGMA_IMAGE_FORMAT,
|
|
24
|
+
DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT: () => DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT,
|
|
25
|
+
DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY: () => DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,
|
|
23
26
|
DEFAULT_REVIEW_VIEWPORTS: () => DEFAULT_REVIEW_VIEWPORTS,
|
|
27
|
+
FIGMA_NODE_REF_SEPARATOR: () => FIGMA_NODE_REF_SEPARATOR,
|
|
28
|
+
REVIEW_FIGMA_TOKEN_MISSING_CODE: () => REVIEW_FIGMA_TOKEN_MISSING_CODE,
|
|
24
29
|
REVIEW_WORKFLOW_STATUS_OPTIONS: () => REVIEW_WORKFLOW_STATUS_OPTIONS,
|
|
30
|
+
ReviewFigmaTokenError: () => ReviewFigmaTokenError,
|
|
31
|
+
collectReviewFigmaReleaseSnapshot: () => collectReviewFigmaReleaseSnapshot,
|
|
32
|
+
createReviewFigmaFrameUrl: () => createReviewFigmaFrameUrl,
|
|
33
|
+
createReviewFigmaImageStoreClient: () => createReviewFigmaImageStoreClient,
|
|
34
|
+
createReviewFigmaImagesSnapshot: () => createReviewFigmaImagesSnapshot,
|
|
35
|
+
createReviewFigmaNodeValue: () => createReviewFigmaNodeValue,
|
|
36
|
+
createReviewFigmaReleaseSnapshot: () => createReviewFigmaReleaseSnapshot,
|
|
25
37
|
createWebReviewKit: () => createWebReviewKit,
|
|
26
38
|
findReviewViewportPreset: () => findReviewViewportPreset,
|
|
27
39
|
getNumberedReviewItems: () => getNumberedReviewItems,
|
|
40
|
+
getReviewFigmaImageMimeType: () => getReviewFigmaImageMimeType,
|
|
41
|
+
getReviewFigmaImageTargetKey: () => getReviewFigmaImageTargetKey,
|
|
28
42
|
getReviewItemScope: () => getReviewItemScope,
|
|
29
43
|
getReviewItemScopeLabel: () => getReviewItemScopeLabel,
|
|
30
44
|
getReviewViewportScope: () => getReviewViewportScope,
|
|
45
|
+
isReviewFigmaTokenError: () => isReviewFigmaTokenError,
|
|
31
46
|
localAdapter: () => localAdapter,
|
|
32
47
|
normalizeReviewItemStatus: () => normalizeReviewItemStatus,
|
|
48
|
+
parseReviewFigmaNodeRef: () => parseReviewFigmaNodeRef,
|
|
49
|
+
readReviewFigmaToken: () => readReviewFigmaToken,
|
|
50
|
+
requireReviewFigmaNodeRef: () => requireReviewFigmaNodeRef,
|
|
51
|
+
requireReviewFigmaToken: () => requireReviewFigmaToken,
|
|
33
52
|
supabaseAdapter: () => supabaseAdapter
|
|
34
53
|
});
|
|
35
54
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -345,8 +364,10 @@ async function unwrapResponse(request, label) {
|
|
|
345
364
|
}
|
|
346
365
|
function buildSupabaseReviewUrl(item, source, options, itemId) {
|
|
347
366
|
if (typeof window === "undefined") return void 0;
|
|
348
|
-
const
|
|
349
|
-
|
|
367
|
+
const url = new URL(
|
|
368
|
+
normalizeReviewUrlPath(options.reviewPathPrefix),
|
|
369
|
+
window.location.origin
|
|
370
|
+
);
|
|
350
371
|
url.searchParams.set("source", source);
|
|
351
372
|
url.searchParams.set("target", item.routeKey || item.normalizedPath || "/");
|
|
352
373
|
url.searchParams.set("w", String(Math.round(item.viewport.width)));
|
|
@@ -354,6 +375,11 @@ function buildSupabaseReviewUrl(item, source, options, itemId) {
|
|
|
354
375
|
if (itemId) url.searchParams.set("item", itemId);
|
|
355
376
|
return url.toString();
|
|
356
377
|
}
|
|
378
|
+
function normalizeReviewUrlPath(value = "/review") {
|
|
379
|
+
const raw = value.trim() || "/review";
|
|
380
|
+
const path = raw.startsWith("/") ? raw : `/${raw}`;
|
|
381
|
+
return path.endsWith("/") ? path : `${path}/`;
|
|
382
|
+
}
|
|
357
383
|
function toAbsoluteReviewUrl(path) {
|
|
358
384
|
if (typeof window === "undefined") return path;
|
|
359
385
|
return new URL(path, window.location.origin).toString();
|
|
@@ -365,6 +391,480 @@ function createSupabaseReviewItemId() {
|
|
|
365
391
|
return `review-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
366
392
|
}
|
|
367
393
|
|
|
394
|
+
// src/figma/image.types.ts
|
|
395
|
+
var DEFAULT_REVIEW_FIGMA_IMAGE_FORMAT = "webp";
|
|
396
|
+
|
|
397
|
+
// src/vite/figma-asset.ts
|
|
398
|
+
function getReviewFigmaImageFormatFromMimeType(mimeType) {
|
|
399
|
+
if (mimeType === "image/webp") return "webp";
|
|
400
|
+
if (mimeType === "image/png") return "png";
|
|
401
|
+
if (mimeType === "image/jpeg") return "jpg";
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// src/figma/parse.ts
|
|
406
|
+
var FIGMA_NODE_REF_SEPARATOR = "->";
|
|
407
|
+
function parseReviewFigmaNodeRef(value) {
|
|
408
|
+
if (typeof value !== "string") return normalizeReviewFigmaNodeRef(value);
|
|
409
|
+
const input = value.trim();
|
|
410
|
+
if (!input) return null;
|
|
411
|
+
return parseReviewFigmaNodeRefValue(input) ?? parseReviewFigmaUrl(input);
|
|
412
|
+
}
|
|
413
|
+
function requireReviewFigmaNodeRef(value) {
|
|
414
|
+
const ref = parseReviewFigmaNodeRef(value);
|
|
415
|
+
if (!ref) {
|
|
416
|
+
throw new Error("A Figma node link or fileKey->nodeId value is required.");
|
|
417
|
+
}
|
|
418
|
+
return ref;
|
|
419
|
+
}
|
|
420
|
+
function createReviewFigmaNodeValue(ref) {
|
|
421
|
+
return `${ref.fileKey}${FIGMA_NODE_REF_SEPARATOR}${ref.nodeId}`;
|
|
422
|
+
}
|
|
423
|
+
function createReviewFigmaFrameUrl(value) {
|
|
424
|
+
const ref = parseReviewFigmaNodeRef(value);
|
|
425
|
+
if (!ref) return null;
|
|
426
|
+
return `https://www.figma.com/design/${encodeURIComponent(
|
|
427
|
+
ref.fileKey
|
|
428
|
+
)}?node-id=${encodeURIComponent(ref.nodeId.replace(/:/g, "-"))}`;
|
|
429
|
+
}
|
|
430
|
+
function parseReviewFigmaNodeRefValue(value) {
|
|
431
|
+
const [fileKey, nodeId, extra] = value.split(FIGMA_NODE_REF_SEPARATOR).map((part) => part.trim());
|
|
432
|
+
if (!fileKey || !nodeId || extra !== void 0) return null;
|
|
433
|
+
return normalizeReviewFigmaNodeRef({ fileKey, nodeId });
|
|
434
|
+
}
|
|
435
|
+
function parseReviewFigmaUrl(value) {
|
|
436
|
+
let url;
|
|
437
|
+
try {
|
|
438
|
+
url = new URL(value);
|
|
439
|
+
} catch {
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
const fileKey = getFigmaFileKey(url);
|
|
443
|
+
const nodeId = normalizeFigmaNodeId(url.searchParams.get("node-id"));
|
|
444
|
+
if (!fileKey || !nodeId) return null;
|
|
445
|
+
return { fileKey, nodeId, sourceUrl: value };
|
|
446
|
+
}
|
|
447
|
+
function getFigmaFileKey(url) {
|
|
448
|
+
if (!/(^|\.)figma\.com$/i.test(url.hostname)) return null;
|
|
449
|
+
const parts = url.pathname.split("/").filter(Boolean);
|
|
450
|
+
const fileKeyIndex = parts.findIndex(
|
|
451
|
+
(part) => ["design", "file", "proto", "board"].includes(part)
|
|
452
|
+
);
|
|
453
|
+
const fileKey = fileKeyIndex >= 0 ? parts[fileKeyIndex + 1] : null;
|
|
454
|
+
return normalizeFigmaFileKey(fileKey);
|
|
455
|
+
}
|
|
456
|
+
function normalizeReviewFigmaNodeRef(value) {
|
|
457
|
+
const fileKey = normalizeFigmaFileKey(value?.fileKey);
|
|
458
|
+
const nodeId = normalizeFigmaNodeId(value?.nodeId);
|
|
459
|
+
if (!fileKey || !nodeId) return null;
|
|
460
|
+
const sourceUrl = normalizeOptionalString(value?.sourceUrl);
|
|
461
|
+
return sourceUrl ? { fileKey, nodeId, sourceUrl } : { fileKey, nodeId };
|
|
462
|
+
}
|
|
463
|
+
function normalizeFigmaFileKey(value) {
|
|
464
|
+
return normalizeOptionalString(value)?.replace(/[?#].*$/, "") ?? null;
|
|
465
|
+
}
|
|
466
|
+
function normalizeFigmaNodeId(value) {
|
|
467
|
+
const nodeId = normalizeOptionalString(value);
|
|
468
|
+
if (!nodeId) return null;
|
|
469
|
+
if (nodeId.includes(":")) return nodeId;
|
|
470
|
+
return nodeId.replace(/-/g, ":");
|
|
471
|
+
}
|
|
472
|
+
function normalizeOptionalString(value) {
|
|
473
|
+
if (typeof value !== "string") return null;
|
|
474
|
+
return value.trim() || null;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// src/figma/token.ts
|
|
478
|
+
var DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY = "FIGMA_TOKEN";
|
|
479
|
+
var REVIEW_FIGMA_TOKEN_MISSING_CODE = "DFWR_FIGMA_TOKEN_MISSING";
|
|
480
|
+
var ReviewFigmaTokenError = class extends Error {
|
|
481
|
+
constructor(envKey = DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY) {
|
|
482
|
+
super(
|
|
483
|
+
`Figma image rendering requires server env ${envKey}. Set ${envKey} in the dev/server environment; do not expose it as VITE_${envKey}.`
|
|
484
|
+
);
|
|
485
|
+
this.code = REVIEW_FIGMA_TOKEN_MISSING_CODE;
|
|
486
|
+
this.name = "ReviewFigmaTokenError";
|
|
487
|
+
this.envKey = envKey;
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
function readReviewFigmaToken(options = {}) {
|
|
491
|
+
if (options.enabled === false) return null;
|
|
492
|
+
const envKey = options.envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY;
|
|
493
|
+
return normalizeReviewFigmaToken(options.token ?? options.env?.[envKey]);
|
|
494
|
+
}
|
|
495
|
+
function requireReviewFigmaToken(options = {}) {
|
|
496
|
+
const envKey = options.envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY;
|
|
497
|
+
const token = readReviewFigmaToken(options);
|
|
498
|
+
if (!token) throw new ReviewFigmaTokenError(envKey);
|
|
499
|
+
return token;
|
|
500
|
+
}
|
|
501
|
+
function isReviewFigmaTokenError(error) {
|
|
502
|
+
if (!error || typeof error !== "object") return false;
|
|
503
|
+
return error instanceof ReviewFigmaTokenError || "code" in error && error.code === REVIEW_FIGMA_TOKEN_MISSING_CODE;
|
|
504
|
+
}
|
|
505
|
+
function normalizeReviewFigmaToken(value) {
|
|
506
|
+
if (typeof value !== "string") return null;
|
|
507
|
+
return value.trim() || null;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// src/figma/render.ts
|
|
511
|
+
var DEFAULT_FIGMA_API_BASE_URL = "https://api.figma.com";
|
|
512
|
+
function createReviewFigmaImageApiUrl({
|
|
513
|
+
apiBaseUrl = DEFAULT_FIGMA_API_BASE_URL,
|
|
514
|
+
fileKey,
|
|
515
|
+
nodeId,
|
|
516
|
+
format = "png",
|
|
517
|
+
scale,
|
|
518
|
+
useAbsoluteBounds
|
|
519
|
+
}) {
|
|
520
|
+
const url = new URL(`/v1/images/${encodeURIComponent(fileKey)}`, apiBaseUrl);
|
|
521
|
+
url.searchParams.set("ids", nodeId);
|
|
522
|
+
url.searchParams.set("format", format);
|
|
523
|
+
if (typeof scale === "number" && Number.isFinite(scale) && scale > 0) {
|
|
524
|
+
url.searchParams.set("scale", String(scale));
|
|
525
|
+
}
|
|
526
|
+
if (useAbsoluteBounds !== void 0) {
|
|
527
|
+
url.searchParams.set("use_absolute_bounds", String(useAbsoluteBounds));
|
|
528
|
+
}
|
|
529
|
+
return url.toString();
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// src/figma/image.store.ts
|
|
533
|
+
var DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT = "/__dfwr/figma-images";
|
|
534
|
+
function createReviewFigmaImageStoreClient(options = {}) {
|
|
535
|
+
const endpoint = options.endpoint ?? DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT;
|
|
536
|
+
const request = createReviewFigmaImageStoreRequest(endpoint, options.fetch);
|
|
537
|
+
return {
|
|
538
|
+
listImages(target) {
|
|
539
|
+
const url = `${endpoint}?target=${encodeURIComponent(
|
|
540
|
+
JSON.stringify(target)
|
|
541
|
+
)}`;
|
|
542
|
+
return request(url);
|
|
543
|
+
},
|
|
544
|
+
async addImage(input) {
|
|
545
|
+
const nextInput = await createClientRenderedAddImageInput(
|
|
546
|
+
input,
|
|
547
|
+
options.clientRender,
|
|
548
|
+
options.fetch
|
|
549
|
+
);
|
|
550
|
+
return request(endpoint, {
|
|
551
|
+
method: "POST",
|
|
552
|
+
body: JSON.stringify(nextInput)
|
|
553
|
+
});
|
|
554
|
+
},
|
|
555
|
+
updateImage(id, patch) {
|
|
556
|
+
return request(`${endpoint}/${encodeURIComponent(id)}`, {
|
|
557
|
+
method: "PATCH",
|
|
558
|
+
body: JSON.stringify(patch)
|
|
559
|
+
});
|
|
560
|
+
},
|
|
561
|
+
reorderImages(input) {
|
|
562
|
+
return request(`${endpoint}/reorder`, {
|
|
563
|
+
method: "PATCH",
|
|
564
|
+
body: JSON.stringify(input)
|
|
565
|
+
});
|
|
566
|
+
},
|
|
567
|
+
deleteImage(id) {
|
|
568
|
+
return request(`${endpoint}/${encodeURIComponent(id)}`, {
|
|
569
|
+
method: "DELETE"
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
async function createClientRenderedAddImageInput(input, clientRender, fetchOption) {
|
|
575
|
+
const options = normalizeClientRenderOptions(clientRender);
|
|
576
|
+
if (!options) return input;
|
|
577
|
+
const token = readClientRenderToken(options);
|
|
578
|
+
if (!token) return input;
|
|
579
|
+
try {
|
|
580
|
+
const asset = await withTimeout(
|
|
581
|
+
createClientRenderedFigmaAsset(input.figmaUrl, token, options, fetchOption),
|
|
582
|
+
options.timeoutMs ?? 1e4
|
|
583
|
+
);
|
|
584
|
+
return {
|
|
585
|
+
...input,
|
|
586
|
+
imageFormat: asset.imageFormat,
|
|
587
|
+
asset
|
|
588
|
+
};
|
|
589
|
+
} catch {
|
|
590
|
+
return input;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
function normalizeClientRenderOptions(clientRender) {
|
|
594
|
+
if (!clientRender) return null;
|
|
595
|
+
if (clientRender === true) return {};
|
|
596
|
+
return clientRender;
|
|
597
|
+
}
|
|
598
|
+
function readClientRenderToken(options) {
|
|
599
|
+
const token = typeof options.token === "function" ? options.token() : options.token;
|
|
600
|
+
return typeof token === "string" ? token.trim() : "";
|
|
601
|
+
}
|
|
602
|
+
async function createClientRenderedFigmaAsset(figmaUrl, token, options, fetchOption) {
|
|
603
|
+
const ref = parseReviewFigmaNodeRef(figmaUrl);
|
|
604
|
+
if (!ref) throw new Error("A Figma node link is required.");
|
|
605
|
+
const requestFetch = fetchOption ?? globalThis.fetch;
|
|
606
|
+
if (!requestFetch) throw new Error("Figma client rendering requires fetch.");
|
|
607
|
+
const renderFormat = options.renderFormat ?? "png";
|
|
608
|
+
const response = await requestFetch(
|
|
609
|
+
createReviewFigmaImageApiUrl({
|
|
610
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
611
|
+
fileKey: ref.fileKey,
|
|
612
|
+
nodeId: ref.nodeId,
|
|
613
|
+
format: renderFormat,
|
|
614
|
+
scale: options.renderScale,
|
|
615
|
+
useAbsoluteBounds: options.useAbsoluteBounds
|
|
616
|
+
}),
|
|
617
|
+
{
|
|
618
|
+
headers: {
|
|
619
|
+
"X-Figma-Token": token
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
);
|
|
623
|
+
const body = await response.json().catch(() => null);
|
|
624
|
+
if (!response.ok) {
|
|
625
|
+
throw new Error(body?.err || `Figma image render failed with ${response.status}`);
|
|
626
|
+
}
|
|
627
|
+
const imageUrl = body?.images?.[ref.nodeId];
|
|
628
|
+
if (!imageUrl) throw new Error(`Figma image render returned no URL for ${ref.nodeId}.`);
|
|
629
|
+
const imageResponse = await requestFetch(imageUrl);
|
|
630
|
+
if (!imageResponse.ok) {
|
|
631
|
+
throw new Error(`Figma image download failed with ${imageResponse.status}`);
|
|
632
|
+
}
|
|
633
|
+
const originalBlob = await imageResponse.blob();
|
|
634
|
+
const originalMimeType = normalizeClientImageMimeType(originalBlob.type) ?? getReviewFigmaImageMimeType(renderFormat === "jpg" ? "jpg" : "png");
|
|
635
|
+
const originalFormat = getReviewFigmaImageFormatFromMimeType(originalMimeType) ?? (renderFormat === "jpg" ? "jpg" : "png");
|
|
636
|
+
const dimensions = await readImageBlobDimensions(originalBlob);
|
|
637
|
+
const shouldConvertToWebp = options.convertToWebp ?? true;
|
|
638
|
+
const convertedBlob = shouldConvertToWebp ? await convertImageBlobToWebp(
|
|
639
|
+
originalBlob,
|
|
640
|
+
options.webpQuality ?? 0.9,
|
|
641
|
+
dimensions
|
|
642
|
+
).catch(() => null) : null;
|
|
643
|
+
const finalBlob = convertedBlob?.type === "image/webp" ? convertedBlob : originalBlob;
|
|
644
|
+
const finalMimeType = normalizeClientImageMimeType(finalBlob.type) ?? originalMimeType;
|
|
645
|
+
const finalFormat = getReviewFigmaImageFormatFromMimeType(finalMimeType) ?? originalFormat;
|
|
646
|
+
return {
|
|
647
|
+
dataUrl: await blobToDataUrl(finalBlob),
|
|
648
|
+
imageFormat: finalFormat,
|
|
649
|
+
mimeType: finalMimeType,
|
|
650
|
+
byteSize: finalBlob.size,
|
|
651
|
+
width: dimensions.width,
|
|
652
|
+
height: dimensions.height
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
async function readImageBlobDimensions(blob) {
|
|
656
|
+
const image = await loadImageBlob(blob);
|
|
657
|
+
return {
|
|
658
|
+
width: image.naturalWidth || image.width,
|
|
659
|
+
height: image.naturalHeight || image.height
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
async function convertImageBlobToWebp(blob, quality, dimensions) {
|
|
663
|
+
if (typeof document === "undefined") return null;
|
|
664
|
+
if (!dimensions.width || !dimensions.height) return null;
|
|
665
|
+
const image = await loadImageBlob(blob);
|
|
666
|
+
const canvas = document.createElement("canvas");
|
|
667
|
+
canvas.width = dimensions.width;
|
|
668
|
+
canvas.height = dimensions.height;
|
|
669
|
+
const context = canvas.getContext("2d");
|
|
670
|
+
if (!context) return null;
|
|
671
|
+
context.drawImage(image, 0, 0);
|
|
672
|
+
return new Promise((resolve) => {
|
|
673
|
+
canvas.toBlob(resolve, "image/webp", quality);
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
function loadImageBlob(blob) {
|
|
677
|
+
return new Promise((resolve, reject) => {
|
|
678
|
+
if (typeof Image === "undefined" || typeof URL === "undefined") {
|
|
679
|
+
reject(new Error("Image decoding is unavailable."));
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
const image = new Image();
|
|
683
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
684
|
+
image.onload = () => {
|
|
685
|
+
URL.revokeObjectURL(objectUrl);
|
|
686
|
+
resolve(image);
|
|
687
|
+
};
|
|
688
|
+
image.onerror = () => {
|
|
689
|
+
URL.revokeObjectURL(objectUrl);
|
|
690
|
+
reject(new Error("Image decoding failed."));
|
|
691
|
+
};
|
|
692
|
+
image.src = objectUrl;
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
function blobToDataUrl(blob) {
|
|
696
|
+
return new Promise((resolve, reject) => {
|
|
697
|
+
const reader = new FileReader();
|
|
698
|
+
reader.onload = () => {
|
|
699
|
+
if (typeof reader.result === "string") {
|
|
700
|
+
resolve(reader.result);
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
reject(new Error("Blob encoding failed."));
|
|
704
|
+
};
|
|
705
|
+
reader.onerror = () => reject(reader.error ?? new Error("Blob encoding failed."));
|
|
706
|
+
reader.readAsDataURL(blob);
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
function normalizeClientImageMimeType(value) {
|
|
710
|
+
const mimeType = value?.split(";")[0]?.trim().toLowerCase();
|
|
711
|
+
if (mimeType === "image/jpg") return "image/jpeg";
|
|
712
|
+
if (mimeType === "image/jpeg" || mimeType === "image/png" || mimeType === "image/webp") {
|
|
713
|
+
return mimeType;
|
|
714
|
+
}
|
|
715
|
+
return null;
|
|
716
|
+
}
|
|
717
|
+
async function withTimeout(promise, timeoutMs) {
|
|
718
|
+
let timeoutId;
|
|
719
|
+
try {
|
|
720
|
+
return await Promise.race([
|
|
721
|
+
promise,
|
|
722
|
+
new Promise((_, reject) => {
|
|
723
|
+
timeoutId = setTimeout(
|
|
724
|
+
() => reject(new Error("Figma client rendering timed out.")),
|
|
725
|
+
timeoutMs
|
|
726
|
+
);
|
|
727
|
+
})
|
|
728
|
+
]);
|
|
729
|
+
} finally {
|
|
730
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
function getReviewFigmaImageTargetKey(target) {
|
|
734
|
+
return JSON.stringify(normalizeReviewFigmaImageTarget(target));
|
|
735
|
+
}
|
|
736
|
+
function getReviewFigmaImageMimeType(format) {
|
|
737
|
+
if (format === "jpg") return "image/jpeg";
|
|
738
|
+
if (format === "png") return "image/png";
|
|
739
|
+
return "image/webp";
|
|
740
|
+
}
|
|
741
|
+
function createReviewFigmaImageStoreRequest(endpoint, fetchOption) {
|
|
742
|
+
return async (input, init = {}) => {
|
|
743
|
+
const requestFetch = fetchOption ?? globalThis.fetch;
|
|
744
|
+
if (!requestFetch) throw new Error("Figma image store requires fetch.");
|
|
745
|
+
const response = await requestFetch(input, {
|
|
746
|
+
...init,
|
|
747
|
+
headers: {
|
|
748
|
+
"Content-Type": "application/json",
|
|
749
|
+
...init.headers ?? {}
|
|
750
|
+
}
|
|
751
|
+
});
|
|
752
|
+
const text = await response.text();
|
|
753
|
+
const body = text ? JSON.parse(text) : null;
|
|
754
|
+
if (!response.ok) {
|
|
755
|
+
const message = typeof body?.error === "string" ? body.error : `Figma image store request failed: ${response.status}`;
|
|
756
|
+
throw new Error(message);
|
|
757
|
+
}
|
|
758
|
+
return body;
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
function normalizeReviewFigmaImageTarget(target) {
|
|
762
|
+
if (target.type === "figma-node") {
|
|
763
|
+
return {
|
|
764
|
+
type: target.type,
|
|
765
|
+
projectId: target.projectId,
|
|
766
|
+
fileKey: target.fileKey,
|
|
767
|
+
nodeId: target.nodeId
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
return {
|
|
771
|
+
type: target.type,
|
|
772
|
+
projectId: target.projectId,
|
|
773
|
+
pageUrl: target.pageUrl,
|
|
774
|
+
slot: target.slot ?? "",
|
|
775
|
+
viewport: target.viewport ? {
|
|
776
|
+
label: target.viewport.label ?? "",
|
|
777
|
+
width: target.viewport.width ?? null,
|
|
778
|
+
height: target.viewport.height ?? null,
|
|
779
|
+
scope: target.viewport.scope ?? ""
|
|
780
|
+
} : null
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// src/figma/image.snapshot.ts
|
|
785
|
+
function createReviewFigmaImagesSnapshot(images, options = {}) {
|
|
786
|
+
const targetKeys = options.targets?.length ? new Set(options.targets.map(getReviewFigmaImageTargetKey)) : null;
|
|
787
|
+
return images.filter((image) => {
|
|
788
|
+
if (options.projectId && image.projectId !== options.projectId) {
|
|
789
|
+
return false;
|
|
790
|
+
}
|
|
791
|
+
if (targetKeys && !targetKeys.has(getReviewFigmaImageTargetKey(image.target))) {
|
|
792
|
+
return false;
|
|
793
|
+
}
|
|
794
|
+
return true;
|
|
795
|
+
}).map(cloneReviewFigmaImage).sort(compareReviewFigmaSnapshotImages);
|
|
796
|
+
}
|
|
797
|
+
function createReviewFigmaReleaseSnapshot({
|
|
798
|
+
images,
|
|
799
|
+
projectId,
|
|
800
|
+
releaseId,
|
|
801
|
+
label,
|
|
802
|
+
createdAt,
|
|
803
|
+
targets
|
|
804
|
+
}) {
|
|
805
|
+
return {
|
|
806
|
+
version: 1,
|
|
807
|
+
projectId,
|
|
808
|
+
...releaseId ? { releaseId } : null,
|
|
809
|
+
...label ? { label } : null,
|
|
810
|
+
createdAt: createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
811
|
+
figmaImagesSnapshot: createReviewFigmaImagesSnapshot(images, {
|
|
812
|
+
projectId,
|
|
813
|
+
targets
|
|
814
|
+
})
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
async function collectReviewFigmaReleaseSnapshot({
|
|
818
|
+
store,
|
|
819
|
+
targets,
|
|
820
|
+
...snapshotOptions
|
|
821
|
+
}) {
|
|
822
|
+
const imagesByTarget = await Promise.all(
|
|
823
|
+
targets.map((target) => store.listImages(target))
|
|
824
|
+
);
|
|
825
|
+
return createReviewFigmaReleaseSnapshot({
|
|
826
|
+
...snapshotOptions,
|
|
827
|
+
targets,
|
|
828
|
+
images: dedupeReviewFigmaImages(imagesByTarget.flat())
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
function dedupeReviewFigmaImages(images) {
|
|
832
|
+
return Array.from(new Map(images.map((image) => [image.id, image])).values());
|
|
833
|
+
}
|
|
834
|
+
function cloneReviewFigmaImage(image) {
|
|
835
|
+
return {
|
|
836
|
+
...image,
|
|
837
|
+
target: cloneReviewFigmaImageTarget(image.target)
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
function cloneReviewFigmaImageTarget(target) {
|
|
841
|
+
if (target.type === "figma-node") {
|
|
842
|
+
return {
|
|
843
|
+
type: target.type,
|
|
844
|
+
projectId: target.projectId,
|
|
845
|
+
fileKey: target.fileKey,
|
|
846
|
+
nodeId: target.nodeId
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
return {
|
|
850
|
+
type: target.type,
|
|
851
|
+
projectId: target.projectId,
|
|
852
|
+
pageUrl: target.pageUrl,
|
|
853
|
+
slot: target.slot,
|
|
854
|
+
viewport: target.viewport ? {
|
|
855
|
+
label: target.viewport.label,
|
|
856
|
+
width: target.viewport.width,
|
|
857
|
+
height: target.viewport.height,
|
|
858
|
+
scope: target.viewport.scope
|
|
859
|
+
} : void 0
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
function compareReviewFigmaSnapshotImages(a, b) {
|
|
863
|
+
return a.projectId.localeCompare(b.projectId) || getReviewFigmaImageTargetKey(a.target).localeCompare(
|
|
864
|
+
getReviewFigmaImageTargetKey(b.target)
|
|
865
|
+
) || a.order - b.order || a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id);
|
|
866
|
+
}
|
|
867
|
+
|
|
368
868
|
// src/core/geometry.ts
|
|
369
869
|
function rectanglesIntersect(a, b) {
|
|
370
870
|
return a.left < b.left + b.width && a.left + a.width > b.left && a.top < b.top + b.height && a.top + a.height > b.top;
|
|
@@ -547,13 +1047,13 @@ function getDomAnchorFromPoint(point, configuredAttribute = "data-qa-id", enviro
|
|
|
547
1047
|
const target = environment.document.elementFromPoint(point.x, point.y);
|
|
548
1048
|
if (!target) return void 0;
|
|
549
1049
|
const candidates = createAnchorCandidates(target, configuredAttribute);
|
|
550
|
-
const
|
|
551
|
-
if (!
|
|
1050
|
+
const primaryCandidate = candidates[0];
|
|
1051
|
+
if (!primaryCandidate) return void 0;
|
|
552
1052
|
return {
|
|
553
|
-
...
|
|
1053
|
+
...primaryCandidate,
|
|
554
1054
|
candidates,
|
|
555
1055
|
htmlSnippet: getElementHtmlSnippet(
|
|
556
|
-
getAnchorSourceElement(target,
|
|
1056
|
+
getAnchorSourceElement(target, primaryCandidate, configuredAttribute) ?? target
|
|
557
1057
|
),
|
|
558
1058
|
source: getDomSourceHint(target)
|
|
559
1059
|
};
|
|
@@ -561,13 +1061,13 @@ function getDomAnchorFromPoint(point, configuredAttribute = "data-qa-id", enviro
|
|
|
561
1061
|
function getDomAnchorFromElement(target, configuredAttribute = "data-qa-id", environment) {
|
|
562
1062
|
if (target.ownerDocument !== environment.document) return void 0;
|
|
563
1063
|
const candidates = createAnchorCandidates(target, configuredAttribute);
|
|
564
|
-
const
|
|
565
|
-
if (!
|
|
1064
|
+
const primaryCandidate = candidates[0];
|
|
1065
|
+
if (!primaryCandidate) return void 0;
|
|
566
1066
|
return {
|
|
567
|
-
...
|
|
1067
|
+
...primaryCandidate,
|
|
568
1068
|
candidates,
|
|
569
1069
|
htmlSnippet: getElementHtmlSnippet(
|
|
570
|
-
getAnchorSourceElement(target,
|
|
1070
|
+
getAnchorSourceElement(target, primaryCandidate, configuredAttribute) ?? target
|
|
571
1071
|
),
|
|
572
1072
|
source: getDomSourceHint(target)
|
|
573
1073
|
};
|
|
@@ -738,7 +1238,7 @@ function createAnchorCandidates(target, configuredAttribute) {
|
|
|
738
1238
|
function findClosestAttributeAnchor(target, attributeNames, confidence, options) {
|
|
739
1239
|
for (const attributeName of attributeNames) {
|
|
740
1240
|
const selector = `[${attributeName}]`;
|
|
741
|
-
const element =
|
|
1241
|
+
const element = tryClosest(target, selector);
|
|
742
1242
|
if (!element) continue;
|
|
743
1243
|
const value = getStableAttributeValue(element, attributeName);
|
|
744
1244
|
if (!value) continue;
|
|
@@ -844,7 +1344,7 @@ function getAnchorSourceElement(target, candidate, configuredAttribute) {
|
|
|
844
1344
|
return target;
|
|
845
1345
|
}
|
|
846
1346
|
}
|
|
847
|
-
function
|
|
1347
|
+
function tryClosest(element, selector) {
|
|
848
1348
|
try {
|
|
849
1349
|
return element.closest(selector);
|
|
850
1350
|
} catch {
|
|
@@ -1444,18 +1944,90 @@ function setDocumentScrollInstantly(environment, position) {
|
|
|
1444
1944
|
);
|
|
1445
1945
|
}
|
|
1446
1946
|
|
|
1947
|
+
// src/core/draft.metrics.ts
|
|
1948
|
+
function getDraftViewportScale(viewport, presets) {
|
|
1949
|
+
const preset = findReviewViewportPreset(viewport, presets);
|
|
1950
|
+
const designWidth = typeof preset.designWidth === "number" && preset.designWidth > 0 ? preset.designWidth : viewport.width;
|
|
1951
|
+
const scale = designWidth > 0 ? viewport.width / designWidth : 1;
|
|
1952
|
+
return { scale, designWidth, presetLabel: preset.label };
|
|
1953
|
+
}
|
|
1954
|
+
function getDraftAdjustmentMetrics(draft, presets) {
|
|
1955
|
+
const adjustment = draft.adjustment;
|
|
1956
|
+
const x = adjustment?.x ?? 0;
|
|
1957
|
+
const y = adjustment?.y ?? 0;
|
|
1958
|
+
const scale = adjustment?.scale ?? 0;
|
|
1959
|
+
const {
|
|
1960
|
+
scale: viewportScale,
|
|
1961
|
+
designWidth,
|
|
1962
|
+
presetLabel
|
|
1963
|
+
} = getDraftViewportScale(draft.viewport, presets);
|
|
1964
|
+
const selection = draft.selection ? toViewportSelection(draft.selection.viewport) : void 0;
|
|
1965
|
+
const scaleCssDelta = scale * viewportScale;
|
|
1966
|
+
const scaleFactor = selection && selection.width > 0 ? Math.max(
|
|
1967
|
+
1 / selection.width,
|
|
1968
|
+
(selection.width + scaleCssDelta) / selection.width
|
|
1969
|
+
) : 1;
|
|
1970
|
+
return {
|
|
1971
|
+
x,
|
|
1972
|
+
y,
|
|
1973
|
+
scale,
|
|
1974
|
+
cssX: x * viewportScale,
|
|
1975
|
+
cssY: y * viewportScale,
|
|
1976
|
+
scaleFactor,
|
|
1977
|
+
viewportScale,
|
|
1978
|
+
designWidth,
|
|
1979
|
+
presetLabel,
|
|
1980
|
+
viewportWidth: draft.viewport.width
|
|
1981
|
+
};
|
|
1982
|
+
}
|
|
1983
|
+
function hasDraftAdjustment(draft, presets) {
|
|
1984
|
+
const metrics = getDraftAdjustmentMetrics(draft, presets);
|
|
1985
|
+
return metrics.x !== 0 || metrics.y !== 0 || metrics.scale !== 0;
|
|
1986
|
+
}
|
|
1987
|
+
function getAdjustedDraftPoint(point, draft, presets) {
|
|
1988
|
+
const metrics = getDraftAdjustmentMetrics(draft, presets);
|
|
1989
|
+
return {
|
|
1990
|
+
x: point.x + metrics.cssX,
|
|
1991
|
+
y: point.y + metrics.cssY
|
|
1992
|
+
};
|
|
1993
|
+
}
|
|
1994
|
+
function getAdjustedDraftSelection(selection, draft, presets) {
|
|
1995
|
+
const metrics = getDraftAdjustmentMetrics(draft, presets);
|
|
1996
|
+
return {
|
|
1997
|
+
...selection,
|
|
1998
|
+
left: selection.left + metrics.cssX,
|
|
1999
|
+
top: selection.top + metrics.cssY,
|
|
2000
|
+
width: selection.width * metrics.scaleFactor,
|
|
2001
|
+
height: selection.height * metrics.scaleFactor
|
|
2002
|
+
};
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
// src/core/typography.tokens.ts
|
|
2006
|
+
var reviewTypographyTokens = `
|
|
2007
|
+
--df-review-font-sans: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
2008
|
+
--df-review-font-mono: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
|
2009
|
+
--df-review-font-size-3xs: 9px;
|
|
2010
|
+
--df-review-font-size-2xs: 10px;
|
|
2011
|
+
--df-review-font-size-xs: 11px;
|
|
2012
|
+
--df-review-font-size-sm: 12px;
|
|
2013
|
+
--df-review-font-size-md: 13px;
|
|
2014
|
+
--df-review-font-size-lg: 14px;
|
|
2015
|
+
--df-review-font-size-xl: 15px;
|
|
2016
|
+
--df-review-font-size-2xl: 18px;
|
|
2017
|
+
--df-review-font-weight-normal: 400;
|
|
2018
|
+
--df-review-font-weight-emphasis: 500;
|
|
2019
|
+
--df-review-line-height-tight: 1.25;
|
|
2020
|
+
--df-review-line-height-base: 1.42;
|
|
2021
|
+
--df-review-line-height-relaxed: 1.55;
|
|
2022
|
+
`;
|
|
2023
|
+
|
|
1447
2024
|
// src/core/overlay.style.ts
|
|
1448
2025
|
function createStyleElement() {
|
|
1449
2026
|
const style = document.createElement("style");
|
|
1450
2027
|
style.textContent = `
|
|
1451
2028
|
:host {
|
|
1452
2029
|
color-scheme: dark;
|
|
1453
|
-
|
|
1454
|
-
--df-review-font-size-2xs: 10px;
|
|
1455
|
-
--df-review-font-size-xs: 11px;
|
|
1456
|
-
--df-review-font-size-sm: 12px;
|
|
1457
|
-
--df-review-font-size-md: 13px;
|
|
1458
|
-
--df-review-font-size-xl: 15px;
|
|
2030
|
+
${reviewTypographyTokens}
|
|
1459
2031
|
--df-review-space-1: 4px;
|
|
1460
2032
|
--df-review-space-1-5: 6px;
|
|
1461
2033
|
--df-review-space-2: 8px;
|
|
@@ -1554,8 +2126,8 @@ function createStyleElement() {
|
|
|
1554
2126
|
}
|
|
1555
2127
|
|
|
1556
2128
|
.dfwr-title {
|
|
1557
|
-
font-size:
|
|
1558
|
-
font-weight:
|
|
2129
|
+
font-size: var(--df-review-font-size-xl);
|
|
2130
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
1559
2131
|
line-height: 1.25;
|
|
1560
2132
|
}
|
|
1561
2133
|
|
|
@@ -1599,11 +2171,15 @@ function createStyleElement() {
|
|
|
1599
2171
|
}
|
|
1600
2172
|
|
|
1601
2173
|
.dfwr-button {
|
|
2174
|
+
display: inline-flex;
|
|
2175
|
+
align-items: center;
|
|
2176
|
+
justify-content: center;
|
|
2177
|
+
gap: 6px;
|
|
1602
2178
|
min-height: var(--df-review-control-height-md);
|
|
1603
2179
|
padding: 0 12px;
|
|
1604
2180
|
border-radius: var(--df-review-radius-sm);
|
|
1605
2181
|
font-size: var(--df-review-font-size-sm);
|
|
1606
|
-
font-weight:
|
|
2182
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
1607
2183
|
}
|
|
1608
2184
|
|
|
1609
2185
|
.dfwr-button:hover,
|
|
@@ -1619,6 +2195,21 @@ function createStyleElement() {
|
|
|
1619
2195
|
color: var(--df-review-color-accent-contrast);
|
|
1620
2196
|
}
|
|
1621
2197
|
|
|
2198
|
+
.dfwr-button:disabled {
|
|
2199
|
+
cursor: default;
|
|
2200
|
+
opacity: 0.62;
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
.dfwr-spinner {
|
|
2204
|
+
display: inline-block;
|
|
2205
|
+
width: 12px;
|
|
2206
|
+
height: 12px;
|
|
2207
|
+
border: 2px solid currentColor;
|
|
2208
|
+
border-right-color: transparent;
|
|
2209
|
+
border-radius: 999px;
|
|
2210
|
+
animation: dfwr-spin 720ms linear infinite;
|
|
2211
|
+
}
|
|
2212
|
+
|
|
1622
2213
|
.dfwr-icon-button {
|
|
1623
2214
|
display: inline-flex;
|
|
1624
2215
|
align-items: center;
|
|
@@ -1628,7 +2219,7 @@ function createStyleElement() {
|
|
|
1628
2219
|
padding: 0 8px;
|
|
1629
2220
|
border-radius: var(--df-review-radius-sm);
|
|
1630
2221
|
font-size: var(--df-review-font-size-xs);
|
|
1631
|
-
font-weight:
|
|
2222
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
1632
2223
|
line-height: 1;
|
|
1633
2224
|
text-transform: uppercase;
|
|
1634
2225
|
}
|
|
@@ -1783,7 +2374,7 @@ function createStyleElement() {
|
|
|
1783
2374
|
0 8px 18px rgba(0, 0, 0, 0.28);
|
|
1784
2375
|
color: #111820;
|
|
1785
2376
|
font-size: var(--df-review-font-size-2xs);
|
|
1786
|
-
font-weight:
|
|
2377
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
1787
2378
|
line-height: 1;
|
|
1788
2379
|
pointer-events: none;
|
|
1789
2380
|
}
|
|
@@ -1809,7 +2400,7 @@ function createStyleElement() {
|
|
|
1809
2400
|
box-shadow: 0 0 0 4px rgba(var(--dfwr-scope-rgb), 0.18);
|
|
1810
2401
|
color: var(--dfwr-scope);
|
|
1811
2402
|
font-size: var(--df-review-font-size-2xs);
|
|
1812
|
-
font-weight:
|
|
2403
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
1813
2404
|
}
|
|
1814
2405
|
|
|
1815
2406
|
.dfwr-bound-marker.is-highlighted {
|
|
@@ -2151,10 +2742,18 @@ function createStyleElement() {
|
|
|
2151
2742
|
gap: 10px;
|
|
2152
2743
|
}
|
|
2153
2744
|
|
|
2745
|
+
.dfwr-form-error {
|
|
2746
|
+
margin: 0;
|
|
2747
|
+
color: #ff8f61;
|
|
2748
|
+
font-size: var(--df-review-font-size-sm);
|
|
2749
|
+
line-height: 1.4;
|
|
2750
|
+
overflow-wrap: anywhere;
|
|
2751
|
+
}
|
|
2752
|
+
|
|
2753
|
+
.dfwr-input,
|
|
2754
|
+
.dfwr-select,
|
|
2154
2755
|
.dfwr-textarea {
|
|
2155
2756
|
width: 100%;
|
|
2156
|
-
min-height: 92px;
|
|
2157
|
-
resize: vertical;
|
|
2158
2757
|
border: 1px solid rgba(255, 255, 255, 0.16);
|
|
2159
2758
|
border-radius: var(--df-review-radius-sm);
|
|
2160
2759
|
padding: 10px;
|
|
@@ -2165,14 +2764,39 @@ function createStyleElement() {
|
|
|
2165
2764
|
line-height: 1.45;
|
|
2166
2765
|
}
|
|
2167
2766
|
|
|
2767
|
+
.dfwr-input,
|
|
2768
|
+
.dfwr-select {
|
|
2769
|
+
min-height: 38px;
|
|
2770
|
+
}
|
|
2771
|
+
|
|
2772
|
+
.dfwr-select {
|
|
2773
|
+
appearance: none;
|
|
2774
|
+
cursor: pointer;
|
|
2775
|
+
}
|
|
2776
|
+
|
|
2777
|
+
.dfwr-textarea {
|
|
2778
|
+
min-height: 92px;
|
|
2779
|
+
resize: vertical;
|
|
2780
|
+
}
|
|
2781
|
+
|
|
2782
|
+
.dfwr-input:focus,
|
|
2783
|
+
.dfwr-select:focus,
|
|
2168
2784
|
.dfwr-textarea:focus {
|
|
2169
2785
|
outline: 2px solid var(--df-review-color-accent-ring);
|
|
2170
2786
|
outline-offset: 1px;
|
|
2171
2787
|
}
|
|
2172
2788
|
|
|
2173
2789
|
@media (hover: none) and (pointer: coarse) {
|
|
2790
|
+
.dfwr-input,
|
|
2791
|
+
.dfwr-select,
|
|
2174
2792
|
.dfwr-textarea {
|
|
2175
|
-
font-size:
|
|
2793
|
+
font-size: var(--df-review-font-size-xl);
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
|
|
2797
|
+
@keyframes dfwr-spin {
|
|
2798
|
+
to {
|
|
2799
|
+
transform: rotate(360deg);
|
|
2176
2800
|
}
|
|
2177
2801
|
}
|
|
2178
2802
|
|
|
@@ -2230,8 +2854,8 @@ function createStyleElement() {
|
|
|
2230
2854
|
color: var(--df-review-color-text);
|
|
2231
2855
|
cursor: pointer;
|
|
2232
2856
|
font: inherit;
|
|
2233
|
-
font-size:
|
|
2234
|
-
font-weight:
|
|
2857
|
+
font-size: var(--df-review-font-size-lg);
|
|
2858
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
2235
2859
|
line-height: 1;
|
|
2236
2860
|
}
|
|
2237
2861
|
|
|
@@ -2284,7 +2908,7 @@ function createStyleElement() {
|
|
|
2284
2908
|
margin-bottom: 10px;
|
|
2285
2909
|
color: rgba(247, 247, 242, 0.74);
|
|
2286
2910
|
font-size: var(--df-review-font-size-sm);
|
|
2287
|
-
font-weight:
|
|
2911
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
2288
2912
|
}
|
|
2289
2913
|
|
|
2290
2914
|
.dfwr-item {
|
|
@@ -2306,6 +2930,8 @@ function createStyleElement() {
|
|
|
2306
2930
|
}
|
|
2307
2931
|
|
|
2308
2932
|
.dfwr-item-body {
|
|
2933
|
+
display: grid;
|
|
2934
|
+
gap: 4px;
|
|
2309
2935
|
min-width: 0;
|
|
2310
2936
|
flex: 1;
|
|
2311
2937
|
}
|
|
@@ -2325,7 +2951,7 @@ function createStyleElement() {
|
|
|
2325
2951
|
border-radius: var(--df-review-radius-pill);
|
|
2326
2952
|
padding: 0 7px;
|
|
2327
2953
|
font-size: var(--df-review-font-size-2xs);
|
|
2328
|
-
font-weight:
|
|
2954
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
2329
2955
|
line-height: 1;
|
|
2330
2956
|
letter-spacing: 0;
|
|
2331
2957
|
text-transform: uppercase;
|
|
@@ -2343,13 +2969,29 @@ function createStyleElement() {
|
|
|
2343
2969
|
color: rgba(247, 247, 242, 0.64);
|
|
2344
2970
|
}
|
|
2345
2971
|
|
|
2972
|
+
.dfwr-item-title {
|
|
2973
|
+
margin: 4px 0 0;
|
|
2974
|
+
color: var(--df-review-color-text);
|
|
2975
|
+
font-size: var(--df-review-font-size-md);
|
|
2976
|
+
font-weight: var(--df-review-font-weight-normal);
|
|
2977
|
+
line-height: 1.35;
|
|
2978
|
+
overflow-wrap: anywhere;
|
|
2979
|
+
}
|
|
2980
|
+
|
|
2346
2981
|
.dfwr-item-comment {
|
|
2982
|
+
margin: 0;
|
|
2983
|
+
color: var(--df-review-color-text-muted);
|
|
2984
|
+
font-size: var(--df-review-font-size-sm);
|
|
2985
|
+
line-height: 1.45;
|
|
2986
|
+
overflow-wrap: anywhere;
|
|
2987
|
+
white-space: pre-wrap;
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2990
|
+
.dfwr-item-comment.is-primary {
|
|
2347
2991
|
margin: 4px 0;
|
|
2348
2992
|
color: var(--df-review-color-text);
|
|
2349
2993
|
font-size: var(--df-review-font-size-md);
|
|
2350
2994
|
line-height: 1.42;
|
|
2351
|
-
overflow-wrap: anywhere;
|
|
2352
|
-
white-space: pre-wrap;
|
|
2353
2995
|
}
|
|
2354
2996
|
|
|
2355
2997
|
.dfwr-item-date {
|
|
@@ -2593,7 +3235,7 @@ var WebReviewKitView = class {
|
|
|
2593
3235
|
);
|
|
2594
3236
|
}
|
|
2595
3237
|
}
|
|
2596
|
-
if (state.isOpen && state.mode === "area" && !state.areaDraft) {
|
|
3238
|
+
if (state.isOpen && state.mode === "area" && !state.areaDraft && !state.isSelectingArea) {
|
|
2597
3239
|
shell.append(this.createAreaLayer());
|
|
2598
3240
|
}
|
|
2599
3241
|
if (state.isOpen && state.mode === "area" && state.areaDraft && this.config.options.ui?.panel === false) {
|
|
@@ -2667,64 +3309,29 @@ var WebReviewKitView = class {
|
|
|
2667
3309
|
this.config.actions.setSelectingArea(false);
|
|
2668
3310
|
this.config.actions.render();
|
|
2669
3311
|
}
|
|
3312
|
+
// Draft adjustment geometry lives in draft.metrics.ts; these thin wrappers
|
|
3313
|
+
// supply the configured viewport presets so call sites stay unchanged.
|
|
3314
|
+
get viewportPresets() {
|
|
3315
|
+
return this.config.options.viewports?.presets;
|
|
3316
|
+
}
|
|
2670
3317
|
getDraftAdjustmentMetrics(draft) {
|
|
2671
|
-
|
|
2672
|
-
const x = adjustment?.x ?? 0;
|
|
2673
|
-
const y = adjustment?.y ?? 0;
|
|
2674
|
-
const scale = adjustment?.scale ?? 0;
|
|
2675
|
-
const {
|
|
2676
|
-
scale: viewportScale,
|
|
2677
|
-
designWidth,
|
|
2678
|
-
presetLabel
|
|
2679
|
-
} = this.getDraftViewportScale(draft.viewport);
|
|
2680
|
-
const selection = draft.selection ? toViewportSelection(draft.selection.viewport) : void 0;
|
|
2681
|
-
const scaleCssDelta = scale * viewportScale;
|
|
2682
|
-
const scaleFactor = selection && selection.width > 0 ? Math.max(
|
|
2683
|
-
1 / selection.width,
|
|
2684
|
-
(selection.width + scaleCssDelta) / selection.width
|
|
2685
|
-
) : 1;
|
|
2686
|
-
return {
|
|
2687
|
-
x,
|
|
2688
|
-
y,
|
|
2689
|
-
scale,
|
|
2690
|
-
cssX: x * viewportScale,
|
|
2691
|
-
cssY: y * viewportScale,
|
|
2692
|
-
scaleFactor,
|
|
2693
|
-
viewportScale,
|
|
2694
|
-
designWidth,
|
|
2695
|
-
presetLabel,
|
|
2696
|
-
viewportWidth: draft.viewport.width
|
|
2697
|
-
};
|
|
3318
|
+
return getDraftAdjustmentMetrics(draft, this.viewportPresets);
|
|
2698
3319
|
}
|
|
2699
3320
|
hasDraftAdjustment(draft) {
|
|
2700
|
-
|
|
2701
|
-
return metrics.x !== 0 || metrics.y !== 0 || metrics.scale !== 0;
|
|
3321
|
+
return hasDraftAdjustment(draft, this.viewportPresets);
|
|
2702
3322
|
}
|
|
2703
3323
|
getAdjustedDraftPoint(point, draft) {
|
|
2704
|
-
|
|
2705
|
-
return {
|
|
2706
|
-
x: point.x + metrics.cssX,
|
|
2707
|
-
y: point.y + metrics.cssY
|
|
2708
|
-
};
|
|
3324
|
+
return getAdjustedDraftPoint(point, draft, this.viewportPresets);
|
|
2709
3325
|
}
|
|
2710
3326
|
getAdjustedDraftSelection(selection, draft) {
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
width: selection.width * metrics.scaleFactor,
|
|
2717
|
-
height: selection.height * metrics.scaleFactor
|
|
2718
|
-
};
|
|
3327
|
+
return getAdjustedDraftSelection(
|
|
3328
|
+
selection,
|
|
3329
|
+
draft,
|
|
3330
|
+
this.viewportPresets
|
|
3331
|
+
);
|
|
2719
3332
|
}
|
|
2720
3333
|
getDraftViewportScale(viewport) {
|
|
2721
|
-
|
|
2722
|
-
viewport,
|
|
2723
|
-
this.config.options.viewports?.presets
|
|
2724
|
-
);
|
|
2725
|
-
const designWidth = typeof preset.designWidth === "number" && preset.designWidth > 0 ? preset.designWidth : viewport.width;
|
|
2726
|
-
const scale = designWidth > 0 ? viewport.width / designWidth : 1;
|
|
2727
|
-
return { scale, designWidth, presetLabel: preset.label };
|
|
3334
|
+
return getDraftViewportScale(viewport, this.viewportPresets);
|
|
2728
3335
|
}
|
|
2729
3336
|
getDraftComposerWidth(environment) {
|
|
2730
3337
|
const bounds = environment.overlayRect;
|
|
@@ -2870,6 +3477,67 @@ var WebReviewKitView = class {
|
|
|
2870
3477
|
return trimmedComment ? `${trimmedComment}
|
|
2871
3478
|
${adjustment}` : adjustment;
|
|
2872
3479
|
}
|
|
3480
|
+
getAssigneeOption(assigneeId) {
|
|
3481
|
+
if (!assigneeId) return void 0;
|
|
3482
|
+
return this.config.options.assigneeOptions?.find(
|
|
3483
|
+
(option) => option.value === assigneeId
|
|
3484
|
+
);
|
|
3485
|
+
}
|
|
3486
|
+
getAssigneeName(assigneeId) {
|
|
3487
|
+
return this.getAssigneeOption(assigneeId)?.label;
|
|
3488
|
+
}
|
|
3489
|
+
createDraftTitleInput(value, onInput) {
|
|
3490
|
+
const input = document.createElement("input");
|
|
3491
|
+
input.className = "dfwr-input";
|
|
3492
|
+
input.placeholder = "Title";
|
|
3493
|
+
input.type = "text";
|
|
3494
|
+
input.value = value ?? "";
|
|
3495
|
+
input.addEventListener("input", () => onInput(input.value));
|
|
3496
|
+
return input;
|
|
3497
|
+
}
|
|
3498
|
+
isTitleFieldEnabled() {
|
|
3499
|
+
return this.config.options.fields?.title === true;
|
|
3500
|
+
}
|
|
3501
|
+
createDraftAssigneeSelect(value, fallbackLabel, onChange) {
|
|
3502
|
+
const assigneeOptions = this.config.options.assigneeOptions ?? [];
|
|
3503
|
+
if (assigneeOptions.length === 0) return void 0;
|
|
3504
|
+
const assigneeTitle = this.config.options.assigneeTitle?.trim() || "Assignee";
|
|
3505
|
+
const select = document.createElement("select");
|
|
3506
|
+
select.className = "dfwr-select";
|
|
3507
|
+
const emptyOption = document.createElement("option");
|
|
3508
|
+
emptyOption.value = "";
|
|
3509
|
+
emptyOption.textContent = assigneeTitle;
|
|
3510
|
+
select.append(emptyOption);
|
|
3511
|
+
const hasUnknownAssignee = Boolean(value) && !assigneeOptions.some((option) => option.value === value);
|
|
3512
|
+
if (hasUnknownAssignee && value) {
|
|
3513
|
+
const option = document.createElement("option");
|
|
3514
|
+
option.value = value;
|
|
3515
|
+
option.textContent = fallbackLabel ?? value;
|
|
3516
|
+
select.append(option);
|
|
3517
|
+
}
|
|
3518
|
+
assigneeOptions.forEach((assigneeOption) => {
|
|
3519
|
+
const option = document.createElement("option");
|
|
3520
|
+
option.value = assigneeOption.value;
|
|
3521
|
+
option.textContent = assigneeOption.label;
|
|
3522
|
+
select.append(option);
|
|
3523
|
+
});
|
|
3524
|
+
select.value = value ?? "";
|
|
3525
|
+
select.addEventListener("change", () => {
|
|
3526
|
+
onChange(select.value || null, this.getAssigneeName(select.value));
|
|
3527
|
+
});
|
|
3528
|
+
return select;
|
|
3529
|
+
}
|
|
3530
|
+
getDraftFields(titleInput, textarea, assigneeSelect) {
|
|
3531
|
+
const title = titleInput?.value.trim();
|
|
3532
|
+
const comment = textarea.value.trim();
|
|
3533
|
+
const assigneeId = assigneeSelect?.value.trim() || void 0;
|
|
3534
|
+
return {
|
|
3535
|
+
title: title || void 0,
|
|
3536
|
+
comment,
|
|
3537
|
+
assigneeId,
|
|
3538
|
+
assigneeName: this.getAssigneeName(assigneeId)
|
|
3539
|
+
};
|
|
3540
|
+
}
|
|
2873
3541
|
getStyleableDraftElement(draft, environment) {
|
|
2874
3542
|
if (draft.previewElement && draft.previewElement.ownerDocument === environment.document && "style" in draft.previewElement) {
|
|
2875
3543
|
return draft.previewElement;
|
|
@@ -3060,6 +3728,9 @@ ${adjustment}` : adjustment;
|
|
|
3060
3728
|
empty.textContent = this.state.noteDraft ? "Write the note in the page box." : this.state.mode === "element" ? "Click an element to add QA." : "Click on the page to place a note.";
|
|
3061
3729
|
return empty;
|
|
3062
3730
|
}
|
|
3731
|
+
// Builds the note draft layer: the on-page marker/highlight plus its composer
|
|
3732
|
+
// popover. When dockComposer is set the composer renders into the side panel
|
|
3733
|
+
// instead of floating next to the marker (used for the docked review mode).
|
|
3063
3734
|
createNotePopover(draft, options = {}) {
|
|
3064
3735
|
const environment = this.config.getEnvironment();
|
|
3065
3736
|
const group = document.createElement("div");
|
|
@@ -3123,6 +3794,14 @@ ${adjustment}` : adjustment;
|
|
|
3123
3794
|
meta.className = "dfwr-item-date";
|
|
3124
3795
|
meta.textContent = formatNoteDraftMeta(draft);
|
|
3125
3796
|
}
|
|
3797
|
+
const titleInput = this.isTitleFieldEnabled() ? this.createDraftTitleInput(draft.title, (title) => {
|
|
3798
|
+
const noteDraft = this.state.noteDraft;
|
|
3799
|
+
if (!noteDraft) return;
|
|
3800
|
+
this.config.actions.setNoteDraft({
|
|
3801
|
+
...noteDraft,
|
|
3802
|
+
title
|
|
3803
|
+
});
|
|
3804
|
+
}) : void 0;
|
|
3126
3805
|
const textarea = document.createElement("textarea");
|
|
3127
3806
|
textarea.className = "dfwr-textarea";
|
|
3128
3807
|
textarea.placeholder = "Review comment";
|
|
@@ -3136,13 +3815,30 @@ ${adjustment}` : adjustment;
|
|
|
3136
3815
|
comment: textarea.value
|
|
3137
3816
|
});
|
|
3138
3817
|
});
|
|
3818
|
+
const assigneeSelect = this.createDraftAssigneeSelect(
|
|
3819
|
+
draft.assigneeId,
|
|
3820
|
+
draft.assigneeName,
|
|
3821
|
+
(assigneeId, assigneeName) => {
|
|
3822
|
+
const noteDraft = this.state.noteDraft;
|
|
3823
|
+
if (!noteDraft) return;
|
|
3824
|
+
this.config.actions.setNoteDraft({
|
|
3825
|
+
...noteDraft,
|
|
3826
|
+
assigneeId,
|
|
3827
|
+
assigneeName
|
|
3828
|
+
});
|
|
3829
|
+
}
|
|
3830
|
+
);
|
|
3139
3831
|
const saveDraft = () => {
|
|
3140
|
-
const comment = textarea.value.trim();
|
|
3141
3832
|
const currentDraft = this.state.noteDraft ?? draft;
|
|
3833
|
+
const fields = this.getDraftFields(titleInput, textarea, assigneeSelect);
|
|
3834
|
+
const comment = fields.comment;
|
|
3142
3835
|
if (!comment && !this.hasDraftAdjustment(currentDraft)) return;
|
|
3143
3836
|
void this.config.actions.createItem({
|
|
3144
3837
|
kind: "note",
|
|
3838
|
+
title: fields.title,
|
|
3145
3839
|
comment: this.withDraftAdjustmentComment(comment, currentDraft),
|
|
3840
|
+
assigneeId: fields.assigneeId,
|
|
3841
|
+
assigneeName: fields.assigneeName,
|
|
3146
3842
|
viewport: currentDraft.viewport,
|
|
3147
3843
|
anchor: currentDraft.anchor,
|
|
3148
3844
|
marker: currentDraft.marker,
|
|
@@ -3160,10 +3856,14 @@ ${adjustment}` : adjustment;
|
|
|
3160
3856
|
const actions = this.createFormActions("Save note", saveDraft, {
|
|
3161
3857
|
leading: adjustmentControls?.actionButton ? [adjustmentControls.actionButton] : void 0
|
|
3162
3858
|
});
|
|
3859
|
+
const error = this.createDraftError();
|
|
3163
3860
|
form.append(
|
|
3164
3861
|
...meta ? [meta] : [],
|
|
3165
3862
|
...adjustmentControls ? [adjustmentControls.panel] : [],
|
|
3863
|
+
...titleInput ? [titleInput] : [],
|
|
3166
3864
|
textarea,
|
|
3865
|
+
...assigneeSelect ? [assigneeSelect] : [],
|
|
3866
|
+
...error ? [error] : [],
|
|
3167
3867
|
actions
|
|
3168
3868
|
);
|
|
3169
3869
|
const dragHandle = isElementDraft && !options.dockComposer ? this.createDraftDragHandle("Move DOM composer") : void 0;
|
|
@@ -3290,6 +3990,9 @@ ${adjustment}` : adjustment;
|
|
|
3290
3990
|
handle.addEventListener("pointerup", stopDrag);
|
|
3291
3991
|
handle.addEventListener("pointercancel", stopDrag);
|
|
3292
3992
|
}
|
|
3993
|
+
// Builds the element-adjustment controls (nudge the previewed element via
|
|
3994
|
+
// arrow keys / buttons). Wires keyboard deltas to the draft transform and
|
|
3995
|
+
// keeps the pin, popover, highlight and textarea in sync as the value changes.
|
|
3293
3996
|
createAdjustmentControls({
|
|
3294
3997
|
draft,
|
|
3295
3998
|
pin,
|
|
@@ -3434,6 +4137,14 @@ ${adjustment}` : adjustment;
|
|
|
3434
4137
|
return form;
|
|
3435
4138
|
}
|
|
3436
4139
|
form.append(this.createAreaMetricsPanel(areaDraft));
|
|
4140
|
+
const titleInput = this.isTitleFieldEnabled() ? this.createDraftTitleInput(areaDraft.title, (title) => {
|
|
4141
|
+
const draft = this.state.areaDraft;
|
|
4142
|
+
if (!draft) return;
|
|
4143
|
+
this.config.actions.setAreaDraft({
|
|
4144
|
+
...draft,
|
|
4145
|
+
title
|
|
4146
|
+
});
|
|
4147
|
+
}) : void 0;
|
|
3437
4148
|
const textarea = document.createElement("textarea");
|
|
3438
4149
|
textarea.className = "dfwr-textarea";
|
|
3439
4150
|
textarea.placeholder = "Area comment";
|
|
@@ -3447,20 +4158,44 @@ ${adjustment}` : adjustment;
|
|
|
3447
4158
|
comment: textarea.value
|
|
3448
4159
|
});
|
|
3449
4160
|
});
|
|
4161
|
+
const assigneeSelect = this.createDraftAssigneeSelect(
|
|
4162
|
+
areaDraft.assigneeId,
|
|
4163
|
+
areaDraft.assigneeName,
|
|
4164
|
+
(assigneeId, assigneeName) => {
|
|
4165
|
+
const draft = this.state.areaDraft;
|
|
4166
|
+
if (!draft) return;
|
|
4167
|
+
this.config.actions.setAreaDraft({
|
|
4168
|
+
...draft,
|
|
4169
|
+
assigneeId,
|
|
4170
|
+
assigneeName
|
|
4171
|
+
});
|
|
4172
|
+
}
|
|
4173
|
+
);
|
|
3450
4174
|
const actions = this.createFormActions("Save area", () => {
|
|
3451
|
-
const comment = textarea.value.trim();
|
|
3452
4175
|
const draft = this.state.areaDraft;
|
|
4176
|
+
const fields = this.getDraftFields(titleInput, textarea, assigneeSelect);
|
|
4177
|
+
const comment = fields.comment;
|
|
3453
4178
|
if (!comment || !draft) return;
|
|
3454
4179
|
void this.config.actions.createItem({
|
|
3455
4180
|
kind: "area",
|
|
4181
|
+
title: fields.title,
|
|
3456
4182
|
comment,
|
|
4183
|
+
assigneeId: fields.assigneeId,
|
|
4184
|
+
assigneeName: fields.assigneeName,
|
|
3457
4185
|
viewport: draft.viewport,
|
|
3458
4186
|
anchor: draft.anchor,
|
|
3459
4187
|
marker: draft.marker,
|
|
3460
4188
|
selection: draft.selection
|
|
3461
4189
|
});
|
|
3462
4190
|
});
|
|
3463
|
-
|
|
4191
|
+
const error = this.createDraftError();
|
|
4192
|
+
form.append(
|
|
4193
|
+
...titleInput ? [titleInput] : [],
|
|
4194
|
+
textarea,
|
|
4195
|
+
...assigneeSelect ? [assigneeSelect] : [],
|
|
4196
|
+
...error ? [error] : [],
|
|
4197
|
+
actions
|
|
4198
|
+
);
|
|
3464
4199
|
return form;
|
|
3465
4200
|
}
|
|
3466
4201
|
createAreaMetricsPanel(draft) {
|
|
@@ -3552,18 +4287,27 @@ ${adjustment}` : adjustment;
|
|
|
3552
4287
|
createFormActions(saveLabel, onSave, options) {
|
|
3553
4288
|
const actions = document.createElement("div");
|
|
3554
4289
|
actions.className = ["dfwr-actions", options?.className].filter(Boolean).join(" ");
|
|
4290
|
+
const isSaving = this.state.isCreatingItem;
|
|
3555
4291
|
const save = document.createElement("button");
|
|
3556
4292
|
save.className = "dfwr-button is-primary";
|
|
3557
4293
|
save.type = "button";
|
|
3558
|
-
save.
|
|
4294
|
+
save.disabled = isSaving;
|
|
4295
|
+
save.setAttribute("aria-busy", isSaving ? "true" : "false");
|
|
4296
|
+
if (isSaving) {
|
|
4297
|
+
save.append(this.createSpinner("dfwr-spinner"), "Saving...");
|
|
4298
|
+
} else {
|
|
4299
|
+
save.textContent = saveLabel;
|
|
4300
|
+
}
|
|
3559
4301
|
save.addEventListener("click", (event) => {
|
|
3560
4302
|
event.preventDefault();
|
|
3561
4303
|
event.stopPropagation();
|
|
4304
|
+
if (this.state.isCreatingItem) return;
|
|
3562
4305
|
onSave();
|
|
3563
4306
|
});
|
|
3564
4307
|
const cancel = document.createElement("button");
|
|
3565
4308
|
cancel.className = "dfwr-button";
|
|
3566
4309
|
cancel.type = "button";
|
|
4310
|
+
cancel.disabled = isSaving;
|
|
3567
4311
|
cancel.textContent = "Cancel";
|
|
3568
4312
|
cancel.addEventListener("click", (event) => {
|
|
3569
4313
|
this.cancelDraft(event);
|
|
@@ -3586,6 +4330,20 @@ ${adjustment}` : adjustment;
|
|
|
3586
4330
|
actions.append(save, cancel);
|
|
3587
4331
|
return actions;
|
|
3588
4332
|
}
|
|
4333
|
+
createSpinner(className) {
|
|
4334
|
+
const spinner = document.createElement("span");
|
|
4335
|
+
spinner.className = className;
|
|
4336
|
+
spinner.setAttribute("aria-hidden", "true");
|
|
4337
|
+
return spinner;
|
|
4338
|
+
}
|
|
4339
|
+
createDraftError() {
|
|
4340
|
+
if (!this.state.draftError) return void 0;
|
|
4341
|
+
const error = document.createElement("p");
|
|
4342
|
+
error.className = "dfwr-form-error";
|
|
4343
|
+
error.setAttribute("role", "alert");
|
|
4344
|
+
error.textContent = this.state.draftError;
|
|
4345
|
+
return error;
|
|
4346
|
+
}
|
|
3589
4347
|
createList() {
|
|
3590
4348
|
const section = document.createElement("div");
|
|
3591
4349
|
section.className = "dfwr-list";
|
|
@@ -3638,14 +4396,20 @@ ${adjustment}` : adjustment;
|
|
|
3638
4396
|
kind.className = "dfwr-item-kind";
|
|
3639
4397
|
kind.textContent = item.kind;
|
|
3640
4398
|
badges.append(scope, kind);
|
|
4399
|
+
const title = this.isTitleFieldEnabled() ? item.title?.trim() : "";
|
|
4400
|
+
const titleElement = title ? document.createElement("strong") : void 0;
|
|
4401
|
+
if (title && titleElement) {
|
|
4402
|
+
titleElement.className = "dfwr-item-title";
|
|
4403
|
+
titleElement.textContent = title;
|
|
4404
|
+
}
|
|
3641
4405
|
const comment = document.createElement("p");
|
|
3642
|
-
comment.className =
|
|
4406
|
+
comment.className = `dfwr-item-comment${title ? "" : " is-primary"}`;
|
|
3643
4407
|
comment.textContent = item.comment;
|
|
3644
4408
|
const date = document.createElement("time");
|
|
3645
4409
|
date.className = "dfwr-item-date";
|
|
3646
4410
|
date.dateTime = item.createdAt;
|
|
3647
4411
|
date.textContent = formatItemMeta(item);
|
|
3648
|
-
body.append(badges, comment, date);
|
|
4412
|
+
body.append(badges, ...titleElement ? [titleElement] : [], comment, date);
|
|
3649
4413
|
const actions = document.createElement("div");
|
|
3650
4414
|
actions.className = "dfwr-item-actions";
|
|
3651
4415
|
actions.addEventListener("click", (event) => event.stopPropagation());
|
|
@@ -3834,7 +4598,14 @@ ${formatItemMeta(item)}`;
|
|
|
3834
4598
|
event,
|
|
3835
4599
|
this.config.getEnvironment()
|
|
3836
4600
|
);
|
|
3837
|
-
|
|
4601
|
+
const currentDraft = this.state.noteDraft;
|
|
4602
|
+
const fields = {
|
|
4603
|
+
title: currentDraft?.title,
|
|
4604
|
+
comment: textarea.value,
|
|
4605
|
+
assigneeId: currentDraft?.assigneeId,
|
|
4606
|
+
assigneeName: currentDraft?.assigneeName
|
|
4607
|
+
};
|
|
4608
|
+
void (this.state.mode === "element" ? this.config.actions.bindElementDraftToPoint(nextPoint, fields) : this.config.actions.bindNoteDraftToPoint(nextPoint, fields));
|
|
3838
4609
|
});
|
|
3839
4610
|
}
|
|
3840
4611
|
createNoteLayer() {
|
|
@@ -3912,10 +4683,14 @@ ${formatItemMeta(item)}`;
|
|
|
3912
4683
|
let startX = 0;
|
|
3913
4684
|
let startY = 0;
|
|
3914
4685
|
let selection;
|
|
4686
|
+
let activePointerId;
|
|
4687
|
+
let isDragging = false;
|
|
4688
|
+
const ownerWindow = layer.ownerDocument.defaultView ?? window;
|
|
3915
4689
|
const updateBox = (event) => {
|
|
4690
|
+
const nextEnvironment = this.config.getEnvironment();
|
|
3916
4691
|
const nextPoint = toTargetPointFromHostEvent(
|
|
3917
4692
|
event,
|
|
3918
|
-
|
|
4693
|
+
nextEnvironment
|
|
3919
4694
|
);
|
|
3920
4695
|
const left = Math.min(startX, nextPoint.x);
|
|
3921
4696
|
const top = Math.min(startY, nextPoint.y);
|
|
@@ -3923,7 +4698,7 @@ ${formatItemMeta(item)}`;
|
|
|
3923
4698
|
const height = Math.abs(nextPoint.y - startY);
|
|
3924
4699
|
const hostPoint = toHostPoint(
|
|
3925
4700
|
{ x: left, y: top },
|
|
3926
|
-
|
|
4701
|
+
nextEnvironment
|
|
3927
4702
|
);
|
|
3928
4703
|
selection = { left, top, width, height };
|
|
3929
4704
|
box.style.left = `${hostPoint.x}px`;
|
|
@@ -3931,9 +4706,68 @@ ${formatItemMeta(item)}`;
|
|
|
3931
4706
|
box.style.width = `${width}px`;
|
|
3932
4707
|
box.style.height = `${height}px`;
|
|
3933
4708
|
};
|
|
4709
|
+
const addDragListeners = () => {
|
|
4710
|
+
ownerWindow.addEventListener("pointermove", handlePointerMove, true);
|
|
4711
|
+
ownerWindow.addEventListener("pointerup", handlePointerUp, true);
|
|
4712
|
+
ownerWindow.addEventListener("pointercancel", handlePointerCancel, true);
|
|
4713
|
+
};
|
|
4714
|
+
const removeDragListeners = () => {
|
|
4715
|
+
ownerWindow.removeEventListener("pointermove", handlePointerMove, true);
|
|
4716
|
+
ownerWindow.removeEventListener("pointerup", handlePointerUp, true);
|
|
4717
|
+
ownerWindow.removeEventListener(
|
|
4718
|
+
"pointercancel",
|
|
4719
|
+
handlePointerCancel,
|
|
4720
|
+
true
|
|
4721
|
+
);
|
|
4722
|
+
};
|
|
4723
|
+
const releasePointerCapture = (event) => {
|
|
4724
|
+
try {
|
|
4725
|
+
if (layer.hasPointerCapture(event.pointerId)) {
|
|
4726
|
+
layer.releasePointerCapture(event.pointerId);
|
|
4727
|
+
}
|
|
4728
|
+
} catch {
|
|
4729
|
+
}
|
|
4730
|
+
};
|
|
4731
|
+
function isActivePointer(event) {
|
|
4732
|
+
return isDragging && event.pointerId === activePointerId;
|
|
4733
|
+
}
|
|
4734
|
+
const finishAreaSelection = (event) => {
|
|
4735
|
+
if (!isActivePointer(event)) return;
|
|
4736
|
+
event.preventDefault();
|
|
4737
|
+
updateBox(event);
|
|
4738
|
+
releasePointerCapture(event);
|
|
4739
|
+
removeDragListeners();
|
|
4740
|
+
isDragging = false;
|
|
4741
|
+
activePointerId = void 0;
|
|
4742
|
+
if (!selection || selection.width < 8 || selection.height < 8) return;
|
|
4743
|
+
this.config.actions.setSelectingArea(true);
|
|
4744
|
+
this.config.actions.render();
|
|
4745
|
+
void this.config.actions.createAreaDraft(selection);
|
|
4746
|
+
};
|
|
4747
|
+
function handlePointerMove(event) {
|
|
4748
|
+
if (!isActivePointer(event)) return;
|
|
4749
|
+
event.preventDefault();
|
|
4750
|
+
updateBox(event);
|
|
4751
|
+
}
|
|
4752
|
+
const handlePointerUp = (event) => {
|
|
4753
|
+
finishAreaSelection(event);
|
|
4754
|
+
};
|
|
4755
|
+
const handlePointerCancel = (event) => {
|
|
4756
|
+
if (!isActivePointer(event)) return;
|
|
4757
|
+
releasePointerCapture(event);
|
|
4758
|
+
removeDragListeners();
|
|
4759
|
+
isDragging = false;
|
|
4760
|
+
activePointerId = void 0;
|
|
4761
|
+
};
|
|
3934
4762
|
layer.addEventListener("pointerdown", (event) => {
|
|
4763
|
+
if (event.button !== 0) return;
|
|
3935
4764
|
event.preventDefault();
|
|
3936
|
-
|
|
4765
|
+
activePointerId = event.pointerId;
|
|
4766
|
+
isDragging = true;
|
|
4767
|
+
try {
|
|
4768
|
+
layer.setPointerCapture(event.pointerId);
|
|
4769
|
+
} catch {
|
|
4770
|
+
}
|
|
3937
4771
|
const startPoint = toTargetPointFromHostEvent(
|
|
3938
4772
|
event,
|
|
3939
4773
|
this.config.getEnvironment()
|
|
@@ -3941,20 +4775,11 @@ ${formatItemMeta(item)}`;
|
|
|
3941
4775
|
startX = startPoint.x;
|
|
3942
4776
|
startY = startPoint.y;
|
|
3943
4777
|
updateBox(event);
|
|
4778
|
+
addDragListeners();
|
|
3944
4779
|
});
|
|
3945
|
-
layer.addEventListener("pointermove",
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
});
|
|
3949
|
-
layer.addEventListener("pointerup", (event) => {
|
|
3950
|
-
if (!layer.hasPointerCapture(event.pointerId)) return;
|
|
3951
|
-
layer.releasePointerCapture(event.pointerId);
|
|
3952
|
-
updateBox(event);
|
|
3953
|
-
if (!selection || selection.width < 8 || selection.height < 8) return;
|
|
3954
|
-
this.config.actions.setSelectingArea(true);
|
|
3955
|
-
this.config.actions.render();
|
|
3956
|
-
void this.config.actions.createAreaDraft(selection);
|
|
3957
|
-
});
|
|
4780
|
+
layer.addEventListener("pointermove", handlePointerMove);
|
|
4781
|
+
layer.addEventListener("pointerup", handlePointerUp);
|
|
4782
|
+
layer.addEventListener("pointercancel", handlePointerCancel);
|
|
3958
4783
|
return layer;
|
|
3959
4784
|
}
|
|
3960
4785
|
};
|
|
@@ -3994,6 +4819,8 @@ var WebReviewKitApp = class {
|
|
|
3994
4819
|
this.isOpen = false;
|
|
3995
4820
|
this.mode = "idle";
|
|
3996
4821
|
this.items = [];
|
|
4822
|
+
this.draftError = "";
|
|
4823
|
+
this.isCreatingItem = false;
|
|
3997
4824
|
this.isSelectingArea = false;
|
|
3998
4825
|
this.handleKeyDown = (event) => {
|
|
3999
4826
|
if (event.key === "Escape" && this.cancelMode()) {
|
|
@@ -4024,6 +4851,8 @@ var WebReviewKitApp = class {
|
|
|
4024
4851
|
items: this.items,
|
|
4025
4852
|
noteDraft: this.noteDraft,
|
|
4026
4853
|
areaDraft: this.areaDraft,
|
|
4854
|
+
draftError: this.draftError,
|
|
4855
|
+
isCreatingItem: this.isCreatingItem,
|
|
4027
4856
|
isSelectingArea: this.isSelectingArea,
|
|
4028
4857
|
highlightedItemId: this.highlightedItemId
|
|
4029
4858
|
}),
|
|
@@ -4038,19 +4867,22 @@ var WebReviewKitApp = class {
|
|
|
4038
4867
|
clearDrafts: () => {
|
|
4039
4868
|
this.noteDraft = void 0;
|
|
4040
4869
|
this.areaDraft = void 0;
|
|
4870
|
+
this.draftError = "";
|
|
4041
4871
|
},
|
|
4042
4872
|
setNoteDraft: (draft) => {
|
|
4043
4873
|
this.noteDraft = draft;
|
|
4874
|
+
this.draftError = "";
|
|
4044
4875
|
},
|
|
4045
4876
|
setAreaDraft: (draft) => {
|
|
4046
4877
|
this.areaDraft = draft;
|
|
4878
|
+
this.draftError = "";
|
|
4047
4879
|
},
|
|
4048
4880
|
setSelectingArea: (isSelectingArea) => {
|
|
4049
4881
|
this.isSelectingArea = isSelectingArea;
|
|
4050
4882
|
},
|
|
4051
4883
|
createItem: (input) => this.createItem(input),
|
|
4052
|
-
bindNoteDraftToPoint: (point,
|
|
4053
|
-
bindElementDraftToPoint: (point,
|
|
4884
|
+
bindNoteDraftToPoint: (point, fields) => this.bindNoteDraftToPoint(point, fields),
|
|
4885
|
+
bindElementDraftToPoint: (point, fields) => this.bindElementDraftToPoint(point, fields),
|
|
4054
4886
|
createAreaDraft: (selection) => this.createAreaDraft(selection)
|
|
4055
4887
|
}
|
|
4056
4888
|
});
|
|
@@ -4119,7 +4951,7 @@ var WebReviewKitApp = class {
|
|
|
4119
4951
|
this.noteDraft = void 0;
|
|
4120
4952
|
this.areaDraft = void 0;
|
|
4121
4953
|
this.isSelectingArea = false;
|
|
4122
|
-
await this.bindElementDraftToElement(element, comment);
|
|
4954
|
+
await this.bindElementDraftToElement(element, { comment });
|
|
4123
4955
|
}
|
|
4124
4956
|
getMode() {
|
|
4125
4957
|
return this.mode;
|
|
@@ -4262,7 +5094,7 @@ var WebReviewKitApp = class {
|
|
|
4262
5094
|
if (!this.shadow) return;
|
|
4263
5095
|
this.view.render(this.shadow, this.createHiddenItemsStyleElement());
|
|
4264
5096
|
}
|
|
4265
|
-
async bindNoteDraftToPoint(point,
|
|
5097
|
+
async bindNoteDraftToPoint(point, fields = {}) {
|
|
4266
5098
|
const environment = this.getEnvironment();
|
|
4267
5099
|
if (!environment) return;
|
|
4268
5100
|
const viewport = getViewportSize(environment);
|
|
@@ -4282,13 +5114,13 @@ var WebReviewKitApp = class {
|
|
|
4282
5114
|
viewport,
|
|
4283
5115
|
anchor,
|
|
4284
5116
|
marker,
|
|
4285
|
-
|
|
5117
|
+
...fields
|
|
4286
5118
|
};
|
|
4287
5119
|
});
|
|
4288
5120
|
this.noteDraft = draft;
|
|
4289
5121
|
this.render();
|
|
4290
5122
|
}
|
|
4291
|
-
async bindElementDraftToPoint(point,
|
|
5123
|
+
async bindElementDraftToPoint(point, fields = {}) {
|
|
4292
5124
|
const environment = this.getEnvironment();
|
|
4293
5125
|
if (!environment) return;
|
|
4294
5126
|
const viewport = getViewportSize(environment);
|
|
@@ -4332,14 +5164,14 @@ var WebReviewKitApp = class {
|
|
|
4332
5164
|
anchor,
|
|
4333
5165
|
marker,
|
|
4334
5166
|
selection: reviewSelection,
|
|
4335
|
-
|
|
5167
|
+
...fields,
|
|
4336
5168
|
previewElement
|
|
4337
5169
|
};
|
|
4338
5170
|
});
|
|
4339
5171
|
this.noteDraft = draft;
|
|
4340
5172
|
this.render();
|
|
4341
5173
|
}
|
|
4342
|
-
async bindElementDraftToElement(element,
|
|
5174
|
+
async bindElementDraftToElement(element, fields = {}) {
|
|
4343
5175
|
const environment = this.getEnvironment();
|
|
4344
5176
|
if (!environment || element.ownerDocument !== environment.document) return;
|
|
4345
5177
|
const viewport = getViewportSize(environment);
|
|
@@ -4372,7 +5204,7 @@ var WebReviewKitApp = class {
|
|
|
4372
5204
|
anchor,
|
|
4373
5205
|
marker,
|
|
4374
5206
|
selection: reviewSelection,
|
|
4375
|
-
|
|
5207
|
+
...fields,
|
|
4376
5208
|
previewElement
|
|
4377
5209
|
};
|
|
4378
5210
|
});
|
|
@@ -4382,26 +5214,33 @@ var WebReviewKitApp = class {
|
|
|
4382
5214
|
}
|
|
4383
5215
|
async createAreaDraft(selection) {
|
|
4384
5216
|
const environment = this.getEnvironment();
|
|
4385
|
-
if (!environment)
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
)
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
5217
|
+
if (!environment) {
|
|
5218
|
+
this.isSelectingArea = false;
|
|
5219
|
+
this.render();
|
|
5220
|
+
return;
|
|
5221
|
+
}
|
|
5222
|
+
try {
|
|
5223
|
+
const viewport = getViewportSize(environment);
|
|
5224
|
+
this.areaDraft = await this.withOverlayHidden(() => {
|
|
5225
|
+
const marker = createSelectionCenterMarker(
|
|
5226
|
+
selection,
|
|
5227
|
+
void 0,
|
|
5228
|
+
environment
|
|
5229
|
+
);
|
|
5230
|
+
const reviewSelection = {
|
|
5231
|
+
viewport: toPublicSelection(selection)
|
|
5232
|
+
};
|
|
5233
|
+
return {
|
|
5234
|
+
viewport,
|
|
5235
|
+
marker,
|
|
5236
|
+
selection: reviewSelection
|
|
5237
|
+
};
|
|
5238
|
+
});
|
|
5239
|
+
this.setModeState("area");
|
|
5240
|
+
} finally {
|
|
5241
|
+
this.isSelectingArea = false;
|
|
5242
|
+
this.render();
|
|
5243
|
+
}
|
|
4405
5244
|
}
|
|
4406
5245
|
async withOverlayHidden(callback) {
|
|
4407
5246
|
if (!this.root) return callback();
|
|
@@ -4415,11 +5254,16 @@ var WebReviewKitApp = class {
|
|
|
4415
5254
|
}
|
|
4416
5255
|
async createItem(input) {
|
|
4417
5256
|
const environment = this.getEnvironment();
|
|
4418
|
-
if (!environment) return;
|
|
5257
|
+
if (!environment || this.isCreatingItem) return;
|
|
4419
5258
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4420
5259
|
const routeKey = getRouteKey(environment);
|
|
4421
5260
|
const viewport = input.viewport ?? getViewportSize(environment);
|
|
4422
5261
|
const createdBy = this.options.userId?.trim();
|
|
5262
|
+
const title = input.title?.trim();
|
|
5263
|
+
const assigneeId = input.assigneeId?.trim() || void 0;
|
|
5264
|
+
const assigneeOption = this.options.assigneeOptions?.find(
|
|
5265
|
+
(option) => option.value === assigneeId
|
|
5266
|
+
);
|
|
4423
5267
|
const item = {
|
|
4424
5268
|
id: createId(),
|
|
4425
5269
|
projectId: this.options.projectId,
|
|
@@ -4429,8 +5273,10 @@ var WebReviewKitApp = class {
|
|
|
4429
5273
|
normalizedPath: routeKey,
|
|
4430
5274
|
scope: input.scope ?? getReviewViewportScope(viewport, this.options.viewports?.presets),
|
|
4431
5275
|
kind: input.kind,
|
|
4432
|
-
title:
|
|
5276
|
+
title: title || void 0,
|
|
4433
5277
|
comment: input.comment,
|
|
5278
|
+
assigneeId,
|
|
5279
|
+
assigneeName: input.assigneeName ?? assigneeOption?.label,
|
|
4434
5280
|
createdBy: createdBy || void 0,
|
|
4435
5281
|
status: "todo",
|
|
4436
5282
|
viewport,
|
|
@@ -4445,13 +5291,23 @@ var WebReviewKitApp = class {
|
|
|
4445
5291
|
createdAt: now,
|
|
4446
5292
|
updatedAt: now
|
|
4447
5293
|
};
|
|
4448
|
-
|
|
4449
|
-
this.
|
|
4450
|
-
this.
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
5294
|
+
this.draftError = "";
|
|
5295
|
+
this.isCreatingItem = true;
|
|
5296
|
+
this.render();
|
|
5297
|
+
try {
|
|
5298
|
+
const createdItem = await this.adapter.create(item);
|
|
5299
|
+
this.setModeState("idle");
|
|
5300
|
+
this.noteDraft = void 0;
|
|
5301
|
+
this.areaDraft = void 0;
|
|
5302
|
+
this.highlightItem(createdItem.id);
|
|
5303
|
+
await this.reload();
|
|
5304
|
+
await this.options.onCreateItem?.(createdItem);
|
|
5305
|
+
} catch (error) {
|
|
5306
|
+
this.draftError = error instanceof Error ? error.message : "Failed to save QA.";
|
|
5307
|
+
} finally {
|
|
5308
|
+
this.isCreatingItem = false;
|
|
5309
|
+
this.render();
|
|
5310
|
+
}
|
|
4455
5311
|
}
|
|
4456
5312
|
async restoreItem(item) {
|
|
4457
5313
|
this.setModeState("idle");
|
|
@@ -4505,16 +5361,35 @@ function createNoopController() {
|
|
|
4505
5361
|
}
|
|
4506
5362
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4507
5363
|
0 && (module.exports = {
|
|
5364
|
+
DEFAULT_REVIEW_FIGMA_IMAGE_FORMAT,
|
|
5365
|
+
DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT,
|
|
5366
|
+
DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,
|
|
4508
5367
|
DEFAULT_REVIEW_VIEWPORTS,
|
|
5368
|
+
FIGMA_NODE_REF_SEPARATOR,
|
|
5369
|
+
REVIEW_FIGMA_TOKEN_MISSING_CODE,
|
|
4509
5370
|
REVIEW_WORKFLOW_STATUS_OPTIONS,
|
|
5371
|
+
ReviewFigmaTokenError,
|
|
5372
|
+
collectReviewFigmaReleaseSnapshot,
|
|
5373
|
+
createReviewFigmaFrameUrl,
|
|
5374
|
+
createReviewFigmaImageStoreClient,
|
|
5375
|
+
createReviewFigmaImagesSnapshot,
|
|
5376
|
+
createReviewFigmaNodeValue,
|
|
5377
|
+
createReviewFigmaReleaseSnapshot,
|
|
4510
5378
|
createWebReviewKit,
|
|
4511
5379
|
findReviewViewportPreset,
|
|
4512
5380
|
getNumberedReviewItems,
|
|
5381
|
+
getReviewFigmaImageMimeType,
|
|
5382
|
+
getReviewFigmaImageTargetKey,
|
|
4513
5383
|
getReviewItemScope,
|
|
4514
5384
|
getReviewItemScopeLabel,
|
|
4515
5385
|
getReviewViewportScope,
|
|
5386
|
+
isReviewFigmaTokenError,
|
|
4516
5387
|
localAdapter,
|
|
4517
5388
|
normalizeReviewItemStatus,
|
|
5389
|
+
parseReviewFigmaNodeRef,
|
|
5390
|
+
readReviewFigmaToken,
|
|
5391
|
+
requireReviewFigmaNodeRef,
|
|
5392
|
+
requireReviewFigmaToken,
|
|
4518
5393
|
supabaseAdapter
|
|
4519
5394
|
});
|
|
4520
5395
|
//# sourceMappingURL=index.cjs.map
|