@preprio/toolkit 0.5.0-rc.10 → 0.5.0-rc.12

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.
Files changed (34) hide show
  1. package/README.md +21 -0
  2. package/dist/astro/index.cjs +31 -2
  3. package/dist/astro/index.js +31 -2
  4. package/dist/{chunk-KD3GK6TO.js → chunk-CABGJW5X.js} +30 -1
  5. package/dist/{chunk-URXSDJOU.js → chunk-H3AFUPYD.js} +9 -0
  6. package/dist/{chunk-F46GJS6Y.js → chunk-HVTNUCF7.js} +1 -1
  7. package/dist/{chunk-IPMB32AZ.js → chunk-LWUSRWFE.js} +2 -2
  8. package/dist/{chunk-IDEC77LZ.js → chunk-M7FT7XXV.js} +93 -20
  9. package/dist/{chunk-TBBU3LDD.js → chunk-TJ7VALNA.js} +1 -1
  10. package/dist/index.cjs +131 -20
  11. package/dist/index.d.cts +6 -1
  12. package/dist/index.d.ts +6 -1
  13. package/dist/index.js +6 -6
  14. package/dist/{live-preview-DLXGXRE5.js → live-preview-SFBGATWC.js} +3 -3
  15. package/dist/nextjs/components.cjs +101 -19
  16. package/dist/nextjs/components.js +3 -3
  17. package/dist/nextjs/index.cjs +31 -2
  18. package/dist/nextjs/index.js +31 -2
  19. package/dist/nextjs/live-action.js +3 -3
  20. package/dist/nextjs/live.cjs +30 -1
  21. package/dist/nextjs/live.js +1 -1
  22. package/dist/nuxt/index.cjs +1 -1
  23. package/dist/nuxt/index.js +1 -1
  24. package/dist/nuxt/{live-preview-2RCU35W5.js → live-preview-XMQQZHMZ.js} +30 -1
  25. package/dist/nuxt/live.cjs +30 -1
  26. package/dist/nuxt/live.js +1 -1
  27. package/dist/react/components.cjs +101 -19
  28. package/dist/react/components.js +101 -19
  29. package/dist/sveltekit/index.cjs +1 -1
  30. package/dist/sveltekit/index.js +1 -1
  31. package/dist/sveltekit/{live-preview-2RCU35W5.js → live-preview-XMQQZHMZ.js} +30 -1
  32. package/dist/sveltekit/live.cjs +30 -1
  33. package/dist/sveltekit/live.js +1 -1
  34. package/package.json +1 -1
@@ -395,6 +395,7 @@ function createIframeBridge(store, options = {}) {
395
395
  const onReferences = options.onReferences;
396
396
  const onAssets = options.onAssets;
397
397
  const onAllowNavigation = options.onAllowNavigation;
398
+ const onItemChanged = options.onItemChanged;
398
399
  let parentOrigin = null;
399
400
  const onKeyDown = (event) => {
400
401
  const key = event.key.toLowerCase();
@@ -485,6 +486,14 @@ function createIframeBridge(store, options = {}) {
485
486
  debug.warn("allowNavigation handler failed", error);
486
487
  }
487
488
  }
489
+ if (data?.event === "prepr:itemChanged") {
490
+ if (typeof data.itemId !== "string" || !data.itemId) return;
491
+ try {
492
+ onItemChanged?.(data.itemId);
493
+ } catch (error) {
494
+ debug.warn("itemChanged handler failed", error);
495
+ }
496
+ }
488
497
  };
