@runelight/vue 0.0.9

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.
@@ -0,0 +1,145 @@
1
+ import { type Component, type InjectionKey, type PropType, type Ref } from "vue";
2
+ export type RunelightVuePreviewProviderEntry = readonly [InjectionKey<any> | string, unknown];
3
+ export type RunelightVuePreviewFrame<Props extends object = Record<string, unknown>> = {
4
+ props?: Props;
5
+ scope?: Record<string, unknown>;
6
+ providers?: readonly RunelightVuePreviewProviderEntry[];
7
+ };
8
+ export type RunelightVuePreviewComponent<Props extends object = Record<string, unknown>> = Component & {
9
+ frames?: Record<string, RunelightVuePreviewFrame<Props>>;
10
+ };
11
+ export type RunelightVuePreviewModule = Record<string, unknown>;
12
+ export type RunelightVuePreviewComponentLoader = (entry: string) => RunelightVuePreviewComponent | Promise<RunelightVuePreviewComponent | undefined> | undefined;
13
+ export type RunelightVuePreviewRouteParams = {
14
+ frameName: string | null;
15
+ frameOverrides: Map<string, string>;
16
+ chrome: string | null;
17
+ entry: string | null;
18
+ poolMode: boolean;
19
+ renderRequestSequence: number;
20
+ sessionId: string | null;
21
+ staticMode: boolean;
22
+ };
23
+ export declare const RunelightVuePreviewClient: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
24
+ chrome: {
25
+ type: PropType<boolean | string | null>;
26
+ default: null;
27
+ };
28
+ defaultEntry: {
29
+ type: StringConstructor;
30
+ default: undefined;
31
+ };
32
+ entry: {
33
+ type: PropType<string | null>;
34
+ default: null;
35
+ };
36
+ frameName: {
37
+ type: PropType<string | null>;
38
+ default: null;
39
+ };
40
+ frameOverrides: {
41
+ type: PropType<Map<string, string>>;
42
+ default: () => Map<string, string>;
43
+ };
44
+ loadComponent: {
45
+ type: PropType<RunelightVuePreviewComponentLoader>;
46
+ required: true;
47
+ };
48
+ missingEntryDetail: {
49
+ type: StringConstructor;
50
+ default: string;
51
+ };
52
+ pool: {
53
+ type: PropType<boolean | string | null>;
54
+ default: null;
55
+ };
56
+ poolMode: {
57
+ type: BooleanConstructor;
58
+ default: boolean;
59
+ };
60
+ renderRequestSequence: {
61
+ type: NumberConstructor;
62
+ default: number;
63
+ };
64
+ sessionId: {
65
+ type: PropType<string | null>;
66
+ default: null;
67
+ };
68
+ staticMode: {
69
+ type: BooleanConstructor;
70
+ default: boolean;
71
+ };
72
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
73
+ [key: string]: any;
74
+ }> | import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
75
+ [key: string]: any;
76
+ }>[], {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
77
+ chrome: {
78
+ type: PropType<boolean | string | null>;
79
+ default: null;
80
+ };
81
+ defaultEntry: {
82
+ type: StringConstructor;
83
+ default: undefined;
84
+ };
85
+ entry: {
86
+ type: PropType<string | null>;
87
+ default: null;
88
+ };
89
+ frameName: {
90
+ type: PropType<string | null>;
91
+ default: null;
92
+ };
93
+ frameOverrides: {
94
+ type: PropType<Map<string, string>>;
95
+ default: () => Map<string, string>;
96
+ };
97
+ loadComponent: {
98
+ type: PropType<RunelightVuePreviewComponentLoader>;
99
+ required: true;
100
+ };
101
+ missingEntryDetail: {
102
+ type: StringConstructor;
103
+ default: string;
104
+ };
105
+ pool: {
106
+ type: PropType<boolean | string | null>;
107
+ default: null;
108
+ };
109
+ poolMode: {
110
+ type: BooleanConstructor;
111
+ default: boolean;
112
+ };
113
+ renderRequestSequence: {
114
+ type: NumberConstructor;
115
+ default: number;
116
+ };
117
+ sessionId: {
118
+ type: PropType<string | null>;
119
+ default: null;
120
+ };
121
+ staticMode: {
122
+ type: BooleanConstructor;
123
+ default: boolean;
124
+ };
125
+ }>> & Readonly<{}>, {
126
+ chrome: string | boolean | null;
127
+ defaultEntry: string;
128
+ entry: string | null;
129
+ frameName: string | null;
130
+ frameOverrides: Map<string, string>;
131
+ missingEntryDetail: string;
132
+ pool: string | boolean | null;
133
+ poolMode: boolean;
134
+ renderRequestSequence: number;
135
+ sessionId: string | null;
136
+ staticMode: boolean;
137
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
138
+ export declare function useRunelightVueFrame(): Ref<RunelightVuePreviewFrame>;
139
+ export declare function readRunelightVuePreviewRouteParams(params: URLSearchParams): RunelightVuePreviewRouteParams;
140
+ export declare function parseRunelightVuePreviewEntry(entry: string): {
141
+ file: string;
142
+ exportName: string;
143
+ };
144
+ export declare function isRunelightVuePreviewComponent(value: unknown): value is RunelightVuePreviewComponent;
145
+ //# sourceMappingURL=preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../src/preview.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,545 @@
1
+ import { computed, defineComponent, h, inject, nextTick, onBeforeUnmount, onMounted, provide, ref, shallowRef, watch, } from "vue";
2
+ import { createGPreviewErrorMessage, createGPreviewPoolReadyMessage, createGPreviewReadyMessage, createGPreviewRenderAcceptedMessage, createGPreviewResizeMessage, createGPreviewTreeMessage, createGPreviewValuesMessage, isGPreviewRenderMessage, isGPreviewRenderTarget, readRunelightPreviewFrameOverridesFromSearchParams, } from "@runelight/core/preview-protocol";
3
+ const RunelightVueFrameSymbol = Symbol("runelight-vue-frame");
4
+ const loadedRunelightVuePreviewEntriesByLoader = new WeakMap();
5
+ const loadingRunelightVuePreviewEntriesByLoader = new WeakMap();
6
+ export const RunelightVuePreviewClient = defineComponent({
7
+ name: "RunelightVuePreviewClient",
8
+ props: {
9
+ chrome: { type: [Boolean, String, null], default: null },
10
+ defaultEntry: { type: String, default: undefined },
11
+ entry: { type: [String, null], default: null },
12
+ frameName: { type: [String, null], default: null },
13
+ frameOverrides: { type: Object, default: () => new Map() },
14
+ loadComponent: { type: Function, required: true },
15
+ missingEntryDetail: {
16
+ type: String,
17
+ default: "Pass ?entry=src/components/.../*.g.vue to render a Runelight Vue preview.",
18
+ },
19
+ pool: { type: [Boolean, String, null], default: null },
20
+ poolMode: { type: Boolean, default: false },
21
+ renderRequestSequence: { type: Number, default: 0 },
22
+ sessionId: { type: [String, null], default: null },
23
+ staticMode: { type: Boolean, default: false },
24
+ },
25
+ setup(props) {
26
+ const routeTarget = computed(() => ({
27
+ frameName: props.frameName,
28
+ frameOverrides: props.frameOverrides,
29
+ chrome: typeof props.chrome === "boolean" ? (props.chrome ? "1" : "0") : props.chrome,
30
+ entry: props.entry ?? props.defaultEntry ?? null,
31
+ poolMode: typeof props.pool === "boolean" ? props.pool : props.pool === "1" || props.poolMode,
32
+ renderRequestSequence: 0,
33
+ sessionId: props.sessionId,
34
+ staticMode: props.staticMode,
35
+ }));
36
+ const renderTarget = useRunelightVuePreviewRenderTarget(routeTarget);
37
+ const showChrome = computed(() => showChromeForPreviewTarget(renderTarget.value.chrome));
38
+ return () => {
39
+ if (!renderTarget.value.entry) {
40
+ if (renderTarget.value.poolMode)
41
+ return h(RunelightVuePreviewDocumentBackground, { showChrome: false });
42
+ return [
43
+ h(RunelightVuePreviewDocumentBackground, { showChrome: showChrome.value }),
44
+ h(RunelightVuePreviewMessage, {
45
+ detail: props.missingEntryDetail,
46
+ sessionId: renderTarget.value.sessionId,
47
+ title: "Missing entry",
48
+ }),
49
+ ];
50
+ }
51
+ return [
52
+ h(RunelightVuePreviewDocumentBackground, { showChrome: showChrome.value }),
53
+ h(RunelightVueEntryPreview, {
54
+ key: previewRenderTargetKey(renderTarget.value),
55
+ componentLoader: props.loadComponent,
56
+ entry: renderTarget.value.entry,
57
+ frameName: renderTarget.value.frameName,
58
+ frameOverrides: renderTarget.value.frameOverrides,
59
+ sessionId: renderTarget.value.sessionId,
60
+ showChrome: showChrome.value,
61
+ staticMode: renderTarget.value.staticMode,
62
+ }),
63
+ ];
64
+ };
65
+ },
66
+ });
67
+ const RunelightVueEntryPreview = defineComponent({
68
+ name: "RunelightVueEntryPreview",
69
+ props: {
70
+ componentLoader: { type: Function, required: true },
71
+ entry: { type: String, required: true },
72
+ frameName: { type: [String, null], default: null },
73
+ frameOverrides: { type: Object, required: true },
74
+ sessionId: { type: [String, null], default: null },
75
+ showChrome: { type: Boolean, required: true },
76
+ staticMode: { type: Boolean, required: true },
77
+ },
78
+ setup(props) {
79
+ const loadedEntry = shallowRef(readLoadedRunelightVuePreviewEntry(props.componentLoader, props.entry));
80
+ watch(() => [props.componentLoader, props.entry], async ([loader, entry], _previous, onCleanup) => {
81
+ const cached = readLoadedRunelightVuePreviewEntry(loader, entry);
82
+ if (cached) {
83
+ loadedEntry.value = cached;
84
+ return;
85
+ }
86
+ let ignore = false;
87
+ onCleanup(() => {
88
+ ignore = true;
89
+ });
90
+ const loaded = await loadRunelightVuePreviewEntry(loader, entry);
91
+ if (!ignore)
92
+ loadedEntry.value = loaded;
93
+ }, { immediate: true });
94
+ return () => {
95
+ if (!loadedEntry.value || loadedEntry.value.entry !== props.entry) {
96
+ return props.showChrome ? h(RunelightVuePreviewMessage, { detail: props.entry, title: "Loading" }) : null;
97
+ }
98
+ if (!loadedEntry.value.component) {
99
+ return h(RunelightVuePreviewMessage, {
100
+ detail: props.entry,
101
+ sessionId: props.sessionId,
102
+ title: "Unknown Runelight entry",
103
+ });
104
+ }
105
+ return h(RunelightVueLoadedEntryPreview, {
106
+ component: loadedEntry.value.component,
107
+ entry: props.entry,
108
+ frameName: props.frameName,
109
+ frameOverrides: props.frameOverrides,
110
+ sessionId: props.sessionId,
111
+ showChrome: props.showChrome,
112
+ staticMode: props.staticMode,
113
+ });
114
+ };
115
+ },
116
+ });
117
+ const RunelightVueLoadedEntryPreview = defineComponent({
118
+ name: "RunelightVueLoadedEntryPreview",
119
+ props: {
120
+ component: { type: Object, required: true },
121
+ entry: { type: String, required: true },
122
+ frameName: { type: [String, null], default: null },
123
+ frameOverrides: { type: Object, required: true },
124
+ sessionId: { type: [String, null], default: null },
125
+ showChrome: { type: Boolean, required: true },
126
+ staticMode: { type: Boolean, required: true },
127
+ },
128
+ setup(props) {
129
+ const frames = computed(() => props.component.frames ?? {});
130
+ const selectedFrames = computed(() => props.frameName ? [[props.frameName, frames.value[props.frameName]]] : Object.entries(frames.value));
131
+ const renderableFrames = computed(() => selectedFrames.value.flatMap(([name, frame]) => (frame ? [{ name, frame }] : [])));
132
+ const enabled = computed(() => selectedFrames.value.length > 0 && renderableFrames.value.length === selectedFrames.value.length);
133
+ useRunelightVuePreviewProtocolMessages(props.sessionId, enabled, { staticMode: props.staticMode });
134
+ return () => {
135
+ if (!enabled.value) {
136
+ return h(RunelightVuePreviewMessage, {
137
+ detail: props.frameName ?? "No frames declared",
138
+ sessionId: props.sessionId,
139
+ title: "Unknown Runelight frame",
140
+ });
141
+ }
142
+ return h("main", {
143
+ style: {
144
+ display: "grid",
145
+ gap: "16px",
146
+ minHeight: props.showChrome ? "100vh" : undefined,
147
+ padding: props.showChrome ? "24px" : 0,
148
+ },
149
+ }, renderableFrames.value.map(({ name, frame }) => h("section", { "data-runelight-preview-frame": name, key: name }, [
150
+ props.showChrome
151
+ ? h("header", {
152
+ style: {
153
+ color: "#64748b",
154
+ font: "12px ui-monospace, SFMono-Regular, Menlo, monospace",
155
+ marginBottom: "8px",
156
+ },
157
+ }, `${props.entry} / ${name}`)
158
+ : null,
159
+ h(RunelightVueFrameProvider, { component: props.component, entry: props.entry, frame, frameName: name }),
160
+ ])));
161
+ };
162
+ },
163
+ });
164
+ const RunelightVueFrameProvider = defineComponent({
165
+ name: "RunelightVueFrameProvider",
166
+ props: {
167
+ component: { type: Object, required: true },
168
+ entry: { type: String, required: true },
169
+ frame: { type: Object, required: true },
170
+ frameName: { type: String, required: true },
171
+ },
172
+ setup(props) {
173
+ const frameRef = shallowRef(props.frame);
174
+ provide(RunelightVueFrameSymbol, frameRef);
175
+ for (const [key, value] of runelightVuePreviewFrameProviderEntries(props.frame)) {
176
+ provide(key, value);
177
+ }
178
+ return () => {
179
+ const boundaryId = `runelight-boundary:${props.entry}:${props.frameName}`;
180
+ return h("div", {
181
+ "data-runelight-boundary-id": boundaryId,
182
+ "data-runelight-boundary-coordinate": props.entry,
183
+ style: { display: "contents" },
184
+ }, [h(props.component, props.frame.props ?? {})]);
185
+ };
186
+ },
187
+ });
188
+ function runelightVuePreviewFrameProviderEntries(frame) {
189
+ return frame.providers ?? [];
190
+ }
191
+ export function useRunelightVueFrame() {
192
+ return inject(RunelightVueFrameSymbol, ref({}));
193
+ }
194
+ const RunelightVuePreviewDocumentBackground = defineComponent({
195
+ name: "RunelightVuePreviewDocumentBackground",
196
+ props: {
197
+ showChrome: { type: Boolean, required: true },
198
+ },
199
+ setup(props) {
200
+ return () => (props.showChrome ? null : h("style", "html, body { background: transparent !important; }"));
201
+ },
202
+ });
203
+ const RunelightVuePreviewMessage = defineComponent({
204
+ name: "RunelightVuePreviewMessage",
205
+ props: {
206
+ detail: { type: String, required: true },
207
+ sessionId: { type: [String, null], default: null },
208
+ title: { type: String, required: true },
209
+ },
210
+ setup(props) {
211
+ onMounted(() => {
212
+ if (props.sessionId) {
213
+ window.parent.postMessage(createGPreviewErrorMessage(props.sessionId, new Error(`${props.title}: ${props.detail}`)), "*");
214
+ }
215
+ });
216
+ return () => h("main", {
217
+ "data-runelight-preview-message": "true",
218
+ style: {
219
+ color: "#172033",
220
+ display: "grid",
221
+ gap: "8px",
222
+ padding: "24px",
223
+ },
224
+ }, [
225
+ h("h1", { style: { fontSize: "18px", fontWeight: 700 } }, props.title),
226
+ h("p", { style: { color: "#64748b", fontSize: "14px" } }, props.detail),
227
+ ]);
228
+ },
229
+ });
230
+ export function readRunelightVuePreviewRouteParams(params) {
231
+ return {
232
+ frameName: params.get("frame"),
233
+ frameOverrides: readRunelightVuePreviewFrameOverrides(params),
234
+ chrome: params.get("chrome"),
235
+ entry: params.get("entry"),
236
+ poolMode: params.get("pool") === "1",
237
+ renderRequestSequence: 0,
238
+ sessionId: params.get("sessionId"),
239
+ staticMode: params.get("static") === "1",
240
+ };
241
+ }
242
+ export function parseRunelightVuePreviewEntry(entry) {
243
+ const [file, exportName] = entry.split("#", 2);
244
+ return { file: file ?? entry, exportName: exportName || "default" };
245
+ }
246
+ export function isRunelightVuePreviewComponent(value) {
247
+ return typeof value === "object" && value !== null;
248
+ }
249
+ function readLoadedRunelightVuePreviewEntry(loadComponent, entry) {
250
+ return loadedRunelightVuePreviewEntriesByLoader.get(loadComponent)?.get(entry) ?? null;
251
+ }
252
+ function loadRunelightVuePreviewEntry(loadComponent, entry) {
253
+ let loadedEntries = loadedRunelightVuePreviewEntriesByLoader.get(loadComponent);
254
+ if (!loadedEntries) {
255
+ loadedEntries = new Map();
256
+ loadedRunelightVuePreviewEntriesByLoader.set(loadComponent, loadedEntries);
257
+ }
258
+ const loadedEntry = loadedEntries.get(entry);
259
+ if (loadedEntry)
260
+ return Promise.resolve(loadedEntry);
261
+ let loadingEntries = loadingRunelightVuePreviewEntriesByLoader.get(loadComponent);
262
+ if (!loadingEntries) {
263
+ loadingEntries = new Map();
264
+ loadingRunelightVuePreviewEntriesByLoader.set(loadComponent, loadingEntries);
265
+ }
266
+ const loadingEntry = loadingEntries.get(entry);
267
+ if (loadingEntry)
268
+ return loadingEntry;
269
+ const nextLoadingEntry = Promise.resolve(loadComponent(entry))
270
+ .then((component) => ({ component: component ?? null, entry }))
271
+ .catch(() => ({ component: null, entry }))
272
+ .then((loaded) => {
273
+ loadedEntries.set(entry, loaded);
274
+ loadingEntries.delete(entry);
275
+ return loaded;
276
+ });
277
+ loadingEntries.set(entry, nextLoadingEntry);
278
+ return nextLoadingEntry;
279
+ }
280
+ function readRunelightVuePreviewMailboxWindow() {
281
+ return typeof window === "undefined" ? null : window;
282
+ }
283
+ let runelightVuePreviewRenderTargetMailbox = null;
284
+ function useRunelightVuePreviewRenderTarget(routeTarget) {
285
+ const target = shallowRef(routeTarget.value);
286
+ let unsubscribe;
287
+ onMounted(() => {
288
+ if (!routeTarget.value.poolMode) {
289
+ target.value = routeTarget.value;
290
+ return;
291
+ }
292
+ const mailbox = ensureRunelightVuePreviewRenderTargetMailbox();
293
+ if (!mailbox)
294
+ return;
295
+ unsubscribe = mailbox.subscribe((nextTarget) => {
296
+ target.value = nextTarget;
297
+ });
298
+ mailbox.announcePoolReady();
299
+ const current = mailbox.getTarget();
300
+ if (current)
301
+ target.value = current;
302
+ });
303
+ onBeforeUnmount(() => {
304
+ unsubscribe?.();
305
+ });
306
+ watch(routeTarget, (nextTarget) => {
307
+ if (!nextTarget.poolMode)
308
+ target.value = nextTarget;
309
+ });
310
+ return target;
311
+ }
312
+ function ensureRunelightVuePreviewRenderTargetMailbox() {
313
+ const mailboxWindow = readRunelightVuePreviewMailboxWindow();
314
+ if (!mailboxWindow)
315
+ return null;
316
+ if (runelightVuePreviewRenderTargetMailbox)
317
+ return runelightVuePreviewRenderTargetMailbox;
318
+ const subscribers = new Set();
319
+ let currentTarget = mailboxWindow.__runelightPreviewPendingRenderTarget
320
+ ? previewRouteParamsFromRenderTarget(mailboxWindow.__runelightPreviewPendingRenderTarget, 0)
321
+ : null;
322
+ let renderRequestSequence = 0;
323
+ let poolReadyAnnounced = false;
324
+ const render = (target) => {
325
+ mailboxWindow.__runelightPreviewPendingRenderTarget = target;
326
+ if (target.sessionId) {
327
+ mailboxWindow.parent.postMessage(createGPreviewRenderAcceptedMessage(target.sessionId), "*");
328
+ }
329
+ renderRequestSequence += 1;
330
+ currentTarget = previewRouteParamsFromRenderTarget(target, renderRequestSequence);
331
+ for (const subscriber of subscribers)
332
+ subscriber(currentTarget);
333
+ };
334
+ mailboxWindow.__runelightPreviewRenderTargetMailbox = { render };
335
+ if (mailboxWindow.__runelightPreviewPrehydrationMailboxInstalled) {
336
+ mailboxWindow.addEventListener("runelight:preview-render-target", (event) => {
337
+ const target = event.detail;
338
+ if (isGPreviewRenderTarget(target))
339
+ render(target);
340
+ });
341
+ }
342
+ else {
343
+ mailboxWindow.addEventListener("message", (event) => {
344
+ if (isGPreviewRenderMessage(event.data))
345
+ render(event.data.target);
346
+ });
347
+ }
348
+ runelightVuePreviewRenderTargetMailbox = {
349
+ announcePoolReady() {
350
+ if (poolReadyAnnounced)
351
+ return;
352
+ poolReadyAnnounced = true;
353
+ if (mailboxWindow.__runelightPreviewPrehydrationMailboxInstalled)
354
+ return;
355
+ mailboxWindow.setTimeout(() => {
356
+ mailboxWindow.parent.postMessage(createGPreviewPoolReadyMessage(), "*");
357
+ }, 0);
358
+ },
359
+ getTarget() {
360
+ return currentTarget;
361
+ },
362
+ render,
363
+ subscribe(subscriber) {
364
+ subscribers.add(subscriber);
365
+ if (currentTarget)
366
+ subscriber(currentTarget);
367
+ return () => {
368
+ subscribers.delete(subscriber);
369
+ };
370
+ },
371
+ };
372
+ return runelightVuePreviewRenderTargetMailbox;
373
+ }
374
+ function previewRouteParamsFromRenderTarget(target, renderRequestSequence) {
375
+ return {
376
+ frameName: target.frameName,
377
+ frameOverrides: new Map(target.frameOverrides ?? []),
378
+ chrome: target.chrome,
379
+ entry: target.entry,
380
+ poolMode: false,
381
+ renderRequestSequence,
382
+ sessionId: target.sessionId,
383
+ staticMode: target.staticMode,
384
+ };
385
+ }
386
+ function showChromeForPreviewTarget(chrome) {
387
+ return chrome === null ? true : chrome !== "0";
388
+ }
389
+ function previewRenderTargetKey(target) {
390
+ return JSON.stringify({
391
+ frameName: target.frameName,
392
+ frameOverrides: [...target.frameOverrides],
393
+ chrome: target.chrome,
394
+ entry: target.entry,
395
+ poolMode: target.poolMode,
396
+ renderRequestSequence: target.renderRequestSequence,
397
+ sessionId: target.sessionId,
398
+ staticMode: target.staticMode,
399
+ });
400
+ }
401
+ function readRunelightVuePreviewFrameOverrides(params) {
402
+ return readRunelightPreviewFrameOverridesFromSearchParams(params);
403
+ }
404
+ function useRunelightVuePreviewProtocolMessages(sessionId, enabled, options = {}) {
405
+ let cleanup;
406
+ onMounted(() => {
407
+ if (!sessionId || !enabled.value)
408
+ return;
409
+ let scheduledFrame = 0;
410
+ let settleTimer = 0;
411
+ let settled = false;
412
+ let resizeObserver;
413
+ const settleStaticPreview = () => {
414
+ settled = true;
415
+ window.removeEventListener("message", handleMessage);
416
+ window.removeEventListener("resize", scheduleLayoutPublish);
417
+ resizeObserver?.disconnect();
418
+ if (scheduledFrame)
419
+ window.cancelAnimationFrame(scheduledFrame);
420
+ if (settleTimer)
421
+ window.clearTimeout(settleTimer);
422
+ };
423
+ const publishLayout = async () => {
424
+ await nextTick();
425
+ const tree = readBoundaryTree();
426
+ window.parent.postMessage(createGPreviewTreeMessage(sessionId, tree), "*");
427
+ window.parent.postMessage(createGPreviewResizeMessage(sessionId, previewContentSize(tree)), "*");
428
+ if (options.staticMode) {
429
+ if (settleTimer)
430
+ window.clearTimeout(settleTimer);
431
+ settleTimer = window.setTimeout(settleStaticPreview, 400);
432
+ }
433
+ };
434
+ const scheduleLayoutPublish = () => {
435
+ if (settled || scheduledFrame)
436
+ return;
437
+ scheduledFrame = window.requestAnimationFrame(() => {
438
+ scheduledFrame = 0;
439
+ void publishLayout();
440
+ });
441
+ };
442
+ const handleMessage = (event) => {
443
+ if (!isRuntimeValuesRequest(event.data, sessionId))
444
+ return;
445
+ const values = readBoundaryValues(event.data.boundaryId);
446
+ if (values) {
447
+ window.parent.postMessage(createGPreviewValuesMessage(sessionId, values), "*");
448
+ }
449
+ };
450
+ window.addEventListener("message", handleMessage);
451
+ window.addEventListener("resize", scheduleLayoutPublish);
452
+ resizeObserver = "ResizeObserver" in window ? new ResizeObserver(scheduleLayoutPublish) : undefined;
453
+ resizeObserver?.observe(document.documentElement);
454
+ if (document.body)
455
+ resizeObserver?.observe(document.body);
456
+ window.parent.postMessage(createGPreviewReadyMessage(sessionId), "*");
457
+ void publishLayout();
458
+ cleanup = () => {
459
+ window.removeEventListener("message", handleMessage);
460
+ window.removeEventListener("resize", scheduleLayoutPublish);
461
+ resizeObserver?.disconnect();
462
+ if (settleTimer)
463
+ window.clearTimeout(settleTimer);
464
+ if (scheduledFrame)
465
+ window.cancelAnimationFrame(scheduledFrame);
466
+ };
467
+ });
468
+ onBeforeUnmount(() => {
469
+ cleanup?.();
470
+ });
471
+ }
472
+ function readBoundaryTree() {
473
+ return [...document.querySelectorAll("[data-runelight-boundary-id]")].map((element) => ({
474
+ id: element.dataset.runelightBoundaryId ?? "",
475
+ coordinate: element.dataset.runelightBoundaryCoordinate ?? "",
476
+ rect: readElementRect(element),
477
+ children: [],
478
+ }));
479
+ }
480
+ function readBoundaryValues(boundaryId) {
481
+ const element = document.querySelector(`[data-runelight-boundary-id="${CSS.escape(boundaryId)}"]`);
482
+ if (!element)
483
+ return undefined;
484
+ return {
485
+ boundaryId,
486
+ props: { type: "undefined" },
487
+ providerValues: [],
488
+ };
489
+ }
490
+ function readElementRect(element) {
491
+ const rect = element.getBoundingClientRect();
492
+ if (rect.width === 0 && rect.height === 0) {
493
+ const childRect = firstVisibleChildRect(element);
494
+ if (childRect)
495
+ return childRect;
496
+ }
497
+ return {
498
+ x: rect.left + window.scrollX,
499
+ y: rect.top + window.scrollY,
500
+ width: rect.width,
501
+ height: rect.height,
502
+ };
503
+ }
504
+ function firstVisibleChildRect(element) {
505
+ for (const child of element.children) {
506
+ if (!(child instanceof HTMLElement))
507
+ continue;
508
+ const rect = child.getBoundingClientRect();
509
+ if (rect.width === 0 && rect.height === 0)
510
+ continue;
511
+ return {
512
+ x: rect.left + window.scrollX,
513
+ y: rect.top + window.scrollY,
514
+ width: rect.width,
515
+ height: rect.height,
516
+ };
517
+ }
518
+ return undefined;
519
+ }
520
+ function previewContentSize(tree) {
521
+ const rects = tree.flatMap((node) => (node.rect ? [node.rect] : []));
522
+ if (rects.length === 0) {
523
+ return {
524
+ width: document.documentElement.scrollWidth,
525
+ height: document.documentElement.scrollHeight,
526
+ };
527
+ }
528
+ const left = Math.min(0, ...rects.map((rect) => rect.x));
529
+ const top = Math.min(0, ...rects.map((rect) => rect.y));
530
+ const right = Math.max(...rects.map((rect) => rect.x + rect.width));
531
+ const bottom = Math.max(...rects.map((rect) => rect.y + rect.height));
532
+ return {
533
+ width: Math.ceil(right - left),
534
+ height: Math.ceil(bottom - top),
535
+ };
536
+ }
537
+ function isRuntimeValuesRequest(message, sessionId) {
538
+ return (typeof message === "object" &&
539
+ message !== null &&
540
+ message.type === "runelight:request-values" &&
541
+ message.protocolVersion === 1 &&
542
+ message.sessionId === sessionId &&
543
+ typeof message.boundaryId === "string");
544
+ }
545
+ //# sourceMappingURL=preview.js.map