@phtngyn/cms 0.2.0-beta.4 → 0.2.0-beta.6

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 (116) hide show
  1. package/README.md +31 -13
  2. package/package.json +7 -7
  3. package/src/ApplicationTypes.d.ts +3 -3
  4. package/src/Authoring.d.ts +11 -3
  5. package/src/{Configuration-BU-UyGoH.js → Configuration-JQ1qyaU0.js} +347 -189
  6. package/src/Configuration-JQ1qyaU0.js.map +1 -0
  7. package/src/Configuration.d.ts +1 -1
  8. package/src/Content.d.ts +7 -6
  9. package/src/Content.js +106 -47
  10. package/src/Content.js.map +1 -1
  11. package/src/ContentTransfer-BxwswWn3.js +86 -0
  12. package/src/ContentTransfer-BxwswWn3.js.map +1 -0
  13. package/src/{ContentTransfer-CRQet3QJ.d.ts → ContentTransfer-Z9FHyLez.d.ts} +33 -9
  14. package/src/{DocumentEngine-ClhFXW9M.js → DocumentEngine-j6Vi7nQo.js} +279 -178
  15. package/src/DocumentEngine-j6Vi7nQo.js.map +1 -0
  16. package/src/DocumentModel.d.ts +9 -2
  17. package/src/DocumentModel.js +17 -4
  18. package/src/DocumentModel.js.map +1 -1
  19. package/src/{Forms-DoB56hfe.js → Forms-CX-hwmJF.js} +18 -11
  20. package/src/Forms-CX-hwmJF.js.map +1 -0
  21. package/src/{Forms-BbkE7QfC.js → Forms-CfNPTiCr.js} +2 -2
  22. package/src/{Forms-BbkE7QfC.js.map → Forms-CfNPTiCr.js.map} +1 -1
  23. package/src/Media-j1bTritO.js +112 -0
  24. package/src/Media-j1bTritO.js.map +1 -0
  25. package/src/Module.js +0 -4
  26. package/src/Module.js.map +1 -1
  27. package/src/References-CCPDAPve.js +45 -0
  28. package/src/References-CCPDAPve.js.map +1 -0
  29. package/src/{S3MediaObjects-CR3p1WTg.js → S3MediaObjects-BRuPQbxl.js} +454 -516
  30. package/src/S3MediaObjects-BRuPQbxl.js.map +1 -0
  31. package/src/cli/content.js +6 -5
  32. package/src/cli/content.js.map +1 -1
  33. package/src/cli.js +16 -3
  34. package/src/cli.js.map +1 -1
  35. package/src/{connection-D5S9Iph-.js → connection-BP80q4d-.js} +2 -2
  36. package/src/{connection-D5S9Iph-.js.map → connection-BP80q4d-.js.map} +1 -1
  37. package/src/{dev-lifecycle-D3f9-upa.js → dev-lifecycle-CExksm2R.js} +27 -7
  38. package/src/dev-lifecycle-CExksm2R.js.map +1 -0
  39. package/src/{installation-fZtNxtoW.js → installation-BoG8E3nJ.js} +7982 -7155
  40. package/src/installation-BoG8E3nJ.js.map +1 -0
  41. package/src/internal/authoring-catalog.js +2 -2
  42. package/src/internal/authoring-compiler.js +35 -9
  43. package/src/internal/authoring-compiler.js.map +1 -1
  44. package/src/internal/authoring-metadata.js +14 -5
  45. package/src/internal/authoring-metadata.js.map +1 -1
  46. package/src/internal/configuration.js +27 -3
  47. package/src/internal/configuration.js.map +1 -1
  48. package/src/internal/dev-lifecycle.js +2 -2
  49. package/src/internal/document-model.js +134 -43
  50. package/src/internal/document-model.js.map +1 -1
  51. package/src/internal/field-metadata.js +2 -1
  52. package/src/internal/field-metadata.js.map +1 -1
  53. package/src/internal/form-capabilities.js +1 -1
  54. package/src/{location-CPX7oj0o.js → location-CMNGO3lU.js} +2 -2
  55. package/src/{location-CPX7oj0o.js.map → location-CMNGO3lU.js.map} +1 -1
  56. package/src/{metadata-BEZPWh-3.js → metadata-UzZmjwxM.js} +2 -2
  57. package/src/{metadata-BEZPWh-3.js.map → metadata-UzZmjwxM.js.map} +1 -1
  58. package/src/{models-C9kF9JlQ.js → models-5j67LMqW.js} +4 -4
  59. package/src/{models-C9kF9JlQ.js.map → models-5j67LMqW.js.map} +1 -1
  60. package/src/{models-ChRxBChy.js → models-VmGmG19-.js} +42 -130
  61. package/src/models-VmGmG19-.js.map +1 -0
  62. package/src/runtime/delivery/DocumentRenderer.js +5 -1
  63. package/src/runtime/delivery/DocumentRenderer.js.map +1 -1
  64. package/src/runtime/delivery/context.js +1 -1
  65. package/src/runtime/delivery/plugin.js +2 -2
  66. package/src/runtime/delivery/preview.d.ts +1 -1
  67. package/src/runtime/delivery/types.d.ts +1 -1
  68. package/src/runtime/delivery/useCmsDocumentSeo.d.ts +1 -1
  69. package/src/runtime/forms/document.js +2 -2
  70. package/src/runtime/forms/useCmsForm.js +2 -2
  71. package/src/runtime/installation.js +1 -1
  72. package/src/runtime/plugin.js +1 -1
  73. package/src/runtime/preview/CmsStudioPreviewMarker.js +18 -4
  74. package/src/runtime/preview/CmsStudioPreviewMarker.js.map +1 -1
  75. package/src/runtime/preview/connection.js +1 -1
  76. package/src/runtime/preview/controller.js +65 -74
  77. package/src/runtime/preview/controller.js.map +1 -1
  78. package/src/runtime/preview/overlay.js +85 -28
  79. package/src/runtime/preview/overlay.js.map +1 -1
  80. package/src/runtime/preview/plugin.client.js +4 -4
  81. package/src/runtime/preview/preview.css +50 -36
  82. package/src/runtime/preview/protocol.d.ts +11 -5
  83. package/src/runtime/preview/protocol.js +17 -5
  84. package/src/runtime/preview/protocol.js.map +1 -1
  85. package/src/runtime/preview/regions.js +142 -0
  86. package/src/runtime/preview/regions.js.map +1 -0
  87. package/src/runtime/preview/store.js +1 -1
  88. package/src/runtime/sitemap.js +9 -10
  89. package/src/runtime/sitemap.js.map +1 -1
  90. package/src/{scalars-B2lgGUnj.js → scalars-CVZqT3L0.js} +2 -6
  91. package/src/scalars-CVZqT3L0.js.map +1 -0
  92. package/src/{store-CCsjHaZm.js → store-PKKB8xa2.js} +49 -66
  93. package/src/store-PKKB8xa2.js.map +1 -0
  94. package/src/{types-DutFRtaG.d.ts → types-DZ0IV-Ow.d.ts} +14 -3
  95. package/studio/CreateDocumentDialog-DqvusuGU.js +335 -0
  96. package/studio/{documentPlanning-CDb31mnl.js → SelectMenu-BgikeouH.js} +16292 -12303
  97. package/studio/{editor-Bzlh3zXt.js → editor-P2rB7Csp.js} +11228 -11927
  98. package/studio/host.css +1 -1
  99. package/studio/host.js +28987 -34295
  100. package/studio/rolldown-runtime-Dy4uBu1J.js +11 -0
  101. package/src/Configuration-BU-UyGoH.js.map +0 -1
  102. package/src/ContentTransfer-BhF9G56s.js +0 -57
  103. package/src/ContentTransfer-BhF9G56s.js.map +0 -1
  104. package/src/DocumentEngine-ClhFXW9M.js.map +0 -1
  105. package/src/Forms-DoB56hfe.js.map +0 -1
  106. package/src/S3MediaObjects-CR3p1WTg.js.map +0 -1
  107. package/src/dev-lifecycle-D3f9-upa.js.map +0 -1
  108. package/src/installation-fZtNxtoW.js.map +0 -1
  109. package/src/models-ChRxBChy.js.map +0 -1
  110. package/src/runtime/preview/targets.js +0 -101
  111. package/src/runtime/preview/targets.js.map +0 -1
  112. package/src/scalars-B2lgGUnj.js.map +0 -1
  113. package/src/store-CCsjHaZm.js.map +0 -1
  114. package/studio/CreateDocumentDialog-DJ8c0WZb.js +0 -256
  115. package/studio/dom-to-image-more.min-D6jGGdrW.js +0 -877
  116. package/studio/rolldown-runtime-B0aSnxlc.js +0 -20