489
498
  return {
490
499
  start() {
@@ -2628,6 +2637,7 @@ var ABSOLUTE_HTTP = /^https?:\/\//i;
2628
2637
  var SOURCE_URL_ATTRIBUTES = ["src", "data-src"];
2629
2638
  var SOURCE_SET_ATTRIBUTES = ["srcset", "data-srcset"];
2630
2639
  var ASSET_ATTR = "data-prepr-asset";
2640
+ var REMOVED_ATTR = "data-prepr-removed";
2631
2641
  var ASSET_SOURCE_ATTRIBUTES = [
2632
2642
  ...SOURCE_URL_ATTRIBUTES,
2633
2643
  ...SOURCE_SET_ATTRIBUTES
@@ -2637,7 +2647,8 @@ var TAG_ATTRIBUTES = [
2637
2647
  "data-prepr-id",
2638
2648
  "data-prepr-field",
2639
2649
  "data-prepr-entry-id",
2640
- ASSET_ATTR
2650
+ ASSET_ATTR,
2651
+ REMOVED_ATTR
2641
2652
  ];
2642
2653
  function decodeSegment(segment) {
2643
2654
  try {
@@ -2756,11 +2767,22 @@ function besideOtherItem(img, isCurrentItem) {
2756
2767
  return false;
2757
2768
  }
2758
2769
  function tagAssetImages(index, itemId, keys, options = {}) {
2759
- const { isCurrentItem } = options;
2770
+ const { isCurrentItem, removed } = options;
2771
+ let showsRemoved = false;
2760
2772
  for (const img of document.getElementsByTagName("img")) {
2761
2773
  const ours = img.hasAttribute(ASSET_ATTR);
2762
- if (!ours && img.hasAttribute("data-prepr-encoded")) continue;
2763
- const match = index.size ? matchAsset(img, index, keys) : void 0;
2774
+ let match = index.size ? matchAsset(img, index, keys) : void 0;
2775
+ let isRemoved = false;
2776
+ if (!match && removed?.size) {
2777
+ match = matchAsset(img, removed, keys);
2778
+ isRemoved = match !== void 0;
2779
+ }
2780
+ if (!ours && img.hasAttribute("data-prepr-encoded")) {
2781
+ const mark = isRemoved && (!isCurrentItem || isCurrentItem(img));
2782
+ setIfChanged(img, REMOVED_ATTR, mark ? "" : null);
2783
+ showsRemoved || (showsRemoved = mark);
2784
+ continue;
2785
+ }
2764
2786
  const asset = match && isCurrentItem && besideOtherItem(img, isCurrentItem) ? void 0 : match;
2765
2787
  if (!asset) {
2766
2788
  if (ours) untagAssetImage(img);
@@ -2771,10 +2793,14 @@ function tagAssetImages(index, itemId, keys, options = {}) {
2771
2793
  setIfChanged(img, "data-prepr-id", itemId);
2772
2794
  setIfChanged(img, "data-prepr-field", asset.field);
2773
2795
  setIfChanged(img, "data-prepr-entry-id", asset.entryId ?? null);
2796
+ setIfChanged(img, REMOVED_ATTR, isRemoved ? "" : null);
2797
+ showsRemoved || (showsRemoved = isRemoved);
2774
2798
  }
2799
+ return showsRemoved;
2775
2800
  }
2776
2801
  function untagAssetImages() {
2777
2802
  document.querySelectorAll(`img[${ASSET_ATTR}]`).forEach(untagAssetImage);
2803
+ document.querySelectorAll(`img[${REMOVED_ATTR}]`).forEach((img) => img.removeAttribute(REMOVED_ATTR));
2778
2804
  }
2779
2805
 
2780
2806
  // src/core/visual-editor/navigation-guard.ts
@@ -2944,12 +2970,19 @@ var debug8 = createScopedLogger("visual-editor");
2944
2970
  var ENCODED_SELECTOR2 = "[data-prepr-encoded]";
2945
2971
  var REFERENCE_ATTR = "data-prepr-reference";
2946
2972
  var REFERENCE_SELECTOR = `[${REFERENCE_ATTR}]`;
2973
+ var OTHER_ITEM_ATTR = "data-prepr-other-item";
2974
+ var OTHER_ITEM_SELECTOR = `[${OTHER_ITEM_ATTR}="on"],[${OTHER_ITEM_ATTR}="off"]`;
2975
+ var CHROME_SELECTOR = "header, footer, nav, [role=banner], [role=contentinfo], [role=navigation]";
2976
+ var SECTIONING_SELECTOR = "article, aside, main, nav, section";
2977
+ var PLAIN_HEADER_FOOTER = "header:not([role]), footer:not([role])";
2947
2978
  var CURSOR_STYLE_MARKER = "data-prepr-visual-editor";
2948
- var CURSOR_STYLES = `${ENCODED_SELECTOR2}{cursor:pointer}html[data-prepr-ve-suspended] ${ENCODED_SELECTOR2}{cursor:auto}`;
2979
+ var REMOVED_STYLES = `img[${REMOVED_ATTR}]{opacity:.2!important;transition:opacity .2s ease}@media (prefers-reduced-motion:reduce){img[${REMOVED_ATTR}]{transition:none}}`;
2980
+ var CURSOR_STYLES = `${ENCODED_SELECTOR2}{cursor:pointer}html[data-prepr-ve-suspended] ${ENCODED_SELECTOR2}{cursor:auto}${REMOVED_STYLES}`;
2949
2981
  var SUSPENDED_ATTR = "data-prepr-ve-suspended";
2950
2982
  var FIND_RETRY_MS = 150;
2951
2983
  var FIND_RETRIES = 6;
2952
2984
  var MAX_CACHED_KEYS = 2e3;
2985
+ var SAVED_FADE_MS = 1e4;
2953
2986
  var LAYOUT_MUTATIONS = {
2954
2987
  childList: true,
2955
2988
  subtree: true,
@@ -2992,6 +3025,9 @@ function createVisualEditor(opts) {
2992
3025
  let guardEnabled = false;
2993
3026
  let assetIndex = /* @__PURE__ */ new Map();
2994
3027
  const assetKeys = /* @__PURE__ */ new Map();
3028
+ const removedAssets = /* @__PURE__ */ new Map();
3029
+ let saved = false;
3030
+ let savedTimeout = null;
2995
3031
  const guard = createNavigationGuard({
2996
3032
  onBlocked: (href) => opts.onNavigationBlocked?.(href),
2997
3033
  showsEditedContent: () => showsEditedContent()
@@ -3023,10 +3059,26 @@ function createVisualEditor(opts) {
3023
3059
  }
3024
3060
  return null;
3025
3061
  };
3062
+ const inSiteChrome = (el) => {
3063
+ let chrome = el.closest(CHROME_SELECTOR);
3064
+ while (chrome) {
3065
+ if (!chrome.matches(PLAIN_HEADER_FOOTER) || !chrome.parentElement?.closest(SECTIONING_SELECTOR)) {
3066
+ return true;
3067
+ }
3068
+ chrome = chrome.parentElement?.closest(CHROME_SELECTOR) ?? null;
3069
+ }
3070
+ return false;
3071
+ };
3072
+ const showsOtherItem = (el) => {
3073
+ const choice = el.closest(OTHER_ITEM_SELECTOR);
3074
+ if (choice) return choice.getAttribute(OTHER_ITEM_ATTR) === "on";
3075
+ return !inSiteChrome(el);
3076
+ };
3026
3077
  const itemKind = (el) => {
3027
3078
  if (isCurrentItem(el)) return "current";
3028
3079
  if (!references) return "other";
3029
- return linkedIdOf(el) ? "referenced" : null;
3080
+ if (linkedIdOf(el)) return "referenced";
3081
+ return showsOtherItem(el) ? "other" : null;
3030
3082
  };
3031
3083
  const referenceFieldOf = (el) => {
3032
3084
  const linked = linkedIdOf(el);
@@ -3099,6 +3151,7 @@ function createVisualEditor(opts) {
3099
3151
  }
3100
3152
  return;
3101
3153
  }
3154
+ if (kind === "other" && (e2.ctrlKey || e2.metaKey || e2.shiftKey)) return;
3102
3155
  e2.preventDefault();
3103
3156
  e2.stopPropagation();
3104
3157
  const item = listItemIn(encoded, e2.target);
@@ -3106,11 +3159,13 @@ function createVisualEditor(opts) {
3106
3159
  select(item?.element ?? encoded, encoded, item?.index);
3107
3160
  const referenceField = kind === "referenced" ? referenceFieldOf(encoded) : void 0;
3108
3161
  const linkedId = kind === "referenced" ? linkedIdOf(encoded) : null;
3162
+ const linkHref = kind === "other" ? leavingLinkHref(e2.target) : null;
3109
3163
  opts.onEdit({
3110
3164
  ...readEditPayload(encoded),
3111
3165
  ...linkedId && { id: linkedId },
3112
3166
  ...item && { block: item.index },
3113
- ...referenceField && { referenceField }
3167
+ ...referenceField && { referenceField },
3168
+ ...linkHref && { linkHref }
3114
3169
  });
3115
3170
  };
3116
3171
  const handleKey = (e2) => {
@@ -3171,15 +3226,32 @@ function createVisualEditor(opts) {
3171
3226
  const el = block !== void 0 ? nthListItem(found, block) ?? found : found;
3172
3227
  select(el, found, block);
3173
3228
  }
3229
+ const hasAssets = () => assetIndex.size > 0 || removedAssets.size > 0;
3174
3230
  function tagAssets() {
3175
3231
  if (assetKeys.size > MAX_CACHED_KEYS) assetKeys.clear();
3176
- tagAssetImages(assetIndex, currentItemId, assetKeys, {
3177
- isCurrentItem: (el) => isCurrentItem(el)
3232
+ const showsRemoved = tagAssetImages(assetIndex, currentItemId, assetKeys, {
3233
+ isCurrentItem: (el) => isCurrentItem(el),
3234
+ removed: removedAssets
3178
3235
  });
3236
+ if (saved && !showsRemoved) forgetRemoved();
3237
+ }
3238
+ function forgetRemoved() {
3239
+ saved = false;
3240
+ if (savedTimeout) clearTimeout(savedTimeout);
3241
+ savedTimeout = null;
3242
+ if (!removedAssets.size) return;
3243
+ removedAssets.clear();
3244
+ if (running) tagAssets();
3245
+ }
3246
+ function itemSaved() {
3247
+ if (!removedAssets.size) return;
3248
+ saved = true;
3249
+ if (savedTimeout) clearTimeout(savedTimeout);
3250
+ savedTimeout = setTimeout(forgetRemoved, SAVED_FADE_MS);
3179
3251
  }
3180
3252
  const handleLayoutFrame = () => {
3181
3253
  mutationFrame = null;
3182
- if (assetIndex.size) tagAssets();
3254
+ if (hasAssets()) tagAssets();
3183
3255
  reselect();
3184
3256
  const hovered = overlay.getHoverTarget();
3185
3257
  if (hovered && !hovered.isConnected) {
@@ -3191,7 +3263,7 @@ function createVisualEditor(opts) {
3191
3263
  };
3192
3264
  const handleMutations = () => {
3193
3265
  if (mutationFrame !== null) return;
3194
- if (!selection && !overlay.getHoverTarget() && !assetIndex.size) return;
3266
+ if (!selection && !overlay.getHoverTarget() && !hasAssets()) return;
3195
3267
  mutationFrame = requestAnimationFrame(handleLayoutFrame);
3196
3268
  };
3197
3269
  function injectCursorStyles() {
@@ -3232,7 +3304,7 @@ function createVisualEditor(opts) {
3232
3304
  });
3233
3305
  mutationObserver = new MutationObserver(handleMutations);
3234
3306
  mutationObserver.observe(document.body, LAYOUT_MUTATIONS);
3235
- if (assetIndex.size) tagAssets();
3307
+ if (hasAssets()) tagAssets();
3236
3308
  syncGuard();
3237
3309
  debug8.log("visual editor started");
3238
3310
  }
@@ -3308,18 +3380,24 @@ function createVisualEditor(opts) {
3308
3380
  });
3309
3381
  }
3310
3382
  function setCurrentItem(itemId, referenceList, componentIds) {
3383
+ if (itemId !== currentItemId) forgetRemoved();
3311
3384
  currentItemId = itemId;
3312
3385
  if (referenceList) setReferences(referenceList, componentIds);
3313
3386
  else if (componentIds) ownComponents = new Set(componentIds);
3314
- if (running && assetIndex.size) tagAssets();
3387
+ if (running && hasAssets()) tagAssets();
3315
3388
  const selected = overlay.getSelectedTarget();
3316
3389
  const owner = selected ? encodedFrom(selected) : null;
3317
3390
  if (owner && !isCurrentItem(owner)) clearSelection();
3318
3391
  }
3319
3392
  function setAssets(assets) {
3320
- const hadAssets = assetIndex.size > 0;
3321
- assetIndex = createAssetIndex(assets);
3322
- if (running && (hadAssets || assetIndex.size)) tagAssets();
3393
+ const hadAssets = hasAssets();
3394
+ const next = createAssetIndex(assets);
3395
+ for (const [key, asset] of assetIndex) {
3396
+ if (!next.has(key)) removedAssets.set(key, asset);
3397
+ }
3398
+ for (const key of next.keys()) removedAssets.delete(key);
3399
+ assetIndex = next;
3400
+ if (running && (hadAssets || hasAssets())) tagAssets();
3323
3401
  }
3324
3402
  function setReferences(referenceList, componentIds) {
3325
3403
  if (componentIds) ownComponents = new Set(componentIds);
@@ -3345,6 +3423,7 @@ function createVisualEditor(opts) {
3345
3423
  setCurrentItem,
3346
3424
  setReferences,
3347
3425
  setAssets,
3426
+ itemSaved,
3348
3427
  setLocale,
3349
3428
  focusField,
3350
3429
  setNavigationGuard,
@@ -3431,7 +3510,8 @@ function createPreprPreview(opts = {}) {
3431
3510
  locale: contentLocale,
3432
3511
  block,
3433
3512
  entryId,
3434
- referenceField
3513
+ referenceField,
3514
+ linkHref
3435
3515
  }) => {
3436
3516
  const safeHref = href ? safeEditUrl(href) : null;
3437
3517
  if (href && !safeHref) {
@@ -3445,7 +3525,8 @@ function createPreprPreview(opts = {}) {
3445
3525
  locale: contentLocale,
3446
3526
  ...block !== void 0 && { block },
3447
3527
  ...entryId && { entryId },
3448
- ...referenceField && { referenceField }
3528
+ ...referenceField && { referenceField },
3529
+ ...linkHref && { linkHref }
3449
3530
  });
3450
3531
  } else if (safeHref) {
3451
3532
  window.open(safeHref, "_blank", "noopener,noreferrer");
@@ -3500,6 +3581,7 @@ function createPreprPreview(opts = {}) {
3500
3581
  },
3501
3582
  onAssets: (assets) => visualEditor?.setAssets(assets),
3502
3583
  onAllowNavigation: (href) => visualEditor?.allowNavigation(href),
3584
+ onItemChanged: () => visualEditor?.itemSaved(),
3503
3585
  onReferences: (referencedIds, componentIds) => visualEditor?.setReferences(referencedIds, componentIds),
3504
3586
  // The editor selecting a field scrolls the matching element into view. The
3505
3587
  // lookup relies on the attributes the stega scan writes, so it finds
@@ -3540,7 +3622,7 @@ function createPreprPreview(opts = {}) {
3540
3622
  }
3541
3623
 
3542
3624
  // src/version.ts
3543
- var VERSION = "0.5.0-rc.10";
3625
+ var VERSION = "0.5.0-rc.12";
3544
3626
 
3545
3627
  // src/core/pixel.ts
3546
3628
  var PIXEL_SCRIPT_URL = "https://cdn.tracking.prepr.io/js/prepr.min.js";
@@ -371,6 +371,7 @@ function createIframeBridge(store, options = {}) {
371
371
  const onReferences = options.onReferences;
372
372
  const onAssets = options.onAssets;
373
373
  const onAllowNavigation = options.onAllowNavigation;
374
+ const onItemChanged = options.onItemChanged;
374
375
  let parentOrigin = null;
375
376
  const onKeyDown = (event) => {
376
377
  const key = event.key.toLowerCase();
@@ -461,6 +462,14 @@ function createIframeBridge(store, options = {}) {
461
462
  debug.warn("allowNavigation handler failed", error);
462
463
  }
463
464
  }
465
+ if (data?.event === "prepr:itemChanged") {
466
+ if (typeof data.itemId !== "string" || !data.itemId) return;
467
+ try {
468
+ onItemChanged?.(data.itemId);
469
+ } catch (error) {
470
+ debug.warn("itemChanged handler failed", error);
471
+ }
472
+ }
464
473
  };
465
474
  return {
466
475
  start() {
@@ -2604,6 +2613,7 @@ var ABSOLUTE_HTTP = /^https?:\/\//i;
2604
2613
  var SOURCE_URL_ATTRIBUTES = ["src", "data-src"];
2605
2614
  var SOURCE_SET_ATTRIBUTES = ["srcset", "data-srcset"];
2606
2615
  var ASSET_ATTR = "data-prepr-asset";
2616
+ var REMOVED_ATTR = "data-prepr-removed";
2607
2617
  var ASSET_SOURCE_ATTRIBUTES = [
2608
2618
  ...SOURCE_URL_ATTRIBUTES,
2609
2619
  ...SOURCE_SET_ATTRIBUTES
@@ -2613,7 +2623,8 @@ var TAG_ATTRIBUTES = [
2613
2623
  "data-prepr-id",
2614
2624
  "data-prepr-field",
2615
2625
  "data-prepr-entry-id",
2616
- ASSET_ATTR
2626
+ ASSET_ATTR,
2627
+ REMOVED_ATTR
2617
2628
  ];
2618
2629
  function decodeSegment(segment) {
2619
2630
  try {
@@ -2732,11 +2743,22 @@ function besideOtherItem(img, isCurrentItem) {
2732
2743
  return false;
2733
2744
  }
2734
2745
  function tagAssetImages(index, itemId, keys, options = {}) {
2735
- const { isCurrentItem } = options;
2746
+ const { isCurrentItem, removed } = options;
2747
+ let showsRemoved = false;
2736
2748
  for (const img of document.getElementsByTagName("img")) {
2737
2749
  const ours = img.hasAttribute(ASSET_ATTR);
2738
- if (!ours && img.hasAttribute("data-prepr-encoded")) continue;
2739
- const match = index.size ? matchAsset(img, index, keys) : void 0;
2750
+ let match = index.size ? matchAsset(img, index, keys) : void 0;
2751
+ let isRemoved = false;
2752
+ if (!match && removed?.size) {
2753
+ match = matchAsset(img, removed, keys);
2754
+ isRemoved = match !== void 0;
2755
+ }
2756
+ if (!ours && img.hasAttribute("data-prepr-encoded")) {
2757
+ const mark = isRemoved && (!isCurrentItem || isCurrentItem(img));
2758
+ setIfChanged(img, REMOVED_ATTR, mark ? "" : null);
2759
+ showsRemoved || (showsRemoved = mark);
2760
+ continue;
2761
+ }
2740
2762
  const asset = match && isCurrentItem && besideOtherItem(img, isCurrentItem) ? void 0 : match;
2741
2763
  if (!asset) {
2742
2764
  if (ours) untagAssetImage(img);
@@ -2747,10 +2769,14 @@ function tagAssetImages(index, itemId, keys, options = {}) {
2747
2769
  setIfChanged(img, "data-prepr-id", itemId);
2748
2770
  setIfChanged(img, "data-prepr-field", asset.field);
2749
2771
  setIfChanged(img, "data-prepr-entry-id", asset.entryId ?? null);
2772
+ setIfChanged(img, REMOVED_ATTR, isRemoved ? "" : null);
2773
+ showsRemoved || (showsRemoved = isRemoved);
2750
2774
  }
2775
+ return showsRemoved;
2751
2776
  }
2752
2777
  function untagAssetImages() {
2753
2778
  document.querySelectorAll(`img[${ASSET_ATTR}]`).forEach(untagAssetImage);
2779
+ document.querySelectorAll(`img[${REMOVED_ATTR}]`).forEach((img) => img.removeAttribute(REMOVED_ATTR));
2754
2780
  }
2755
2781
 
2756
2782
  // src/core/visual-editor/navigation-guard.ts
@@ -2920,12 +2946,19 @@ var debug8 = createScopedLogger("visual-editor");
2920
2946
  var ENCODED_SELECTOR2 = "[data-prepr-encoded]";
2921
2947
  var REFERENCE_ATTR = "data-prepr-reference";
2922
2948
  var REFERENCE_SELECTOR = `[${REFERENCE_ATTR}]`;
2949
+ var OTHER_ITEM_ATTR = "data-prepr-other-item";
2950
+ var OTHER_ITEM_SELECTOR = `[${OTHER_ITEM_ATTR}="on"],[${OTHER_ITEM_ATTR}="off"]`;
2951
+ var CHROME_SELECTOR = "header, footer, nav, [role=banner], [role=contentinfo], [role=navigation]";
2952
+ var SECTIONING_SELECTOR = "article, aside, main, nav, section";
2953
+ var PLAIN_HEADER_FOOTER = "header:not([role]), footer:not([role])";
2923
2954
  var CURSOR_STYLE_MARKER = "data-prepr-visual-editor";
2924
- var CURSOR_STYLES = `${ENCODED_SELECTOR2}{cursor:pointer}html[data-prepr-ve-suspended] ${ENCODED_SELECTOR2}{cursor:auto}`;
2955
+ var REMOVED_STYLES = `img[${REMOVED_ATTR}]{opacity:.2!important;transition:opacity .2s ease}@media (prefers-reduced-motion:reduce){img[${REMOVED_ATTR}]{transition:none}}`;
2956
+ var CURSOR_STYLES = `${ENCODED_SELECTOR2}{cursor:pointer}html[data-prepr-ve-suspended] ${ENCODED_SELECTOR2}{cursor:auto}${REMOVED_STYLES}`;
2925
2957
  var SUSPENDED_ATTR = "data-prepr-ve-suspended";
2926
2958
  var FIND_RETRY_MS = 150;
2927
2959
  var FIND_RETRIES = 6;
2928
2960
  var MAX_CACHED_KEYS = 2e3;
2961
+ var SAVED_FADE_MS = 1e4;
2929
2962
  var LAYOUT_MUTATIONS = {
2930
2963
  childList: true,
2931
2964
  subtree: true,
@@ -2968,6 +3001,9 @@ function createVisualEditor(opts) {
2968
3001
  let guardEnabled = false;
2969
3002
  let assetIndex = /* @__PURE__ */ new Map();
2970
3003
  const assetKeys = /* @__PURE__ */ new Map();
3004
+ const removedAssets = /* @__PURE__ */ new Map();
3005
+ let saved = false;
3006
+ let savedTimeout = null;
2971
3007
  const guard = createNavigationGuard({
2972
3008
  onBlocked: (href) => opts.onNavigationBlocked?.(href),
2973
3009
  showsEditedContent: () => showsEditedContent()
@@ -2999,10 +3035,26 @@ function createVisualEditor(opts) {
2999
3035
  }
3000
3036
  return null;
3001
3037
  };
3038
+ const inSiteChrome = (el) => {
3039
+ let chrome = el.closest(CHROME_SELECTOR);
3040
+ while (chrome) {
3041
+ if (!chrome.matches(PLAIN_HEADER_FOOTER) || !chrome.parentElement?.closest(SECTIONING_SELECTOR)) {
3042
+ return true;
3043
+ }
3044
+ chrome = chrome.parentElement?.closest(CHROME_SELECTOR) ?? null;
3045
+ }
3046
+ return false;
3047
+ };
3048
+ const showsOtherItem = (el) => {
3049
+ const choice = el.closest(OTHER_ITEM_SELECTOR);
3050
+ if (choice) return choice.getAttribute(OTHER_ITEM_ATTR) === "on";
3051
+ return !inSiteChrome(el);
3052
+ };
3002
3053
  const itemKind = (el) => {
3003
3054
  if (isCurrentItem(el)) return "current";
3004
3055
  if (!references) return "other";
3005
- return linkedIdOf(el) ? "referenced" : null;
3056
+ if (linkedIdOf(el)) return "referenced";
3057
+ return showsOtherItem(el) ? "other" : null;
3006
3058
  };
3007
3059
  const referenceFieldOf = (el) => {
3008
3060
  const linked = linkedIdOf(el);
@@ -3075,6 +3127,7 @@ function createVisualEditor(opts) {
3075
3127
  }
3076
3128
  return;
3077
3129
  }
3130
+ if (kind === "other" && (e2.ctrlKey || e2.metaKey || e2.shiftKey)) return;
3078
3131
  e2.preventDefault();
3079
3132
  e2.stopPropagation();
3080
3133
  const item = listItemIn(encoded, e2.target);
@@ -3082,11 +3135,13 @@ function createVisualEditor(opts) {
3082
3135
  select(item?.element ?? encoded, encoded, item?.index);
3083
3136
  const referenceField = kind === "referenced" ? referenceFieldOf(encoded) : void 0;
3084
3137
  const linkedId = kind === "referenced" ? linkedIdOf(encoded) : null;
3138
+ const linkHref = kind === "other" ? leavingLinkHref(e2.target) : null;
3085
3139
  opts.onEdit({
3086
3140
  ...readEditPayload(encoded),
3087
3141
  ...linkedId && { id: linkedId },
3088
3142
  ...item && { block: item.index },
3089
- ...referenceField && { referenceField }
3143
+ ...referenceField && { referenceField },
3144
+ ...linkHref && { linkHref }
3090
3145
  });
3091
3146
  };
3092
3147
  const handleKey = (e2) => {
@@ -3147,15 +3202,32 @@ function createVisualEditor(opts) {
3147
3202
  const el = block !== void 0 ? nthListItem(found, block) ?? found : found;
3148
3203
  select(el, found, block);
3149
3204
  }
3205
+ const hasAssets = () => assetIndex.size > 0 || removedAssets.size > 0;
3150
3206
  function tagAssets() {
3151
3207
  if (assetKeys.size > MAX_CACHED_KEYS) assetKeys.clear();
3152
- tagAssetImages(assetIndex, currentItemId, assetKeys, {
3153
- isCurrentItem: (el) => isCurrentItem(el)
3208
+ const showsRemoved = tagAssetImages(assetIndex, currentItemId, assetKeys, {
3209
+ isCurrentItem: (el) => isCurrentItem(el),
3210
+ removed: removedAssets
3154
3211
  });
3212
+ if (saved && !showsRemoved) forgetRemoved();
3213
+ }
3214
+ function forgetRemoved() {
3215
+ saved = false;
3216
+ if (savedTimeout) clearTimeout(savedTimeout);
3217
+ savedTimeout = null;
3218
+ if (!removedAssets.size) return;
3219
+ removedAssets.clear();
3220
+ if (running) tagAssets();
3221
+ }
3222
+ function itemSaved() {
3223
+ if (!removedAssets.size) return;
3224
+ saved = true;
3225
+ if (savedTimeout) clearTimeout(savedTimeout);
3226
+ savedTimeout = setTimeout(forgetRemoved, SAVED_FADE_MS);
3155
3227
  }
3156
3228
  const handleLayoutFrame = () => {
3157
3229
  mutationFrame = null;
3158
- if (assetIndex.size) tagAssets();
3230
+ if (hasAssets()) tagAssets();
3159
3231
  reselect();
3160
3232
  const hovered = overlay.getHoverTarget();
3161
3233
  if (hovered && !hovered.isConnected) {
@@ -3167,7 +3239,7 @@ function createVisualEditor(opts) {
3167
3239
  };
3168
3240
  const handleMutations = () => {
3169
3241
  if (mutationFrame !== null) return;
3170
- if (!selection && !overlay.getHoverTarget() && !assetIndex.size) return;
3242
+ if (!selection && !overlay.getHoverTarget() && !hasAssets()) return;
3171
3243
  mutationFrame = requestAnimationFrame(handleLayoutFrame);
3172
3244
  };
3173
3245
  function injectCursorStyles() {
@@ -3208,7 +3280,7 @@ function createVisualEditor(opts) {
3208
3280
  });
3209
3281
  mutationObserver = new MutationObserver(handleMutations);
3210
3282
  mutationObserver.observe(document.body, LAYOUT_MUTATIONS);
3211
- if (assetIndex.size) tagAssets();
3283
+ if (hasAssets()) tagAssets();
3212
3284
  syncGuard();
3213
3285
  debug8.log("visual editor started");
3214
3286
  }
@@ -3284,18 +3356,24 @@ function createVisualEditor(opts) {
3284
3356
  });
3285
3357
  }
3286
3358
  function setCurrentItem(itemId, referenceList, componentIds) {
3359
+ if (itemId !== currentItemId) forgetRemoved();
3287
3360
  currentItemId = itemId;
3288
3361
  if (referenceList) setReferences(referenceList, componentIds);
3289
3362
  else if (componentIds) ownComponents = new Set(componentIds);
3290
- if (running && assetIndex.size) tagAssets();
3363
+ if (running && hasAssets()) tagAssets();
3291
3364
  const selected = overlay.getSelectedTarget();
3292
3365
  const owner = selected ? encodedFrom(selected) : null;
3293
3366
  if (owner && !isCurrentItem(owner)) clearSelection();
3294
3367
  }
3295
3368
  function setAssets(assets) {
3296
- const hadAssets = assetIndex.size > 0;
3297
- assetIndex = createAssetIndex(assets);
3298
- if (running && (hadAssets || assetIndex.size)) tagAssets();
3369
+ const hadAssets = hasAssets();
3370
+ const next = createAssetIndex(assets);
3371
+ for (const [key, asset] of assetIndex) {
3372
+ if (!next.has(key)) removedAssets.set(key, asset);
3373
+ }
3374
+ for (const key of next.keys()) removedAssets.delete(key);
3375
+ assetIndex = next;
3376
+ if (running && (hadAssets || hasAssets())) tagAssets();
3299
3377
  }
3300
3378
  function setReferences(referenceList, componentIds) {
3301
3379
  if (componentIds) ownComponents = new Set(componentIds);
@@ -3321,6 +3399,7 @@ function createVisualEditor(opts) {
3321
3399
  setCurrentItem,
3322
3400
  setReferences,
3323
3401
  setAssets,
3402
+ itemSaved,
3324
3403
  setLocale,
3325
3404
  focusField,
3326
3405
  setNavigationGuard,
@@ -3407,7 +3486,8 @@ function createPreprPreview(opts = {}) {
3407
3486
  locale: contentLocale,
3408
3487
  block,
3409
3488
  entryId,
3410
- referenceField
3489
+ referenceField,
3490
+ linkHref
3411
3491
  }) => {
3412
3492
  const safeHref = href ? safeEditUrl(href) : null;
3413
3493
  if (href && !safeHref) {
@@ -3421,7 +3501,8 @@ function createPreprPreview(opts = {}) {
3421
3501
  locale: contentLocale,
3422
3502
  ...block !== void 0 && { block },
3423
3503
  ...entryId && { entryId },
3424
- ...referenceField && { referenceField }
3504
+ ...referenceField && { referenceField },
3505
+ ...linkHref && { linkHref }
3425
3506
  });
3426
3507
  } else if (safeHref) {
3427
3508
  window.open(safeHref, "_blank", "noopener,noreferrer");
@@ -3476,6 +3557,7 @@ function createPreprPreview(opts = {}) {
3476
3557
  },
3477
3558
  onAssets: (assets) => visualEditor?.setAssets(assets),
3478
3559
  onAllowNavigation: (href) => visualEditor?.allowNavigation(href),
3560
+ onItemChanged: () => visualEditor?.itemSaved(),
3479
3561
  onReferences: (referencedIds, componentIds) => visualEditor?.setReferences(referencedIds, componentIds),
3480
3562
  // The editor selecting a field scrolls the matching element into view. The
3481
3563
  // lookup relies on the attributes the stega scan writes, so it finds
@@ -3516,7 +3598,7 @@ function createPreprPreview(opts = {}) {
3516
3598
  }
3517
3599
 
3518
3600
  // src/version.ts
3519
- var VERSION = "0.5.0-rc.10";
3601
+ var VERSION = "0.5.0-rc.12";
3520
3602
 
3521
3603
  // src/core/pixel.ts
3522
3604
  var PIXEL_SCRIPT_URL = "https://cdn.tracking.prepr.io/js/prepr.min.js";
@@ -35,7 +35,7 @@ __export(sveltekit_exports, {
35
35
  module.exports = __toCommonJS(sveltekit_exports);
36
36
 
37
37
  // src/version.ts
38
- var VERSION = "0.5.0-rc.10";
38
+ var VERSION = "0.5.0-rc.12";
39
39
 
40
40
  // src/core/constants.ts
41
41
  var COOKIE_PREVIEW_MODE = "Prepr-Preview-Mode";
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var VERSION = "0.5.0-rc.10";
2
+ var VERSION = "0.5.0-rc.12";
3
3
 
4
4
  // src/core/constants.ts
5
5
  var COOKIE_PREVIEW_MODE = "Prepr-Preview-Mode";
@@ -160,8 +160,32 @@ function shapeLike(template, incoming) {
160
160
  }
161
161
  return out;
162
162
  }
163
+ function isAsset(value) {
164
+ return isPlainObject(value) && (typeof value.url === "string" || value.__typename === "Asset");
165
+ }
166
+ function hasId(list, entry) {
167
+ return entry._id !== void 0 && list.some((c) => isPlainObject(c) && c._id === entry._id);
168
+ }
169
+ function isAssetValue(value) {
170
+ return Array.isArray(value) ? value.length > 0 && value.every(isAsset) : isAsset(value);
171
+ }
172
+ function keepRemovedAssets(current, next) {
173
+ const listed = /* @__PURE__ */ new Set();
174
+ for (const entry of next) {
175
+ if (isPlainObject(entry) && entry._id !== void 0) listed.add(entry._id);
176
+ }
177
+ let out = next;
178
+ current.forEach((entry, position) => {
179
+ if (!isPlainObject(entry) || entry._id === void 0) return;
180
+ if (listed.has(entry._id)) return;
181
+ if (out === next) out = [...next];
182
+ out.splice(Math.min(position, out.length), 0, entry);
183
+ });
184
+ return out;
185
+ }
163
186
  function mergeStructured(current, incoming) {
164
187
  if (incoming === null || Array.isArray(incoming) && !incoming.length) {
188
+ if (isAssetValue(current)) return current;
165
189
  if (Array.isArray(current)) return current.length ? [] : current;
166
190
  return null;
167
191
  }
@@ -169,13 +193,18 @@ function mergeStructured(current, incoming) {
169
193
  if (current === null || current === void 0) return incoming;
170
194
  if (Array.isArray(current)) {
171
195
  const template = current.find(isPlainObject);
172
- return list.map((entry) => {
196
+ const next = list.map((entry) => {
173
197
  const match = current.find(
174
198
  (c) => isPlainObject(c) && c._id !== void 0 && c._id === entry._id
175
199
  );
176
200
  if (isPlainObject(match)) return mergeObject(match, entry);
177
201
  return isPlainObject(template) ? shapeLike(template, entry) : entry;
178
202
  });
203
+ if (!isAssetValue(current) || list.some((entry) => !hasId(current, entry))) {
204
+ return next;
205
+ }
206
+ const kept = keepRemovedAssets(current, next);
207
+ return kept.length === current.length && kept.every((entry, i) => entry === current[i]) ? current : kept;
179
208
  }
180
209
  if (isPlainObject(current)) return mergeObject(current, list[0]);
181
210
  return current;