@designfever/web-review-kit 0.4.0 → 0.4.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.
@@ -400,8 +400,8 @@ function getDomAnchorFromPoint(point, configuredAttribute = "data-qa-id", enviro
400
400
  source: getDomSourceHint(target)
401
401
  };
402
402
  }
403
- function getElementViewportSelection(anchor, environment) {
404
- const element = getAnchorElement(anchor, environment);
403
+ function getElementViewportSelection(anchor, environment, preferredSelection) {
404
+ const element = getAnchorElement(anchor, environment, preferredSelection);
405
405
  if (!element) return void 0;
406
406
  const rect = element.getBoundingClientRect();
407
407
  if (rect.width <= 0 || rect.height <= 0) return void 0;
@@ -440,22 +440,35 @@ function getAnchorCandidates(anchor) {
440
440
  ...anchor.candidates ?? []
441
441
  ]);
442
442
  }
443
- function resolveAnchorElement(anchor, environment) {
443
+ function resolveAnchorElement(anchor, environment, preferredSelection) {
444
444
  const matches = getAnchorCandidates(anchor).flatMap((candidate) => {
445
- const match = queryBestAnchorCandidate(
446
- candidate,
447
- candidate.textFingerprint ?? anchor.textFingerprint,
448
- environment
449
- );
450
- if (!match) return [];
451
- const confidence = roundRatio(
452
- (candidate.confidence ?? 0.5) * match.score
453
- );
454
- return [{
455
- element: match.element,
456
- candidate,
457
- confidence
458
- }];
445
+ const textFingerprint = candidate.textFingerprint ?? anchor.textFingerprint;
446
+ if (!preferredSelection) {
447
+ const match = queryBestAnchorCandidate(
448
+ candidate,
449
+ textFingerprint,
450
+ environment
451
+ );
452
+ if (!match) return [];
453
+ const confidence = roundRatio(
454
+ (candidate.confidence ?? 0.5) * match.score
455
+ );
456
+ return [{
457
+ element: match.element,
458
+ candidate,
459
+ confidence
460
+ }];
461
+ }
462
+ return queryAnchorElements(candidate.selector, environment).map((element) => {
463
+ const confidence = roundRatio(
464
+ (candidate.confidence ?? 0.5) * getTextFingerprintScore(textFingerprint, getTextFingerprint(element)) * getSelectionMatchScore(element, preferredSelection)
465
+ );
466
+ return {
467
+ element,
468
+ candidate,
469
+ confidence
470
+ };
471
+ });
459
472
  });
460
473
  return matches.sort((a, b) => b.confidence - a.confidence)[0];
461
474
  }
@@ -465,8 +478,8 @@ function cssEscape(value) {
465
478
  }
466
479
  return value.replace(/[^a-zA-Z0-9_-]/g, "\\$&");
467
480
  }