@@ -1,38 +1,22 @@
1
- import { a as EditorNodeId, l as RevisionNumber, r as DocumentId } from "../../scalars-B2lgGUnj.js";
2
- import { Et as RequestId } from "../../models-ChRxBChy.js";
3
- import { r as PreviewIntentId } from "../../models-C9kF9JlQ.js";
1
+ import { a as EditorNodeId, c as RevisionNumber, r as DocumentId } from "../../scalars-CVZqT3L0.js";
2
+ import { st as RequestId } from "../../models-VmGmG19-.js";
3
+ import { r as PreviewIntentId } from "../../models-5j67LMqW.js";
4
4
  import { mountPreviewOverlay } from "./overlay.js";
5
- import { clearPreviewTargets, decoratePreviewTargets, findPreviewNodeId, nearestPreviewElement, resolvePreviewTarget } from "./targets.js";
6
- import { Crypto, Effect, PlatformError, Schema } from "effect";
5
+ import { mountPreviewRegions } from "./regions.js";
6
+ import { Schema } from "effect";
7
7
 
8
8
  //#region src/runtime/preview/controller.ts
9
- const browserCrypto = Crypto.make({
10
- digest: (algorithm, data) => Effect.tryPromise({
11
- catch: (cause) => PlatformError.systemError({
12
- _tag: unknownSystemErrorTag,
13
- cause,
14
- description: "Could not compute digest",
15
- method: "digest",
16
- module: "Crypto"
17
- }),
18
- try: () => globalThis.crypto.subtle.digest(algorithm, Uint8Array.from(data)).then((digest) => new Uint8Array(digest))
19
- }),
20
- randomBytes: (size) => globalThis.crypto.getRandomValues(new Uint8Array(size))
21
- });
22
- const unknownSystemErrorTag = "Unknown";
23
- const randomUuid = () => Effect.runSync(browserCrypto.randomUUIDv4.pipe(Effect.orDie));
24
9
  function mountPreviewController(options) {
25
10
  const { bridge, root, store, view } = options;
26
11
  const selection = mountPreviewOverlay(root, view);
27
12
  const hover = mountPreviewOverlay(root, view, "hover");
13
+ const regions = mountPreviewRegions(root, view);
28
14
  let active = false;
29
15
  let desiredFocus;
30
16
  let fallbacks = {};
31
- let hoveredElements = [];
32
17
  let mode = "edit";
33
18
  let renderSequence = 0;
34
19
  let revision = RevisionNumber.make(0);
35
- let selectedElements = [];
36
20
  let lastApplied = "";
37
21
  let revealedIntentId = "";
38
22
  const sendManifest = () => {
@@ -43,9 +27,6 @@ function mountPreviewController(options) {
43
27
  });
44
28
  };
45
29
  const stopManifest = store.subscribe(sendManifest);
