@designfever/web-review-kit 0.6.0 → 0.7.1
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 +32 -0
- package/README.md +35 -10
- package/dist/chunk-2ZLU5FTD.js +602 -0
- package/dist/chunk-2ZLU5FTD.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 +1037 -144
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -3
- package/dist/index.d.ts +29 -3
- package/dist/index.js +50 -3
- package/dist/index.js.map +1 -1
- package/dist/react-shell.cjs +10339 -5168
- package/dist/react-shell.cjs.map +1 -1
- package/dist/react-shell.d.cts +39 -4
- package/dist/react-shell.d.ts +39 -4
- package/dist/react-shell.js +9687 -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 +1144 -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 +829 -5
- package/dist/vite.js.map +1 -1
- package/docs/README.md +13 -7
- package/docs/adapters.md +128 -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 +330 -0
- package/docs/figma-overlay.md +11 -4
- package/docs/installation.md +42 -7
- package/docs/release-notes-0.7.0.md +132 -0
- package/docs/release-notes-0.7.1.md +34 -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,498 @@ 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
|
+
figmaToken: readReviewFigmaImageToken(
|
|
554
|
+
options.token ?? getStoredReviewFigmaImageToken
|
|
555
|
+
)
|
|
556
|
+
});
|
|
557
|
+
},
|
|
558
|
+
updateImage(id, patch) {
|
|
559
|
+
return request(`${endpoint}/${encodeURIComponent(id)}`, {
|
|
560
|
+
method: "PATCH",
|
|
561
|
+
body: JSON.stringify(patch)
|
|
562
|
+
});
|
|
563
|
+
},
|
|
564
|
+
reorderImages(input) {
|
|
565
|
+
return request(`${endpoint}/reorder`, {
|
|
566
|
+
method: "PATCH",
|
|
567
|
+
body: JSON.stringify(input)
|
|
568
|
+
});
|
|
569
|
+
},
|
|
570
|
+
deleteImage(id) {
|
|
571
|
+
return request(`${endpoint}/${encodeURIComponent(id)}`, {
|
|
572
|
+
method: "DELETE"
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
async function createClientRenderedAddImageInput(input, clientRender, fetchOption) {
|
|
578
|
+
const options = normalizeClientRenderOptions(clientRender);
|
|
579
|
+
if (!options) return input;
|
|
580
|
+
const token = readClientRenderToken(options);
|
|
581
|
+
if (!token) return input;
|
|
582
|
+
try {
|
|
583
|
+
const asset = await withTimeout(
|
|
584
|
+
createClientRenderedFigmaAsset(input.figmaUrl, token, options, fetchOption),
|
|
585
|
+
options.timeoutMs ?? 1e4
|
|
586
|
+
);
|
|
587
|
+
return {
|
|
588
|
+
...input,
|
|
589
|
+
imageFormat: asset.imageFormat,
|
|
590
|
+
asset
|
|
591
|
+
};
|
|
592
|
+
} catch {
|
|
593
|
+
return input;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
function normalizeClientRenderOptions(clientRender) {
|
|
597
|
+
if (!clientRender) return null;
|
|
598
|
+
if (clientRender === true) return {};
|
|
599
|
+
return clientRender;
|
|
600
|
+
}
|
|
601
|
+
function readClientRenderToken(options) {
|
|
602
|
+
const token = typeof options.token === "function" ? options.token() : options.token;
|
|
603
|
+
return typeof token === "string" ? token.trim() : "";
|
|
604
|
+
}
|
|
605
|
+
async function createClientRenderedFigmaAsset(figmaUrl, token, options, fetchOption) {
|
|
606
|
+
const ref = parseReviewFigmaNodeRef(figmaUrl);
|
|
607
|
+
if (!ref) throw new Error("A Figma node link is required.");
|
|
608
|
+
const requestFetch = fetchOption ?? globalThis.fetch;
|
|
609
|
+
if (!requestFetch) throw new Error("Figma client rendering requires fetch.");
|
|
610
|
+
const renderFormat = options.renderFormat ?? "png";
|
|
611
|
+
const response = await requestFetch(
|
|
612
|
+
createReviewFigmaImageApiUrl({
|
|
613
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
614
|
+
fileKey: ref.fileKey,
|
|
615
|
+
nodeId: ref.nodeId,
|
|
616
|
+
format: renderFormat,
|
|
617
|
+
scale: options.renderScale,
|
|
618
|
+
useAbsoluteBounds: options.useAbsoluteBounds
|
|
619
|
+
}),
|
|
620
|
+
{
|
|
621
|
+
headers: {
|
|
622
|
+
"X-Figma-Token": token
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
);
|
|
626
|
+
const body = await response.json().catch(() => null);
|
|
627
|
+
if (!response.ok) {
|
|
628
|
+
throw new Error(body?.err || `Figma image render failed with ${response.status}`);
|
|
629
|
+
}
|
|
630
|
+
const imageUrl = body?.images?.[ref.nodeId];
|
|
631
|
+
if (!imageUrl) throw new Error(`Figma image render returned no URL for ${ref.nodeId}.`);
|
|
632
|
+
const imageResponse = await requestFetch(imageUrl);
|
|
633
|
+
if (!imageResponse.ok) {
|
|
634
|
+
throw new Error(`Figma image download failed with ${imageResponse.status}`);
|
|
635
|
+
}
|
|
636
|
+
const originalBlob = await imageResponse.blob();
|
|
637
|
+
const originalMimeType = normalizeClientImageMimeType(originalBlob.type) ?? getReviewFigmaImageMimeType(renderFormat === "jpg" ? "jpg" : "png");
|
|
638
|
+
const originalFormat = getReviewFigmaImageFormatFromMimeType(originalMimeType) ?? (renderFormat === "jpg" ? "jpg" : "png");
|
|
639
|
+
const dimensions = await readImageBlobDimensions(originalBlob);
|
|
640
|
+
const shouldConvertToWebp = options.convertToWebp ?? true;
|
|
641
|
+
const convertedBlob = shouldConvertToWebp ? await convertImageBlobToWebp(
|
|
642
|
+
originalBlob,
|
|
643
|
+
options.webpQuality ?? 0.9,
|
|
644
|
+
dimensions
|
|
645
|
+
).catch(() => null) : null;
|
|
646
|
+
const finalBlob = convertedBlob?.type === "image/webp" ? convertedBlob : originalBlob;
|
|
647
|
+
const finalMimeType = normalizeClientImageMimeType(finalBlob.type) ?? originalMimeType;
|
|
648
|
+
const finalFormat = getReviewFigmaImageFormatFromMimeType(finalMimeType) ?? originalFormat;
|
|
649
|
+
return {
|
|
650
|
+
dataUrl: await blobToDataUrl(finalBlob),
|
|
651
|
+
imageFormat: finalFormat,
|
|
652
|
+
mimeType: finalMimeType,
|
|
653
|
+
byteSize: finalBlob.size,
|
|
654
|
+
width: dimensions.width,
|
|
655
|
+
height: dimensions.height
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
async function readImageBlobDimensions(blob) {
|
|
659
|
+
const image = await loadImageBlob(blob);
|
|
660
|
+
return {
|
|
661
|
+
width: image.naturalWidth || image.width,
|
|
662
|
+
height: image.naturalHeight || image.height
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
async function convertImageBlobToWebp(blob, quality, dimensions) {
|
|
666
|
+
if (typeof document === "undefined") return null;
|
|
667
|
+
if (!dimensions.width || !dimensions.height) return null;
|
|
668
|
+
const image = await loadImageBlob(blob);
|
|
669
|
+
const canvas = document.createElement("canvas");
|
|
670
|
+
canvas.width = dimensions.width;
|
|
671
|
+
canvas.height = dimensions.height;
|
|
672
|
+
const context = canvas.getContext("2d");
|
|
673
|
+
if (!context) return null;
|
|
674
|
+
context.drawImage(image, 0, 0);
|
|
675
|
+
return new Promise((resolve) => {
|
|
676
|
+
canvas.toBlob(resolve, "image/webp", quality);
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
function loadImageBlob(blob) {
|
|
680
|
+
return new Promise((resolve, reject) => {
|
|
681
|
+
if (typeof Image === "undefined" || typeof URL === "undefined") {
|
|
682
|
+
reject(new Error("Image decoding is unavailable."));
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
const image = new Image();
|
|
686
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
687
|
+
image.onload = () => {
|
|
688
|
+
URL.revokeObjectURL(objectUrl);
|
|
689
|
+
resolve(image);
|
|
690
|
+
};
|
|
691
|
+
image.onerror = () => {
|
|
692
|
+
URL.revokeObjectURL(objectUrl);
|
|
693
|
+
reject(new Error("Image decoding failed."));
|
|
694
|
+
};
|
|
695
|
+
image.src = objectUrl;
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
function blobToDataUrl(blob) {
|
|
699
|
+
return new Promise((resolve, reject) => {
|
|
700
|
+
const reader = new FileReader();
|
|
701
|
+
reader.onload = () => {
|
|
702
|
+
if (typeof reader.result === "string") {
|
|
703
|
+
resolve(reader.result);
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
reject(new Error("Blob encoding failed."));
|
|
707
|
+
};
|
|
708
|
+
reader.onerror = () => reject(reader.error ?? new Error("Blob encoding failed."));
|
|
709
|
+
reader.readAsDataURL(blob);
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
function normalizeClientImageMimeType(value) {
|
|
713
|
+
const mimeType = value?.split(";")[0]?.trim().toLowerCase();
|
|
714
|
+
if (mimeType === "image/jpg") return "image/jpeg";
|
|
715
|
+
if (mimeType === "image/jpeg" || mimeType === "image/png" || mimeType === "image/webp") {
|
|
716
|
+
return mimeType;
|
|
717
|
+
}
|
|
718
|
+
return null;
|
|
719
|
+
}
|
|
720
|
+
async function withTimeout(promise, timeoutMs) {
|
|
721
|
+
let timeoutId;
|
|
722
|
+
try {
|
|
723
|
+
return await Promise.race([
|
|
724
|
+
promise,
|
|
725
|
+
new Promise((_, reject) => {
|
|
726
|
+
timeoutId = setTimeout(
|
|
727
|
+
() => reject(new Error("Figma client rendering timed out.")),
|
|
728
|
+
timeoutMs
|
|
729
|
+
);
|
|
730
|
+
})
|
|
731
|
+
]);
|
|
732
|
+
} finally {
|
|
733
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
function getReviewFigmaImageTargetKey(target) {
|
|
737
|
+
return JSON.stringify(normalizeReviewFigmaImageTarget(target));
|
|
738
|
+
}
|
|
739
|
+
function getReviewFigmaImageMimeType(format) {
|
|
740
|
+
if (format === "jpg") return "image/jpeg";
|
|
741
|
+
if (format === "png") return "image/png";
|
|
742
|
+
return "image/webp";
|
|
743
|
+
}
|
|
744
|
+
function createReviewFigmaImageStoreRequest(endpoint, fetchOption) {
|
|
745
|
+
return async (input, init = {}) => {
|
|
746
|
+
const requestFetch = fetchOption ?? globalThis.fetch;
|
|
747
|
+
if (!requestFetch) throw new Error("Figma image store requires fetch.");
|
|
748
|
+
const figmaToken = init.figmaToken ?? "";
|
|
749
|
+
const { figmaToken: _figmaToken, ...requestInit } = init;
|
|
750
|
+
const response = await requestFetch(input, {
|
|
751
|
+
...requestInit,
|
|
752
|
+
headers: {
|
|
753
|
+
"Content-Type": "application/json",
|
|
754
|
+
...figmaToken ? { "X-Figma-Token": figmaToken } : {},
|
|
755
|
+
...requestInit.headers ?? {}
|
|
756
|
+
}
|
|
757
|
+
});
|
|
758
|
+
const text = await response.text();
|
|
759
|
+
const body = text ? JSON.parse(text) : null;
|
|
760
|
+
if (!response.ok) {
|
|
761
|
+
const message = typeof body?.error === "string" ? body.error : `Figma image store request failed: ${response.status}`;
|
|
762
|
+
throw new Error(message);
|
|
763
|
+
}
|
|
764
|
+
return body;
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
function readReviewFigmaImageToken(provider) {
|
|
768
|
+
const token = typeof provider === "function" ? provider() : provider;
|
|
769
|
+
return typeof token === "string" ? token.trim() : "";
|
|
770
|
+
}
|
|
771
|
+
function getStoredReviewFigmaImageToken() {
|
|
772
|
+
if (typeof window === "undefined") return "";
|
|
773
|
+
try {
|
|
774
|
+
return window.localStorage.getItem("figma-token") ?? "";
|
|
775
|
+
} catch {
|
|
776
|
+
return "";
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
function normalizeReviewFigmaImageTarget(target) {
|
|
780
|
+
if (target.type === "figma-node") {
|
|
781
|
+
return {
|
|
782
|
+
type: target.type,
|
|
783
|
+
projectId: target.projectId,
|
|
784
|
+
fileKey: target.fileKey,
|
|
785
|
+
nodeId: target.nodeId
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
return {
|
|
789
|
+
type: target.type,
|
|
790
|
+
projectId: target.projectId,
|
|
791
|
+
pageUrl: target.pageUrl,
|
|
792
|
+
slot: target.slot ?? "",
|
|
793
|
+
viewport: target.viewport ? {
|
|
794
|
+
label: target.viewport.label ?? "",
|
|
795
|
+
width: target.viewport.width ?? null,
|
|
796
|
+
height: target.viewport.height ?? null,
|
|
797
|
+
scope: target.viewport.scope ?? ""
|
|
798
|
+
} : null
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// src/figma/image.snapshot.ts
|
|
803
|
+
function createReviewFigmaImagesSnapshot(images, options = {}) {
|
|
804
|
+
const targetKeys = options.targets?.length ? new Set(options.targets.map(getReviewFigmaImageTargetKey)) : null;
|
|
805
|
+
return images.filter((image) => {
|
|
806
|
+
if (options.projectId && image.projectId !== options.projectId) {
|
|
807
|
+
return false;
|
|
808
|
+
}
|
|
809
|
+
if (targetKeys && !targetKeys.has(getReviewFigmaImageTargetKey(image.target))) {
|
|
810
|
+
return false;
|
|
811
|
+
}
|
|
812
|
+
return true;
|
|
813
|
+
}).map(cloneReviewFigmaImage).sort(compareReviewFigmaSnapshotImages);
|
|
814
|
+
}
|
|
815
|
+
function createReviewFigmaReleaseSnapshot({
|
|
816
|
+
images,
|
|
817
|
+
projectId,
|
|
818
|
+
releaseId,
|
|
819
|
+
label,
|
|
820
|
+
createdAt,
|
|
821
|
+
targets
|
|
822
|
+
}) {
|
|
823
|
+
return {
|
|
824
|
+
version: 1,
|
|
825
|
+
projectId,
|
|
826
|
+
...releaseId ? { releaseId } : null,
|
|
827
|
+
...label ? { label } : null,
|
|
828
|
+
createdAt: createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
829
|
+
figmaImagesSnapshot: createReviewFigmaImagesSnapshot(images, {
|
|
830
|
+
projectId,
|
|
831
|
+
targets
|
|
832
|
+
})
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
async function collectReviewFigmaReleaseSnapshot({
|
|
836
|
+
store,
|
|
837
|
+
targets,
|
|
838
|
+
...snapshotOptions
|
|
839
|
+
}) {
|
|
840
|
+
const imagesByTarget = await Promise.all(
|
|
841
|
+
targets.map((target) => store.listImages(target))
|
|
842
|
+
);
|
|
843
|
+
return createReviewFigmaReleaseSnapshot({
|
|
844
|
+
...snapshotOptions,
|
|
845
|
+
targets,
|
|
846
|
+
images: dedupeReviewFigmaImages(imagesByTarget.flat())
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
function dedupeReviewFigmaImages(images) {
|
|
850
|
+
return Array.from(new Map(images.map((image) => [image.id, image])).values());
|
|
851
|
+
}
|
|
852
|
+
function cloneReviewFigmaImage(image) {
|
|
853
|
+
return {
|
|
854
|
+
...image,
|
|
855
|
+
target: cloneReviewFigmaImageTarget(image.target)
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
function cloneReviewFigmaImageTarget(target) {
|
|
859
|
+
if (target.type === "figma-node") {
|
|
860
|
+
return {
|
|
861
|
+
type: target.type,
|
|
862
|
+
projectId: target.projectId,
|
|
863
|
+
fileKey: target.fileKey,
|
|
864
|
+
nodeId: target.nodeId
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
return {
|
|
868
|
+
type: target.type,
|
|
869
|
+
projectId: target.projectId,
|
|
870
|
+
pageUrl: target.pageUrl,
|
|
871
|
+
slot: target.slot,
|
|
872
|
+
viewport: target.viewport ? {
|
|
873
|
+
label: target.viewport.label,
|
|
874
|
+
width: target.viewport.width,
|
|
875
|
+
height: target.viewport.height,
|
|
876
|
+
scope: target.viewport.scope
|
|
877
|
+
} : void 0
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
function compareReviewFigmaSnapshotImages(a, b) {
|
|
881
|
+
return a.projectId.localeCompare(b.projectId) || getReviewFigmaImageTargetKey(a.target).localeCompare(
|
|
882
|
+
getReviewFigmaImageTargetKey(b.target)
|
|
883
|
+
) || a.order - b.order || a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id);
|
|
884
|
+
}
|
|
885
|
+
|
|
368
886
|
// src/core/geometry.ts
|
|
369
887
|
function rectanglesIntersect(a, b) {
|
|
370
888
|
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 +1065,13 @@ function getDomAnchorFromPoint(point, configuredAttribute = "data-qa-id", enviro
|
|
|
547
1065
|
const target = environment.document.elementFromPoint(point.x, point.y);
|
|
548
1066
|
if (!target) return void 0;
|
|
549
1067
|
const candidates = createAnchorCandidates(target, configuredAttribute);
|
|
550
|
-
const
|
|
551
|
-
if (!
|
|
1068
|
+
const primaryCandidate = candidates[0];
|
|
1069
|
+
if (!primaryCandidate) return void 0;
|
|
552
1070
|
return {
|
|
553
|
-
...
|
|
1071
|
+
...primaryCandidate,
|
|
554
1072
|
candidates,
|
|
555
1073
|
htmlSnippet: getElementHtmlSnippet(
|
|
556
|
-
getAnchorSourceElement(target,
|
|
1074
|
+
getAnchorSourceElement(target, primaryCandidate, configuredAttribute) ?? target
|
|
557
1075
|
),
|
|
558
1076
|
source: getDomSourceHint(target)
|
|
559
1077
|
};
|
|
@@ -561,13 +1079,13 @@ function getDomAnchorFromPoint(point, configuredAttribute = "data-qa-id", enviro
|
|
|
561
1079
|
function getDomAnchorFromElement(target, configuredAttribute = "data-qa-id", environment) {
|
|
562
1080
|
if (target.ownerDocument !== environment.document) return void 0;
|
|
563
1081
|
const candidates = createAnchorCandidates(target, configuredAttribute);
|
|
564
|
-
const
|
|
565
|
-
if (!
|
|
1082
|
+
const primaryCandidate = candidates[0];
|
|
1083
|
+
if (!primaryCandidate) return void 0;
|
|
566
1084
|
return {
|
|
567
|
-
...
|
|
1085
|
+
...primaryCandidate,
|
|
568
1086
|
candidates,
|
|
569
1087
|
htmlSnippet: getElementHtmlSnippet(
|
|
570
|
-
getAnchorSourceElement(target,
|
|
1088
|
+
getAnchorSourceElement(target, primaryCandidate, configuredAttribute) ?? target
|
|
571
1089
|
),
|
|
572
1090
|
source: getDomSourceHint(target)
|
|
573
1091
|
};
|
|
@@ -738,7 +1256,7 @@ function createAnchorCandidates(target, configuredAttribute) {
|
|
|
738
1256
|
function findClosestAttributeAnchor(target, attributeNames, confidence, options) {
|
|
739
1257
|
for (const attributeName of attributeNames) {
|
|
740
1258
|
const selector = `[${attributeName}]`;
|
|
741
|
-
const element =
|
|
1259
|
+
const element = tryClosest(target, selector);
|
|
742
1260
|
if (!element) continue;
|
|
743
1261
|
const value = getStableAttributeValue(element, attributeName);
|
|
744
1262
|
if (!value) continue;
|
|
@@ -844,7 +1362,7 @@ function getAnchorSourceElement(target, candidate, configuredAttribute) {
|
|
|
844
1362
|
return target;
|
|
845
1363
|
}
|
|
846
1364
|
}
|
|
847
|
-
function
|
|
1365
|
+
function tryClosest(element, selector) {
|
|
848
1366
|
try {
|
|
849
1367
|
return element.closest(selector);
|
|
850
1368
|
} catch {
|
|
@@ -1444,18 +1962,90 @@ function setDocumentScrollInstantly(environment, position) {
|
|
|
1444
1962
|
);
|
|
1445
1963
|
}
|
|
1446
1964
|
|
|
1965
|
+
// src/core/draft.metrics.ts
|
|
1966
|
+
function getDraftViewportScale(viewport, presets) {
|
|
1967
|
+
const preset = findReviewViewportPreset(viewport, presets);
|
|
1968
|
+
const designWidth = typeof preset.designWidth === "number" && preset.designWidth > 0 ? preset.designWidth : viewport.width;
|
|
1969
|
+
const scale = designWidth > 0 ? viewport.width / designWidth : 1;
|
|
1970
|
+
return { scale, designWidth, presetLabel: preset.label };
|
|
1971
|
+
}
|
|
1972
|
+
function getDraftAdjustmentMetrics(draft, presets) {
|
|
1973
|
+
const adjustment = draft.adjustment;
|
|
1974
|
+
const x = adjustment?.x ?? 0;
|
|
1975
|
+
const y = adjustment?.y ?? 0;
|
|
1976
|
+
const scale = adjustment?.scale ?? 0;
|
|
1977
|
+
const {
|
|
1978
|
+
scale: viewportScale,
|
|
1979
|
+
designWidth,
|
|
1980
|
+
presetLabel
|
|
1981
|
+
} = getDraftViewportScale(draft.viewport, presets);
|
|
1982
|
+
const selection = draft.selection ? toViewportSelection(draft.selection.viewport) : void 0;
|
|
1983
|
+
const scaleCssDelta = scale * viewportScale;
|
|
1984
|
+
const scaleFactor = selection && selection.width > 0 ? Math.max(
|
|
1985
|
+
1 / selection.width,
|
|
1986
|
+
(selection.width + scaleCssDelta) / selection.width
|
|
1987
|
+
) : 1;
|
|
1988
|
+
return {
|
|
1989
|
+
x,
|
|
1990
|
+
y,
|
|
1991
|
+
scale,
|
|
1992
|
+
cssX: x * viewportScale,
|
|
1993
|
+
cssY: y * viewportScale,
|
|
1994
|
+
scaleFactor,
|
|
1995
|
+
viewportScale,
|
|
1996
|
+
designWidth,
|
|
1997
|
+
presetLabel,
|
|
1998
|
+
viewportWidth: draft.viewport.width
|
|
1999
|
+
};
|
|
2000
|
+
}
|
|
2001
|
+
function hasDraftAdjustment(draft, presets) {
|
|
2002
|
+
const metrics = getDraftAdjustmentMetrics(draft, presets);
|
|
2003
|
+
return metrics.x !== 0 || metrics.y !== 0 || metrics.scale !== 0;
|
|
2004
|
+
}
|
|
2005
|
+
function getAdjustedDraftPoint(point, draft, presets) {
|
|
2006
|
+
const metrics = getDraftAdjustmentMetrics(draft, presets);
|
|
2007
|
+
return {
|
|
2008
|
+
x: point.x + metrics.cssX,
|
|
2009
|
+
y: point.y + metrics.cssY
|
|
2010
|
+
};
|
|
2011
|
+
}
|
|
2012
|
+
function getAdjustedDraftSelection(selection, draft, presets) {
|
|
2013
|
+
const metrics = getDraftAdjustmentMetrics(draft, presets);
|
|
2014
|
+
return {
|
|
2015
|
+
...selection,
|
|
2016
|
+
left: selection.left + metrics.cssX,
|
|
2017
|
+
top: selection.top + metrics.cssY,
|
|
2018
|
+
width: selection.width * metrics.scaleFactor,
|
|
2019
|
+
height: selection.height * metrics.scaleFactor
|
|
2020
|
+
};
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
// src/core/typography.tokens.ts
|
|
2024
|
+
var reviewTypographyTokens = `
|
|
2025
|
+
--df-review-font-sans: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
2026
|
+
--df-review-font-mono: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
|
2027
|
+
--df-review-font-size-3xs: 9px;
|
|
2028
|
+
--df-review-font-size-2xs: 10px;
|
|
2029
|
+
--df-review-font-size-xs: 11px;
|
|
2030
|
+
--df-review-font-size-sm: 12px;
|
|
2031
|
+
--df-review-font-size-md: 13px;
|
|
2032
|
+
--df-review-font-size-lg: 14px;
|
|
2033
|
+
--df-review-font-size-xl: 15px;
|
|
2034
|
+
--df-review-font-size-2xl: 18px;
|
|
2035
|
+
--df-review-font-weight-normal: 400;
|
|
2036
|
+
--df-review-font-weight-emphasis: 500;
|
|
2037
|
+
--df-review-line-height-tight: 1.25;
|
|
2038
|
+
--df-review-line-height-base: 1.42;
|
|
2039
|
+
--df-review-line-height-relaxed: 1.55;
|
|
2040
|
+
`;
|
|
2041
|
+
|
|
1447
2042
|
// src/core/overlay.style.ts
|
|
1448
2043
|
function createStyleElement() {
|
|
1449
2044
|
const style = document.createElement("style");
|
|
1450
2045
|
style.textContent = `
|
|
1451
2046
|
:host {
|
|
1452
2047
|
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;
|
|
2048
|
+
${reviewTypographyTokens}
|
|
1459
2049
|
--df-review-space-1: 4px;
|
|
1460
2050
|
--df-review-space-1-5: 6px;
|
|
1461
2051
|
--df-review-space-2: 8px;
|
|
@@ -1554,8 +2144,8 @@ function createStyleElement() {
|
|
|
1554
2144
|
}
|
|
1555
2145
|
|
|
1556
2146
|
.dfwr-title {
|
|
1557
|
-
font-size:
|
|
1558
|
-
font-weight:
|
|
2147
|
+
font-size: var(--df-review-font-size-xl);
|
|
2148
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
1559
2149
|
line-height: 1.25;
|
|
1560
2150
|
}
|
|
1561
2151
|
|
|
@@ -1599,11 +2189,15 @@ function createStyleElement() {
|
|
|
1599
2189
|
}
|
|
1600
2190
|
|
|
1601
2191
|
.dfwr-button {
|
|
2192
|
+
display: inline-flex;
|
|
2193
|
+
align-items: center;
|
|
2194
|
+
justify-content: center;
|
|
2195
|
+
gap: 6px;
|
|
1602
2196
|
min-height: var(--df-review-control-height-md);
|
|
1603
2197
|
padding: 0 12px;
|
|
1604
2198
|
border-radius: var(--df-review-radius-sm);
|
|
1605
2199
|
font-size: var(--df-review-font-size-sm);
|
|
1606
|
-
font-weight:
|
|
2200
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
1607
2201
|
}
|
|
1608
2202
|
|
|
1609
2203
|
.dfwr-button:hover,
|
|
@@ -1619,6 +2213,21 @@ function createStyleElement() {
|
|
|
1619
2213
|
color: var(--df-review-color-accent-contrast);
|
|
1620
2214
|
}
|
|
1621
2215
|
|
|
2216
|
+
.dfwr-button:disabled {
|
|
2217
|
+
cursor: default;
|
|
2218
|
+
opacity: 0.62;
|
|
2219
|
+
}
|
|
2220
|
+
|
|
2221
|
+
.dfwr-spinner {
|
|
2222
|
+
display: inline-block;
|
|
2223
|
+
width: 12px;
|
|
2224
|
+
height: 12px;
|
|
2225
|
+
border: 2px solid currentColor;
|
|
2226
|
+
border-right-color: transparent;
|
|
2227
|
+
border-radius: 999px;
|
|
2228
|
+
animation: dfwr-spin 720ms linear infinite;
|
|
2229
|
+
}
|
|
2230
|
+
|
|
1622
2231
|
.dfwr-icon-button {
|
|
1623
2232
|
display: inline-flex;
|
|
1624
2233
|
align-items: center;
|
|
@@ -1628,7 +2237,7 @@ function createStyleElement() {
|
|
|
1628
2237
|
padding: 0 8px;
|
|
1629
2238
|
border-radius: var(--df-review-radius-sm);
|
|
1630
2239
|
font-size: var(--df-review-font-size-xs);
|
|
1631
|
-
font-weight:
|
|
2240
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
1632
2241
|
line-height: 1;
|
|
1633
2242
|
text-transform: uppercase;
|
|
1634
2243
|
}
|
|
@@ -1783,7 +2392,7 @@ function createStyleElement() {
|
|
|
1783
2392
|
0 8px 18px rgba(0, 0, 0, 0.28);
|
|
1784
2393
|
color: #111820;
|
|
1785
2394
|
font-size: var(--df-review-font-size-2xs);
|
|
1786
|
-
font-weight:
|
|
2395
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
1787
2396
|
line-height: 1;
|
|
1788
2397
|
pointer-events: none;
|
|
1789
2398
|
}
|
|
@@ -1809,7 +2418,7 @@ function createStyleElement() {
|
|
|
1809
2418
|
box-shadow: 0 0 0 4px rgba(var(--dfwr-scope-rgb), 0.18);
|
|
1810
2419
|
color: var(--dfwr-scope);
|
|
1811
2420
|
font-size: var(--df-review-font-size-2xs);
|
|
1812
|
-
font-weight:
|
|
2421
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
1813
2422
|
}
|
|
1814
2423
|
|
|
1815
2424
|
.dfwr-bound-marker.is-highlighted {
|
|
@@ -2151,10 +2760,18 @@ function createStyleElement() {
|
|
|
2151
2760
|
gap: 10px;
|
|
2152
2761
|
}
|
|
2153
2762
|
|
|
2763
|
+
.dfwr-form-error {
|
|
2764
|
+
margin: 0;
|
|
2765
|
+
color: #ff8f61;
|
|
2766
|
+
font-size: var(--df-review-font-size-sm);
|
|
2767
|
+
line-height: 1.4;
|
|
2768
|
+
overflow-wrap: anywhere;
|
|
2769
|
+
}
|
|
2770
|
+
|
|
2771
|
+
.dfwr-input,
|
|
2772
|
+
.dfwr-select,
|
|
2154
2773
|
.dfwr-textarea {
|
|
2155
2774
|
width: 100%;
|
|
2156
|
-
min-height: 92px;
|
|
2157
|
-
resize: vertical;
|
|
2158
2775
|
border: 1px solid rgba(255, 255, 255, 0.16);
|
|
2159
2776
|
border-radius: var(--df-review-radius-sm);
|
|
2160
2777
|
padding: 10px;
|
|
@@ -2165,14 +2782,39 @@ function createStyleElement() {
|
|
|
2165
2782
|
line-height: 1.45;
|
|
2166
2783
|
}
|
|
2167
2784
|
|
|
2785
|
+
.dfwr-input,
|
|
2786
|
+
.dfwr-select {
|
|
2787
|
+
min-height: 38px;
|
|
2788
|
+
}
|
|
2789
|
+
|
|
2790
|
+
.dfwr-select {
|
|
2791
|
+
appearance: none;
|
|
2792
|
+
cursor: pointer;
|
|
2793
|
+
}
|
|
2794
|
+
|
|
2795
|
+
.dfwr-textarea {
|
|
2796
|
+
min-height: 92px;
|
|
2797
|
+
resize: vertical;
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
.dfwr-input:focus,
|
|
2801
|
+
.dfwr-select:focus,
|
|
2168
2802
|
.dfwr-textarea:focus {
|
|
2169
2803
|
outline: 2px solid var(--df-review-color-accent-ring);
|
|
2170
2804
|
outline-offset: 1px;
|
|
2171
2805
|
}
|
|
2172
2806
|
|
|
2173
2807
|
@media (hover: none) and (pointer: coarse) {
|
|
2808
|
+
.dfwr-input,
|
|
2809
|
+
.dfwr-select,
|
|
2174
2810
|
.dfwr-textarea {
|
|
2175
|
-
font-size:
|
|
2811
|
+
font-size: var(--df-review-font-size-xl);
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
|
|
2815
|
+
@keyframes dfwr-spin {
|
|
2816
|
+
to {
|
|
2817
|
+
transform: rotate(360deg);
|
|
2176
2818
|
}
|
|
2177
2819
|
}
|
|
2178
2820
|
|
|
@@ -2230,8 +2872,8 @@ function createStyleElement() {
|
|
|
2230
2872
|
color: var(--df-review-color-text);
|
|
2231
2873
|
cursor: pointer;
|
|
2232
2874
|
font: inherit;
|
|
2233
|
-
font-size:
|
|
2234
|
-
font-weight:
|
|
2875
|
+
font-size: var(--df-review-font-size-lg);
|
|
2876
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
2235
2877
|
line-height: 1;
|
|
2236
2878
|
}
|
|
2237
2879
|
|
|
@@ -2284,7 +2926,7 @@ function createStyleElement() {
|
|
|
2284
2926
|
margin-bottom: 10px;
|
|
2285
2927
|
color: rgba(247, 247, 242, 0.74);
|
|
2286
2928
|
font-size: var(--df-review-font-size-sm);
|
|
2287
|
-
font-weight:
|
|
2929
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
2288
2930
|
}
|
|
2289
2931
|
|
|
2290
2932
|
.dfwr-item {
|
|
@@ -2306,6 +2948,8 @@ function createStyleElement() {
|
|
|
2306
2948
|
}
|
|
2307
2949
|
|
|
2308
2950
|
.dfwr-item-body {
|
|
2951
|
+
display: grid;
|
|
2952
|
+
gap: 4px;
|
|
2309
2953
|
min-width: 0;
|
|
2310
2954
|
flex: 1;
|
|
2311
2955
|
}
|
|
@@ -2325,7 +2969,7 @@ function createStyleElement() {
|
|
|
2325
2969
|
border-radius: var(--df-review-radius-pill);
|
|
2326
2970
|
padding: 0 7px;
|
|
2327
2971
|
font-size: var(--df-review-font-size-2xs);
|
|
2328
|
-
font-weight:
|
|
2972
|
+
font-weight: var(--df-review-font-weight-emphasis);
|
|
2329
2973
|
line-height: 1;
|
|
2330
2974
|
letter-spacing: 0;
|
|
2331
2975
|
text-transform: uppercase;
|
|
@@ -2343,13 +2987,29 @@ function createStyleElement() {
|
|
|
2343
2987
|
color: rgba(247, 247, 242, 0.64);
|
|
2344
2988
|
}
|
|
2345
2989
|
|
|
2990
|
+
.dfwr-item-title {
|
|
2991
|
+
margin: 4px 0 0;
|
|
2992
|
+
color: var(--df-review-color-text);
|
|
2993
|
+
font-size: var(--df-review-font-size-md);
|
|
2994
|
+
font-weight: var(--df-review-font-weight-normal);
|
|
2995
|
+
line-height: 1.35;
|
|
2996
|
+
overflow-wrap: anywhere;
|
|
2997
|
+
}
|
|
2998
|
+
|
|
2346
2999
|
.dfwr-item-comment {
|
|
3000
|
+
margin: 0;
|
|
3001
|
+
color: var(--df-review-color-text-muted);
|
|
3002
|
+
font-size: var(--df-review-font-size-sm);
|
|
3003
|
+
line-height: 1.45;
|
|
3004
|
+
overflow-wrap: anywhere;
|
|
3005
|
+
white-space: pre-wrap;
|
|
3006
|
+
}
|
|
3007
|
+
|
|
3008
|
+
.dfwr-item-comment.is-primary {
|
|
2347
3009
|
margin: 4px 0;
|
|
2348
3010
|
color: var(--df-review-color-text);
|
|
2349
3011
|
font-size: var(--df-review-font-size-md);
|
|
2350
3012
|
line-height: 1.42;
|
|
2351
|
-
overflow-wrap: anywhere;
|
|
2352
|
-
white-space: pre-wrap;
|
|
2353
3013
|
}
|
|
2354
3014
|
|
|
2355
3015
|
.dfwr-item-date {
|
|
@@ -2593,7 +3253,7 @@ var WebReviewKitView = class {
|
|
|
2593
3253
|
);
|
|
2594
3254
|
}
|
|
2595
3255
|
}
|
|
2596
|
-
if (state.isOpen && state.mode === "area" && !state.areaDraft) {
|
|
3256
|
+
if (state.isOpen && state.mode === "area" && !state.areaDraft && !state.isSelectingArea) {
|
|
2597
3257
|
shell.append(this.createAreaLayer());
|
|
2598
3258
|
}
|
|
2599
3259
|
if (state.isOpen && state.mode === "area" && state.areaDraft && this.config.options.ui?.panel === false) {
|
|
@@ -2667,64 +3327,29 @@ var WebReviewKitView = class {
|
|
|
2667
3327
|
this.config.actions.setSelectingArea(false);
|
|
2668
3328
|
this.config.actions.render();
|
|
2669
3329
|
}
|
|
3330
|
+
// Draft adjustment geometry lives in draft.metrics.ts; these thin wrappers
|
|
3331
|
+
// supply the configured viewport presets so call sites stay unchanged.
|
|
3332
|
+
get viewportPresets() {
|
|
3333
|
+
return this.config.options.viewports?.presets;
|
|
3334
|
+
}
|
|
2670
3335
|
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
|
-
};
|
|
3336
|
+
return getDraftAdjustmentMetrics(draft, this.viewportPresets);
|
|
2698
3337
|
}
|
|
2699
3338
|
hasDraftAdjustment(draft) {
|
|
2700
|
-
|
|
2701
|
-
return metrics.x !== 0 || metrics.y !== 0 || metrics.scale !== 0;
|
|
3339
|
+
return hasDraftAdjustment(draft, this.viewportPresets);
|
|
2702
3340
|
}
|
|
2703
3341
|
getAdjustedDraftPoint(point, draft) {
|
|
2704
|
-
|
|
2705
|
-
return {
|
|
2706
|
-
x: point.x + metrics.cssX,
|
|
2707
|
-
y: point.y + metrics.cssY
|
|
2708
|
-
};
|
|
3342
|
+
return getAdjustedDraftPoint(point, draft, this.viewportPresets);
|
|
2709
3343
|
}
|
|
2710
3344
|
getAdjustedDraftSelection(selection, draft) {
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
width: selection.width * metrics.scaleFactor,
|
|
2717
|
-
height: selection.height * metrics.scaleFactor
|
|
2718
|
-
};
|
|
3345
|
+
return getAdjustedDraftSelection(
|
|
3346
|
+
selection,
|
|
3347
|
+
draft,
|
|
3348
|
+
this.viewportPresets
|
|
3349
|
+
);
|
|
2719
3350
|
}
|
|
2720
3351
|
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 };
|
|
3352
|
+
return getDraftViewportScale(viewport, this.viewportPresets);
|
|
2728
3353
|
}
|
|
2729
3354
|
getDraftComposerWidth(environment) {
|
|
2730
3355
|
const bounds = environment.overlayRect;
|
|
@@ -2870,6 +3495,67 @@ var WebReviewKitView = class {
|
|
|
2870
3495
|
return trimmedComment ? `${trimmedComment}
|
|
2871
3496
|
${adjustment}` : adjustment;
|
|
2872
3497
|
}
|
|
3498
|
+
getAssigneeOption(assigneeId) {
|
|
3499
|
+
if (!assigneeId) return void 0;
|
|
3500
|
+
return this.config.options.assigneeOptions?.find(
|
|
3501
|
+
(option) => option.value === assigneeId
|
|
3502
|
+
);
|
|
3503
|
+
}
|
|
3504
|
+
getAssigneeName(assigneeId) {
|
|
3505
|
+
return this.getAssigneeOption(assigneeId)?.label;
|
|
3506
|
+
}
|
|
3507
|
+
createDraftTitleInput(value, onInput) {
|
|
3508
|
+
const input = document.createElement("input");
|
|
3509
|
+
input.className = "dfwr-input";
|
|
3510
|
+
input.placeholder = "Title";
|
|
3511
|
+
input.type = "text";
|
|
3512
|
+
input.value = value ?? "";
|
|
3513
|
+
input.addEventListener("input", () => onInput(input.value));
|
|
3514
|
+
return input;
|
|
3515
|
+
}
|
|
3516
|
+
isTitleFieldEnabled() {
|
|
3517
|
+
return this.config.options.fields?.title === true;
|
|
3518
|
+
}
|
|
3519
|
+
createDraftAssigneeSelect(value, fallbackLabel, onChange) {
|
|
3520
|
+
const assigneeOptions = this.config.options.assigneeOptions ?? [];
|
|
3521
|
+
if (assigneeOptions.length === 0) return void 0;
|
|
3522
|
+
const assigneeTitle = this.config.options.assigneeTitle?.trim() || "Assignee";
|
|
3523
|
+
const select = document.createElement("select");
|
|
3524
|
+
select.className = "dfwr-select";
|
|
3525
|
+
const emptyOption = document.createElement("option");
|
|
3526
|
+
emptyOption.value = "";
|
|
3527
|
+
emptyOption.textContent = assigneeTitle;
|
|
3528
|
+
select.append(emptyOption);
|
|
3529
|
+
const hasUnknownAssignee = Boolean(value) && !assigneeOptions.some((option) => option.value === value);
|
|
3530
|
+
if (hasUnknownAssignee && value) {
|
|
3531
|
+
const option = document.createElement("option");
|
|
3532
|
+
option.value = value;
|
|
3533
|
+
option.textContent = fallbackLabel ?? value;
|
|
3534
|
+
select.append(option);
|
|
3535
|
+
}
|
|
3536
|
+
assigneeOptions.forEach((assigneeOption) => {
|
|
3537
|
+
const option = document.createElement("option");
|
|
3538
|
+
option.value = assigneeOption.value;
|
|
3539
|
+
option.textContent = assigneeOption.label;
|
|
3540
|
+
select.append(option);
|
|
3541
|
+
});
|
|
3542
|
+
select.value = value ?? "";
|
|
3543
|
+
select.addEventListener("change", () => {
|
|
3544
|
+
onChange(select.value || null, this.getAssigneeName(select.value));
|
|
3545
|
+
});
|
|
3546
|
+
return select;
|
|
3547
|
+
}
|
|
3548
|
+
getDraftFields(titleInput, textarea, assigneeSelect) {
|
|
3549
|
+
const title = titleInput?.value.trim();
|
|
3550
|
+
const comment = textarea.value.trim();
|
|
3551
|
+
const assigneeId = assigneeSelect?.value.trim() || void 0;
|
|
3552
|
+
return {
|
|
3553
|
+
title: title || void 0,
|
|
3554
|
+
comment,
|
|
3555
|
+
assigneeId,
|
|
3556
|
+
assigneeName: this.getAssigneeName(assigneeId)
|
|
3557
|
+
};
|
|
3558
|
+
}
|
|
2873
3559
|
getStyleableDraftElement(draft, environment) {
|
|
2874
3560
|
if (draft.previewElement && draft.previewElement.ownerDocument === environment.document && "style" in draft.previewElement) {
|
|
2875
3561
|
return draft.previewElement;
|
|
@@ -3060,6 +3746,9 @@ ${adjustment}` : adjustment;
|
|
|
3060
3746
|
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
3747
|
return empty;
|
|
3062
3748
|
}
|
|
3749
|
+
// Builds the note draft layer: the on-page marker/highlight plus its composer
|
|
3750
|
+
// popover. When dockComposer is set the composer renders into the side panel
|
|
3751
|
+
// instead of floating next to the marker (used for the docked review mode).
|
|
3063
3752
|
createNotePopover(draft, options = {}) {
|
|
3064
3753
|
const environment = this.config.getEnvironment();
|
|
3065
3754
|
const group = document.createElement("div");
|
|
@@ -3123,6 +3812,14 @@ ${adjustment}` : adjustment;
|
|
|
3123
3812
|
meta.className = "dfwr-item-date";
|
|
3124
3813
|
meta.textContent = formatNoteDraftMeta(draft);
|
|
3125
3814
|
}
|
|
3815
|
+
const titleInput = this.isTitleFieldEnabled() ? this.createDraftTitleInput(draft.title, (title) => {
|
|
3816
|
+
const noteDraft = this.state.noteDraft;
|
|
3817
|
+
if (!noteDraft) return;
|
|
3818
|
+
this.config.actions.setNoteDraft({
|
|
3819
|
+
...noteDraft,
|
|
3820
|
+
title
|
|
3821
|
+
});
|
|
3822
|
+
}) : void 0;
|
|
3126
3823
|
const textarea = document.createElement("textarea");
|
|
3127
3824
|
textarea.className = "dfwr-textarea";
|
|
3128
3825
|
textarea.placeholder = "Review comment";
|
|
@@ -3136,13 +3833,30 @@ ${adjustment}` : adjustment;
|
|
|
3136
3833
|
comment: textarea.value
|
|
3137
3834
|
});
|
|
3138
3835
|
});
|
|
3836
|
+
const assigneeSelect = this.createDraftAssigneeSelect(
|
|
3837
|
+
draft.assigneeId,
|
|
3838
|
+
draft.assigneeName,
|
|
3839
|
+
(assigneeId, assigneeName) => {
|
|
3840
|
+
const noteDraft = this.state.noteDraft;
|
|
3841
|
+
if (!noteDraft) return;
|
|
3842
|
+
this.config.actions.setNoteDraft({
|
|
3843
|
+
...noteDraft,
|
|
3844
|
+
assigneeId,
|
|
3845
|
+
assigneeName
|
|
3846
|
+
});
|
|
3847
|
+
}
|
|
3848
|
+
);
|
|
3139
3849
|
const saveDraft = () => {
|
|
3140
|
-
const comment = textarea.value.trim();
|
|
3141
3850
|
const currentDraft = this.state.noteDraft ?? draft;
|
|
3851
|
+
const fields = this.getDraftFields(titleInput, textarea, assigneeSelect);
|
|
3852
|
+
const comment = fields.comment;
|
|
3142
3853
|
if (!comment && !this.hasDraftAdjustment(currentDraft)) return;
|
|
3143
3854
|
void this.config.actions.createItem({
|
|
3144
3855
|
kind: "note",
|
|
3856
|
+
title: fields.title,
|
|
3145
3857
|
comment: this.withDraftAdjustmentComment(comment, currentDraft),
|
|
3858
|
+
assigneeId: fields.assigneeId,
|
|
3859
|
+
assigneeName: fields.assigneeName,
|
|
3146
3860
|
viewport: currentDraft.viewport,
|
|
3147
3861
|
anchor: currentDraft.anchor,
|
|
3148
3862
|
marker: currentDraft.marker,
|
|
@@ -3160,10 +3874,14 @@ ${adjustment}` : adjustment;
|
|
|
3160
3874
|
const actions = this.createFormActions("Save note", saveDraft, {
|
|
3161
3875
|
leading: adjustmentControls?.actionButton ? [adjustmentControls.actionButton] : void 0
|
|
3162
3876
|
});
|
|
3877
|
+
const error = this.createDraftError();
|
|
3163
3878
|
form.append(
|
|
3164
3879
|
...meta ? [meta] : [],
|
|
3165
3880
|
...adjustmentControls ? [adjustmentControls.panel] : [],
|
|
3881
|
+
...titleInput ? [titleInput] : [],
|
|
3166
3882
|
textarea,
|
|
3883
|
+
...assigneeSelect ? [assigneeSelect] : [],
|
|
3884
|
+
...error ? [error] : [],
|
|
3167
3885
|
actions
|
|
3168
3886
|
);
|
|
3169
3887
|
const dragHandle = isElementDraft && !options.dockComposer ? this.createDraftDragHandle("Move DOM composer") : void 0;
|
|
@@ -3290,6 +4008,9 @@ ${adjustment}` : adjustment;
|
|
|
3290
4008
|
handle.addEventListener("pointerup", stopDrag);
|
|
3291
4009
|
handle.addEventListener("pointercancel", stopDrag);
|
|
3292
4010
|
}
|
|
4011
|
+
// Builds the element-adjustment controls (nudge the previewed element via
|
|
4012
|
+
// arrow keys / buttons). Wires keyboard deltas to the draft transform and
|
|
4013
|
+
// keeps the pin, popover, highlight and textarea in sync as the value changes.
|
|
3293
4014
|
createAdjustmentControls({
|
|
3294
4015
|
draft,
|
|
3295
4016
|
pin,
|
|
@@ -3434,6 +4155,14 @@ ${adjustment}` : adjustment;
|
|
|
3434
4155
|
return form;
|
|
3435
4156
|
}
|
|
3436
4157
|
form.append(this.createAreaMetricsPanel(areaDraft));
|
|
4158
|
+
const titleInput = this.isTitleFieldEnabled() ? this.createDraftTitleInput(areaDraft.title, (title) => {
|
|
4159
|
+
const draft = this.state.areaDraft;
|
|
4160
|
+
if (!draft) return;
|
|
4161
|
+
this.config.actions.setAreaDraft({
|
|
4162
|
+
...draft,
|
|
4163
|
+
title
|
|
4164
|
+
});
|
|
4165
|
+
}) : void 0;
|
|
3437
4166
|
const textarea = document.createElement("textarea");
|
|
3438
4167
|
textarea.className = "dfwr-textarea";
|
|
3439
4168
|
textarea.placeholder = "Area comment";
|
|
@@ -3447,20 +4176,44 @@ ${adjustment}` : adjustment;
|
|
|
3447
4176
|
comment: textarea.value
|
|
3448
4177
|
});
|
|
3449
4178
|
});
|
|
4179
|
+
const assigneeSelect = this.createDraftAssigneeSelect(
|
|
4180
|
+
areaDraft.assigneeId,
|
|
4181
|
+
areaDraft.assigneeName,
|
|
4182
|
+
(assigneeId, assigneeName) => {
|
|
4183
|
+
const draft = this.state.areaDraft;
|
|
4184
|
+
if (!draft) return;
|
|
4185
|
+
this.config.actions.setAreaDraft({
|
|
4186
|
+
...draft,
|
|
4187
|
+
assigneeId,
|
|
4188
|
+
assigneeName
|
|
4189
|
+
});
|
|
4190
|
+
}
|
|
4191
|
+
);
|
|
3450
4192
|
const actions = this.createFormActions("Save area", () => {
|
|
3451
|
-
const comment = textarea.value.trim();
|
|
3452
4193
|
const draft = this.state.areaDraft;
|
|
4194
|
+
const fields = this.getDraftFields(titleInput, textarea, assigneeSelect);
|
|
4195
|
+
const comment = fields.comment;
|
|
3453
4196
|
if (!comment || !draft) return;
|
|
3454
4197
|
void this.config.actions.createItem({
|
|
3455
4198
|
kind: "area",
|
|
4199
|
+
title: fields.title,
|
|
3456
4200
|
comment,
|
|
4201
|
+
assigneeId: fields.assigneeId,
|
|
4202
|
+
assigneeName: fields.assigneeName,
|
|
3457
4203
|
viewport: draft.viewport,
|
|
3458
4204
|
anchor: draft.anchor,
|
|
3459
4205
|
marker: draft.marker,
|
|
3460
4206
|
selection: draft.selection
|
|
3461
4207
|
});
|
|
3462
4208
|
});
|
|
3463
|
-
|
|
4209
|
+
const error = this.createDraftError();
|
|
4210
|
+
form.append(
|
|
4211
|
+
...titleInput ? [titleInput] : [],
|
|
4212
|
+
textarea,
|
|
4213
|
+
...assigneeSelect ? [assigneeSelect] : [],
|
|
4214
|
+
...error ? [error] : [],
|
|
4215
|
+
actions
|
|
4216
|
+
);
|
|
3464
4217
|
return form;
|
|
3465
4218
|
}
|
|
3466
4219
|
createAreaMetricsPanel(draft) {
|
|
@@ -3552,18 +4305,27 @@ ${adjustment}` : adjustment;
|
|
|
3552
4305
|
createFormActions(saveLabel, onSave, options) {
|
|
3553
4306
|
const actions = document.createElement("div");
|
|
3554
4307
|
actions.className = ["dfwr-actions", options?.className].filter(Boolean).join(" ");
|
|
4308
|
+
const isSaving = this.state.isCreatingItem;
|
|
3555
4309
|
const save = document.createElement("button");
|
|
3556
4310
|
save.className = "dfwr-button is-primary";
|
|
3557
4311
|
save.type = "button";
|
|
3558
|
-
save.
|
|
4312
|
+
save.disabled = isSaving;
|
|
4313
|
+
save.setAttribute("aria-busy", isSaving ? "true" : "false");
|
|
4314
|
+
if (isSaving) {
|
|
4315
|
+
save.append(this.createSpinner("dfwr-spinner"), "Saving...");
|
|
4316
|
+
} else {
|
|
4317
|
+
save.textContent = saveLabel;
|
|
4318
|
+
}
|
|
3559
4319
|
save.addEventListener("click", (event) => {
|
|
3560
4320
|
event.preventDefault();
|
|
3561
4321
|
event.stopPropagation();
|
|
4322
|
+
if (this.state.isCreatingItem) return;
|
|
3562
4323
|
onSave();
|
|
3563
4324
|
});
|
|
3564
4325
|
const cancel = document.createElement("button");
|
|
3565
4326
|
cancel.className = "dfwr-button";
|
|
3566
4327
|
cancel.type = "button";
|
|
4328
|
+
cancel.disabled = isSaving;
|
|
3567
4329
|
cancel.textContent = "Cancel";
|
|
3568
4330
|
cancel.addEventListener("click", (event) => {
|
|
3569
4331
|
this.cancelDraft(event);
|
|
@@ -3586,6 +4348,20 @@ ${adjustment}` : adjustment;
|
|
|
3586
4348
|
actions.append(save, cancel);
|
|
3587
4349
|
return actions;
|
|
3588
4350
|
}
|
|
4351
|
+
createSpinner(className) {
|
|
4352
|
+
const spinner = document.createElement("span");
|
|
4353
|
+
spinner.className = className;
|
|
4354
|
+
spinner.setAttribute("aria-hidden", "true");
|
|
4355
|
+
return spinner;
|
|
4356
|
+
}
|
|
4357
|
+
createDraftError() {
|
|
4358
|
+
if (!this.state.draftError) return void 0;
|
|
4359
|
+
const error = document.createElement("p");
|
|
4360
|
+
error.className = "dfwr-form-error";
|
|
4361
|
+
error.setAttribute("role", "alert");
|
|
4362
|
+
error.textContent = this.state.draftError;
|
|
4363
|
+
return error;
|
|
4364
|
+
}
|
|
3589
4365
|
createList() {
|
|
3590
4366
|
const section = document.createElement("div");
|
|
3591
4367
|
section.className = "dfwr-list";
|
|
@@ -3638,14 +4414,20 @@ ${adjustment}` : adjustment;
|
|
|
3638
4414
|
kind.className = "dfwr-item-kind";
|
|
3639
4415
|
kind.textContent = item.kind;
|
|
3640
4416
|
badges.append(scope, kind);
|
|
4417
|
+
const title = this.isTitleFieldEnabled() ? item.title?.trim() : "";
|
|
4418
|
+
const titleElement = title ? document.createElement("strong") : void 0;
|
|
4419
|
+
if (title && titleElement) {
|
|
4420
|
+
titleElement.className = "dfwr-item-title";
|
|
4421
|
+
titleElement.textContent = title;
|
|
4422
|
+
}
|
|
3641
4423
|
const comment = document.createElement("p");
|
|
3642
|
-
comment.className =
|
|
4424
|
+
comment.className = `dfwr-item-comment${title ? "" : " is-primary"}`;
|
|
3643
4425
|
comment.textContent = item.comment;
|
|
3644
4426
|
const date = document.createElement("time");
|
|
3645
4427
|
date.className = "dfwr-item-date";
|
|
3646
4428
|
date.dateTime = item.createdAt;
|
|
3647
4429
|
date.textContent = formatItemMeta(item);
|
|
3648
|
-
body.append(badges, comment, date);
|
|
4430
|
+
body.append(badges, ...titleElement ? [titleElement] : [], comment, date);
|
|
3649
4431
|
const actions = document.createElement("div");
|
|
3650
4432
|
actions.className = "dfwr-item-actions";
|
|
3651
4433
|
actions.addEventListener("click", (event) => event.stopPropagation());
|
|
@@ -3834,7 +4616,14 @@ ${formatItemMeta(item)}`;
|
|
|
3834
4616
|
event,
|
|
3835
4617
|
this.config.getEnvironment()
|
|
3836
4618
|
);
|
|
3837
|
-
|
|
4619
|
+
const currentDraft = this.state.noteDraft;
|
|
4620
|
+
const fields = {
|
|
4621
|
+
title: currentDraft?.title,
|
|
4622
|
+
comment: textarea.value,
|
|
4623
|
+
assigneeId: currentDraft?.assigneeId,
|
|
4624
|
+
assigneeName: currentDraft?.assigneeName
|
|
4625
|
+
};
|
|
4626
|
+
void (this.state.mode === "element" ? this.config.actions.bindElementDraftToPoint(nextPoint, fields) : this.config.actions.bindNoteDraftToPoint(nextPoint, fields));
|
|
3838
4627
|
});
|
|
3839
4628
|
}
|
|
3840
4629
|
createNoteLayer() {
|
|
@@ -3912,10 +4701,14 @@ ${formatItemMeta(item)}`;
|
|
|
3912
4701
|
let startX = 0;
|
|
3913
4702
|
let startY = 0;
|
|
3914
4703
|
let selection;
|
|
4704
|
+
let activePointerId;
|
|
4705
|
+
let isDragging = false;
|
|
4706
|
+
const ownerWindow = layer.ownerDocument.defaultView ?? window;
|
|
3915
4707
|
const updateBox = (event) => {
|
|
4708
|
+
const nextEnvironment = this.config.getEnvironment();
|
|
3916
4709
|
const nextPoint = toTargetPointFromHostEvent(
|
|
3917
4710
|
event,
|
|
3918
|
-
|
|
4711
|
+
nextEnvironment
|
|
3919
4712
|
);
|
|
3920
4713
|
const left = Math.min(startX, nextPoint.x);
|
|
3921
4714
|
const top = Math.min(startY, nextPoint.y);
|
|
@@ -3923,7 +4716,7 @@ ${formatItemMeta(item)}`;
|
|
|
3923
4716
|
const height = Math.abs(nextPoint.y - startY);
|
|
3924
4717
|
const hostPoint = toHostPoint(
|
|
3925
4718
|
{ x: left, y: top },
|
|
3926
|
-
|
|
4719
|
+
nextEnvironment
|
|
3927
4720
|
);
|
|
3928
4721
|
selection = { left, top, width, height };
|
|
3929
4722
|
box.style.left = `${hostPoint.x}px`;
|
|
@@ -3931,9 +4724,68 @@ ${formatItemMeta(item)}`;
|
|
|
3931
4724
|
box.style.width = `${width}px`;
|
|
3932
4725
|
box.style.height = `${height}px`;
|
|
3933
4726
|
};
|
|
4727
|
+
const addDragListeners = () => {
|
|
4728
|
+
ownerWindow.addEventListener("pointermove", handlePointerMove, true);
|
|
4729
|
+
ownerWindow.addEventListener("pointerup", handlePointerUp, true);
|
|
4730
|
+
ownerWindow.addEventListener("pointercancel", handlePointerCancel, true);
|
|
4731
|
+
};
|
|
4732
|
+
const removeDragListeners = () => {
|
|
4733
|
+
ownerWindow.removeEventListener("pointermove", handlePointerMove, true);
|
|
4734
|
+
ownerWindow.removeEventListener("pointerup", handlePointerUp, true);
|
|
4735
|
+
ownerWindow.removeEventListener(
|
|
4736
|
+
"pointercancel",
|
|
4737
|
+
handlePointerCancel,
|
|
4738
|
+
true
|
|
4739
|
+
);
|
|
4740
|
+
};
|
|
4741
|
+
const releasePointerCapture = (event) => {
|
|
4742
|
+
try {
|
|
4743
|
+
if (layer.hasPointerCapture(event.pointerId)) {
|
|
4744
|
+
layer.releasePointerCapture(event.pointerId);
|
|
4745
|
+
}
|
|
4746
|
+
} catch {
|
|
4747
|
+
}
|
|
4748
|
+
};
|
|
4749
|
+
function isActivePointer(event) {
|
|
4750
|
+
return isDragging && event.pointerId === activePointerId;
|
|
4751
|
+
}
|
|
4752
|
+
const finishAreaSelection = (event) => {
|
|
4753
|
+
if (!isActivePointer(event)) return;
|
|
4754
|
+
event.preventDefault();
|
|
4755
|
+
updateBox(event);
|
|
4756
|
+
releasePointerCapture(event);
|
|
4757
|
+
removeDragListeners();
|
|
4758
|
+
isDragging = false;
|
|
4759
|
+
activePointerId = void 0;
|
|
4760
|
+
if (!selection || selection.width < 8 || selection.height < 8) return;
|
|
4761
|
+
this.config.actions.setSelectingArea(true);
|
|
4762
|
+
this.config.actions.render();
|
|
4763
|
+
void this.config.actions.createAreaDraft(selection);
|
|
4764
|
+
};
|
|
4765
|
+
function handlePointerMove(event) {
|
|
4766
|
+
if (!isActivePointer(event)) return;
|
|
4767
|
+
event.preventDefault();
|
|
4768
|
+
updateBox(event);
|
|
4769
|
+
}
|
|
4770
|
+
const handlePointerUp = (event) => {
|
|
4771
|
+
finishAreaSelection(event);
|
|
4772
|
+
};
|
|
4773
|
+
const handlePointerCancel = (event) => {
|
|
4774
|
+
if (!isActivePointer(event)) return;
|
|
4775
|
+
releasePointerCapture(event);
|
|
4776
|
+
removeDragListeners();
|
|
4777
|
+
isDragging = false;
|
|
4778
|
+
activePointerId = void 0;
|
|
4779
|
+
};
|
|
3934
4780
|
layer.addEventListener("pointerdown", (event) => {
|
|
4781
|
+
if (event.button !== 0) return;
|
|
3935
4782
|
event.preventDefault();
|
|
3936
|
-
|
|
4783
|
+
activePointerId = event.pointerId;
|
|
4784
|
+
isDragging = true;
|
|
4785
|
+
try {
|
|
4786
|
+
layer.setPointerCapture(event.pointerId);
|
|
4787
|
+
} catch {
|
|
4788
|
+
}
|
|
3937
4789
|
const startPoint = toTargetPointFromHostEvent(
|
|
3938
4790
|
event,
|
|
3939
4791
|
this.config.getEnvironment()
|
|
@@ -3941,20 +4793,11 @@ ${formatItemMeta(item)}`;
|
|
|
3941
4793
|
startX = startPoint.x;
|
|
3942
4794
|
startY = startPoint.y;
|
|
3943
4795
|
updateBox(event);
|
|
4796
|
+
addDragListeners();
|
|
3944
4797
|
});
|
|
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
|
-
});
|
|
4798
|
+
layer.addEventListener("pointermove", handlePointerMove);
|
|
4799
|
+
layer.addEventListener("pointerup", handlePointerUp);
|
|
4800
|
+
layer.addEventListener("pointercancel", handlePointerCancel);
|
|
3958
4801
|
return layer;
|
|
3959
4802
|
}
|
|
3960
4803
|
};
|
|
@@ -3994,6 +4837,8 @@ var WebReviewKitApp = class {
|
|
|
3994
4837
|
this.isOpen = false;
|
|
3995
4838
|
this.mode = "idle";
|
|
3996
4839
|
this.items = [];
|
|
4840
|
+
this.draftError = "";
|
|
4841
|
+
this.isCreatingItem = false;
|
|
3997
4842
|
this.isSelectingArea = false;
|
|
3998
4843
|
this.handleKeyDown = (event) => {
|
|
3999
4844
|
if (event.key === "Escape" && this.cancelMode()) {
|
|
@@ -4024,6 +4869,8 @@ var WebReviewKitApp = class {
|
|
|
4024
4869
|
items: this.items,
|
|
4025
4870
|
noteDraft: this.noteDraft,
|
|
4026
4871
|
areaDraft: this.areaDraft,
|
|
4872
|
+
draftError: this.draftError,
|
|
4873
|
+
isCreatingItem: this.isCreatingItem,
|
|
4027
4874
|
isSelectingArea: this.isSelectingArea,
|
|
4028
4875
|
highlightedItemId: this.highlightedItemId
|
|
4029
4876
|
}),
|
|
@@ -4038,19 +4885,22 @@ var WebReviewKitApp = class {
|
|
|
4038
4885
|
clearDrafts: () => {
|
|
4039
4886
|
this.noteDraft = void 0;
|
|
4040
4887
|
this.areaDraft = void 0;
|
|
4888
|
+
this.draftError = "";
|
|
4041
4889
|
},
|
|
4042
4890
|
setNoteDraft: (draft) => {
|
|
4043
4891
|
this.noteDraft = draft;
|
|
4892
|
+
this.draftError = "";
|
|
4044
4893
|
},
|
|
4045
4894
|
setAreaDraft: (draft) => {
|
|
4046
4895
|
this.areaDraft = draft;
|
|
4896
|
+
this.draftError = "";
|
|
4047
4897
|
},
|
|
4048
4898
|
setSelectingArea: (isSelectingArea) => {
|
|
4049
4899
|
this.isSelectingArea = isSelectingArea;
|
|
4050
4900
|
},
|
|
4051
4901
|
createItem: (input) => this.createItem(input),
|
|
4052
|
-
bindNoteDraftToPoint: (point,
|
|
4053
|
-
bindElementDraftToPoint: (point,
|
|
4902
|
+
bindNoteDraftToPoint: (point, fields) => this.bindNoteDraftToPoint(point, fields),
|
|
4903
|
+
bindElementDraftToPoint: (point, fields) => this.bindElementDraftToPoint(point, fields),
|
|
4054
4904
|
createAreaDraft: (selection) => this.createAreaDraft(selection)
|
|
4055
4905
|
}
|
|
4056
4906
|
});
|
|
@@ -4119,7 +4969,7 @@ var WebReviewKitApp = class {
|
|
|
4119
4969
|
this.noteDraft = void 0;
|
|
4120
4970
|
this.areaDraft = void 0;
|
|
4121
4971
|
this.isSelectingArea = false;
|
|
4122
|
-
await this.bindElementDraftToElement(element, comment);
|
|
4972
|
+
await this.bindElementDraftToElement(element, { comment });
|
|
4123
4973
|
}
|
|
4124
4974
|
getMode() {
|
|
4125
4975
|
return this.mode;
|
|
@@ -4262,7 +5112,7 @@ var WebReviewKitApp = class {
|
|
|
4262
5112
|
if (!this.shadow) return;
|
|
4263
5113
|
this.view.render(this.shadow, this.createHiddenItemsStyleElement());
|
|
4264
5114
|
}
|
|
4265
|
-
async bindNoteDraftToPoint(point,
|
|
5115
|
+
async bindNoteDraftToPoint(point, fields = {}) {
|
|
4266
5116
|
const environment = this.getEnvironment();
|
|
4267
5117
|
if (!environment) return;
|
|
4268
5118
|
const viewport = getViewportSize(environment);
|
|
@@ -4282,13 +5132,13 @@ var WebReviewKitApp = class {
|
|
|
4282
5132
|
viewport,
|
|
4283
5133
|
anchor,
|
|
4284
5134
|
marker,
|
|
4285
|
-
|
|
5135
|
+
...fields
|
|
4286
5136
|
};
|
|
4287
5137
|
});
|
|
4288
5138
|
this.noteDraft = draft;
|
|
4289
5139
|
this.render();
|
|
4290
5140
|
}
|
|
4291
|
-
async bindElementDraftToPoint(point,
|
|
5141
|
+
async bindElementDraftToPoint(point, fields = {}) {
|
|
4292
5142
|
const environment = this.getEnvironment();
|
|
4293
5143
|
if (!environment) return;
|
|
4294
5144
|
const viewport = getViewportSize(environment);
|
|
@@ -4332,14 +5182,14 @@ var WebReviewKitApp = class {
|
|
|
4332
5182
|
anchor,
|
|
4333
5183
|
marker,
|
|
4334
5184
|
selection: reviewSelection,
|
|
4335
|
-
|
|
5185
|
+
...fields,
|
|
4336
5186
|
previewElement
|
|
4337
5187
|
};
|
|
4338
5188
|
});
|
|
4339
5189
|
this.noteDraft = draft;
|
|
4340
5190
|
this.render();
|
|
4341
5191
|
}
|
|
4342
|
-
async bindElementDraftToElement(element,
|
|
5192
|
+
async bindElementDraftToElement(element, fields = {}) {
|
|
4343
5193
|
const environment = this.getEnvironment();
|
|
4344
5194
|
if (!environment || element.ownerDocument !== environment.document) return;
|
|
4345
5195
|
const viewport = getViewportSize(environment);
|
|
@@ -4372,7 +5222,7 @@ var WebReviewKitApp = class {
|
|
|
4372
5222
|
anchor,
|
|
4373
5223
|
marker,
|
|
4374
5224
|
selection: reviewSelection,
|
|
4375
|
-
|
|
5225
|
+
...fields,
|
|
4376
5226
|
previewElement
|
|
4377
5227
|
};
|
|
4378
5228
|
});
|
|
@@ -4382,26 +5232,33 @@ var WebReviewKitApp = class {
|
|
|
4382
5232
|
}
|
|
4383
5233
|
async createAreaDraft(selection) {
|
|
4384
5234
|
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
|
-
|
|
5235
|
+
if (!environment) {
|
|
5236
|
+
this.isSelectingArea = false;
|
|
5237
|
+
this.render();
|
|
5238
|
+
return;
|
|
5239
|
+
}
|
|
5240
|
+
try {
|
|
5241
|
+
const viewport = getViewportSize(environment);
|
|
5242
|
+
this.areaDraft = await this.withOverlayHidden(() => {
|
|
5243
|
+
const marker = createSelectionCenterMarker(
|
|
5244
|
+
selection,
|
|
5245
|
+
void 0,
|
|
5246
|
+
environment
|
|
5247
|
+
);
|
|
5248
|
+
const reviewSelection = {
|
|
5249
|
+
viewport: toPublicSelection(selection)
|
|
5250
|
+
};
|
|
5251
|
+
return {
|
|
5252
|
+
viewport,
|
|
5253
|
+
marker,
|
|
5254
|
+
selection: reviewSelection
|
|
5255
|
+
};
|
|
5256
|
+
});
|
|
5257
|
+
this.setModeState("area");
|
|
5258
|
+
} finally {
|
|
5259
|
+
this.isSelectingArea = false;
|
|
5260
|
+
this.render();
|
|
5261
|
+
}
|
|
4405
5262
|
}
|
|
4406
5263
|
async withOverlayHidden(callback) {
|
|
4407
5264
|
if (!this.root) return callback();
|
|
@@ -4415,11 +5272,16 @@ var WebReviewKitApp = class {
|
|
|
4415
5272
|
}
|
|
4416
5273
|
async createItem(input) {
|
|
4417
5274
|
const environment = this.getEnvironment();
|
|
4418
|
-
if (!environment) return;
|
|
5275
|
+
if (!environment || this.isCreatingItem) return;
|
|
4419
5276
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4420
5277
|
const routeKey = getRouteKey(environment);
|
|
4421
5278
|
const viewport = input.viewport ?? getViewportSize(environment);
|
|
4422
5279
|
const createdBy = this.options.userId?.trim();
|
|
5280
|
+
const title = input.title?.trim();
|
|
5281
|
+
const assigneeId = input.assigneeId?.trim() || void 0;
|
|
5282
|
+
const assigneeOption = this.options.assigneeOptions?.find(
|
|
5283
|
+
(option) => option.value === assigneeId
|
|
5284
|
+
);
|
|
4423
5285
|
const item = {
|
|
4424
5286
|
id: createId(),
|
|
4425
5287
|
projectId: this.options.projectId,
|
|
@@ -4429,8 +5291,10 @@ var WebReviewKitApp = class {
|
|
|
4429
5291
|
normalizedPath: routeKey,
|
|
4430
5292
|
scope: input.scope ?? getReviewViewportScope(viewport, this.options.viewports?.presets),
|
|
4431
5293
|
kind: input.kind,
|
|
4432
|
-
title:
|
|
5294
|
+
title: title || void 0,
|
|
4433
5295
|
comment: input.comment,
|
|
5296
|
+
assigneeId,
|
|
5297
|
+
assigneeName: input.assigneeName ?? assigneeOption?.label,
|
|
4434
5298
|
createdBy: createdBy || void 0,
|
|
4435
5299
|
status: "todo",
|
|
4436
5300
|
viewport,
|
|
@@ -4445,13 +5309,23 @@ var WebReviewKitApp = class {
|
|
|
4445
5309
|
createdAt: now,
|
|
4446
5310
|
updatedAt: now
|
|
4447
5311
|
};
|
|
4448
|
-
|
|
4449
|
-
this.
|
|
4450
|
-
this.
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
5312
|
+
this.draftError = "";
|
|
5313
|
+
this.isCreatingItem = true;
|
|
5314
|
+
this.render();
|
|
5315
|
+
try {
|
|
5316
|
+
const createdItem = await this.adapter.create(item);
|
|
5317
|
+
this.setModeState("idle");
|
|
5318
|
+
this.noteDraft = void 0;
|
|
5319
|
+
this.areaDraft = void 0;
|
|
5320
|
+
this.highlightItem(createdItem.id);
|
|
5321
|
+
await this.reload();
|
|
5322
|
+
await this.options.onCreateItem?.(createdItem);
|
|
5323
|
+
} catch (error) {
|
|
5324
|
+
this.draftError = error instanceof Error ? error.message : "Failed to save QA.";
|
|
5325
|
+
} finally {
|
|
5326
|
+
this.isCreatingItem = false;
|
|
5327
|
+
this.render();
|
|
5328
|
+
}
|
|
4455
5329
|
}
|
|
4456
5330
|
async restoreItem(item) {
|
|
4457
5331
|
this.setModeState("idle");
|
|
@@ -4505,16 +5379,35 @@ function createNoopController() {
|
|
|
4505
5379
|
}
|
|
4506
5380
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4507
5381
|
0 && (module.exports = {
|
|
5382
|
+
DEFAULT_REVIEW_FIGMA_IMAGE_FORMAT,
|
|
5383
|
+
DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT,
|
|
5384
|
+
DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,
|
|
4508
5385
|
DEFAULT_REVIEW_VIEWPORTS,
|
|
5386
|
+
FIGMA_NODE_REF_SEPARATOR,
|
|
5387
|
+
REVIEW_FIGMA_TOKEN_MISSING_CODE,
|
|
4509
5388
|
REVIEW_WORKFLOW_STATUS_OPTIONS,
|
|
5389
|
+
ReviewFigmaTokenError,
|
|
5390
|
+
collectReviewFigmaReleaseSnapshot,
|
|
5391
|
+
createReviewFigmaFrameUrl,
|
|
5392
|
+
createReviewFigmaImageStoreClient,
|
|
5393
|
+
createReviewFigmaImagesSnapshot,
|
|
5394
|
+
createReviewFigmaNodeValue,
|
|
5395
|
+
createReviewFigmaReleaseSnapshot,
|
|
4510
5396
|
createWebReviewKit,
|
|
4511
5397
|
findReviewViewportPreset,
|
|
4512
5398
|
getNumberedReviewItems,
|
|
5399
|
+
getReviewFigmaImageMimeType,
|
|
5400
|
+
getReviewFigmaImageTargetKey,
|
|
4513
5401
|
getReviewItemScope,
|
|
4514
5402
|
getReviewItemScopeLabel,
|
|
4515
5403
|
getReviewViewportScope,
|
|
5404
|
+
isReviewFigmaTokenError,
|
|
4516
5405
|
localAdapter,
|
|
4517
5406
|
normalizeReviewItemStatus,
|
|
5407
|
+
parseReviewFigmaNodeRef,
|
|
5408
|
+
readReviewFigmaToken,
|
|
5409
|
+
requireReviewFigmaNodeRef,
|
|
5410
|
+
requireReviewFigmaToken,
|
|
4518
5411
|
supabaseAdapter
|
|
4519
5412
|
});
|
|
4520
5413
|
//# sourceMappingURL=index.cjs.map
|