@phtngyn/cms 0.2.0-beta.4 → 0.2.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -13
- package/package.json +7 -7
- package/src/ApplicationTypes.d.ts +3 -3
- package/src/Authoring.d.ts +11 -3
- package/src/{Configuration-BU-UyGoH.js → Configuration-JQ1qyaU0.js} +347 -189
- package/src/Configuration-JQ1qyaU0.js.map +1 -0
- package/src/Configuration.d.ts +1 -1
- package/src/Content.d.ts +7 -6
- package/src/Content.js +106 -47
- package/src/Content.js.map +1 -1
- package/src/ContentTransfer-BxwswWn3.js +86 -0
- package/src/ContentTransfer-BxwswWn3.js.map +1 -0
- package/src/{ContentTransfer-CRQet3QJ.d.ts → ContentTransfer-Z9FHyLez.d.ts} +33 -9
- package/src/{DocumentEngine-ClhFXW9M.js → DocumentEngine-j6Vi7nQo.js} +279 -178
- package/src/DocumentEngine-j6Vi7nQo.js.map +1 -0
- package/src/DocumentModel.d.ts +9 -2
- package/src/DocumentModel.js +17 -4
- package/src/DocumentModel.js.map +1 -1
- package/src/{Forms-DoB56hfe.js → Forms-CX-hwmJF.js} +18 -11
- package/src/Forms-CX-hwmJF.js.map +1 -0
- package/src/{Forms-BbkE7QfC.js → Forms-CfNPTiCr.js} +2 -2
- package/src/{Forms-BbkE7QfC.js.map → Forms-CfNPTiCr.js.map} +1 -1
- package/src/Media-j1bTritO.js +112 -0
- package/src/Media-j1bTritO.js.map +1 -0
- package/src/Module.js +0 -4
- package/src/Module.js.map +1 -1
- package/src/References-CCPDAPve.js +45 -0
- package/src/References-CCPDAPve.js.map +1 -0
- package/src/{S3MediaObjects-CR3p1WTg.js → S3MediaObjects-BRuPQbxl.js} +454 -516
- package/src/S3MediaObjects-BRuPQbxl.js.map +1 -0
- package/src/cli/content.js +6 -5
- package/src/cli/content.js.map +1 -1
- package/src/cli.js +16 -3
- package/src/cli.js.map +1 -1
- package/src/{connection-D5S9Iph-.js → connection-BP80q4d-.js} +2 -2
- package/src/{connection-D5S9Iph-.js.map → connection-BP80q4d-.js.map} +1 -1
- package/src/{dev-lifecycle-D3f9-upa.js → dev-lifecycle-DdBluSiR.js} +26 -6
- package/src/dev-lifecycle-DdBluSiR.js.map +1 -0
- package/src/{installation-fZtNxtoW.js → installation-BoG8E3nJ.js} +7982 -7155
- package/src/installation-BoG8E3nJ.js.map +1 -0
- package/src/internal/authoring-catalog.js +2 -2
- package/src/internal/authoring-compiler.js +35 -9
- package/src/internal/authoring-compiler.js.map +1 -1
- package/src/internal/authoring-metadata.js +14 -5
- package/src/internal/authoring-metadata.js.map +1 -1
- package/src/internal/configuration.js +27 -3
- package/src/internal/configuration.js.map +1 -1
- package/src/internal/dev-lifecycle.js +2 -2
- package/src/internal/document-model.js +134 -43
- package/src/internal/document-model.js.map +1 -1
- package/src/internal/field-metadata.js +2 -1
- package/src/internal/field-metadata.js.map +1 -1
- package/src/internal/form-capabilities.js +1 -1
- package/src/{location-CPX7oj0o.js → location-CMNGO3lU.js} +2 -2
- package/src/{location-CPX7oj0o.js.map → location-CMNGO3lU.js.map} +1 -1
- package/src/{metadata-BEZPWh-3.js → metadata-UzZmjwxM.js} +2 -2
- package/src/{metadata-BEZPWh-3.js.map → metadata-UzZmjwxM.js.map} +1 -1
- package/src/{models-C9kF9JlQ.js → models-5j67LMqW.js} +4 -4
- package/src/{models-C9kF9JlQ.js.map → models-5j67LMqW.js.map} +1 -1
- package/src/{models-ChRxBChy.js → models-VmGmG19-.js} +42 -130
- package/src/models-VmGmG19-.js.map +1 -0
- package/src/runtime/delivery/DocumentRenderer.js +5 -1
- package/src/runtime/delivery/DocumentRenderer.js.map +1 -1
- package/src/runtime/delivery/context.js +1 -1
- package/src/runtime/delivery/plugin.js +2 -2
- package/src/runtime/delivery/preview.d.ts +1 -1
- package/src/runtime/delivery/types.d.ts +1 -1
- package/src/runtime/delivery/useCmsDocumentSeo.d.ts +1 -1
- package/src/runtime/forms/document.js +2 -2
- package/src/runtime/forms/useCmsForm.js +2 -2
- package/src/runtime/installation.js +1 -1
- package/src/runtime/plugin.js +1 -1
- package/src/runtime/preview/CmsStudioPreviewMarker.js +18 -4
- package/src/runtime/preview/CmsStudioPreviewMarker.js.map +1 -1
- package/src/runtime/preview/connection.js +1 -1
- package/src/runtime/preview/controller.js +65 -74
- package/src/runtime/preview/controller.js.map +1 -1
- package/src/runtime/preview/overlay.js +85 -28
- package/src/runtime/preview/overlay.js.map +1 -1
- package/src/runtime/preview/plugin.client.js +4 -4
- package/src/runtime/preview/preview.css +50 -36
- package/src/runtime/preview/protocol.d.ts +11 -5
- package/src/runtime/preview/protocol.js +17 -5
- package/src/runtime/preview/protocol.js.map +1 -1
- package/src/runtime/preview/regions.js +142 -0
- package/src/runtime/preview/regions.js.map +1 -0
- package/src/runtime/preview/store.js +1 -1
- package/src/runtime/sitemap.js +9 -10
- package/src/runtime/sitemap.js.map +1 -1
- package/src/{scalars-B2lgGUnj.js → scalars-CVZqT3L0.js} +2 -6
- package/src/scalars-CVZqT3L0.js.map +1 -0
- package/src/{store-CCsjHaZm.js → store-PKKB8xa2.js} +49 -66
- package/src/store-PKKB8xa2.js.map +1 -0
- package/src/{types-DutFRtaG.d.ts → types-DZ0IV-Ow.d.ts} +14 -3
- package/studio/CreateDocumentDialog-vQ8hDs2C.js +253 -0
- package/studio/{documentPlanning-CDb31mnl.js → documentPlanning-Bx3W9KkD.js} +12054 -12085
- package/studio/{editor-Bzlh3zXt.js → editor-P2rB7Csp.js} +11228 -11927
- package/studio/host.css +1 -1
- package/studio/host.js +23594 -24903
- package/studio/rolldown-runtime-Dy4uBu1J.js +11 -0
- package/src/Configuration-BU-UyGoH.js.map +0 -1
- package/src/ContentTransfer-BhF9G56s.js +0 -57
- package/src/ContentTransfer-BhF9G56s.js.map +0 -1
- package/src/DocumentEngine-ClhFXW9M.js.map +0 -1
- package/src/Forms-DoB56hfe.js.map +0 -1
- package/src/S3MediaObjects-CR3p1WTg.js.map +0 -1
- package/src/dev-lifecycle-D3f9-upa.js.map +0 -1
- package/src/installation-fZtNxtoW.js.map +0 -1
- package/src/models-ChRxBChy.js.map +0 -1
- package/src/runtime/preview/targets.js +0 -101
- package/src/runtime/preview/targets.js.map +0 -1
- package/src/scalars-B2lgGUnj.js.map +0 -1
- package/src/store-CCsjHaZm.js.map +0 -1
- package/studio/CreateDocumentDialog-DJ8c0WZb.js +0 -256
- package/studio/dom-to-image-more.min-D6jGGdrW.js +0 -877
- package/studio/rolldown-runtime-B0aSnxlc.js +0 -20
|
@@ -1,38 +1,22 @@
|
|
|
1
|
-
import { a as EditorNodeId,
|
|
2
|
-
import {
|
|
3
|
-
import { r as PreviewIntentId } from "../../models-
|
|
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 {
|
|
6
|
-
import {
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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.
|
|
60
|
+
selection.show([]);
|
|
77
61
|
sendApplied("exact");
|
|
78
62
|
return;
|
|
79
63
|
}
|
|
80
64
|
const scope = documentRoot(intent.documentId);
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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(
|
|
74
|
+
revealElement(regions.nearest(target.geometries), view);
|
|
90
75
|
}
|
|
91
76
|
sendApplied(result);
|
|
92
77
|
};
|
|
93
|
-
const hoverElements = (target) => {
|
|
94
|
-
|
|
95
|
-
|
|
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 =
|
|
84
|
+
const nodeId = regions.hitTest(x, y, scope);
|
|
102
85
|
if (!nodeId) {
|
|
103
|
-
hover.
|
|
86
|
+
hover.show([]);
|
|
104
87
|
return;
|
|
105
88
|
}
|
|
106
|
-
|
|
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
|
-
|
|
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
|
-
|
|
126
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
115
|
+
selection.setPending(false);
|
|
116
|
+
selection.show([]);
|
|
117
|
+
regions.clear();
|
|
137
118
|
} else {
|
|
138
|
-
|
|
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 =
|
|
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(
|
|
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(
|
|
185
|
+
requestId: RequestId.make(globalThis.crypto.randomUUID()),
|
|
205
186
|
revision,
|
|
206
187
|
type: "preview:activate"
|
|
207
188
|
};
|
|
208
|
-
if (
|
|
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
|
-
|
|
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))
|
|
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
|
-
|
|
227
|
+
regions.decorate();
|
|
238
228
|
if (desiredFocus) applyFocus(false);
|
|
239
229
|
});
|
|
240
230
|
});
|
|
241
231
|
root.addEventListener("click", handleClick, true);
|
|
242
|
-
root.addEventListener("
|
|
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("
|
|
246
|
+
root.removeEventListener("pointermove", handlePointerMove, true);
|
|
256
247
|
root.removeEventListener("pointerout", handlePointerOut, true);
|
|
257
|
-
|
|
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,
|
|
27
|
+
for (const [index, target] of targets.entries()) {
|
|
14
28
|
const outline = outlines[index];
|
|
15
29
|
if (!outline) continue;
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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.
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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<
|
|
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":";;AASA,SAAgB,oBAAoB,MAAgB,MAAc,OAA8B,aAAa;CAC3G,MAAM,OAAO,KAAK,cAAc,KAAK;CACrC,MAAM,kBAAkB,SAAS,cAAc,sBAAsB;CACrE,KAAK,aAAa,4BAA4B,EAAE;CAChD,KAAK,aAAa,eAAe,MAAM;CACvC,KAAK,gBAAgB,OAAO,IAAI;CAChC,IAAI,iCAAiB,IAAI,IAAa;CACtC,IAAI,UAAgD,CAAC;CACrD,IAAI,WAA+B,CAAC;CACpC,IAAI,QAAQ;CACZ,IAAI,YAAY;CAChB,IAAI,cAAc;CAElB,MAAM,eAAe,eAA2D;EAC9E,KAAK,MAAM,WAAW,gBAAgB,QAAQ,gBAAgB,eAAe;EAC7E,iBAAiB,IAAI,IAAI,WAAW,SAAS,aAAa,SAAS,QAAQ,KAAK,CAAC,CAAC,CAAC;EAEnF,KAAK,MAAM,WAAW,gBAAgB,QAAQ,aAAa,iBAAiB,EAAE;CAChF;CAEA,MAAM,sBAAsB,SAAsB,SAAkB,aAA+B;EACjG,MAAM,YAAY,SAAS,eAAe,WAAW,CAAC;EAEtD,QAAQ,gBAAgB,4BAA4B,SAAS;EAE7D,OAAO,YAAY;CACrB;CAEA,MAAM,iBAAuB;EAC3B,IAAI,WAAW;EAEf,KAAK,MAAM,CAAC,OAAO,WAAW,QAAQ,QAAQ,GAAG;GAC/C,MAAM,UAAU,SAAS;GAEzB,IAAI,CAAC,SAAS;GACd,MAAM,UAAU,OAAO,QAAQ;GAC/B,MAAM,OAAO,eAAe,MAAM;GAYlC,IAAI,EATF,SAAS,WACR,SAAS,WAAW,CAAC,SAAS,aAAa,mBAAmB,MAC/D,KAAK,QAAQ,KACb,KAAK,SAAS,KACd,KAAK,SAAS,KACd,KAAK,MAAM,KAAK,eAChB,KAAK,QAAQ,KACb,KAAK,OAAO,KAAK,aAEL;IACZ,MAAM,iBAAiB,aAAa,QAAQ,aAAa,qBAAqB;IAE9E,QAAQ,SAAS,CAAC;IAClB,WAAW,mBAAmB,SAAS,gBAAgB,QAAQ;IAE/D;GACF;GAEA,QAAQ,SAAS;GACjB,QAAQ,aAAa,uBAAuB,EAAE;GAC9C,WAAW,mBAAmB,SAAS,MAAM,QAAQ;GACrD,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,MAAM,aAAa,QAAQ,cAA2B,oCAAoC,CAAC,EAAE,eAAe;GAC5G,MAAM,YAAY,KAAK,IAAI,IAAI,MAAM,KAAK,IAAI,GAAG,KAAK,aAAa,IAAI,aAAa,IAAI,CAAC;GAEzF,QAAQ,MAAM,YAAY,oBAAoB,GAAG,UAAU,GAAG;GAC9D,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,WAAW,OAAsB;GAC/B,YAAY;GACZ,KAAK,gBAAgB,oBAAoB,KAAK;EAChD;EACA,KAAK,YAAkD,eAA2C,CAAC,GAAS;GAC1G,MAAM,iBACJ,WAAW,WAAW,QAAQ,UAAU,WAAW,MAAM,UAAU,UAAU,aAAa,QAAQ,MAAM;GAE1G,MAAM,YACJ,aAAa,UAAU,aACnB,aAAa,aAAa,SAAS,uBAClC,aAAa,SAAS;GAE7B,IAAI,gBAAgB;IAClB,IAAI,OAAO,KAAK,qBAAqB,KAAK;IAC1C,UAAU;IACV,WAAW,WAAW,UAAU;KAC9B,MAAM,UAAU,KAAK,cAAc,KAAK;KACxC,QAAQ,aAAa,SAAS,cAAc,+BAA+B,0BAA0B,EAAE;KAEvG,IAAI,SAAS,aAAa;MACxB,MAAM,QAAQ,KAAK,cAAc,MAAM;MACvC,MAAM,aAAa,oCAAoC,EAAE;MACzD,QAAQ,OAAO,KAAK;KACtB;KAEA,OAAO;IACT,CAAC;IACD,KAAK,gBAAgB,GAAG,QAAQ;IAChC,SAAS;GACX;GAEA,YAAY,OAAO;GAEnB,KAAK,MAAM,WAAW,UAAU;IAC9B,MAAM,QAAQ,QAAQ,cAA2B,oCAAoC;IAErF,IAAI,OAAO,MAAM,cAAc;IAE/B,IAAI,aAAa,OAAO,QAAQ,aAAa,kBAAkB,aAAa,KAAK;SAC5E,QAAQ,gBAAgB,gBAAgB;IAE7C,QAAQ,gBAAgB,mBAAmB,aAAa,WAAW,IAAI;GACzE;GAEA,IAAI,CAAC,aAAa,UAAU,CAAC,SAAS,QAAQ;GAE9C,IAAI,aAAa,KAAK,qBAAqB,WAAW;GACtD,cAAc,KAAK,4BAA4B;IAC7C,cAAc,KAAK,4BAA4B;KAC7C,KAAK,MAAM,WAAW,UAAU,QAAQ,gBAAgB,iBAAiB;KACzE,cAAc;IAChB,CAAC;GACH,CAAC;EACH;EACA,UAAgB;GACd,IAAI,OAAO,KAAK,qBAAqB,KAAK;GAE1C,IAAI,aAAa,KAAK,qBAAqB,WAAW;GACtD,YAAY,CAAC,CAAC;GACd,KAAK,OAAO;EACd;CACF;AACF;AAEA,SAAS,eAAe,UASV;CACZ,MAAM,aAAa,SAAS,MAAM;CAElC,IAAI,CAAC,WAAW,QAAQ,OAAO;CAC/B,MAAM,OAAO,KAAK,IAAI,GAAG,WAAW,KAAK,cAAc,UAAU,IAAI,CAAC;CACtE,MAAM,MAAM,KAAK,IAAI,GAAG,WAAW,KAAK,cAAc,UAAU,GAAG,CAAC;CACpE,MAAM,QAAQ,KAAK,IAAI,GAAG,WAAW,KAAK,cAAc,UAAU,KAAK,CAAC;CACxE,MAAM,SAAS,KAAK,IAAI,GAAG,WAAW,KAAK,cAAc,UAAU,MAAM,CAAC;CAE1E,OAAO;EAAE;EAAQ,QAAQ,SAAS;EAAK;EAAM;EAAO;EAAK,OAAO,QAAQ;CAAK;AAC/E"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { o as PublicPath } from "../../scalars-
|
|
2
|
-
import { t as openSitePreviewConnection } from "../../connection-
|
|
3
|
-
import { n as readPreviewLocation, r as withPreviewLocation } from "../../location-
|
|
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-
|
|
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";
|