@designfever/web-review-kit 0.5.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 +36 -10
- package/dist/chunk-AB5B6O77.js +584 -0
- package/dist/chunk-AB5B6O77.js.map +1 -0
- package/dist/{chunk-TWCSIBMY.js → chunk-RPVLRULC.js} +736 -201
- 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 +1251 -202
- 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 +10135 -4070
- 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 +9258 -3707
- 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-RvVa5ns-.d.cts → types-DT9Z66mV.d.cts} +14 -1
- package/dist/{types-RvVa5ns-.d.ts → types-DT9Z66mV.d.ts} +14 -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 -5
- package/docs/adapters.md +126 -0
- package/docs/adaptor.sample.ts +13 -1
- package/docs/architecture.md +6 -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 +44 -8
- package/docs/release-notes-0.6.0.md +108 -0
- package/docs/release-notes-0.7.0.md +128 -0
- package/package.json +6 -2
- package/dist/chunk-TWCSIBMY.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;
|
|
@@ -1520,6 +2092,14 @@ function createStyleElement() {
|
|
|
1520
2092
|
cursor: default;
|
|
1521
2093
|
}
|
|
1522
2094
|
|
|
2095
|
+
.dfwr-shell.is-docked-composer {
|
|
2096
|
+
position: relative;
|
|
2097
|
+
inset: auto;
|
|
2098
|
+
z-index: auto;
|
|
2099
|
+
padding: 0;
|
|
2100
|
+
pointer-events: auto;
|
|
2101
|
+
}
|
|
2102
|
+
|
|
1523
2103
|
.dfwr-panel {
|
|
1524
2104
|
position: fixed;
|
|
1525
2105
|
right: 16px;
|
|
@@ -1546,8 +2126,8 @@ function createStyleElement() {
|
|
|
1546
2126
|
}
|
|
1547
2127
|
|
|
1548
2128
|
.dfwr-title {
|
|
1549
|
-
font-size:
|
|
1550
|
-
font-weight:
|
|
2129
|
+
font-size: var(--df-review-font-size-xl);
|
|
2130
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
1551
2131
|
line-height: 1.25;
|
|
1552
2132
|
}
|
|
1553
2133
|
|
|
@@ -1591,11 +2171,15 @@ function createStyleElement() {
|
|
|
1591
2171
|
}
|
|
1592
2172
|
|
|
1593
2173
|
.dfwr-button {
|
|
2174
|
+
display: inline-flex;
|
|
2175
|
+
align-items: center;
|
|
2176
|
+
justify-content: center;
|
|
2177
|
+
gap: 6px;
|
|
1594
2178
|
min-height: var(--df-review-control-height-md);
|
|
1595
2179
|
padding: 0 12px;
|
|
1596
2180
|
border-radius: var(--df-review-radius-sm);
|
|
1597
2181
|
font-size: var(--df-review-font-size-sm);
|
|
1598
|
-
font-weight:
|
|
2182
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
1599
2183
|
}
|
|
1600
2184
|
|
|
1601
2185
|
.dfwr-button:hover,
|
|
@@ -1611,6 +2195,21 @@ function createStyleElement() {
|
|
|
1611
2195
|
color: var(--df-review-color-accent-contrast);
|
|
1612
2196
|
}
|
|
1613
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
|
+
|
|
1614
2213
|
.dfwr-icon-button {
|
|
1615
2214
|
display: inline-flex;
|
|
1616
2215
|
align-items: center;
|
|
@@ -1620,7 +2219,7 @@ function createStyleElement() {
|
|
|
1620
2219
|
padding: 0 8px;
|
|
1621
2220
|
border-radius: var(--df-review-radius-sm);
|
|
1622
2221
|
font-size: var(--df-review-font-size-xs);
|
|
1623
|
-
font-weight:
|
|
2222
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
1624
2223
|
line-height: 1;
|
|
1625
2224
|
text-transform: uppercase;
|
|
1626
2225
|
}
|
|
@@ -1775,7 +2374,7 @@ function createStyleElement() {
|
|
|
1775
2374
|
0 8px 18px rgba(0, 0, 0, 0.28);
|
|
1776
2375
|
color: #111820;
|
|
1777
2376
|
font-size: var(--df-review-font-size-2xs);
|
|
1778
|
-
font-weight:
|
|
2377
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
1779
2378
|
line-height: 1;
|
|
1780
2379
|
pointer-events: none;
|
|
1781
2380
|
}
|
|
@@ -1801,7 +2400,7 @@ function createStyleElement() {
|
|
|
1801
2400
|
box-shadow: 0 0 0 4px rgba(var(--dfwr-scope-rgb), 0.18);
|
|
1802
2401
|
color: var(--dfwr-scope);
|
|
1803
2402
|
font-size: var(--df-review-font-size-2xs);
|
|
1804
|
-
font-weight:
|
|
2403
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
1805
2404
|
}
|
|
1806
2405
|
|
|
1807
2406
|
.dfwr-bound-marker.is-highlighted {
|
|
@@ -2020,6 +2619,20 @@ function createStyleElement() {
|
|
|
2020
2619
|
border-color: rgba(99, 215, 199, 0.56);
|
|
2021
2620
|
}
|
|
2022
2621
|
|
|
2622
|
+
.dfwr-shell.is-docked-composer .dfwr-note-popover.is-docked-composer,
|
|
2623
|
+
.dfwr-shell.is-docked-composer .dfwr-area-draft.is-docked-composer {
|
|
2624
|
+
position: relative;
|
|
2625
|
+
left: auto;
|
|
2626
|
+
right: auto;
|
|
2627
|
+
top: auto;
|
|
2628
|
+
z-index: auto;
|
|
2629
|
+
max-height: none;
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2632
|
+
.dfwr-shell.is-docked-composer .dfwr-textarea {
|
|
2633
|
+
min-height: 184px;
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2023
2636
|
.dfwr-note-popover.is-dragging,
|
|
2024
2637
|
.dfwr-area-draft.is-dragging {
|
|
2025
2638
|
user-select: none;
|
|
@@ -2068,6 +2681,50 @@ function createStyleElement() {
|
|
|
2068
2681
|
padding: 0;
|
|
2069
2682
|
}
|
|
2070
2683
|
|
|
2684
|
+
.dfwr-actions.has-leading {
|
|
2685
|
+
align-items: center;
|
|
2686
|
+
justify-content: space-between;
|
|
2687
|
+
gap: 12px;
|
|
2688
|
+
}
|
|
2689
|
+
|
|
2690
|
+
.dfwr-actions-leading,
|
|
2691
|
+
.dfwr-actions-primary {
|
|
2692
|
+
display: flex;
|
|
2693
|
+
align-items: center;
|
|
2694
|
+
gap: 8px;
|
|
2695
|
+
min-width: 0;
|
|
2696
|
+
}
|
|
2697
|
+
|
|
2698
|
+
.dfwr-actions-primary {
|
|
2699
|
+
margin-left: auto;
|
|
2700
|
+
}
|
|
2701
|
+
|
|
2702
|
+
.dfwr-shell.is-docked-composer .dfwr-actions.has-leading {
|
|
2703
|
+
align-items: stretch;
|
|
2704
|
+
}
|
|
2705
|
+
|
|
2706
|
+
.dfwr-shell.is-docked-composer .dfwr-actions.has-leading .dfwr-button,
|
|
2707
|
+
.dfwr-shell.is-docked-composer .dfwr-actions.has-leading .dfwr-adjust-toggle {
|
|
2708
|
+
height: var(--df-review-control-height-md);
|
|
2709
|
+
min-height: var(--df-review-control-height-md);
|
|
2710
|
+
border-radius: var(--df-review-radius-sm);
|
|
2711
|
+
}
|
|
2712
|
+
|
|
2713
|
+
.dfwr-shell.is-docked-composer .dfwr-actions.has-leading .dfwr-button {
|
|
2714
|
+
min-width: 96px;
|
|
2715
|
+
padding: 0 12px;
|
|
2716
|
+
font-size: var(--df-review-font-size-sm);
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
.dfwr-shell.is-docked-composer .dfwr-actions.has-leading .dfwr-adjust-toggle {
|
|
2720
|
+
width: var(--df-review-control-height-md);
|
|
2721
|
+
}
|
|
2722
|
+
|
|
2723
|
+
.dfwr-shell.is-docked-composer .dfwr-actions.has-leading .dfwr-adjust-toggle svg {
|
|
2724
|
+
width: 18px;
|
|
2725
|
+
height: 18px;
|
|
2726
|
+
}
|
|
2727
|
+
|
|
2071
2728
|
.dfwr-note-actions {
|
|
2072
2729
|
justify-content: flex-end;
|
|
2073
2730
|
}
|
|
@@ -2085,10 +2742,18 @@ function createStyleElement() {
|
|
|
2085
2742
|
gap: 10px;
|
|
2086
2743
|
}
|
|
2087
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,
|
|
2088
2755
|
.dfwr-textarea {
|
|
2089
2756
|
width: 100%;
|
|
2090
|
-
min-height: 92px;
|
|
2091
|
-
resize: vertical;
|
|
2092
2757
|
border: 1px solid rgba(255, 255, 255, 0.16);
|
|
2093
2758
|
border-radius: var(--df-review-radius-sm);
|
|
2094
2759
|
padding: 10px;
|
|
@@ -2099,14 +2764,39 @@ function createStyleElement() {
|
|
|
2099
2764
|
line-height: 1.45;
|
|
2100
2765
|
}
|
|
2101
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,
|
|
2102
2784
|
.dfwr-textarea:focus {
|
|
2103
2785
|
outline: 2px solid var(--df-review-color-accent-ring);
|
|
2104
2786
|
outline-offset: 1px;
|
|
2105
2787
|
}
|
|
2106
2788
|
|
|
2107
2789
|
@media (hover: none) and (pointer: coarse) {
|
|
2790
|
+
.dfwr-input,
|
|
2791
|
+
.dfwr-select,
|
|
2108
2792
|
.dfwr-textarea {
|
|
2109
|
-
font-size:
|
|
2793
|
+
font-size: var(--df-review-font-size-xl);
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
|
|
2797
|
+
@keyframes dfwr-spin {
|
|
2798
|
+
to {
|
|
2799
|
+
transform: rotate(360deg);
|
|
2110
2800
|
}
|
|
2111
2801
|
}
|
|
2112
2802
|
|
|
@@ -2164,8 +2854,8 @@ function createStyleElement() {
|
|
|
2164
2854
|
color: var(--df-review-color-text);
|
|
2165
2855
|
cursor: pointer;
|
|
2166
2856
|
font: inherit;
|
|
2167
|
-
font-size:
|
|
2168
|
-
font-weight:
|
|
2857
|
+
font-size: var(--df-review-font-size-lg);
|
|
2858
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
2169
2859
|
line-height: 1;
|
|
2170
2860
|
}
|
|
2171
2861
|
|
|
@@ -2218,7 +2908,7 @@ function createStyleElement() {
|
|
|
2218
2908
|
margin-bottom: 10px;
|
|
2219
2909
|
color: rgba(247, 247, 242, 0.74);
|
|
2220
2910
|
font-size: var(--df-review-font-size-sm);
|
|
2221
|
-
font-weight:
|
|
2911
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
2222
2912
|
}
|
|
2223
2913
|
|
|
2224
2914
|
.dfwr-item {
|
|
@@ -2240,6 +2930,8 @@ function createStyleElement() {
|
|
|
2240
2930
|
}
|
|
2241
2931
|
|
|
2242
2932
|
.dfwr-item-body {
|
|
2933
|
+
display: grid;
|
|
2934
|
+
gap: 4px;
|
|
2243
2935
|
min-width: 0;
|
|
2244
2936
|
flex: 1;
|
|
2245
2937
|
}
|
|
@@ -2259,7 +2951,7 @@ function createStyleElement() {
|
|
|
2259
2951
|
border-radius: var(--df-review-radius-pill);
|
|
2260
2952
|
padding: 0 7px;
|
|
2261
2953
|
font-size: var(--df-review-font-size-2xs);
|
|
2262
|
-
font-weight:
|
|
2954
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
2263
2955
|
line-height: 1;
|
|
2264
2956
|
letter-spacing: 0;
|
|
2265
2957
|
text-transform: uppercase;
|
|
@@ -2277,13 +2969,29 @@ function createStyleElement() {
|
|
|
2277
2969
|
color: rgba(247, 247, 242, 0.64);
|
|
2278
2970
|
}
|
|
2279
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
|
+
|
|
2280
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 {
|
|
2281
2991
|
margin: 4px 0;
|
|
2282
2992
|
color: var(--df-review-color-text);
|
|
2283
2993
|
font-size: var(--df-review-font-size-md);
|
|
2284
2994
|
line-height: 1.42;
|
|
2285
|
-
overflow-wrap: anywhere;
|
|
2286
|
-
white-space: pre-wrap;
|
|
2287
2995
|
}
|
|
2288
2996
|
|
|
2289
2997
|
.dfwr-item-date {
|
|
@@ -2392,10 +3100,12 @@ function createStyleElement() {
|
|
|
2392
3100
|
|
|
2393
3101
|
@media (max-width: 520px) {
|
|
2394
3102
|
.dfwr-panel {
|
|
3103
|
+
left: 8px;
|
|
2395
3104
|
right: 8px;
|
|
2396
|
-
top:
|
|
2397
|
-
|
|
2398
|
-
|
|
3105
|
+
top: auto;
|
|
3106
|
+
bottom: 8px;
|
|
3107
|
+
width: auto;
|
|
3108
|
+
max-height: min(70vh, calc(100vh - 16px));
|
|
2399
3109
|
}
|
|
2400
3110
|
}
|
|
2401
3111
|
`;
|
|
@@ -2475,6 +3185,7 @@ var WebReviewKitView = class {
|
|
|
2475
3185
|
}
|
|
2476
3186
|
clearDraftPreview() {
|
|
2477
3187
|
this.restoreDraftPreview();
|
|
3188
|
+
this.clearShellComposer();
|
|
2478
3189
|
}
|
|
2479
3190
|
render(shadow, hiddenItemsStyle) {
|
|
2480
3191
|
const state = this.state;
|
|
@@ -2485,11 +3196,13 @@ var WebReviewKitView = class {
|
|
|
2485
3196
|
shadow.append(createStyleElement());
|
|
2486
3197
|
shadow.append(hiddenItemsStyle);
|
|
2487
3198
|
const hasDismissableDraft = Boolean(state.noteDraft || state.areaDraft);
|
|
3199
|
+
const shouldDockComposer = this.config.options.ui?.panel === false && hasDismissableDraft && Boolean(this.getShellComposerHost());
|
|
3200
|
+
let dockedComposer;
|
|
2488
3201
|
const shell = document.createElement("div");
|
|
2489
3202
|
shell.className = [
|
|
2490
3203
|
"dfwr-shell",
|
|
2491
3204
|
state.isOpen ? "is-open" : "",
|
|
2492
|
-
hasDismissableDraft ? "has-dismissible-draft" : ""
|
|
3205
|
+
hasDismissableDraft && !shouldDockComposer ? "has-dismissible-draft" : ""
|
|
2493
3206
|
].filter(Boolean).join(" ");
|
|
2494
3207
|
shell.setAttribute("aria-hidden", state.isOpen ? "false" : "true");
|
|
2495
3208
|
if (this.config.options.ui?.panel !== false) {
|
|
@@ -2506,105 +3219,119 @@ var WebReviewKitView = class {
|
|
|
2506
3219
|
shell.append(panel);
|
|
2507
3220
|
}
|
|
2508
3221
|
shell.append(this.createMarkerLayer());
|
|
2509
|
-
if (state.isOpen && hasDismissableDraft) {
|
|
3222
|
+
if (state.isOpen && hasDismissableDraft && !shouldDockComposer) {
|
|
2510
3223
|
shell.append(this.createDraftCancelLayer());
|
|
2511
3224
|
}
|
|
2512
3225
|
if (state.isOpen && (state.mode === "note" || state.mode === "element")) {
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
3226
|
+
if (state.noteDraft) {
|
|
3227
|
+
const noteDraft = this.createNotePopover(state.noteDraft, {
|
|
3228
|
+
dockComposer: shouldDockComposer
|
|
3229
|
+
});
|
|
3230
|
+
shell.append(noteDraft.layer);
|
|
3231
|
+
dockedComposer = noteDraft.composer;
|
|
3232
|
+
} else {
|
|
3233
|
+
shell.append(
|
|
3234
|
+
state.mode === "element" ? this.createElementLayer() : this.createNoteLayer()
|
|
3235
|
+
);
|
|
3236
|
+
}
|
|
2516
3237
|
}
|
|
2517
|
-
if (state.isOpen && state.mode === "area" && !state.areaDraft) {
|
|
3238
|
+
if (state.isOpen && state.mode === "area" && !state.areaDraft && !state.isSelectingArea) {
|
|
2518
3239
|
shell.append(this.createAreaLayer());
|
|
2519
3240
|
}
|
|
2520
3241
|
if (state.isOpen && state.mode === "area" && state.areaDraft && this.config.options.ui?.panel === false) {
|
|
2521
3242
|
if (state.areaDraft.selection) {
|
|
2522
3243
|
shell.append(this.createAreaDraftOverlay(state.areaDraft));
|
|
2523
3244
|
}
|
|
2524
|
-
|
|
3245
|
+
const areaComposer = this.createAreaDraftPopover(state.areaDraft, {
|
|
3246
|
+
dockComposer: shouldDockComposer
|
|
3247
|
+
});
|
|
3248
|
+
if (shouldDockComposer) {
|
|
3249
|
+
dockedComposer = areaComposer;
|
|
3250
|
+
} else {
|
|
3251
|
+
shell.append(areaComposer);
|
|
3252
|
+
}
|
|
2525
3253
|
}
|
|
2526
3254
|
shadow.append(shell);
|
|
3255
|
+
this.renderShellComposer(dockedComposer);
|
|
2527
3256
|
}
|
|
2528
3257
|
get state() {
|
|
2529
3258
|
return this.config.getState();
|
|
2530
3259
|
}
|
|
3260
|
+
getShellComposerHost() {
|
|
3261
|
+
const environment = this.config.getEnvironment();
|
|
3262
|
+
if (this.config.options.ui?.panel !== false) return void 0;
|
|
3263
|
+
return environment?.composerHost ?? void 0;
|
|
3264
|
+
}
|
|
3265
|
+
renderShellComposer(composer) {
|
|
3266
|
+
const host = composer ? this.getShellComposerHost() : void 0;
|
|
3267
|
+
if (!host || !composer) {
|
|
3268
|
+
this.clearShellComposer();
|
|
3269
|
+
return;
|
|
3270
|
+
}
|
|
3271
|
+
if (this.shellComposerHost && this.shellComposerHost !== host) {
|
|
3272
|
+
this.clearShellComposer();
|
|
3273
|
+
}
|
|
3274
|
+
this.shellComposerHost = host;
|
|
3275
|
+
host.dataset.hasDraftComposer = "true";
|
|
3276
|
+
if (host.parentElement) {
|
|
3277
|
+
host.parentElement.dataset.hasDraftComposer = "true";
|
|
3278
|
+
}
|
|
3279
|
+
const shell = document.createElement("div");
|
|
3280
|
+
shell.className = "dfwr-shell is-open is-shell-draft is-docked-composer";
|
|
3281
|
+
shell.append(composer);
|
|
3282
|
+
host.replaceChildren(createStyleElement(), shell);
|
|
3283
|
+
}
|
|
3284
|
+
clearShellComposer() {
|
|
3285
|
+
const host = this.shellComposerHost;
|
|
3286
|
+
host?.replaceChildren();
|
|
3287
|
+
if (host) {
|
|
3288
|
+
delete host.dataset.hasDraftComposer;
|
|
3289
|
+
delete host.parentElement?.dataset.hasDraftComposer;
|
|
3290
|
+
}
|
|
3291
|
+
this.shellComposerHost = void 0;
|
|
3292
|
+
}
|
|
2531
3293
|
createDraftCancelLayer() {
|
|
2532
3294
|
const layer = document.createElement("div");
|
|
2533
3295
|
layer.className = "dfwr-draft-cancel-layer";
|
|
2534
3296
|
layer.setAttribute("aria-hidden", "true");
|
|
2535
|
-
const cancel = (event) => {
|
|
2536
|
-
event.preventDefault();
|
|
2537
|
-
event.stopPropagation();
|
|
2538
|
-
event.stopImmediatePropagation();
|
|
2539
|
-
this.config.actions.setModeState("idle");
|
|
2540
|
-
this.config.actions.clearDrafts();
|
|
2541
|
-
this.config.actions.setSelectingArea(false);
|
|
2542
|
-
this.config.actions.render();
|
|
2543
|
-
};
|
|
2544
3297
|
layer.addEventListener("pointerdown", (event) => {
|
|
2545
3298
|
if (event.button !== 0) return;
|
|
2546
|
-
|
|
3299
|
+
this.cancelDraft(event);
|
|
2547
3300
|
});
|
|
2548
3301
|
return layer;
|
|
2549
3302
|
}
|
|
3303
|
+
cancelDraft(event) {
|
|
3304
|
+
event?.preventDefault();
|
|
3305
|
+
event?.stopPropagation();
|
|
3306
|
+
event?.stopImmediatePropagation();
|
|
3307
|
+
this.config.actions.setModeState("idle");
|
|
3308
|
+
this.config.actions.clearDrafts();
|
|
3309
|
+
this.config.actions.setSelectingArea(false);
|
|
3310
|
+
this.config.actions.render();
|
|
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
|
+
}
|
|
2550
3317
|
getDraftAdjustmentMetrics(draft) {
|
|
2551
|
-
|
|
2552
|
-
const x = adjustment?.x ?? 0;
|
|
2553
|
-
const y = adjustment?.y ?? 0;
|
|
2554
|
-
const scale = adjustment?.scale ?? 0;
|
|
2555
|
-
const {
|
|
2556
|
-
scale: viewportScale,
|
|
2557
|
-
designWidth,
|
|
2558
|
-
presetLabel
|
|
2559
|
-
} = this.getDraftViewportScale(draft.viewport);
|
|
2560
|
-
const selection = draft.selection ? toViewportSelection(draft.selection.viewport) : void 0;
|
|
2561
|
-
const scaleCssDelta = scale * viewportScale;
|
|
2562
|
-
const scaleFactor = selection && selection.width > 0 ? Math.max(
|
|
2563
|
-
1 / selection.width,
|
|
2564
|
-
(selection.width + scaleCssDelta) / selection.width
|
|
2565
|
-
) : 1;
|
|
2566
|
-
return {
|
|
2567
|
-
x,
|
|
2568
|
-
y,
|
|
2569
|
-
scale,
|
|
2570
|
-
cssX: x * viewportScale,
|
|
2571
|
-
cssY: y * viewportScale,
|
|
2572
|
-
scaleFactor,
|
|
2573
|
-
viewportScale,
|
|
2574
|
-
designWidth,
|
|
2575
|
-
presetLabel,
|
|
2576
|
-
viewportWidth: draft.viewport.width
|
|
2577
|
-
};
|
|
3318
|
+
return getDraftAdjustmentMetrics(draft, this.viewportPresets);
|
|
2578
3319
|
}
|
|
2579
3320
|
hasDraftAdjustment(draft) {
|
|
2580
|
-
|
|
2581
|
-
return metrics.x !== 0 || metrics.y !== 0 || metrics.scale !== 0;
|
|
3321
|
+
return hasDraftAdjustment(draft, this.viewportPresets);
|
|
2582
3322
|
}
|
|
2583
3323
|
getAdjustedDraftPoint(point, draft) {
|
|
2584
|
-
|
|
2585
|
-
return {
|
|
2586
|
-
x: point.x + metrics.cssX,
|
|
2587
|
-
y: point.y + metrics.cssY
|
|
2588
|
-
};
|
|
3324
|
+
return getAdjustedDraftPoint(point, draft, this.viewportPresets);
|
|
2589
3325
|
}
|
|
2590
3326
|
getAdjustedDraftSelection(selection, draft) {
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
width: selection.width * metrics.scaleFactor,
|
|
2597
|
-
height: selection.height * metrics.scaleFactor
|
|
2598
|
-
};
|
|
3327
|
+
return getAdjustedDraftSelection(
|
|
3328
|
+
selection,
|
|
3329
|
+
draft,
|
|
3330
|
+
this.viewportPresets
|
|
3331
|
+
);
|
|
2599
3332
|
}
|
|
2600
3333
|
getDraftViewportScale(viewport) {
|
|
2601
|
-
|
|
2602
|
-
viewport,
|
|
2603
|
-
this.config.options.viewports?.presets
|
|
2604
|
-
);
|
|
2605
|
-
const designWidth = typeof preset.designWidth === "number" && preset.designWidth > 0 ? preset.designWidth : viewport.width;
|
|
2606
|
-
const scale = designWidth > 0 ? viewport.width / designWidth : 1;
|
|
2607
|
-
return { scale, designWidth, presetLabel: preset.label };
|
|
3334
|
+
return getDraftViewportScale(viewport, this.viewportPresets);
|
|
2608
3335
|
}
|
|
2609
3336
|
getDraftComposerWidth(environment) {
|
|
2610
3337
|
const bounds = environment.overlayRect;
|
|
@@ -2750,6 +3477,67 @@ var WebReviewKitView = class {
|
|
|
2750
3477
|
return trimmedComment ? `${trimmedComment}
|
|
2751
3478
|
${adjustment}` : adjustment;
|
|
2752
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
|
+
}
|
|
2753
3541
|
getStyleableDraftElement(draft, environment) {
|
|
2754
3542
|
if (draft.previewElement && draft.previewElement.ownerDocument === environment.document && "style" in draft.previewElement) {
|
|
2755
3543
|
return draft.previewElement;
|
|
@@ -2940,11 +3728,14 @@ ${adjustment}` : adjustment;
|
|
|
2940
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.";
|
|
2941
3729
|
return empty;
|
|
2942
3730
|
}
|
|
2943
|
-
|
|
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).
|
|
3734
|
+
createNotePopover(draft, options = {}) {
|
|
2944
3735
|
const environment = this.config.getEnvironment();
|
|
2945
3736
|
const group = document.createElement("div");
|
|
2946
3737
|
group.className = "dfwr-note-draft";
|
|
2947
|
-
if (!environment) return group;
|
|
3738
|
+
if (!environment) return { layer: group, composer: void 0 };
|
|
2948
3739
|
const isElementDraft = this.state.mode === "element" && Boolean(draft.selection);
|
|
2949
3740
|
const hostPoint = toHostPoint(
|
|
2950
3741
|
isElementDraft ? this.getAdjustedDraftPoint(draft.marker.viewport, draft) : draft.marker.viewport,
|
|
@@ -2968,8 +3759,14 @@ ${adjustment}` : adjustment;
|
|
|
2968
3759
|
pin.style.top = `${hostPoint.y}px`;
|
|
2969
3760
|
const popover = document.createElement("div");
|
|
2970
3761
|
const position = getPopoverPosition(hostPoint, environment);
|
|
2971
|
-
popover.className =
|
|
2972
|
-
|
|
3762
|
+
popover.className = [
|
|
3763
|
+
"dfwr-note-popover",
|
|
3764
|
+
isElementDraft ? "is-composer" : "",
|
|
3765
|
+
options.dockComposer ? "is-docked-composer" : ""
|
|
3766
|
+
].filter(Boolean).join(" ");
|
|
3767
|
+
if (options.dockComposer) {
|
|
3768
|
+
popover.style.width = "100%";
|
|
3769
|
+
} else if (isElementDraft) {
|
|
2973
3770
|
const selection = draft.selection ? toHostSelection(
|
|
2974
3771
|
this.getAdjustedDraftSelection(
|
|
2975
3772
|
toViewportSelection(draft.selection.viewport),
|
|
@@ -2997,6 +3794,14 @@ ${adjustment}` : adjustment;
|
|
|
2997
3794
|
meta.className = "dfwr-item-date";
|
|
2998
3795
|
meta.textContent = formatNoteDraftMeta(draft);
|
|
2999
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;
|
|
3000
3805
|
const textarea = document.createElement("textarea");
|
|
3001
3806
|
textarea.className = "dfwr-textarea";
|
|
3002
3807
|
textarea.placeholder = "Review comment";
|
|
@@ -3010,13 +3815,30 @@ ${adjustment}` : adjustment;
|
|
|
3010
3815
|
comment: textarea.value
|
|
3011
3816
|
});
|
|
3012
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
|
+
);
|
|
3013
3831
|
const saveDraft = () => {
|
|
3014
|
-
const comment = textarea.value.trim();
|
|
3015
3832
|
const currentDraft = this.state.noteDraft ?? draft;
|
|
3833
|
+
const fields = this.getDraftFields(titleInput, textarea, assigneeSelect);
|
|
3834
|
+
const comment = fields.comment;
|
|
3016
3835
|
if (!comment && !this.hasDraftAdjustment(currentDraft)) return;
|
|
3017
3836
|
void this.config.actions.createItem({
|
|
3018
3837
|
kind: "note",
|
|
3838
|
+
title: fields.title,
|
|
3019
3839
|
comment: this.withDraftAdjustmentComment(comment, currentDraft),
|
|
3840
|
+
assigneeId: fields.assigneeId,
|
|
3841
|
+
assigneeName: fields.assigneeName,
|
|
3020
3842
|
viewport: currentDraft.viewport,
|
|
3021
3843
|
anchor: currentDraft.anchor,
|
|
3022
3844
|
marker: currentDraft.marker,
|
|
@@ -3028,18 +3850,31 @@ ${adjustment}` : adjustment;
|
|
|
3028
3850
|
pin,
|
|
3029
3851
|
popover,
|
|
3030
3852
|
selectionHighlight,
|
|
3031
|
-
textarea
|
|
3853
|
+
textarea,
|
|
3854
|
+
dockToggle: options.dockComposer
|
|
3032
3855
|
}) : void 0;
|
|
3033
|
-
const actions = this.createFormActions("Save note", saveDraft
|
|
3856
|
+
const actions = this.createFormActions("Save note", saveDraft, {
|
|
3857
|
+
leading: adjustmentControls?.actionButton ? [adjustmentControls.actionButton] : void 0
|
|
3858
|
+
});
|
|
3859
|
+
const error = this.createDraftError();
|
|
3034
3860
|
form.append(
|
|
3035
3861
|
...meta ? [meta] : [],
|
|
3036
3862
|
...adjustmentControls ? [adjustmentControls.panel] : [],
|
|
3863
|
+
...titleInput ? [titleInput] : [],
|
|
3037
3864
|
textarea,
|
|
3865
|
+
...assigneeSelect ? [assigneeSelect] : [],
|
|
3866
|
+
...error ? [error] : [],
|
|
3038
3867
|
actions
|
|
3039
3868
|
);
|
|
3040
|
-
const dragHandle = isElementDraft ? this.createDraftDragHandle("Move DOM composer") : void 0;
|
|
3041
|
-
popover.append(
|
|
3042
|
-
|
|
3869
|
+
const dragHandle = isElementDraft && !options.dockComposer ? this.createDraftDragHandle("Move DOM composer") : void 0;
|
|
3870
|
+
popover.append(
|
|
3871
|
+
...dragHandle ? [dragHandle] : [],
|
|
3872
|
+
form
|
|
3873
|
+
);
|
|
3874
|
+
group.append(pin);
|
|
3875
|
+
if (!options.dockComposer) {
|
|
3876
|
+
group.append(popover);
|
|
3877
|
+
}
|
|
3043
3878
|
if (dragHandle) {
|
|
3044
3879
|
this.attachDraftComposerDrag(popover, dragHandle, (composerPosition) => {
|
|
3045
3880
|
const noteDraft = this.state.noteDraft ?? draft;
|
|
@@ -3052,18 +3887,23 @@ ${adjustment}` : adjustment;
|
|
|
3052
3887
|
}
|
|
3053
3888
|
this.attachDraftPinDrag(
|
|
3054
3889
|
pin,
|
|
3055
|
-
isElementDraft ? void 0 : popover,
|
|
3890
|
+
isElementDraft || options.dockComposer ? void 0 : popover,
|
|
3056
3891
|
meta,
|
|
3057
3892
|
textarea
|
|
3058
3893
|
);
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3894
|
+
if (!options.dockComposer) {
|
|
3895
|
+
window.setTimeout(() => {
|
|
3896
|
+
if (draft.adjustment?.isActive) {
|
|
3897
|
+
adjustmentControls?.focusTarget.focus();
|
|
3898
|
+
return;
|
|
3899
|
+
}
|
|
3900
|
+
textarea.focus();
|
|
3901
|
+
}, 0);
|
|
3902
|
+
}
|
|
3903
|
+
return {
|
|
3904
|
+
layer: group,
|
|
3905
|
+
composer: options.dockComposer ? popover : void 0
|
|
3906
|
+
};
|
|
3067
3907
|
}
|
|
3068
3908
|
createDraftDragHandle(label) {
|
|
3069
3909
|
const handle = document.createElement("button");
|
|
@@ -3150,12 +3990,16 @@ ${adjustment}` : adjustment;
|
|
|
3150
3990
|
handle.addEventListener("pointerup", stopDrag);
|
|
3151
3991
|
handle.addEventListener("pointercancel", stopDrag);
|
|
3152
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.
|
|
3153
3996
|
createAdjustmentControls({
|
|
3154
3997
|
draft,
|
|
3155
3998
|
pin,
|
|
3156
3999
|
popover,
|
|
3157
4000
|
selectionHighlight,
|
|
3158
|
-
textarea
|
|
4001
|
+
textarea,
|
|
4002
|
+
dockToggle
|
|
3159
4003
|
}) {
|
|
3160
4004
|
const panel = document.createElement("div");
|
|
3161
4005
|
panel.className = "dfwr-adjust-panel is-dom-adjust-panel";
|
|
@@ -3231,12 +4075,16 @@ ${adjustment}` : adjustment;
|
|
|
3231
4075
|
}
|
|
3232
4076
|
}));
|
|
3233
4077
|
});
|
|
3234
|
-
header.append(help
|
|
4078
|
+
header.append(help);
|
|
4079
|
+
if (!dockToggle) {
|
|
4080
|
+
header.append(adjust);
|
|
4081
|
+
}
|
|
3235
4082
|
panel.append(header, xyStatus, scaleStatus);
|
|
3236
4083
|
syncControls(draft);
|
|
3237
4084
|
return {
|
|
3238
4085
|
panel,
|
|
3239
|
-
focusTarget: adjust
|
|
4086
|
+
focusTarget: adjust,
|
|
4087
|
+
actionButton: dockToggle ? adjust : void 0
|
|
3240
4088
|
};
|
|
3241
4089
|
}
|
|
3242
4090
|
getAdjustmentKeyDelta(event) {
|
|
@@ -3289,6 +4137,14 @@ ${adjustment}` : adjustment;
|
|
|
3289
4137
|
return form;
|
|
3290
4138
|
}
|
|
3291
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;
|
|
3292
4148
|
const textarea = document.createElement("textarea");
|
|
3293
4149
|
textarea.className = "dfwr-textarea";
|
|
3294
4150
|
textarea.placeholder = "Area comment";
|
|
@@ -3302,20 +4158,44 @@ ${adjustment}` : adjustment;
|
|
|
3302
4158
|
comment: textarea.value
|
|
3303
4159
|
});
|
|
3304
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
|
+
);
|
|
3305
4174
|
const actions = this.createFormActions("Save area", () => {
|
|
3306
|
-
const comment = textarea.value.trim();
|
|
3307
4175
|
const draft = this.state.areaDraft;
|
|
4176
|
+
const fields = this.getDraftFields(titleInput, textarea, assigneeSelect);
|
|
4177
|
+
const comment = fields.comment;
|
|
3308
4178
|
if (!comment || !draft) return;
|
|
3309
4179
|
void this.config.actions.createItem({
|
|
3310
4180
|
kind: "area",
|
|
4181
|
+
title: fields.title,
|
|
3311
4182
|
comment,
|
|
4183
|
+
assigneeId: fields.assigneeId,
|
|
4184
|
+
assigneeName: fields.assigneeName,
|
|
3312
4185
|
viewport: draft.viewport,
|
|
3313
4186
|
anchor: draft.anchor,
|
|
3314
4187
|
marker: draft.marker,
|
|
3315
4188
|
selection: draft.selection
|
|
3316
4189
|
});
|
|
3317
4190
|
});
|
|
3318
|
-
|
|
4191
|
+
const error = this.createDraftError();
|
|
4192
|
+
form.append(
|
|
4193
|
+
...titleInput ? [titleInput] : [],
|
|
4194
|
+
textarea,
|
|
4195
|
+
...assigneeSelect ? [assigneeSelect] : [],
|
|
4196
|
+
...error ? [error] : [],
|
|
4197
|
+
actions
|
|
4198
|
+
);
|
|
3319
4199
|
return form;
|
|
3320
4200
|
}
|
|
3321
4201
|
createAreaMetricsPanel(draft) {
|
|
@@ -3362,11 +4242,17 @@ ${adjustment}` : adjustment;
|
|
|
3362
4242
|
}
|
|
3363
4243
|
return layer;
|
|
3364
4244
|
}
|
|
3365
|
-
createAreaDraftPopover(draft) {
|
|
4245
|
+
createAreaDraftPopover(draft, options = {}) {
|
|
3366
4246
|
const environment = this.config.getEnvironment();
|
|
3367
4247
|
const popover = document.createElement("div");
|
|
3368
|
-
popover.className =
|
|
3369
|
-
|
|
4248
|
+
popover.className = [
|
|
4249
|
+
"dfwr-area-draft",
|
|
4250
|
+
"is-composer",
|
|
4251
|
+
options.dockComposer ? "is-docked-composer" : ""
|
|
4252
|
+
].filter(Boolean).join(" ");
|
|
4253
|
+
if (options.dockComposer) {
|
|
4254
|
+
popover.style.width = "100%";
|
|
4255
|
+
} else if (environment && draft.selection) {
|
|
3370
4256
|
const selection = toHostSelection(
|
|
3371
4257
|
toViewportSelection(draft.selection.viewport),
|
|
3372
4258
|
environment
|
|
@@ -3382,40 +4268,61 @@ ${adjustment}` : adjustment;
|
|
|
3382
4268
|
popover.style.width = `${composer.width}px`;
|
|
3383
4269
|
popover.style.right = "auto";
|
|
3384
4270
|
}
|
|
3385
|
-
const dragHandle = this.createDraftDragHandle("Move area composer");
|
|
3386
|
-
popover.append(
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
4271
|
+
const dragHandle = options.dockComposer ? void 0 : this.createDraftDragHandle("Move area composer");
|
|
4272
|
+
popover.append(
|
|
4273
|
+
...dragHandle ? [dragHandle] : [],
|
|
4274
|
+
this.createAreaForm()
|
|
4275
|
+
);
|
|
4276
|
+
if (dragHandle) {
|
|
4277
|
+
this.attachDraftComposerDrag(popover, dragHandle, (composerPosition) => {
|
|
4278
|
+
const areaDraft = this.state.areaDraft ?? draft;
|
|
4279
|
+
this.config.actions.setAreaDraft({
|
|
4280
|
+
...areaDraft,
|
|
4281
|
+
composerPosition
|
|
4282
|
+
});
|
|
3392
4283
|
});
|
|
3393
|
-
}
|
|
4284
|
+
}
|
|
3394
4285
|
return popover;
|
|
3395
4286
|
}
|
|
3396
4287
|
createFormActions(saveLabel, onSave, options) {
|
|
3397
4288
|
const actions = document.createElement("div");
|
|
3398
4289
|
actions.className = ["dfwr-actions", options?.className].filter(Boolean).join(" ");
|
|
4290
|
+
const isSaving = this.state.isCreatingItem;
|
|
3399
4291
|
const save = document.createElement("button");
|
|
3400
4292
|
save.className = "dfwr-button is-primary";
|
|
3401
4293
|
save.type = "button";
|
|
3402
|
-
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
|
+
}
|
|
3403
4301
|
save.addEventListener("click", (event) => {
|
|
3404
4302
|
event.preventDefault();
|
|
3405
4303
|
event.stopPropagation();
|
|
4304
|
+
if (this.state.isCreatingItem) return;
|
|
3406
4305
|
onSave();
|
|
3407
4306
|
});
|
|
3408
4307
|
const cancel = document.createElement("button");
|
|
3409
4308
|
cancel.className = "dfwr-button";
|
|
3410
4309
|
cancel.type = "button";
|
|
4310
|
+
cancel.disabled = isSaving;
|
|
3411
4311
|
cancel.textContent = "Cancel";
|
|
3412
4312
|
cancel.addEventListener("click", (event) => {
|
|
3413
|
-
|
|
3414
|
-
event.stopPropagation();
|
|
3415
|
-
this.config.actions.setModeState("idle");
|
|
3416
|
-
this.config.actions.clearDrafts();
|
|
3417
|
-
this.config.actions.render();
|
|
4313
|
+
this.cancelDraft(event);
|
|
3418
4314
|
});
|
|
4315
|
+
if (options?.leading?.length) {
|
|
4316
|
+
actions.classList.add("has-leading");
|
|
4317
|
+
const leading = document.createElement("div");
|
|
4318
|
+
leading.className = "dfwr-actions-leading";
|
|
4319
|
+
leading.append(...options.leading);
|
|
4320
|
+
const primary = document.createElement("div");
|
|
4321
|
+
primary.className = "dfwr-actions-primary";
|
|
4322
|
+
primary.append(save, cancel);
|
|
4323
|
+
actions.append(leading, primary);
|
|
4324
|
+
return actions;
|
|
4325
|
+
}
|
|
3419
4326
|
if (options?.beforeSave?.length || options?.className) {
|
|
3420
4327
|
actions.append(cancel, ...options.beforeSave ?? [], save);
|
|
3421
4328
|
return actions;
|
|
@@ -3423,6 +4330,20 @@ ${adjustment}` : adjustment;
|
|
|
3423
4330
|
actions.append(save, cancel);
|
|
3424
4331
|
return actions;
|
|
3425
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
|
+
}
|
|
3426
4347
|
createList() {
|
|
3427
4348
|
const section = document.createElement("div");
|
|
3428
4349
|
section.className = "dfwr-list";
|
|
@@ -3475,14 +4396,20 @@ ${adjustment}` : adjustment;
|
|
|
3475
4396
|
kind.className = "dfwr-item-kind";
|
|
3476
4397
|
kind.textContent = item.kind;
|
|
3477
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
|
+
}
|
|
3478
4405
|
const comment = document.createElement("p");
|
|
3479
|
-
comment.className =
|
|
4406
|
+
comment.className = `dfwr-item-comment${title ? "" : " is-primary"}`;
|
|
3480
4407
|
comment.textContent = item.comment;
|
|
3481
4408
|
const date = document.createElement("time");
|
|
3482
4409
|
date.className = "dfwr-item-date";
|
|
3483
4410
|
date.dateTime = item.createdAt;
|
|
3484
4411
|
date.textContent = formatItemMeta(item);
|
|
3485
|
-
body.append(badges, comment, date);
|
|
4412
|
+
body.append(badges, ...titleElement ? [titleElement] : [], comment, date);
|
|
3486
4413
|
const actions = document.createElement("div");
|
|
3487
4414
|
actions.className = "dfwr-item-actions";
|
|
3488
4415
|
actions.addEventListener("click", (event) => event.stopPropagation());
|
|
@@ -3671,7 +4598,14 @@ ${formatItemMeta(item)}`;
|
|
|
3671
4598
|
event,
|
|
3672
4599
|
this.config.getEnvironment()
|
|
3673
4600
|
);
|
|
3674
|
-
|
|
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));
|
|
3675
4609
|
});
|
|
3676
4610
|
}
|
|
3677
4611
|
createNoteLayer() {
|
|
@@ -3749,10 +4683,14 @@ ${formatItemMeta(item)}`;
|
|
|
3749
4683
|
let startX = 0;
|
|
3750
4684
|
let startY = 0;
|
|
3751
4685
|
let selection;
|
|
4686
|
+
let activePointerId;
|
|
4687
|
+
let isDragging = false;
|
|
4688
|
+
const ownerWindow = layer.ownerDocument.defaultView ?? window;
|
|
3752
4689
|
const updateBox = (event) => {
|
|
4690
|
+
const nextEnvironment = this.config.getEnvironment();
|
|
3753
4691
|
const nextPoint = toTargetPointFromHostEvent(
|
|
3754
4692
|
event,
|
|
3755
|
-
|
|
4693
|
+
nextEnvironment
|
|
3756
4694
|
);
|
|
3757
4695
|
const left = Math.min(startX, nextPoint.x);
|
|
3758
4696
|
const top = Math.min(startY, nextPoint.y);
|
|
@@ -3760,7 +4698,7 @@ ${formatItemMeta(item)}`;
|
|
|
3760
4698
|
const height = Math.abs(nextPoint.y - startY);
|
|
3761
4699
|
const hostPoint = toHostPoint(
|
|
3762
4700
|
{ x: left, y: top },
|
|
3763
|
-
|
|
4701
|
+
nextEnvironment
|
|
3764
4702
|
);
|
|
3765
4703
|
selection = { left, top, width, height };
|
|
3766
4704
|
box.style.left = `${hostPoint.x}px`;
|
|
@@ -3768,9 +4706,68 @@ ${formatItemMeta(item)}`;
|
|
|
3768
4706
|
box.style.width = `${width}px`;
|
|
3769
4707
|
box.style.height = `${height}px`;
|
|
3770
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
|
+
};
|
|
3771
4762
|
layer.addEventListener("pointerdown", (event) => {
|
|
4763
|
+
if (event.button !== 0) return;
|
|
3772
4764
|
event.preventDefault();
|
|
3773
|
-
|
|
4765
|
+
activePointerId = event.pointerId;
|
|
4766
|
+
isDragging = true;
|
|
4767
|
+
try {
|
|
4768
|
+
layer.setPointerCapture(event.pointerId);
|
|
4769
|
+
} catch {
|
|
4770
|
+
}
|
|
3774
4771
|
const startPoint = toTargetPointFromHostEvent(
|
|
3775
4772
|
event,
|
|
3776
4773
|
this.config.getEnvironment()
|
|
@@ -3778,20 +4775,11 @@ ${formatItemMeta(item)}`;
|
|
|
3778
4775
|
startX = startPoint.x;
|
|
3779
4776
|
startY = startPoint.y;
|
|
3780
4777
|
updateBox(event);
|
|
4778
|
+
addDragListeners();
|
|
3781
4779
|
});
|
|
3782
|
-
layer.addEventListener("pointermove",
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
});
|
|
3786
|
-
layer.addEventListener("pointerup", (event) => {
|
|
3787
|
-
if (!layer.hasPointerCapture(event.pointerId)) return;
|
|
3788
|
-
layer.releasePointerCapture(event.pointerId);
|
|
3789
|
-
updateBox(event);
|
|
3790
|
-
if (!selection || selection.width < 8 || selection.height < 8) return;
|
|
3791
|
-
this.config.actions.setSelectingArea(true);
|
|
3792
|
-
this.config.actions.render();
|
|
3793
|
-
void this.config.actions.createAreaDraft(selection);
|
|
3794
|
-
});
|
|
4780
|
+
layer.addEventListener("pointermove", handlePointerMove);
|
|
4781
|
+
layer.addEventListener("pointerup", handlePointerUp);
|
|
4782
|
+
layer.addEventListener("pointercancel", handlePointerCancel);
|
|
3795
4783
|
return layer;
|
|
3796
4784
|
}
|
|
3797
4785
|
};
|
|
@@ -3831,6 +4819,8 @@ var WebReviewKitApp = class {
|
|
|
3831
4819
|
this.isOpen = false;
|
|
3832
4820
|
this.mode = "idle";
|
|
3833
4821
|
this.items = [];
|
|
4822
|
+
this.draftError = "";
|
|
4823
|
+
this.isCreatingItem = false;
|
|
3834
4824
|
this.isSelectingArea = false;
|
|
3835
4825
|
this.handleKeyDown = (event) => {
|
|
3836
4826
|
if (event.key === "Escape" && this.cancelMode()) {
|
|
@@ -3844,9 +4834,10 @@ var WebReviewKitApp = class {
|
|
|
3844
4834
|
this.toggle();
|
|
3845
4835
|
};
|
|
3846
4836
|
this.handleViewportChange = () => {
|
|
3847
|
-
if (!this.isOpen || this.renderFrame) return;
|
|
4837
|
+
if (!this.isOpen || this.renderFrame || this.isDraftComposerFocused()) return;
|
|
3848
4838
|
this.renderFrame = window.requestAnimationFrame(() => {
|
|
3849
4839
|
this.renderFrame = void 0;
|
|
4840
|
+
if (this.isDraftComposerFocused()) return;
|
|
3850
4841
|
this.render();
|
|
3851
4842
|
});
|
|
3852
4843
|
};
|
|
@@ -3860,6 +4851,8 @@ var WebReviewKitApp = class {
|
|
|
3860
4851
|
items: this.items,
|
|
3861
4852
|
noteDraft: this.noteDraft,
|
|
3862
4853
|
areaDraft: this.areaDraft,
|
|
4854
|
+
draftError: this.draftError,
|
|
4855
|
+
isCreatingItem: this.isCreatingItem,
|
|
3863
4856
|
isSelectingArea: this.isSelectingArea,
|
|
3864
4857
|
highlightedItemId: this.highlightedItemId
|
|
3865
4858
|
}),
|
|
@@ -3874,19 +4867,22 @@ var WebReviewKitApp = class {
|
|
|
3874
4867
|
clearDrafts: () => {
|
|
3875
4868
|
this.noteDraft = void 0;
|
|
3876
4869
|
this.areaDraft = void 0;
|
|
4870
|
+
this.draftError = "";
|
|
3877
4871
|
},
|
|
3878
4872
|
setNoteDraft: (draft) => {
|
|
3879
4873
|
this.noteDraft = draft;
|
|
4874
|
+
this.draftError = "";
|
|
3880
4875
|
},
|
|
3881
4876
|
setAreaDraft: (draft) => {
|
|
3882
4877
|
this.areaDraft = draft;
|
|
4878
|
+
this.draftError = "";
|
|
3883
4879
|
},
|
|
3884
4880
|
setSelectingArea: (isSelectingArea) => {
|
|
3885
4881
|
this.isSelectingArea = isSelectingArea;
|
|
3886
4882
|
},
|
|
3887
4883
|
createItem: (input) => this.createItem(input),
|
|
3888
|
-
bindNoteDraftToPoint: (point,
|
|
3889
|
-
bindElementDraftToPoint: (point,
|
|
4884
|
+
bindNoteDraftToPoint: (point, fields) => this.bindNoteDraftToPoint(point, fields),
|
|
4885
|
+
bindElementDraftToPoint: (point, fields) => this.bindElementDraftToPoint(point, fields),
|
|
3890
4886
|
createAreaDraft: (selection) => this.createAreaDraft(selection)
|
|
3891
4887
|
}
|
|
3892
4888
|
});
|
|
@@ -3955,7 +4951,7 @@ var WebReviewKitApp = class {
|
|
|
3955
4951
|
this.noteDraft = void 0;
|
|
3956
4952
|
this.areaDraft = void 0;
|
|
3957
4953
|
this.isSelectingArea = false;
|
|
3958
|
-
await this.bindElementDraftToElement(element, comment);
|
|
4954
|
+
await this.bindElementDraftToElement(element, { comment });
|
|
3959
4955
|
}
|
|
3960
4956
|
getMode() {
|
|
3961
4957
|
return this.mode;
|
|
@@ -4023,6 +5019,14 @@ var WebReviewKitApp = class {
|
|
|
4023
5019
|
this.render();
|
|
4024
5020
|
return true;
|
|
4025
5021
|
}
|
|
5022
|
+
isDraftComposerFocused() {
|
|
5023
|
+
if (!this.noteDraft && !this.areaDraft) return false;
|
|
5024
|
+
const composerHost = this.getEnvironment()?.composerHost;
|
|
5025
|
+
const activeElement = composerHost?.ownerDocument.activeElement;
|
|
5026
|
+
return Boolean(
|
|
5027
|
+
composerHost && activeElement && composerHost.contains(activeElement)
|
|
5028
|
+
);
|
|
5029
|
+
}
|
|
4026
5030
|
getEnvironment() {
|
|
4027
5031
|
const target = typeof this.options.target === "function" ? this.options.target() : this.options.target;
|
|
4028
5032
|
if (!target) {
|
|
@@ -4051,6 +5055,7 @@ var WebReviewKitApp = class {
|
|
|
4051
5055
|
height: target.window.innerHeight
|
|
4052
5056
|
};
|
|
4053
5057
|
const overlayRect = target.getOverlayRect?.() ?? rect;
|
|
5058
|
+
const composerHost = target.getComposerHost?.();
|
|
4054
5059
|
return {
|
|
4055
5060
|
window: target.window,
|
|
4056
5061
|
document: target.document,
|
|
@@ -4065,7 +5070,8 @@ var WebReviewKitApp = class {
|
|
|
4065
5070
|
top: overlayRect.top,
|
|
4066
5071
|
width: overlayRect.width,
|
|
4067
5072
|
height: overlayRect.height
|
|
4068
|
-
}
|
|
5073
|
+
},
|
|
5074
|
+
composerHost
|
|
4069
5075
|
};
|
|
4070
5076
|
} catch {
|
|
4071
5077
|
return void 0;
|
|
@@ -4088,7 +5094,7 @@ var WebReviewKitApp = class {
|
|
|
4088
5094
|
if (!this.shadow) return;
|
|
4089
5095
|
this.view.render(this.shadow, this.createHiddenItemsStyleElement());
|
|
4090
5096
|
}
|
|
4091
|
-
async bindNoteDraftToPoint(point,
|
|
5097
|
+
async bindNoteDraftToPoint(point, fields = {}) {
|
|
4092
5098
|
const environment = this.getEnvironment();
|
|
4093
5099
|
if (!environment) return;
|
|
4094
5100
|
const viewport = getViewportSize(environment);
|
|
@@ -4108,13 +5114,13 @@ var WebReviewKitApp = class {
|
|
|
4108
5114
|
viewport,
|
|
4109
5115
|
anchor,
|
|
4110
5116
|
marker,
|
|
4111
|
-
|
|
5117
|
+
...fields
|
|
4112
5118
|
};
|
|
4113
5119
|
});
|
|
4114
5120
|
this.noteDraft = draft;
|
|
4115
5121
|
this.render();
|
|
4116
5122
|
}
|
|
4117
|
-
async bindElementDraftToPoint(point,
|
|
5123
|
+
async bindElementDraftToPoint(point, fields = {}) {
|
|
4118
5124
|
const environment = this.getEnvironment();
|
|
4119
5125
|
if (!environment) return;
|
|
4120
5126
|
const viewport = getViewportSize(environment);
|
|
@@ -4158,14 +5164,14 @@ var WebReviewKitApp = class {
|
|
|
4158
5164
|
anchor,
|
|
4159
5165
|
marker,
|
|
4160
5166
|
selection: reviewSelection,
|
|
4161
|
-
|
|
5167
|
+
...fields,
|
|
4162
5168
|
previewElement
|
|
4163
5169
|
};
|
|
4164
5170
|
});
|
|
4165
5171
|
this.noteDraft = draft;
|
|
4166
5172
|
this.render();
|
|
4167
5173
|
}
|
|
4168
|
-
async bindElementDraftToElement(element,
|
|
5174
|
+
async bindElementDraftToElement(element, fields = {}) {
|
|
4169
5175
|
const environment = this.getEnvironment();
|
|
4170
5176
|
if (!environment || element.ownerDocument !== environment.document) return;
|
|
4171
5177
|
const viewport = getViewportSize(environment);
|
|
@@ -4198,7 +5204,7 @@ var WebReviewKitApp = class {
|
|
|
4198
5204
|
anchor,
|
|
4199
5205
|
marker,
|
|
4200
5206
|
selection: reviewSelection,
|
|
4201
|
-
|
|
5207
|
+
...fields,
|
|
4202
5208
|
previewElement
|
|
4203
5209
|
};
|
|
4204
5210
|
});
|
|
@@ -4208,26 +5214,33 @@ var WebReviewKitApp = class {
|
|
|
4208
5214
|
}
|
|
4209
5215
|
async createAreaDraft(selection) {
|
|
4210
5216
|
const environment = this.getEnvironment();
|
|
4211
|
-
if (!environment)
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
)
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
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
|
+
}
|
|
4231
5244
|
}
|
|
4232
5245
|
async withOverlayHidden(callback) {
|
|
4233
5246
|
if (!this.root) return callback();
|
|
@@ -4241,11 +5254,16 @@ var WebReviewKitApp = class {
|
|
|
4241
5254
|
}
|
|
4242
5255
|
async createItem(input) {
|
|
4243
5256
|
const environment = this.getEnvironment();
|
|
4244
|
-
if (!environment) return;
|
|
5257
|
+
if (!environment || this.isCreatingItem) return;
|
|
4245
5258
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4246
5259
|
const routeKey = getRouteKey(environment);
|
|
4247
5260
|
const viewport = input.viewport ?? getViewportSize(environment);
|
|
4248
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
|
+
);
|
|
4249
5267
|
const item = {
|
|
4250
5268
|
id: createId(),
|
|
4251
5269
|
projectId: this.options.projectId,
|
|
@@ -4255,8 +5273,10 @@ var WebReviewKitApp = class {
|
|
|
4255
5273
|
normalizedPath: routeKey,
|
|
4256
5274
|
scope: input.scope ?? getReviewViewportScope(viewport, this.options.viewports?.presets),
|
|
4257
5275
|
kind: input.kind,
|
|
4258
|
-
title:
|
|
5276
|
+
title: title || void 0,
|
|
4259
5277
|
comment: input.comment,
|
|
5278
|
+
assigneeId,
|
|
5279
|
+
assigneeName: input.assigneeName ?? assigneeOption?.label,
|
|
4260
5280
|
createdBy: createdBy || void 0,
|
|
4261
5281
|
status: "todo",
|
|
4262
5282
|
viewport,
|
|
@@ -4271,13 +5291,23 @@ var WebReviewKitApp = class {
|
|
|
4271
5291
|
createdAt: now,
|
|
4272
5292
|
updatedAt: now
|
|
4273
5293
|
};
|
|
4274
|
-
|
|
4275
|
-
this.
|
|
4276
|
-
this.
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
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
|
+
}
|
|
4281
5311
|
}
|
|
4282
5312
|
async restoreItem(item) {
|
|
4283
5313
|
this.setModeState("idle");
|
|
@@ -4331,16 +5361,35 @@ function createNoopController() {
|
|
|
4331
5361
|
}
|
|
4332
5362
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4333
5363
|
0 && (module.exports = {
|
|
5364
|
+
DEFAULT_REVIEW_FIGMA_IMAGE_FORMAT,
|
|
5365
|
+
DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT,
|
|
5366
|
+
DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,
|
|
4334
5367
|
DEFAULT_REVIEW_VIEWPORTS,
|
|
5368
|
+
FIGMA_NODE_REF_SEPARATOR,
|
|
5369
|
+
REVIEW_FIGMA_TOKEN_MISSING_CODE,
|
|
4335
5370
|
REVIEW_WORKFLOW_STATUS_OPTIONS,
|
|
5371
|
+
ReviewFigmaTokenError,
|
|
5372
|
+
collectReviewFigmaReleaseSnapshot,
|
|
5373
|
+
createReviewFigmaFrameUrl,
|
|
5374
|
+
createReviewFigmaImageStoreClient,
|
|
5375
|
+
createReviewFigmaImagesSnapshot,
|
|
5376
|
+
createReviewFigmaNodeValue,
|
|
5377
|
+
createReviewFigmaReleaseSnapshot,
|
|
4336
5378
|
createWebReviewKit,
|
|
4337
5379
|
findReviewViewportPreset,
|
|
4338
5380
|
getNumberedReviewItems,
|
|
5381
|
+
getReviewFigmaImageMimeType,
|
|
5382
|
+
getReviewFigmaImageTargetKey,
|
|
4339
5383
|
getReviewItemScope,
|
|
4340
5384
|
getReviewItemScopeLabel,
|
|
4341
5385
|
getReviewViewportScope,
|
|
5386
|
+
isReviewFigmaTokenError,
|
|
4342
5387
|
localAdapter,
|
|
4343
5388
|
normalizeReviewItemStatus,
|
|
5389
|
+
parseReviewFigmaNodeRef,
|
|
5390
|
+
readReviewFigmaToken,
|
|
5391
|
+
requireReviewFigmaNodeRef,
|
|
5392
|
+
requireReviewFigmaToken,
|
|
4344
5393
|
supabaseAdapter
|
|
4345
5394
|
});
|
|
4346
5395
|
//# sourceMappingURL=index.cjs.map
|