46
- const clearElements = (elements, attribute) => {
47
- for (const element of elements) element.removeAttribute(attribute);
48
- };
49
30
  const sendApplied = (result) => {
50
31
  if (!desiredFocus) return;
51
32
  const signature = `${desiredFocus.id}:${result}`;
@@ -64,54 +45,54 @@ function mountPreviewController(options) {
64
45
  };
65
46
  const documentRoot = (documentId) => [...root.querySelectorAll("[data-cms-document]")].find((element) => element.getAttribute("data-cms-document") === documentId) ?? root;
66
47
  const applyFocus = (reveal = false) => {
67
- clearElements(selectedElements, "data-cms-selected");
68
- selectedElements = [];
69
- if (!active || mode !== "edit") {
70
- selection.set([]);
48
+ if (!active && mode === "edit") {
49
+ selection.setPending(true);
50
+ return;
51
+ }
52
+ selection.setPending(false);
53
+ if (mode !== "edit") {
54
+ selection.show([]);
71
55
  return;
72
56
  }
73
57
  const intent = desiredFocus;
74
58
  const nodeId = intent?.nodeId;
75
59
  if (!intent || !nodeId) {
76
- selection.set([]);
60
+ selection.show([]);
77
61
  sendApplied("exact");
78
62
  return;
79
63
  }
80
64
  const scope = documentRoot(intent.documentId);
81
- decoratePreviewTargets(scope);
82
- const target = resolvePreviewTarget(scope, nodeId, fallbacks);
83
- selectedElements = target.elements;
84
- selection.set(selectedElements);
85
- for (const element of selectedElements) element.setAttribute("data-cms-selected", "");
86
- const result = !target.resolvedNodeId ? "missing" : target.resolvedNodeId === nodeId ? "exact" : "fallback";
65
+ const target = regions.locate(nodeId, fallbacks, scope);
66
+ const result = !target.nodeId ? "missing" : target.nodeId === nodeId ? "exact" : "fallback";
67
+ selection.show(target.geometries, {
68
+ label: intent.label,
69
+ match: result === "missing" ? void 0 : result,
70
+ reveal
71
+ });
87
72
  if (reveal && result !== "missing" && revealedIntentId !== intent.id) {
88
73
  revealedIntentId = intent.id;
89
- revealElement(nearestPreviewElement(selectedElements), view);
74
+ revealElement(regions.nearest(target.geometries), view);
90
75
  }
91
76
  sendApplied(result);
92
77
  };
93
- const hoverElements = (target) => {
94
- clearElements(hoveredElements, "data-cms-hovered");
95
- hoveredElements = [];
96
- if (!active || mode !== "edit" || !target) {
97
- hover.set([]);
78
+ const hoverElements = (target, x, y) => {
79
+ if (!active || mode !== "edit" || !target || x === void 0 || y === void 0) {
80
+ hover.show([]);
98
81
  return;
99
82
  }
100
83
  const scope = target.closest("[data-cms-document]") ?? root;
101
- const nodeId = findPreviewNodeId(scope, target);
84
+ const nodeId = regions.hitTest(x, y, scope);
102
85
  if (!nodeId) {
103
- hover.set([]);
86
+ hover.show([]);
104
87
  return;
105
88
  }
106
- hoveredElements = resolvePreviewTarget(scope, nodeId, fallbacks).elements;
107
- for (const element of hoveredElements) element.setAttribute("data-cms-hovered", "");
108
- hover.set(hoveredElements);
89
+ hover.show(regions.locate(nodeId, fallbacks, scope).geometries);
109
90
  };
110
91
  const settleRender = (sequence) => {
111
92
  view.requestAnimationFrame(() => view.requestAnimationFrame(() => {
112
93
  if (sequence !== renderSequence) return;
113
94
  active = true;
114
- decoratePreviewTargets(root);
95
+ regions.decorate();
115
96
  bridge.send({
116
97
  revision,
117
98
  type: "preview:rendered"
@@ -122,8 +103,8 @@ function mountPreviewController(options) {
122
103
  const stopBridge = bridge.subscribe((message) => {
123
104
  if (message.type === "studio:connect") {
124
105
  active = true;
125
- clearPreviewTargets(root);
126
- decoratePreviewTargets(root);
106
+ regions.clear();
107
+ regions.decorate();
127
108
  sendManifest();
128
109
  return;
129
110
  }
@@ -131,11 +112,11 @@ function mountPreviewController(options) {
131
112
  mode = message.mode;
132
113
  if (mode === "browse") {
133
114
  hoverElements();
134
- clearElements(selectedElements, "data-cms-selected");
135
- selectedElements = [];
136
- selection.set([]);
115
+ selection.setPending(false);
116
+ selection.show([]);
117
+ regions.clear();
137
118
  } else {
138
- decoratePreviewTargets(root);
119
+ regions.decorate();
139
120
  applyFocus();
140
121
  }
141
122
  return;
@@ -153,6 +134,7 @@ function mountPreviewController(options) {
153
134
  if (message.type !== "studio:patch" || message.revision < revision) return;
154
135
  revision = message.revision;
155
136
  active = false;
137
+ selection.setPending(true);
156
138
  desiredFocus = message.focus;
157
139
  lastApplied = "";
158
140
  const sequence = ++renderSequence;
@@ -184,12 +166,11 @@ function mountPreviewController(options) {
184
166
  if (!target || !scope || !Schema.is(DocumentId)(documentIdValue)) return;
185
167
  event.preventDefault();
186
168
  event.stopPropagation();
187
- const nodeIdValue = findPreviewNodeId(scope, target);
169
+ const nodeIdValue = regions.hitTest(event.clientX, event.clientY, scope);
188
170
  const nodeId = Schema.is(EditorNodeId)(nodeIdValue) ? nodeIdValue : void 0;
189
- const field = target.closest("[data-cms-field]")?.getAttribute("data-cms-field") || void 0;
190
171
  const activationFocus = {
191
172
  documentId: documentIdValue,
192
- id: PreviewIntentId.make(randomUuid()),
173
+ id: PreviewIntentId.make(globalThis.crypto.randomUUID()),
193
174
  origin: "preview",
194
175
  reveal: false,
195
176
  revision
@@ -201,29 +182,38 @@ function mountPreviewController(options) {
201
182
  lastApplied = "";
202
183
  const activation = {
203
184
  documentId: desiredFocus.documentId,
204
- requestId: RequestId.make(randomUuid()),
185
+ requestId: RequestId.make(globalThis.crypto.randomUUID()),
205
186
  revision,
206
187
  type: "preview:activate"
207
188
  };
208
- if (field && nodeId) bridge.send({
209
- ...activation,
210
- field,
211
- nodeId
212
- });
213
- else if (field) bridge.send({
214
- ...activation,
215
- field
216
- });
217
- else if (nodeId) bridge.send({
189
+ if (nodeId) bridge.send({
218
190
  ...activation,
219
191
  nodeId
220
192
  });
221
193
  else bridge.send(activation);
222
194
  applyFocus();
223
195
  };
224
- const handlePointerOver = (event) => hoverElements(event.target instanceof Element ? event.target : void 0);
196
+ let hoverFrame = 0;
197
+ let hoverPointer;
198
+ const handlePointerMove = (event) => {
199
+ if (!(event.target instanceof Element)) return;
200
+ hoverPointer = {
201
+ target: event.target,
202
+ x: event.clientX,
203
+ y: event.clientY
204
+ };
205
+ if (hoverFrame) return;
206
+ hoverFrame = view.requestAnimationFrame(() => {
207
+ hoverFrame = 0;
208
+ const pointer = hoverPointer;
209
+ if (pointer) hoverElements(pointer.target, pointer.x, pointer.y);
210
+ });
211
+ };
225
212
  const handlePointerOut = (event) => {
226
- if (!(event.relatedTarget instanceof Element)) hoverElements();
213
+ if (!(event.relatedTarget instanceof Element)) {
214
+ hoverPointer = void 0;
215
+ hoverElements();
216
+ }
227
217
  };
228
218
  let mutationQueued = false;
229
219
  const observer = new MutationObserver((records) => {
@@ -234,12 +224,12 @@ function mountPreviewController(options) {
234
224
  mutationQueued = false;
235
225
  if (!active) return;
236
226
  hoverElements();
237
- decoratePreviewTargets(root);
227
+ regions.decorate();
238
228
  if (desiredFocus) applyFocus(false);
239
229
  });
240
230
  });
241
231
  root.addEventListener("click", handleClick, true);
242
- root.addEventListener("pointerover", handlePointerOver, true);
232
+ root.addEventListener("pointermove", handlePointerMove, true);
243
233
  root.addEventListener("pointerout", handlePointerOut, true);
244
234
  observer.observe(root.documentElement, {
245
235
  childList: true,
@@ -249,12 +239,13 @@ function mountPreviewController(options) {
249
239
  stopBridge();
250
240
  stopManifest();
251
241
  observer.disconnect();
242
+ if (hoverFrame) view.cancelAnimationFrame(hoverFrame);
252
243
  selection.destroy();
253
244
  hover.destroy();
254
245
  root.removeEventListener("click", handleClick, true);
255
- root.removeEventListener("pointerover", handlePointerOver, true);
246
+ root.removeEventListener("pointermove", handlePointerMove, true);
256
247
  root.removeEventListener("pointerout", handlePointerOut, true);
257
- clearPreviewTargets(root);
248
+ regions.clear();
258
249
  };
259
250
  }
260
251
  function revealElement(element, view) {
@@ -1 +1 @@
1
- {"version":3,"file":"controller.js","names":[],"sources":["../../../../../src/runtime/preview/controller.ts"],"sourcesContent":["import type { DocumentPreviewFocusIntent, PreviewSiteBridge, StudioPreviewPayload } from '@internal/protocol/Preview'\nimport type { StudioPreviewStore } from './store'\nimport { DocumentId, EditorNodeId, RevisionNumber } from '@internal/protocol/Documents'\nimport { RequestId } from '@internal/protocol/Identity'\nimport { PreviewIntentId } from '@internal/protocol/Preview'\nimport { Crypto, Effect, PlatformError, Schema } from 'effect'\nimport { mountPreviewOverlay } from './overlay'\nimport {\n clearPreviewTargets,\n decoratePreviewTargets,\n findPreviewNodeId,\n nearestPreviewElement,\n resolvePreviewTarget,\n} from './targets'\n\nconst browserCrypto = Crypto.make({\n digest: (algorithm, data) =>\n Effect.tryPromise({\n catch: (cause) =>\n PlatformError.systemError({\n _tag: unknownSystemErrorTag,\n cause,\n description: 'Could not compute digest',\n method: 'digest',\n module: 'Crypto',\n }),\n try: () =>\n globalThis.crypto.subtle.digest(algorithm, Uint8Array.from(data)).then((digest) => new Uint8Array(digest)),\n }),\n randomBytes: (size) => globalThis.crypto.getRandomValues(new Uint8Array(size)),\n})\n\nconst unknownSystemErrorTag: PlatformError.SystemErrorTag = 'Unknown'\n\nconst randomUuid = (): string => Effect.runSync(browserCrypto.randomUUIDv4.pipe(Effect.orDie))\n\nexport function mountPreviewController(options: {\n readonly bridge: PreviewSiteBridge\n readonly navigate: (path: string) => Promise<void> | void\n readonly onPatchError: (message: string | null) => void\n readonly root: Document\n readonly store: StudioPreviewStore\n readonly view: Window\n}): () => void {\n type PreviewFallbacks = Extract<StudioPreviewPayload, { readonly type: 'studio:patch' }>['fallbacks']\n\n const { bridge, root, store, view } = options\n const selection = mountPreviewOverlay(root, view)\n const hover = mountPreviewOverlay(root, view, 'hover')\n let active = false\n let desiredFocus: DocumentPreviewFocusIntent | undefined\n let fallbacks: PreviewFallbacks = {}\n let hoveredElements: ReadonlyArray<Element> = []\n let mode: 'browse' | 'edit' = 'edit'\n let renderSequence = 0\n let revision = RevisionNumber.make(0)\n let selectedElements: ReadonlyArray<Element> = []\n let lastApplied = ''\n let revealedIntentId = ''\n\n const sendManifest = (): void => {\n bridge.send({ manifest: store.manifest(), revision, type: 'preview:manifest' })\n }\n\n const stopManifest = store.subscribe(sendManifest)\n\n const clearElements = (elements: ReadonlyArray<Element>, attribute: string): void => {\n for (const element of elements) element.removeAttribute(attribute)\n }\n\n const sendApplied = (result: 'exact' | 'fallback' | 'missing'): void => {\n if (!desiredFocus) return\n const signature = `${desiredFocus.id}:${result}`\n\n if (signature === lastApplied) return\n lastApplied = signature\n\n const applied = {\n intentId: desiredFocus.id,\n result,\n revision,\n type: 'preview:applied',\n } as const\n\n bridge.send(desiredFocus.nodeId ? { ...applied, nodeId: desiredFocus.nodeId } : applied)\n }\n\n const documentRoot = (documentId: string): ParentNode =>\n [...root.querySelectorAll('[data-cms-document]')].find(\n (element) => element.getAttribute('data-cms-document') === documentId,\n ) ?? root\n\n const applyFocus = (reveal = false): void => {\n clearElements(selectedElements, 'data-cms-selected')\n selectedElements = []\n\n if (!active || mode !== 'edit') {\n selection.set([])\n\n return\n }\n\n const intent = desiredFocus\n const nodeId = intent?.nodeId\n\n if (!intent || !nodeId) {\n selection.set([])\n sendApplied('exact')\n\n return\n }\n\n const scope = documentRoot(intent.documentId)\n decoratePreviewTargets(scope)\n const target = resolvePreviewTarget(scope, nodeId, fallbacks)\n selectedElements = target.elements\n selection.set(selectedElements)\n\n for (const element of selectedElements) element.setAttribute('data-cms-selected', '')\n const result = !target.resolvedNodeId ? 'missing' : target.resolvedNodeId === nodeId ? 'exact' : 'fallback'\n\n if (reveal && result !== 'missing' && revealedIntentId !== intent.id) {\n revealedIntentId = intent.id\n revealElement(nearestPreviewElement(selectedElements), view)\n }\n\n sendApplied(result)\n }\n\n const hoverElements = (target?: Element): void => {\n clearElements(hoveredElements, 'data-cms-hovered')\n hoveredElements = []\n\n if (!active || mode !== 'edit' || !target) {\n hover.set([])\n\n return\n }\n\n const scope = target.closest('[data-cms-document]') ?? root\n const nodeId = findPreviewNodeId(scope, target)\n\n if (!nodeId) {\n hover.set([])\n\n return\n }\n\n hoveredElements = resolvePreviewTarget(scope, nodeId, fallbacks).elements\n\n for (const element of hoveredElements) element.setAttribute('data-cms-hovered', '')\n hover.set(hoveredElements)\n }\n\n const settleRender = (sequence: number): void => {\n view.requestAnimationFrame(() =>\n view.requestAnimationFrame(() => {\n if (sequence !== renderSequence) return\n active = true\n decoratePreviewTargets(root)\n bridge.send({ revision, type: 'preview:rendered' })\n applyFocus(desiredFocus?.reveal)\n }),\n )\n }\n\n const stopBridge = bridge.subscribe((message) => {\n if (message.type === 'studio:connect') {\n active = true\n clearPreviewTargets(root)\n decoratePreviewTargets(root)\n sendManifest()\n\n return\n }\n\n if (message.type === 'studio:mode') {\n mode = message.mode\n\n if (mode === 'browse') {\n hoverElements()\n clearElements(selectedElements, 'data-cms-selected')\n selectedElements = []\n selection.set([])\n } else {\n decoratePreviewTargets(root)\n applyFocus()\n }\n\n return\n }\n\n if (message.type === 'studio:navigate') {\n void options.navigate(message.path)\n\n return\n }\n\n if (message.type === 'studio:focus') {\n if (message.focus.revision !== revision) return\n desiredFocus = message.focus\n applyFocus(desiredFocus.reveal)\n\n return\n }\n\n if (message.type !== 'studio:patch' || message.revision < revision) return\n revision = message.revision\n active = false\n desiredFocus = message.focus\n lastApplied = ''\n const sequence = ++renderSequence\n void store.patch(message.document).then(\n (localFallbacks) => {\n if (sequence !== renderSequence) return\n options.onPatchError(null)\n fallbacks = Object.assign({}, message.fallbacks, localFallbacks)\n settleRender(sequence)\n },\n () => {\n if (sequence !== renderSequence) return\n options.onPatchError('Preview could not apply this update.')\n },\n )\n })\n\n const handleClick = (event: MouseEvent) => {\n if (!active) return\n const target = event.target instanceof Element ? event.target : null\n\n if (mode === 'browse') {\n const link = target?.closest('a[href]')\n\n if (\n !(link instanceof HTMLAnchorElement) ||\n link.target ||\n link.hasAttribute('download') ||\n event.metaKey ||\n event.ctrlKey ||\n event.shiftKey ||\n event.altKey\n )\n return\n const destination = new URL(link.href, view.location.href)\n\n if (destination.origin !== view.location.origin) return\n event.preventDefault()\n event.stopPropagation()\n void options.navigate(`${destination.pathname}${destination.search}${destination.hash}`)\n\n return\n }\n\n const scope = target?.closest('[data-cms-document]')\n const documentIdValue = scope?.getAttribute('data-cms-document')\n\n if (!target || !scope || !Schema.is(DocumentId)(documentIdValue)) return\n event.preventDefault()\n event.stopPropagation()\n const nodeIdValue = findPreviewNodeId(scope, target)\n const nodeId = Schema.is(EditorNodeId)(nodeIdValue) ? nodeIdValue : undefined\n const field = target.closest('[data-cms-field]')?.getAttribute('data-cms-field') || undefined\n\n const activationFocus = {\n documentId: documentIdValue,\n id: PreviewIntentId.make(randomUuid()),\n origin: 'preview',\n reveal: false,\n revision,\n } as const\n\n desiredFocus = nodeId ? { ...activationFocus, nodeId } : activationFocus\n lastApplied = ''\n\n const activation = {\n documentId: desiredFocus.documentId,\n requestId: RequestId.make(randomUuid()),\n revision,\n type: 'preview:activate',\n } as const\n\n if (field && nodeId) bridge.send({ ...activation, field, nodeId })\n else if (field) bridge.send({ ...activation, field })\n else if (nodeId) bridge.send({ ...activation, nodeId })\n else bridge.send(activation)\n applyFocus()\n }\n\n const handlePointerOver = (event: PointerEvent) =>\n hoverElements(event.target instanceof Element ? event.target : undefined)\n\n const handlePointerOut = (event: PointerEvent) => {\n if (!(event.relatedTarget instanceof Element)) hoverElements()\n }\n\n let mutationQueued = false\n\n const observer = new MutationObserver((records) => {\n if (\n records.every((record) => record.target instanceof Element && record.target.closest('[data-cms-preview-overlay]'))\n )\n return\n\n if (mutationQueued) return\n mutationQueued = true\n queueMicrotask(() => {\n mutationQueued = false\n\n if (!active) return\n hoverElements()\n decoratePreviewTargets(root)\n\n if (desiredFocus) applyFocus(false)\n })\n })\n\n root.addEventListener('click', handleClick, true)\n root.addEventListener('pointerover', handlePointerOver, true)\n root.addEventListener('pointerout', handlePointerOut, true)\n observer.observe(root.documentElement, { childList: true, subtree: true })\n\n return () => {\n stopBridge()\n stopManifest()\n observer.disconnect()\n selection.destroy()\n hover.destroy()\n root.removeEventListener('click', handleClick, true)\n root.removeEventListener('pointerover', handlePointerOver, true)\n root.removeEventListener('pointerout', handlePointerOut, true)\n clearPreviewTargets(root)\n }\n}\n\nfunction revealElement(element: Element | undefined, view: Window): void {\n if (!element) return\n const rect = element.getBoundingClientRect()\n\n if (rect.bottom > 0 && rect.top < view.innerHeight && rect.right > 0 && rect.left < view.innerWidth) return\n element.scrollIntoView({\n behavior: 'instant',\n block: 'center',\n inline: 'nearest',\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"controller.js","names":[],"sources":["../../../../../src/runtime/preview/controller.ts"],"sourcesContent":["import type { DocumentPreviewFocusIntent, PreviewSiteBridge, StudioPreviewPayload } from '@internal/protocol/Preview'\nimport type { StudioPreviewStore } from './store'\nimport { DocumentId, EditorNodeId, RevisionNumber } from '@internal/protocol/Documents'\nimport { RequestId } from '@internal/protocol/Identity'\nimport { PreviewIntentId } from '@internal/protocol/Preview'\nimport { Schema } from 'effect'\nimport { mountPreviewOverlay } from './overlay'\nimport { mountPreviewRegions } from './regions'\n\nexport function mountPreviewController(options: {\n readonly bridge: PreviewSiteBridge\n readonly navigate: (path: string) => Promise<void> | void\n readonly onPatchError: (message: string | null) => void\n readonly root: Document\n readonly store: StudioPreviewStore\n readonly view: Window\n}): () => void {\n type PreviewFallbacks = Extract<StudioPreviewPayload, { readonly type: 'studio:patch' }>['fallbacks']\n\n const { bridge, root, store, view } = options\n const selection = mountPreviewOverlay(root, view)\n const hover = mountPreviewOverlay(root, view, 'hover')\n const regions = mountPreviewRegions(root, view)\n let active = false\n let desiredFocus: DocumentPreviewFocusIntent | undefined\n let fallbacks: PreviewFallbacks = {}\n let mode: 'browse' | 'edit' = 'edit'\n let renderSequence = 0\n let revision = RevisionNumber.make(0)\n let lastApplied = ''\n let revealedIntentId = ''\n\n const sendManifest = (): void => {\n bridge.send({ manifest: store.manifest(), revision, type: 'preview:manifest' })\n }\n\n const stopManifest = store.subscribe(sendManifest)\n\n const sendApplied = (result: 'exact' | 'fallback' | 'missing'): void => {\n if (!desiredFocus) return\n const signature = `${desiredFocus.id}:${result}`\n\n if (signature === lastApplied) return\n lastApplied = signature\n\n const applied = {\n intentId: desiredFocus.id,\n result,\n revision,\n type: 'preview:applied',\n } as const\n\n bridge.send(desiredFocus.nodeId ? { ...applied, nodeId: desiredFocus.nodeId } : applied)\n }\n\n const documentRoot = (documentId: string): ParentNode =>\n [...root.querySelectorAll('[data-cms-document]')].find(\n (element) => element.getAttribute('data-cms-document') === documentId,\n ) ?? root\n\n const applyFocus = (reveal = false): void => {\n if (!active && mode === 'edit') {\n selection.setPending(true)\n\n return\n }\n\n selection.setPending(false)\n\n if (mode !== 'edit') {\n selection.show([])\n\n return\n }\n\n const intent = desiredFocus\n const nodeId = intent?.nodeId\n\n if (!intent || !nodeId) {\n selection.show([])\n sendApplied('exact')\n\n return\n }\n\n const scope = documentRoot(intent.documentId)\n const target = regions.locate(nodeId, fallbacks, scope)\n const result = !target.nodeId ? 'missing' : target.nodeId === nodeId ? 'exact' : 'fallback'\n\n selection.show(target.geometries, {\n label: intent.label,\n match: result === 'missing' ? undefined : result,\n reveal,\n })\n\n if (reveal && result !== 'missing' && revealedIntentId !== intent.id) {\n revealedIntentId = intent.id\n revealElement(regions.nearest(target.geometries), view)\n }\n\n sendApplied(result)\n }\n\n const hoverElements = (target?: Element, x?: number, y?: number): void => {\n if (!active || mode !== 'edit' || !target || x === undefined || y === undefined) {\n hover.show([])\n\n return\n }\n\n const scope = target.closest('[data-cms-document]') ?? root\n const nodeId = regions.hitTest(x, y, scope)\n\n if (!nodeId) {\n hover.show([])\n\n return\n }\n\n hover.show(regions.locate(nodeId, fallbacks, scope).geometries)\n }\n\n const settleRender = (sequence: number): void => {\n view.requestAnimationFrame(() =>\n view.requestAnimationFrame(() => {\n if (sequence !== renderSequence) return\n active = true\n regions.decorate()\n bridge.send({ revision, type: 'preview:rendered' })\n applyFocus(desiredFocus?.reveal)\n }),\n )\n }\n\n const stopBridge = bridge.subscribe((message) => {\n if (message.type === 'studio:connect') {\n active = true\n regions.clear()\n regions.decorate()\n sendManifest()\n\n return\n }\n\n if (message.type === 'studio:mode') {\n mode = message.mode\n\n if (mode === 'browse') {\n hoverElements()\n selection.setPending(false)\n selection.show([])\n regions.clear()\n } else {\n regions.decorate()\n applyFocus()\n }\n\n return\n }\n\n if (message.type === 'studio:navigate') {\n void options.navigate(message.path)\n\n return\n }\n\n if (message.type === 'studio:focus') {\n if (message.focus.revision !== revision) return\n desiredFocus = message.focus\n applyFocus(desiredFocus.reveal)\n\n return\n }\n\n if (message.type !== 'studio:patch' || message.revision < revision) return\n revision = message.revision\n active = false\n selection.setPending(true)\n desiredFocus = message.focus\n lastApplied = ''\n const sequence = ++renderSequence\n void store.patch(message.document).then(\n (localFallbacks) => {\n if (sequence !== renderSequence) return\n options.onPatchError(null)\n fallbacks = Object.assign({}, message.fallbacks, localFallbacks)\n settleRender(sequence)\n },\n () => {\n if (sequence !== renderSequence) return\n options.onPatchError('Preview could not apply this update.')\n },\n )\n })\n\n const handleClick = (event: MouseEvent) => {\n if (!active) return\n const target = event.target instanceof Element ? event.target : null\n\n if (mode === 'browse') {\n const link = target?.closest('a[href]')\n\n if (\n !(link instanceof HTMLAnchorElement) ||\n link.target ||\n link.hasAttribute('download') ||\n event.metaKey ||\n event.ctrlKey ||\n event.shiftKey ||\n event.altKey\n )\n return\n const destination = new URL(link.href, view.location.href)\n\n if (destination.origin !== view.location.origin) return\n event.preventDefault()\n event.stopPropagation()\n void options.navigate(`${destination.pathname}${destination.search}${destination.hash}`)\n\n return\n }\n\n const scope = target?.closest('[data-cms-document]')\n const documentIdValue = scope?.getAttribute('data-cms-document')\n\n if (!target || !scope || !Schema.is(DocumentId)(documentIdValue)) return\n event.preventDefault()\n event.stopPropagation()\n const nodeIdValue = regions.hitTest(event.clientX, event.clientY, scope)\n const nodeId = Schema.is(EditorNodeId)(nodeIdValue) ? nodeIdValue : undefined\n\n const activationFocus = {\n documentId: documentIdValue,\n // oxlint-disable-next-line effecttsgo/crypto-random-uuid -- Browser interactions use the native Web Crypto host API.\n id: PreviewIntentId.make(globalThis.crypto.randomUUID()),\n origin: 'preview',\n reveal: false,\n revision,\n } as const\n\n desiredFocus = nodeId ? { ...activationFocus, nodeId } : activationFocus\n lastApplied = ''\n\n const activation = {\n documentId: desiredFocus.documentId,\n // oxlint-disable-next-line effecttsgo/crypto-random-uuid -- Browser interactions use the native Web Crypto host API.\n requestId: RequestId.make(globalThis.crypto.randomUUID()),\n revision,\n type: 'preview:activate',\n } as const\n\n if (nodeId) bridge.send({ ...activation, nodeId })\n else bridge.send(activation)\n applyFocus()\n }\n\n let hoverFrame = 0\n let hoverPointer: { readonly target: Element; readonly x: number; readonly y: number } | undefined\n\n const handlePointerMove = (event: PointerEvent) => {\n if (!(event.target instanceof Element)) return\n hoverPointer = { target: event.target, x: event.clientX, y: event.clientY }\n\n if (hoverFrame) return\n hoverFrame = view.requestAnimationFrame(() => {\n hoverFrame = 0\n const pointer = hoverPointer\n\n if (pointer) hoverElements(pointer.target, pointer.x, pointer.y)\n })\n }\n\n const handlePointerOut = (event: PointerEvent) => {\n if (!(event.relatedTarget instanceof Element)) {\n hoverPointer = undefined\n hoverElements()\n }\n }\n\n let mutationQueued = false\n\n const observer = new MutationObserver((records) => {\n if (\n records.every((record) => record.target instanceof Element && record.target.closest('[data-cms-preview-overlay]'))\n )\n return\n\n if (mutationQueued) return\n mutationQueued = true\n queueMicrotask(() => {\n mutationQueued = false\n\n if (!active) return\n hoverElements()\n regions.decorate()\n\n if (desiredFocus) applyFocus(false)\n })\n })\n\n root.addEventListener('click', handleClick, true)\n root.addEventListener('pointermove', handlePointerMove, true)\n root.addEventListener('pointerout', handlePointerOut, true)\n observer.observe(root.documentElement, { childList: true, subtree: true })\n\n return () => {\n stopBridge()\n stopManifest()\n observer.disconnect()\n\n if (hoverFrame) view.cancelAnimationFrame(hoverFrame)\n selection.destroy()\n hover.destroy()\n root.removeEventListener('click', handleClick, true)\n root.removeEventListener('pointermove', handlePointerMove, true)\n root.removeEventListener('pointerout', handlePointerOut, true)\n regions.clear()\n }\n}\n\nfunction revealElement(element: Element | undefined, view: Window): void {\n if (!element) return\n const rect = element.getBoundingClientRect()\n\n if (rect.bottom > 0 && rect.top < view.innerHeight && rect.right > 0 && rect.left < view.innerWidth) return\n element.scrollIntoView({\n behavior: 'instant',\n block: 'center',\n inline: 'nearest',\n })\n}\n"],"mappings":"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"}
@@ -2,63 +2,120 @@
2
2
  /** Preview-only chrome stays separate from the website's layout and element styles. */
3
3
  function mountPreviewOverlay(root, view, kind = "selection") {
4
4
  const host = root.createElement("div");
5
+ const targetAttribute = kind === "selection" ? "data-cms-selected" : "data-cms-hovered";
5
6
  host.setAttribute("data-cms-preview-overlay", "");
6
7
  host.setAttribute("aria-hidden", "true");
7
8
  root.documentElement.append(host);
9
+ let markedElements = /* @__PURE__ */ new Set();
8
10
  let targets = [];
9
11
  let outlines = [];
10
12
  let frame = 0;
13
+ let isPending = false;
14
+ let revealFrame = 0;
15
+ const markTargets = (geometries) => {
16
+ for (const element of markedElements) element.removeAttribute(targetAttribute);
17
+ markedElements = new Set(geometries.flatMap((geometry) => geometry.element() ?? []));
18
+ for (const element of markedElements) element.setAttribute(targetAttribute, "");
19
+ };
20
+ const setLabelVisibility = (outline, visible, labelled) => {
21
+ const showLabel = kind === "selection" && visible && !labelled;
22
+ outline.toggleAttribute("data-cms-selection-label", showLabel);
23
+ return labelled || showLabel;
24
+ };
11
25
  const position = () => {
12
26
  let labelled = false;
13
- for (const [index, element] of targets.entries()) {
27
+ for (const [index, target] of targets.entries()) {
14
28
  const outline = outlines[index];
15
29
  if (!outline) continue;
16
- const rect = element.getBoundingClientRect();
17
- const visible = element.isConnected && (kind !== "hover" || !element.hasAttribute("data-cms-selected")) && rect.width > 0 && rect.height > 0 && rect.bottom > 0 && rect.top < view.innerHeight && rect.right > 0 && rect.left < view.innerWidth;
18
- outline.hidden = !visible;
19
- outline.toggleAttribute("data-cms-selection-label", kind === "selection" && visible && !labelled);
20
- if (!visible) continue;
21
- labelled = true;
30
+ const element = target.element();
31
+ const rect = geometryBounds(target);
32
+ if (!(rect !== void 0 && (kind !== "hover" || !element?.hasAttribute("data-cms-selected")) && rect.width > 0 && rect.height > 0 && rect.bottom > 0 && rect.top < view.innerHeight && rect.right > 0 && rect.left < view.innerWidth)) {
33
+ const retainPosition = isPending && outline.hasAttribute("data-cms-positioned");
34
+ outline.hidden = !retainPosition;
35
+ labelled = setLabelVisibility(outline, retainPosition, labelled);
36
+ continue;
37
+ }
38
+ outline.hidden = false;
39
+ outline.setAttribute("data-cms-positioned", "");
40
+ labelled = setLabelVisibility(outline, true, labelled);
22
41
  const left = Math.max(2, rect.left - 3);
23
42
  const right = Math.min(view.innerWidth - 2, rect.right + 3);
24
43
  outline.style.left = `${left}px`;
25
44
  outline.style.top = `${rect.top - 3}px`;
26
45
  outline.style.width = `${Math.max(0, right - left)}px`;
27
46
  outline.style.height = `${rect.height + 6}px`;
28
- outline.style.setProperty("--cms-label-left", `${Math.max(0, Math.min(view.innerWidth - 76, left) - left)}px`);
47
+ const labelWidth = outline.querySelector("[data-cms-selection-label-content]")?.offsetWidth ?? 0;
48
+ const labelLeft = Math.max(4 - left, Math.min(0, view.innerWidth - 4 - labelWidth - left));
49
+ outline.style.setProperty("--cms-label-left", `${labelLeft}px`);
29
50
  outline.style.setProperty("--cms-label-top", `${Math.max(4 - rect.top, rect.top >= 30 ? -26 : 4)}px`);
30
51
  }
31
52
  frame = targets.length ? view.requestAnimationFrame(position) : 0;
32
53
  };
33
54
  return {
34
- set(elements) {
35
- if (elements.length === targets.length && elements.every((element, index) => element === targets[index])) return;
36
- if (frame) view.cancelAnimationFrame(frame);
37
- targets = elements;
38
- outlines = elements.map(() => {
39
- const outline = root.createElement("div");
40
- outline.setAttribute(kind === "selection" ? "data-cms-selection-outline" : "data-cms-hover-outline", "");
41
- if (kind === "hover") for (const corner of [
42
- "top-left",
43
- "top-right",
44
- "bottom-right",
45
- "bottom-left"
46
- ]) {
47
- const mark = root.createElement("span");
48
- mark.setAttribute("data-cms-hover-corner", corner);
49
- outline.append(mark);
50
- }
51
- return outline;
55
+ setPending(value) {
56
+ isPending = value;
57
+ host.toggleAttribute("data-cms-pending", value);
58
+ },
59
+ show(geometries, presentation = {}) {
60
+ const targetsChanged = geometries.length !== targets.length || geometries.some((geometry, index) => geometry !== targets[index]);
61
+ const labelText = presentation.match === "fallback" ? `Nearest · ${presentation.label ?? "selected content"}` : presentation.label ?? "Selected";
62
+ if (targetsChanged) {
63
+ if (frame) view.cancelAnimationFrame(frame);
64
+ targets = geometries;
65
+ outlines = geometries.map(() => {
66
+ const outline = root.createElement("div");
67
+ outline.setAttribute(kind === "selection" ? "data-cms-selection-outline" : "data-cms-hover-outline", "");
68
+ if (kind === "selection") {
69
+ const label = root.createElement("span");
70
+ label.setAttribute("data-cms-selection-label-content", "");
71
+ outline.append(label);
72
+ }
73
+ return outline;
74
+ });
75
+ host.replaceChildren(...outlines);
76
+ position();
77
+ }
78
+ markTargets(targets);
79
+ for (const outline of outlines) {
80
+ const label = outline.querySelector("[data-cms-selection-label-content]");
81
+ if (label) label.textContent = labelText;
82
+ if (presentation.match) outline.setAttribute("data-cms-match", presentation.match);
83
+ else outline.removeAttribute("data-cms-match");
84
+ outline.toggleAttribute("data-cms-reveal", presentation.reveal === true);
85
+ }
86
+ if (!presentation.reveal || !outlines.length) return;
87
+ if (revealFrame) view.cancelAnimationFrame(revealFrame);
88
+ revealFrame = view.requestAnimationFrame(() => {
89
+ revealFrame = view.requestAnimationFrame(() => {
90
+ for (const outline of outlines) outline.removeAttribute("data-cms-reveal");
91
+ revealFrame = 0;
92
+ });
52
93
  });
53
- host.replaceChildren(...outlines);
54
- position();
55
94
  },
56
95
  destroy() {
57
96
  if (frame) view.cancelAnimationFrame(frame);
97
+ if (revealFrame) view.cancelAnimationFrame(revealFrame);
98
+ markTargets([]);
58
99
  host.remove();
59
100
  }
60
101
  };
61
102
  }
103
+ function geometryBounds(geometry) {
104
+ const rectangles = geometry.rects();
105
+ if (!rectangles.length) return void 0;
106
+ const left = Math.min(...rectangles.map((rectangle) => rectangle.left));
107
+ const top = Math.min(...rectangles.map((rectangle) => rectangle.top));
108
+ const right = Math.max(...rectangles.map((rectangle) => rectangle.right));
109
+ const bottom = Math.max(...rectangles.map((rectangle) => rectangle.bottom));
110
+ return {
111
+ bottom,
112
+ height: bottom - top,
113
+ left,
114
+ right,
115
+ top,
116
+ width: right - left
117
+ };
118
+ }
62
119
 
63
120
  //#endregion
64
121
  export { mountPreviewOverlay };
@@ -1 +1 @@
1
- {"version":3,"file":"overlay.js","names":[],"sources":["../../../../../src/runtime/preview/overlay.ts"],"sourcesContent":["/** Preview-only chrome stays separate from the website's layout and element styles. */\nexport function mountPreviewOverlay(root: Document, view: Window, kind: 'selection' | 'hover' = 'selection') {\n const host = root.createElement('div')\n host.setAttribute('data-cms-preview-overlay', '')\n host.setAttribute('aria-hidden', 'true')\n root.documentElement.append(host)\n let targets: ReadonlyArray<Element> = []\n let outlines: Array<HTMLElement> = []\n let frame = 0\n\n const position = (): void => {\n let labelled = false\n\n for (const [index, element] of targets.entries()) {\n const outline = outlines[index]\n\n if (!outline) continue\n const rect = element.getBoundingClientRect()\n\n const visible =\n element.isConnected &&\n (kind !== 'hover' || !element.hasAttribute('data-cms-selected')) &&\n rect.width > 0 &&\n rect.height > 0 &&\n rect.bottom > 0 &&\n rect.top < view.innerHeight &&\n rect.right > 0 &&\n rect.left < view.innerWidth\n\n outline.hidden = !visible\n outline.toggleAttribute('data-cms-selection-label', kind === 'selection' && visible && !labelled)\n\n if (!visible) continue\n labelled = true\n const left = Math.max(2, rect.left - 3)\n const right = Math.min(view.innerWidth - 2, rect.right + 3)\n outline.style.left = `${left}px`\n outline.style.top = `${rect.top - 3}px`\n outline.style.width = `${Math.max(0, right - left)}px`\n outline.style.height = `${rect.height + 6}px`\n outline.style.setProperty('--cms-label-left', `${Math.max(0, Math.min(view.innerWidth - 76, left) - left)}px`)\n outline.style.setProperty('--cms-label-top', `${Math.max(4 - rect.top, rect.top >= 30 ? -26 : 4)}px`)\n }\n\n frame = targets.length ? view.requestAnimationFrame(position) : 0\n }\n\n return {\n set(elements: ReadonlyArray<Element>): void {\n if (elements.length === targets.length && elements.every((element, index) => element === targets[index])) return\n\n if (frame) view.cancelAnimationFrame(frame)\n targets = elements\n outlines = elements.map(() => {\n const outline = root.createElement('div')\n outline.setAttribute(kind === 'selection' ? 'data-cms-selection-outline' : 'data-cms-hover-outline', '')\n\n if (kind === 'hover') {\n for (const corner of ['top-left', 'top-right', 'bottom-right', 'bottom-left']) {\n const mark = root.createElement('span')\n mark.setAttribute('data-cms-hover-corner', corner)\n outline.append(mark)\n }\n }\n\n return outline\n })\n host.replaceChildren(...outlines)\n position()\n },\n destroy(): void {\n if (frame) view.cancelAnimationFrame(frame)\n host.remove()\n },\n }\n}\n"],"mappings":";;AACA,SAAgB,oBAAoB,MAAgB,MAAc,OAA8B,aAAa;CAC3G,MAAM,OAAO,KAAK,cAAc,KAAK;CACrC,KAAK,aAAa,4BAA4B,EAAE;CAChD,KAAK,aAAa,eAAe,MAAM;CACvC,KAAK,gBAAgB,OAAO,IAAI;CAChC,IAAI,UAAkC,CAAC;CACvC,IAAI,WAA+B,CAAC;CACpC,IAAI,QAAQ;CAEZ,MAAM,iBAAuB;EAC3B,IAAI,WAAW;EAEf,KAAK,MAAM,CAAC,OAAO,YAAY,QAAQ,QAAQ,GAAG;GAChD,MAAM,UAAU,SAAS;GAEzB,IAAI,CAAC,SAAS;GACd,MAAM,OAAO,QAAQ,sBAAsB;GAE3C,MAAM,UACJ,QAAQ,gBACP,SAAS,WAAW,CAAC,QAAQ,aAAa,mBAAmB,MAC9D,KAAK,QAAQ,KACb,KAAK,SAAS,KACd,KAAK,SAAS,KACd,KAAK,MAAM,KAAK,eAChB,KAAK,QAAQ,KACb,KAAK,OAAO,KAAK;GAEnB,QAAQ,SAAS,CAAC;GAClB,QAAQ,gBAAgB,4BAA4B,SAAS,eAAe,WAAW,CAAC,QAAQ;GAEhG,IAAI,CAAC,SAAS;GACd,WAAW;GACX,MAAM,OAAO,KAAK,IAAI,GAAG,KAAK,OAAO,CAAC;GACtC,MAAM,QAAQ,KAAK,IAAI,KAAK,aAAa,GAAG,KAAK,QAAQ,CAAC;GAC1D,QAAQ,MAAM,OAAO,GAAG,KAAK;GAC7B,QAAQ,MAAM,MAAM,GAAG,KAAK,MAAM,EAAE;GACpC,QAAQ,MAAM,QAAQ,GAAG,KAAK,IAAI,GAAG,QAAQ,IAAI,EAAE;GACnD,QAAQ,MAAM,SAAS,GAAG,KAAK,SAAS,EAAE;GAC1C,QAAQ,MAAM,YAAY,oBAAoB,GAAG,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,aAAa,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG;GAC7G,QAAQ,MAAM,YAAY,mBAAmB,GAAG,KAAK,IAAI,IAAI,KAAK,KAAK,KAAK,OAAO,KAAK,MAAM,CAAC,EAAE,GAAG;EACtG;EAEA,QAAQ,QAAQ,SAAS,KAAK,sBAAsB,QAAQ,IAAI;CAClE;CAEA,OAAO;EACL,IAAI,UAAwC;GAC1C,IAAI,SAAS,WAAW,QAAQ,UAAU,SAAS,OAAO,SAAS,UAAU,YAAY,QAAQ,MAAM,GAAG;GAE1G,IAAI,OAAO,KAAK,qBAAqB,KAAK;GAC1C,UAAU;GACV,WAAW,SAAS,UAAU;IAC5B,MAAM,UAAU,KAAK,cAAc,KAAK;IACxC,QAAQ,aAAa,SAAS,cAAc,+BAA+B,0BAA0B,EAAE;IAEvG,IAAI,SAAS,SACX,KAAK,MAAM,UAAU;KAAC;KAAY;KAAa;KAAgB;IAAa,GAAG;KAC7E,MAAM,OAAO,KAAK,cAAc,MAAM;KACtC,KAAK,aAAa,yBAAyB,MAAM;KACjD,QAAQ,OAAO,IAAI;IACrB;IAGF,OAAO;GACT,CAAC;GACD,KAAK,gBAAgB,GAAG,QAAQ;GAChC,SAAS;EACX;EACA,UAAgB;GACd,IAAI,OAAO,KAAK,qBAAqB,KAAK;GAC1C,KAAK,OAAO;EACd;CACF;AACF"}
1
+ {"version":3,"file":"overlay.js","names":[],"sources":["../../../../../src/runtime/preview/overlay.ts"],"sourcesContent":["import type { PreviewRegionGeometry } from './regions'\n\ninterface PreviewOverlayPresentation {\n readonly label?: string\n readonly match?: 'exact' | 'fallback'\n readonly reveal?: boolean\n}\n\n/** Preview-only chrome stays separate from the website's layout and element styles. */\nexport function mountPreviewOverlay(root: Document, view: Window, kind: 'selection' | 'hover' = 'selection') {\n const host = root.createElement('div')\n const targetAttribute = kind === 'selection' ? 'data-cms-selected' : 'data-cms-hovered'\n host.setAttribute('data-cms-preview-overlay', '')\n host.setAttribute('aria-hidden', 'true')\n root.documentElement.append(host)\n let markedElements = new Set<Element>()\n let targets: ReadonlyArray<PreviewRegionGeometry> = []\n let outlines: Array<HTMLElement> = []\n let frame = 0\n let isPending = false\n let revealFrame = 0\n\n const markTargets = (geometries: ReadonlyArray<PreviewRegionGeometry>): void => {\n for (const element of markedElements) element.removeAttribute(targetAttribute)\n markedElements = new Set(geometries.flatMap((geometry) => geometry.element() ?? []))\n\n for (const element of markedElements) element.setAttribute(targetAttribute, '')\n }\n\n const setLabelVisibility = (outline: HTMLElement, visible: boolean, labelled: boolean): boolean => {\n const showLabel = kind === 'selection' && visible && !labelled\n\n outline.toggleAttribute('data-cms-selection-label', showLabel)\n\n return labelled || showLabel\n }\n\n const position = (): void => {\n let labelled = false\n\n for (const [index, target] of targets.entries()) {\n const outline = outlines[index]\n\n if (!outline) continue\n const element = target.element()\n const rect = geometryBounds(target)\n\n const visible =\n rect !== undefined &&\n (kind !== 'hover' || !element?.hasAttribute('data-cms-selected')) &&\n rect.width > 0 &&\n rect.height > 0 &&\n rect.bottom > 0 &&\n rect.top < view.innerHeight &&\n rect.right > 0 &&\n rect.left < view.innerWidth\n\n if (!visible) {\n const retainPosition = isPending && outline.hasAttribute('data-cms-positioned')\n\n outline.hidden = !retainPosition\n labelled = setLabelVisibility(outline, retainPosition, labelled)\n\n continue\n }\n\n outline.hidden = false\n outline.setAttribute('data-cms-positioned', '')\n labelled = setLabelVisibility(outline, true, labelled)\n const left = Math.max(2, rect.left - 3)\n const right = Math.min(view.innerWidth - 2, rect.right + 3)\n outline.style.left = `${left}px`\n outline.style.top = `${rect.top - 3}px`\n outline.style.width = `${Math.max(0, right - left)}px`\n outline.style.height = `${rect.height + 6}px`\n const labelWidth = outline.querySelector<HTMLElement>('[data-cms-selection-label-content]')?.offsetWidth ?? 0\n const labelLeft = Math.max(4 - left, Math.min(0, view.innerWidth - 4 - labelWidth - left))\n\n outline.style.setProperty('--cms-label-left', `${labelLeft}px`)\n outline.style.setProperty('--cms-label-top', `${Math.max(4 - rect.top, rect.top >= 30 ? -26 : 4)}px`)\n }\n\n frame = targets.length ? view.requestAnimationFrame(position) : 0\n }\n\n return {\n setPending(value: boolean): void {\n isPending = value\n host.toggleAttribute('data-cms-pending', value)\n },\n show(geometries: ReadonlyArray<PreviewRegionGeometry>, presentation: PreviewOverlayPresentation = {}): void {\n const targetsChanged =\n geometries.length !== targets.length || geometries.some((geometry, index) => geometry !== targets[index])\n\n const labelText =\n presentation.match === 'fallback'\n ? `Nearest · ${presentation.label ?? 'selected content'}`\n : (presentation.label ?? 'Selected')\n\n if (targetsChanged) {\n if (frame) view.cancelAnimationFrame(frame)\n targets = geometries\n outlines = geometries.map(() => {\n const outline = root.createElement('div')\n outline.setAttribute(kind === 'selection' ? 'data-cms-selection-outline' : 'data-cms-hover-outline', '')\n\n if (kind === 'selection') {\n const label = root.createElement('span')\n label.setAttribute('data-cms-selection-label-content', '')\n outline.append(label)\n }\n\n return outline\n })\n host.replaceChildren(...outlines)\n position()\n }\n\n markTargets(targets)\n\n for (const outline of outlines) {\n const label = outline.querySelector<HTMLElement>('[data-cms-selection-label-content]')\n\n if (label) label.textContent = labelText\n\n if (presentation.match) outline.setAttribute('data-cms-match', presentation.match)\n else outline.removeAttribute('data-cms-match')\n\n outline.toggleAttribute('data-cms-reveal', presentation.reveal === true)\n }\n\n if (!presentation.reveal || !outlines.length) return\n\n if (revealFrame) view.cancelAnimationFrame(revealFrame)\n revealFrame = view.requestAnimationFrame(() => {\n revealFrame = view.requestAnimationFrame(() => {\n for (const outline of outlines) outline.removeAttribute('data-cms-reveal')\n revealFrame = 0\n })\n })\n },\n destroy(): void {\n if (frame) view.cancelAnimationFrame(frame)\n\n if (revealFrame) view.cancelAnimationFrame(revealFrame)\n markTargets([])\n host.remove()\n },\n }\n}\n\nfunction geometryBounds(geometry: PreviewRegionGeometry):\n | {\n readonly bottom: number\n readonly height: number\n readonly left: number\n readonly right: number\n readonly top: number\n readonly width: number\n }\n | undefined {\n const rectangles = geometry.rects()\n\n if (!rectangles.length) return undefined\n const left = Math.min(...rectangles.map((rectangle) => rectangle.left))\n const top = Math.min(...rectangles.map((rectangle) => rectangle.top))\n const right = Math.max(...rectangles.map((rectangle) => rectangle.right))\n const bottom = Math.max(...rectangles.map((rectangle) => rectangle.bottom))\n\n return { bottom, height: bottom - top, left, right, top, width: right - left }\n}\n"],"mappings":"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"}
@@ -1,10 +1,10 @@
1
- import { o as PublicPath } from "../../scalars-B2lgGUnj.js";
2
- import { t as openSitePreviewConnection } from "../../connection-D5S9Iph-.js";
3
- import { n as readPreviewLocation, r as withPreviewLocation } from "../../location-CPX7oj0o.js";
1
+ import { o as PublicPath } from "../../scalars-CVZqT3L0.js";
2
+ import { t as openSitePreviewConnection } from "../../connection-BP80q4d-.js";
3
+ import { n as readPreviewLocation, r as withPreviewLocation } from "../../location-CMNGO3lU.js";
4
4
  import { formComponentsKey } from "../delivery/blocks.js";
5
5
  import { documentPreviewStoreKey } from "../delivery/preview.js";
6
6
  import { mountPreviewController } from "./controller.js";
7
- import { t as createStudioPreviewStore } from "../../store-CCsjHaZm.js";
7
+ import { t as createStudioPreviewStore } from "../../store-PKKB8xa2.js";
8
8
  import { Result, Schema } from "effect";
9
9
  import { inject, watch } from "vue";
10
10
  import { defineNuxtPlugin, navigateTo, onNuxtReady, useNuxtApp, useState } from "nuxt/app";