468
- function getAnchorElement(anchor, environment) {
469
- return typeof anchor === "string" ? queryAnchorElement(anchor, environment) : resolveAnchorElement(anchor, environment)?.element;
481
+ function getAnchorElement(anchor, environment, preferredSelection) {
482
+ return typeof anchor === "string" ? queryAnchorElement(anchor, environment) : resolveAnchorElement(anchor, environment, preferredSelection)?.element;
470
483
  }
471
484
  function createAnchorCandidates(target, configuredAttribute) {
472
485
  const targetCandidates = [];
@@ -830,6 +843,38 @@ function getTextFingerprintScore(expected, actual) {
830
843
  const matches = expectedTokens.filter((token) => actualTokens.has(token));
831
844
  return clamp(matches.length / expectedTokens.length, 0.25, 0.76);
832
845
  }
846
+ function getSelectionMatchScore(element, selection) {
847
+ const rect = element.getBoundingClientRect();
848
+ if (rect.width <= 0 || rect.height <= 0) return 0.05;
849
+ const overlapLeft = Math.max(rect.left, selection.left);
850
+ const overlapTop = Math.max(rect.top, selection.top);
851
+ const overlapRight = Math.min(rect.right, selection.left + selection.width);
852
+ const overlapBottom = Math.min(rect.bottom, selection.top + selection.height);
853
+ const overlapWidth = Math.max(0, overlapRight - overlapLeft);
854
+ const overlapHeight = Math.max(0, overlapBottom - overlapTop);
855
+ const overlapArea = overlapWidth * overlapHeight;
856
+ if (overlapArea > 0) {
857
+ const selectionArea = Math.max(1, selection.width * selection.height);
858
+ const rectArea = Math.max(1, rect.width * rect.height);
859
+ return 1 + overlapArea / Math.min(selectionArea, rectArea);
860
+ }
861
+ const rectCenterX = rect.left + rect.width / 2;
862
+ const rectCenterY = rect.top + rect.height / 2;
863
+ const selectionCenterX = selection.left + selection.width / 2;
864
+ const selectionCenterY = selection.top + selection.height / 2;
865
+ const distance = Math.hypot(
866
+ rectCenterX - selectionCenterX,
867
+ rectCenterY - selectionCenterY
868
+ );
869
+ const basis = Math.max(
870
+ 1,
871
+ rect.width,
872
+ rect.height,
873
+ selection.width,
874
+ selection.height
875
+ );
876
+ return clamp(1 / (1 + distance / basis), 0.05, 0.95);
877
+ }
833
878
  function getFingerprintTokens(value) {
834
879
  return value.toLowerCase().split(/[\s/|,.:;()[\]{}"'`~!?<>]+/).map((token) => token.trim()).filter((token) => token.length > 1);
835
880
  }
@@ -1864,32 +1909,6 @@ function createStyleElement() {
1864
1909
  pointer-events: none;
1865
1910
  }
1866
1911
 
1867
- .dfwr-adjust-hud {
1868
- position: fixed;
1869
- z-index: 5;
1870
- display: inline-flex;
1871
- align-items: center;
1872
- min-height: 22px;
1873
- padding: 0 8px;
1874
- border: 1px solid rgba(99, 215, 199, 0.72);
1875
- border-radius: var(--df-review-radius-sm);
1876
- background: rgba(21, 25, 29, 0.92);
1877
- box-shadow:
1878
- 0 0 0 3px rgba(99, 215, 199, 0.14),
1879
- 0 8px 18px rgba(0, 0, 0, 0.26);
1880
- color: #63d7c7;
1881
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
1882
- font-size: var(--df-review-font-size-2xs);
1883
- font-weight: 800;
1884
- line-height: 1;
1885
- pointer-events: none;
1886
- white-space: nowrap;
1887
- }
1888
-
1889
- .dfwr-adjust-hud[hidden] {
1890
- display: none;
1891
- }
1892
-
1893
1912
  .dfwr-empty,
1894
1913
  .dfwr-error {
1895
1914
  margin: 0;
@@ -2252,7 +2271,6 @@ var WebReviewKitView = class {
2252
2271
  if (event.button !== 0) return;
2253
2272
  cancel(event);
2254
2273
  });
2255
- layer.addEventListener("click", cancel);
2256
2274
  return layer;
2257
2275
  }
2258
2276
  getDraftAdjustmentMetrics(draft) {
@@ -2432,14 +2450,6 @@ var WebReviewKitView = class {
2432
2450
  if (!draft.selection) return void 0;
2433
2451
  return toViewportSelection(draft.selection.viewport);
2434
2452
  }
2435
- formatDraftAdjustmentStatus(draft) {
2436
- const metrics = this.getDraftAdjustmentMetrics(draft);
2437
- return [
2438
- `x ${this.formatSignedPx(metrics.x)}`,
2439
- `y ${this.formatSignedPx(metrics.y)}`,
2440
- `scale ${this.formatSignedPx(metrics.scale)}`
2441
- ].join(" / ");
2442
- }
2443
2453
  getDraftAdjustmentMetricLines(draft) {
2444
2454
  const metrics = this.getDraftAdjustmentMetrics(draft);
2445
2455
  return [
@@ -2451,6 +2461,7 @@ var WebReviewKitView = class {
2451
2461
  }
2452
2462
  withDraftAdjustmentComment(comment, draft) {
2453
2463
  if (!this.hasDraftAdjustment(draft)) return comment;
2464
+ const trimmedComment = comment.trim();
2454
2465
  const metrics = this.getDraftAdjustmentMetrics(draft);
2455
2466
  const adjustment = [
2456
2467
  `${this.getAdjustmentLabel()}: x ${this.formatSignedPx(
@@ -2462,12 +2473,20 @@ var WebReviewKitView = class {
2462
2473
  metrics.viewportWidth
2463
2474
  )}/design ${Math.round(metrics.designWidth)})`
2464
2475
  ].join(" ");
2465
- return `${comment.trim()}
2466
- ${adjustment}`;
2476
+ return trimmedComment ? `${trimmedComment}
2477
+ ${adjustment}` : adjustment;
2467
2478
  }
2468
2479
  getStyleableDraftElement(draft, environment) {
2480
+ if (draft.previewElement && draft.previewElement.ownerDocument === environment.document && "style" in draft.previewElement) {
2481
+ return draft.previewElement;
2482
+ }
2469
2483
  if (!draft.anchor) return void 0;
2470
- const element = resolveAnchorElement(draft.anchor, environment)?.element;
2484
+ const preferredSelection = draft.selection ? toViewportSelection(draft.selection.viewport) : void 0;
2485
+ const element = resolveAnchorElement(
2486
+ draft.anchor,
2487
+ environment,
2488
+ preferredSelection
2489
+ )?.element;
2471
2490
  if (!element) return void 0;
2472
2491
  if ("style" in element) return element;
2473
2492
  return void 0;
@@ -2487,39 +2506,77 @@ ${adjustment}`;
2487
2506
  this.restoreDraftPreview();
2488
2507
  }
2489
2508
  if (!this.draftPreview) {
2490
- const computedTransform = environment.window.getComputedStyle(element).transform;
2509
+ const computedStyle = environment.window.getComputedStyle(element);
2510
+ const clone = element.cloneNode(true);
2511
+ this.removeDuplicateIds(clone);
2512
+ this.copyComputedStyle(element, clone, environment);
2513
+ this.positionDraftPreviewClone(clone, element, computedStyle);
2514
+ environment.document.body?.appendChild(clone);
2491
2515
  this.draftPreview = {
2492
2516
  element,
2493
- transform: element.style.transform,
2494
- transformOrigin: element.style.transformOrigin,
2495
- transition: element.style.transition,
2496
- willChange: element.style.willChange,
2497
- baseTransform: element.style.transform || (computedTransform && computedTransform !== "none" ? computedTransform : "")
2517
+ clone,
2518
+ visibility: element.style.visibility
2498
2519
  };
2520
+ element.style.visibility = "hidden";
2499
2521
  }
2500
2522
  const metrics = this.getDraftAdjustmentMetrics(draft);
2501
2523
  const translate = `translate(${this.toCssNumber(metrics.cssX)}px, ${this.toCssNumber(
2502
2524
  metrics.cssY
2503
2525
  )}px)`;
2504
2526
  const scale = metrics.scaleFactor === 1 ? "" : `scale(${this.toCssNumber(metrics.scaleFactor)})`;
2505
- element.style.transition = "none";
2506
- element.style.willChange = "transform";
2507
- element.style.transformOrigin = "top left";
2508
- element.style.transform = [
2509
- this.draftPreview.baseTransform,
2510
- translate,
2511
- scale
2512
- ].filter(Boolean).join(" ");
2527
+ this.draftPreview.clone.style.transform = [translate, scale].filter(Boolean).join(" ");
2513
2528
  }
2514
2529
  restoreDraftPreview() {
2515
2530
  if (!this.draftPreview) return;
2516
- const { element, transform, transformOrigin, transition, willChange } = this.draftPreview;
2517
- element.style.transform = transform;
2518
- element.style.transformOrigin = transformOrigin;
2519
- element.style.transition = transition;
2520
- element.style.willChange = willChange;
2531
+ const { element, clone, visibility } = this.draftPreview;
2532
+ clone.remove();
2533
+ element.style.visibility = visibility;
2521
2534
  this.draftPreview = void 0;
2522
2535
  }
2536
+ positionDraftPreviewClone(clone, element, computedStyle) {
2537
+ const rect = element.getBoundingClientRect();
2538
+ clone.setAttribute("data-dfwr-adjust-preview", "true");
2539
+ clone.setAttribute("aria-hidden", "true");
2540
+ clone.style.position = "fixed";
2541
+ clone.style.left = `${this.toCssNumber(rect.left)}px`;
2542
+ clone.style.top = `${this.toCssNumber(rect.top)}px`;
2543
+ clone.style.right = "auto";
2544
+ clone.style.bottom = "auto";
2545
+ clone.style.width = `${this.toCssNumber(rect.width)}px`;
2546
+ clone.style.height = `${this.toCssNumber(rect.height)}px`;
2547
+ clone.style.maxWidth = "none";
2548
+ clone.style.maxHeight = "none";
2549
+ clone.style.margin = "0";
2550
+ clone.style.boxSizing = "border-box";
2551
+ clone.style.display = this.getDraftPreviewDisplay(computedStyle.display);
2552
+ clone.style.zIndex = "2147483646";
2553
+ clone.style.pointerEvents = "none";
2554
+ clone.style.transition = "none";
2555
+ clone.style.willChange = "transform";
2556
+ clone.style.transformOrigin = "top left";
2557
+ clone.style.transform = "none";
2558
+ }
2559
+ getDraftPreviewDisplay(display) {
2560
+ if (display === "inline" || display === "contents") return "inline-block";
2561
+ return display || "block";
2562
+ }
2563
+ copyComputedStyle(element, clone, environment) {
2564
+ const computedStyle = environment.window.getComputedStyle(element);
2565
+ for (let index = 0; index < computedStyle.length; index += 1) {
2566
+ const property = computedStyle.item(index);
2567
+ clone.style.setProperty(
2568
+ property,
2569
+ computedStyle.getPropertyValue(property),
2570
+ computedStyle.getPropertyPriority(property)
2571
+ );
2572
+ }
2573
+ }
2574
+ removeDuplicateIds(element) {
2575
+ element.removeAttribute("id");
2576
+ element.querySelectorAll("[id]").forEach((child) => {
2577
+ child.removeAttribute("id");
2578
+ });
2579
+ }
2523
2580
  toCssNumber(value) {
2524
2581
  return Math.round(value * 1e3) / 1e3;
2525
2582
  }
@@ -2681,8 +2738,8 @@ ${adjustment}`;
2681
2738
  });
2682
2739
  const saveDraft = () => {
2683
2740
  const comment = textarea.value.trim();
2684
- if (!comment) return;
2685
2741
  const currentDraft = this.state.noteDraft ?? draft;
2742
+ if (!comment && !this.hasDraftAdjustment(currentDraft)) return;
2686
2743
  void this.config.actions.createItem({
2687
2744
  kind: "note",
2688
2745
  comment: this.withDraftAdjustmentComment(comment, currentDraft),
@@ -2692,13 +2749,8 @@ ${adjustment}`;
2692
2749
  selection: currentDraft.selection
2693
2750
  });
2694
2751
  };
2695
- const adjustmentHud = isElementDraft ? this.createAdjustmentHud(draft, environment) : void 0;
2696
- if (adjustmentHud) {
2697
- group.append(adjustmentHud);
2698
- }
2699
2752
  const adjustmentControls = isElementDraft ? this.createAdjustmentControls({
2700
2753
  draft,
2701
- hud: adjustmentHud,
2702
2754
  pin,
2703
2755
  popover,
2704
2756
  selectionHighlight,
@@ -2826,7 +2878,6 @@ ${adjustment}`;
2826
2878
  }
2827
2879
  createAdjustmentControls({
2828
2880
  draft,
2829
- hud,
2830
2881
  pin,
2831
2882
  popover,
2832
2883
  selectionHighlight,
@@ -2864,7 +2915,6 @@ ${adjustment}`;
2864
2915
  scaleStatus.textContent = scaleLine;
2865
2916
  this.syncDraftAdjustmentUi({
2866
2917
  draft: nextDraft,
2867
- hud,
2868
2918
  pin,
2869
2919
  selectionHighlight
2870
2920
  });
@@ -2925,16 +2975,8 @@ ${adjustment}`;
2925
2975
  if (event.key.toLowerCase() === "s") return { x: 0, y: 0, scale: -step };
2926
2976
  return void 0;
2927
2977
  }
2928
- createAdjustmentHud(draft, environment) {
2929
- const hud = document.createElement("div");
2930
- hud.className = "dfwr-adjust-hud";
2931
- hud.setAttribute("aria-hidden", "true");
2932
- this.syncAdjustmentHud(hud, draft, environment);
2933
- return hud;
2934
- }
2935
2978
  syncDraftAdjustmentUi({
2936
2979
  draft,
2937
- hud,
2938
2980
  pin,
2939
2981
  selectionHighlight
2940
2982
  }) {
@@ -2959,26 +3001,8 @@ ${adjustment}`;
2959
3001
  selectionHighlight.style.width = `${rect.width}px`;
2960
3002
  selectionHighlight.style.height = `${rect.height}px`;
2961
3003
  }
2962
- if (hud) {
2963
- this.syncAdjustmentHud(hud, draft, environment);
2964
- }
2965
3004
  this.syncDraftPreview(draft);
2966
3005
  }
2967
- syncAdjustmentHud(hud, draft, environment) {
2968
- if (!draft.selection) return;
2969
- const rect = toHostSelection(
2970
- this.getAdjustedDraftSelection(
2971
- toViewportSelection(draft.selection.viewport),
2972
- draft
2973
- ),
2974
- environment
2975
- );
2976
- const isVisible = draft.adjustment?.isActive === true || this.hasDraftAdjustment(draft);
2977
- hud.hidden = !isVisible;
2978
- hud.textContent = this.formatDraftAdjustmentStatus(draft);
2979
- hud.style.left = `${Math.max(4, rect.left)}px`;
2980
- hud.style.top = `${Math.max(4, rect.top - 28)}px`;
2981
- }
2982
3006
  createAreaForm() {
2983
3007
  const form = document.createElement("form");
2984
3008
  form.className = "dfwr-form";
@@ -3102,12 +3126,18 @@ ${adjustment}`;
3102
3126
  save.className = "dfwr-button is-primary";
3103
3127
  save.type = "button";
3104
3128
  save.textContent = saveLabel;
3105
- save.addEventListener("click", onSave);
3129
+ save.addEventListener("click", (event) => {
3130
+ event.preventDefault();
3131
+ event.stopPropagation();
3132
+ onSave();
3133
+ });
3106
3134
  const cancel = document.createElement("button");
3107
3135
  cancel.className = "dfwr-button";
3108
3136
  cancel.type = "button";
3109
3137
  cancel.textContent = "Cancel";
3110
- cancel.addEventListener("click", () => {
3138
+ cancel.addEventListener("click", (event) => {
3139
+ event.preventDefault();
3140
+ event.stopPropagation();
3111
3141
  this.config.actions.setModeState("idle");
3112
3142
  this.config.actions.clearDrafts();
3113
3143
  this.config.actions.render();
@@ -3805,13 +3835,26 @@ var WebReviewKitApp = class {
3805
3835
  const viewport = getViewportSize(environment);
3806
3836
  const nextPoint = clampPoint(point, environment);
3807
3837
  const draft = await this.withOverlayHidden(() => {
3838
+ const pointSelection = getPointSelection(nextPoint);
3839
+ const targetElement = environment.document.elementFromPoint(
3840
+ nextPoint.x,
3841
+ nextPoint.y
3842
+ );
3843
+ const previewElement = targetElement && "style" in targetElement ? targetElement : void 0;
3844
+ const targetRect = targetElement?.getBoundingClientRect();
3845
+ const clickedSelection = targetRect && targetRect.width > 0 && targetRect.height > 0 ? {
3846
+ left: targetRect.left,
3847
+ top: targetRect.top,
3848
+ width: targetRect.width,
3849
+ height: targetRect.height
3850
+ } : void 0;
3808
3851
  const anchor = getDomAnchorFromPoint(
3809
3852
  nextPoint,
3810
3853
  this.options.anchors?.attribute,
3811
3854
  environment
3812
3855
  );
3813
- const elementSelection = anchor ? getElementViewportSelection(anchor, environment) : void 0;
3814
- const selection = elementSelection ?? getPointSelection(nextPoint);
3856
+ const elementSelection = anchor ? clickedSelection ?? getElementViewportSelection(anchor, environment, pointSelection) : void 0;
3857
+ const selection = elementSelection ?? pointSelection;
3815
3858
  const markerPoint = elementSelection ? { x: selection.left, y: selection.top } : getSelectionCenter(selection);
3816
3859
  const reviewSelection = elementSelection ? {
3817
3860
  viewport: toPublicSelection(elementSelection),
@@ -3830,7 +3873,8 @@ var WebReviewKitApp = class {
3830
3873
  anchor,
3831
3874
  marker,
3832
3875
  selection: reviewSelection,
3833
- comment
3876
+ comment,
3877
+ previewElement
3834
3878
  };
3835
3879
  });
3836
3880
  this.noteDraft = draft;
@@ -3970,4 +4014,4 @@ export {
3970
4014
  getNumberedReviewItems,
3971
4015
  createWebReviewKit
3972
4016
  };
3973
- //# sourceMappingURL=chunk-QFNYQCTA.js.map
4017
+ //# sourceMappingURL=chunk-6L2KJ7XL.js.map