vue-wswg-editor 0.0.15 → 0.0.17

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue-wswg-editor",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "type": "module",
5
5
  "main": "./dist/vue-wswg-editor.es.js",
6
6
  "module": "./dist/vue-wswg-editor.es.js",
@@ -88,10 +88,16 @@
88
88
  "vite": "^5.4.19",
89
89
  "vitepress": "^1.6.4",
90
90
  "vitest": "^2.1.9",
91
+ "vue-router": "^4.0.0",
91
92
  "vue-tsc": "^2.2.12",
92
93
  "yup": "^1.7.1"
93
94
  },
94
95
  "peerDependencies": {
95
96
  "vue": "^3.4.0"
97
+ },
98
+ "peerDependenciesMeta": {
99
+ "vue-router": {
100
+ "optional": true
101
+ }
96
102
  }
97
103
  }
@@ -1,6 +1,13 @@
1
1
  <template>
2
2
  <div ref="containerRef" class="iframe-preview-container">
3
- <iframe ref="iframeRef" title="Page preview" :src="iframeSrc" class="iframe-preview" frameborder="0"></iframe>
3
+ <iframe
4
+ ref="iframeRef"
5
+ title="Page preview"
6
+ :src="iframeSrc"
7
+ class="iframe-preview"
8
+ frameborder="0"
9
+ @load="onIframeLoad"
10
+ ></iframe>
4
11
  </div>
5
12
  </template>
6
13
 
@@ -50,19 +57,54 @@ const iframeSrc = ref<string>("");
50
57
  const blocksKey = computed(() => props.blocksKey || "blocks");
51
58
  const settingsKey = computed(() => props.settingsKey || "settings");
52
59
 
60
+ // Handle iframe load event
61
+ function onIframeLoad() {
62
+ console.info("[parent] Iframe load event fired");
63
+ console.info("[parent] Iframe ref:", iframeRef.value);
64
+ console.info("[parent] Iframe contentWindow:", iframeRef.value?.contentWindow);
65
+ if (iframeRef.value?.contentWindow) {
66
+ try {
67
+ console.info("[parent] Iframe document:", iframeRef.value.contentWindow.document);
68
+ console.info("[parent] Iframe document body:", iframeRef.value.contentWindow.document.body);
69
+ console.info(
70
+ "[parent] Iframe document body innerHTML length:",
71
+ iframeRef.value.contentWindow.document.body.innerHTML.length
72
+ );
73
+ } catch (error) {
74
+ console.info("[parent] Cannot access iframe document (cross-origin?):", error);
75
+ }
76
+ }
77
+ }
78
+
53
79
  // Generate blob URL for iframe
54
80
  function createIframeSrc(): string {
81
+ console.info("[parent] Creating iframe src, module URL:", iframeAppModuleUrl);
55
82
  const html = generateIframeHTML(iframeAppModuleUrl);
56
83
  const blob = new Blob([html], { type: "text/html" });
57
- return URL.createObjectURL(blob);
84
+ const blobUrl = URL.createObjectURL(blob);
85
+ console.info("[parent] Iframe blob URL created:", blobUrl);
86
+ return blobUrl;
58
87
  }
59
88
 
60
89
  // Update iframe content - send pageData to Vue app in iframe
61
90
  async function updateIframeContent() {
62
- if (!iframeRef.value || !iframeReady.value) return;
63
- if (!props.pageData || !props.pageData[blocksKey.value]) return;
91
+ console.info("[parent] updateIframeContent called", {
92
+ hasIframeRef: !!iframeRef.value,
93
+ iframeReady: iframeReady.value,
94
+ hasPageData: !!props.pageData,
95
+ hasBlocks: !!(props.pageData && props.pageData[blocksKey.value]),
96
+ });
97
+ if (!iframeRef.value || !iframeReady.value) {
98
+ console.info("[parent] Skipping update - iframe not ready");
99
+ return;
100
+ }
101
+ if (!props.pageData || !props.pageData[blocksKey.value]) {
102
+ console.info("[parent] Skipping update - no pageData or blocks");
103
+ return;
104
+ }
64
105
 
65
106
  await nextTick();
107
+ console.info("[parent] Sending pageData update to iframe");
66
108
  sendPageDataUpdate(iframeRef.value, props.pageData, blocksKey.value, settingsKey.value, props.theme || "default");
67
109
  }
68
110
 
@@ -70,9 +112,15 @@ async function updateIframeContent() {
70
112
  let messageListener: ((event: MessageEvent) => void) | null = null;
71
113
 
72
114
  function setupMessageListener() {
115
+ console.info("[parent] Setting up message listener");
73
116
  messageListener = (event: MessageEvent) => {
74
117
  // Only handle messages from our iframe
75
- if (event.source !== iframeRef.value?.contentWindow) return;
118
+ if (event.source !== iframeRef.value?.contentWindow) {
119
+ console.info("[parent] Ignoring message from different source");
120
+ return;
121
+ }
122
+
123
+ console.info("[parent] Received message from iframe:", event.data);
76
124
 
77
125
  handleIframeMessage(event, {
78
126
  onBlockClick: (_blockId: string, block: any) => {
@@ -91,12 +139,15 @@ function setupMessageListener() {
91
139
  emit("click-partial", partialValue);
92
140
  },
93
141
  onIframeReady: () => {
142
+ console.info("[parent] IFRAME_READY received, setting iframeReady to true");
94
143
  iframeReady.value = true;
95
144
  // Wait a bit for iframe Vue app to be fully ready
96
145
  setTimeout(() => {
146
+ console.info("[parent] Timeout completed, calling updateIframeContent");
97
147
  updateIframeContent();
98
148
  // Send initial settingsOpen state
99
149
  if (iframeRef.value && props.settingsOpen !== undefined) {
150
+ console.info("[parent] Sending initial settingsOpen state");
100
151
  sendSettingsOpen(iframeRef.value, props.settingsOpen);
101
152
  }
102
153
  }, 100);
@@ -105,6 +156,7 @@ function setupMessageListener() {
105
156
  };
106
157
 
107
158
  window.addEventListener("message", messageListener);
159
+ console.info("[parent] Message listener added");
108
160
  }
109
161
 
110
162
  function cleanupMessageListener() {
@@ -160,7 +212,9 @@ watch(
160
212
  );
161
213
 
162
214
  onMounted(() => {
215
+ console.info("[parent] IframePreview component mounted");
163
216
  iframeSrc.value = createIframeSrc();
217
+ console.info("[parent] Iframe src set:", iframeSrc.value);
164
218
  setupMessageListener();
165
219
  });
166
220
 
@@ -130,43 +130,74 @@ export function generateIframeHTML(iframeAppModuleUrl?: string): string {
130
130
  // The import map above allows vue-router to resolve "vue" as a bare module specifier
131
131
  const appScript = iframeAppModuleUrl
132
132
  ? `<script type="module">
133
+ console.info('[iframe] Script module starting...');
134
+ console.info('[iframe] Module URL:', '${iframeAppModuleUrl}');
135
+
133
136
  import { createApp } from 'vue';
137
+ console.info('[iframe] Vue imported successfully');
138
+
134
139
  import * as VueRouter from 'vue-router';
140
+ console.info('[iframe] VueRouter imported successfully');
135
141
  // Make vue-router available globally so it can be accessed in the iframe app
136
142
  window.VueRouter = VueRouter;
137
143
 
144
+ console.info('[iframe] Attempting to import createIframeApp from:', '${iframeAppModuleUrl}');
138
145
  import { createIframeApp } from '${iframeAppModuleUrl}';
146
+ console.info('[iframe] createIframeApp imported successfully');
139
147
 
140
148
  const appEl = document.getElementById('app');
149
+ console.info('[iframe] App element found:', appEl);
141
150
  if (appEl) {
142
- createIframeApp(appEl).catch(error => {
143
- console.error('Failed to create iframe app:', error);
151
+ console.info('[iframe] Calling createIframeApp...');
152
+ createIframeApp(appEl).then(() => {
153
+ console.info('[iframe] createIframeApp completed successfully');
154
+ }).catch(error => {
155
+ console.error('[iframe] Failed to create iframe app:', error);
156
+ console.error('[iframe] Error stack:', error.stack);
144
157
  });
158
+ } else {
159
+ console.error('[iframe] App element (#app) not found!');
145
160
  }
146
161
  </script>`
147
162
  : `<script type="module">
148
163
  // Fallback: Wait for parent to send module URL via postMessage
164
+ console.info('[iframe] Fallback script module starting...');
149
165
  import { createApp } from 'vue';
166
+ console.info('[iframe] Vue imported successfully (fallback)');
150
167
  import * as VueRouter from 'vue-router';
168
+ console.info('[iframe] VueRouter imported successfully (fallback)');
151
169
  // Make vue-router available globally so it can be imported in the iframe app
152
170
  window.VueRouter = VueRouter;
153
171
 
154
172
  let appInitialized = false;
155
173
 
156
174
  window.addEventListener('message', async (event) => {
175
+ console.info('[iframe] Received message:', event.data);
157
176
  if (event.data.type === 'INIT_IFRAME_APP' && event.data.moduleUrl && !appInitialized) {
158
177
  appInitialized = true;
178
+ console.info('[iframe] Initializing app with module URL:', event.data.moduleUrl);
159
179
  try {
160
180
  const { createIframeApp } = await import(event.data.moduleUrl);
181
+ console.info('[iframe] createIframeApp imported successfully (fallback)');
161
182
  const appEl = document.getElementById('app');
183
+ console.info('[iframe] App element found (fallback):', appEl);
162
184
  if (appEl) {
163
- createIframeApp(appEl);
185
+ console.info('[iframe] Calling createIframeApp (fallback)...');
186
+ createIframeApp(appEl).then(() => {
187
+ console.info('[iframe] createIframeApp completed successfully (fallback)');
188
+ }).catch(error => {
189
+ console.error('[iframe] createIframeApp failed (fallback):', error);
190
+ });
191
+ } else {
192
+ console.error('[iframe] App element not found (fallback)!');
164
193
  }
165
194
  } catch (error) {
166
- console.error('Failed to load iframe app module:', error);
195
+ console.error('[iframe] Failed to load iframe app module:', error);
196
+ console.error('[iframe] Error stack:', error.stack);
167
197
  // Fallback: create minimal Vue app
168
198
  const appEl = document.getElementById('app');
169
199
  if (appEl) {
200
+ console.info('[iframe] Creating fallback Vue app');
170
201
  createApp({
171
202
  template: '<div class="p-4">Loading preview...</div>'
172
203
  }).mount(appEl);
@@ -177,7 +208,10 @@ export function generateIframeHTML(iframeAppModuleUrl?: string): string {
177
208
 
178
209
  // Notify parent that we're ready to receive module URL
179
210
  if (window.parent) {
211
+ console.info('[iframe] Notifying parent that iframe is ready for app');
180
212
  window.parent.postMessage({ type: 'IFRAME_READY_FOR_APP' }, '*');
213
+ } else {
214
+ console.warn('[iframe] window.parent is not available');
181
215
  }
182
216
  </script>`;
183
217
 
@@ -45,6 +45,9 @@ export interface IframeAppCallbacks {
45
45
  * Create and mount the Vue app for the iframe preview
46
46
  */
47
47
  export async function createIframeApp(container: HTMLElement): Promise<App> {
48
+ console.info("[iframe] createIframeApp called with container:", container);
49
+ console.info("[iframe] Container element:", container.tagName, container.id, container.className);
50
+
48
51
  // State
49
52
  const pageData = ref<Record<string, any> | null>(null);
50
53
  const activeBlock = ref<Block | null>(null);
@@ -53,6 +56,7 @@ export async function createIframeApp(container: HTMLElement): Promise<App> {
53
56
  const blocksKey = ref<string>("blocks");
54
57
  const settingsKey = ref<string>("settings");
55
58
  const theme = ref<string>("default");
59
+ console.info("[iframe] State initialized");
56
60
  // Serialize data for postMessage (handles Vue reactive proxies)
57
61
  function serializeForPostMessage(data: any): any {
58
62
  try {
@@ -75,14 +79,23 @@ export async function createIframeApp(container: HTMLElement): Promise<App> {
75
79
  }
76
80
 
77
81
  // Handle messages from parent
82
+ console.info("[iframe] Setting up message listener");
78
83
  window.addEventListener("message", (event: MessageEvent) => {
79
84
  const msg = event.data;
85
+ console.info("[iframe] Received message:", msg?.type, msg);
80
86
  if (!msg || !msg.type) return;
81
87
 
82
88
  switch (msg.type) {
83
89
  case "UPDATE_PAGE_DATA":
90
+ console.info("[iframe] UPDATE_PAGE_DATA received", {
91
+ hasPageData: !!msg.pageData,
92
+ blocksKey: msg.blocksKey,
93
+ settingsKey: msg.settingsKey,
94
+ theme: msg.theme,
95
+ });
84
96
  if (msg.pageData) {
85
97
  pageData.value = msg.pageData;
98
+ console.info("[iframe] pageData updated:", pageData.value);
86
99
  }
87
100
  if (msg.blocksKey) {
88
101
  blocksKey.value = msg.blocksKey;
@@ -114,25 +127,36 @@ export async function createIframeApp(container: HTMLElement): Promise<App> {
114
127
  });
115
128
 
116
129
  // Create Vue app using render function (since we're using runtime-only Vue)
130
+ console.info("[iframe] Creating Vue app...");
117
131
  const app = createApp({
118
132
  components: {
119
133
  EditorPageRenderer,
120
134
  },
121
135
  setup() {
136
+ console.info("[iframe] Vue app setup() called");
122
137
  // Ensure PageRenderer has time to load blocks before rendering
123
138
  const isPageReady = ref(false);
124
139
 
125
140
  // Watch for pageData changes
141
+ console.info("[iframe] Setting up pageData watcher");
126
142
  watch(
127
143
  () => pageData.value,
128
144
  async (newPageData) => {
145
+ console.info("[iframe] pageData watcher triggered:", {
146
+ hasNewPageData: !!newPageData,
147
+ blocksKey: blocksKey.value,
148
+ hasBlocks: !!(newPageData && newPageData[blocksKey.value]),
149
+ });
129
150
  if (newPageData && newPageData[blocksKey.value]) {
130
151
  // Give PageRenderer time to load block modules
131
152
  // PageRenderer loads blocks in onBeforeMount, so we need to wait
153
+ console.info("[iframe] Waiting 200ms for PageRenderer to load blocks...");
132
154
  await new Promise((resolve) => setTimeout(resolve, 200));
133
155
  isPageReady.value = true;
156
+ console.info("[iframe] isPageReady set to true");
134
157
  } else {
135
158
  isPageReady.value = false;
159
+ console.info("[iframe] isPageReady set to false (no pageData)");
136
160
  }
137
161
  },
138
162
  { immediate: true }
@@ -144,7 +168,15 @@ export async function createIframeApp(container: HTMLElement): Promise<App> {
144
168
  const hasPageData = currentPageData && currentPageData[blocksKey.value];
145
169
  const blocks = hasPageData && currentPageData ? currentPageData[blocksKey.value] : [];
146
170
 
171
+ console.info("[iframe] Render function called:", {
172
+ isPageReady: isPageReady.value,
173
+ hasPageData: !!currentPageData,
174
+ hasBlocks: hasPageData,
175
+ blocksCount: blocks?.length || 0,
176
+ });
177
+
147
178
  if (isPageReady.value && currentPageData) {
179
+ console.info("[iframe] Rendering EditorPageRenderer");
148
180
  return h(EditorPageRenderer, {
149
181
  blocks: blocks,
150
182
  layout: currentPageData[settingsKey.value]?.layout,
@@ -156,6 +188,7 @@ export async function createIframeApp(container: HTMLElement): Promise<App> {
156
188
  theme: theme.value,
157
189
  });
158
190
  } else {
191
+ console.info("[iframe] Rendering loading state");
159
192
  // Show loading state while PageRenderer loads blocks
160
193
  return h("div", { class: "bg-white px-5 py-12 md:py-20" }, [
161
194
  h("div", { class: "mx-auto max-w-md pb-7 text-center" }, [
@@ -199,17 +232,23 @@ export async function createIframeApp(container: HTMLElement): Promise<App> {
199
232
 
200
233
  // Try to install Unhead plugin if available
201
234
  // This is needed for layouts that use useHead from @vueuse/head
235
+ console.info("[iframe] Attempting to install @vueuse/head plugin...");
202
236
  try {
203
237
  // Dynamic import to check if @vueuse/head is available
204
238
  // This module is externalized in vite.config.ts so it won't be bundled
205
239
  const headModule = await dynamicImport("@vueuse/head");
206
240
  if (headModule && typeof headModule.createHead === "function") {
241
+ console.info("[iframe] @vueuse/head found, installing plugin");
207
242
  const head = headModule.createHead();
208
243
  app.use(head);
244
+ console.info("[iframe] @vueuse/head plugin installed");
245
+ } else {
246
+ console.info("[iframe] @vueuse/head module found but createHead is not a function");
209
247
  }
210
- } catch {
248
+ } catch (error) {
211
249
  // @vueuse/head not available - layouts using useHead will show warnings but won't break
212
250
  // This is expected if the consuming app doesn't use @vueuse/head
251
+ console.info("[iframe] @vueuse/head not available (this is OK):", error);
213
252
  }
214
253
 
215
254
  // Try to use vue-router - first try from CDN, but components use consuming app's vue-router
@@ -217,25 +256,29 @@ export async function createIframeApp(container: HTMLElement): Promise<App> {
217
256
  // with different Symbol keys. We need to use the same instance they're using.
218
257
  // Try to import vue-router dynamically - this will use the consuming app's vue-router
219
258
  // if it's available in the module resolution context
259
+ console.info("[iframe] Attempting to install vue-router...");
220
260
  let VueRouter: any = null;
221
261
  let routerInstalled = false;
222
262
 
223
263
  try {
224
- // Try dynamic import - this should resolve to the consuming app's vue-router
225
- // when the library is consumed as source code
226
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
227
- // @ts-ignore - vue-router may not be available in all consuming apps
228
- const routerModule = await import("vue-router");
264
+ // Use dynamic import helper to prevent static analysis
265
+ // This should resolve to the consuming app's vue-router when the library is consumed as source code
266
+ console.info("[iframe] Trying to import vue-router dynamically...");
267
+ const routerModule = await dynamicImport("vue-router");
229
268
  VueRouter = routerModule;
230
- } catch {
269
+ console.info("[iframe] vue-router imported successfully:", !!VueRouter);
270
+ } catch (error) {
231
271
  // Fallback to CDN version
272
+ console.info("[iframe] Dynamic import failed, trying CDN version:", error);
232
273
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
233
274
  // @ts-ignore
234
275
  VueRouter = typeof window !== "undefined" ? (window as any).VueRouter : null;
276
+ console.info("[iframe] VueRouter from window:", !!VueRouter);
235
277
  }
236
278
 
237
279
  if (VueRouter && typeof VueRouter.createRouter === "function") {
238
280
  try {
281
+ console.info("[iframe] Creating vue-router instance...");
239
282
  // Create a minimal router instance
240
283
  const router = VueRouter.createRouter({
241
284
  history: VueRouter.createMemoryHistory(),
@@ -250,6 +293,7 @@ export async function createIframeApp(container: HTMLElement): Promise<App> {
250
293
  },
251
294
  ],
252
295
  });
296
+ console.info("[iframe] Router created, setting up resolve override...");
253
297
 
254
298
  // Store original resolve to call it first, then ensure matched array exists
255
299
  const originalResolve = router.resolve.bind(router);
@@ -273,10 +317,13 @@ export async function createIframeApp(container: HTMLElement): Promise<App> {
273
317
 
274
318
  // Install router BEFORE mounting - this provides the router injection
275
319
  // The app.use() call should handle the Symbol keys automatically
320
+ console.info("[iframe] Installing router on app...");
276
321
  app.use(router);
277
322
 
278
323
  // Wait for router to be ready before mounting
324
+ console.info("[iframe] Waiting for router to be ready...");
279
325
  await router.isReady();
326
+ console.info("[iframe] Router is ready");
280
327
 
281
328
  // Ensure currentRoute has proper structure with matched array
282
329
  // currentRoute.value is readonly, so we navigate to ensure route is properly resolved
@@ -285,23 +332,34 @@ export async function createIframeApp(container: HTMLElement): Promise<App> {
285
332
  const currentRoute = router.currentRoute.value;
286
333
  if (currentRoute && (!currentRoute.matched || currentRoute.matched.length === 0)) {
287
334
  // Navigate to ensure route has proper matched array
335
+ console.info("[iframe] Navigating to / to ensure route has matched array");
288
336
  await router.push("/");
289
337
  }
290
338
 
291
339
  routerInstalled = true;
292
- } catch {
340
+ console.info("[iframe] Router installed successfully");
341
+ } catch (error) {
293
342
  // Fall through to mount without router
343
+ console.error("[iframe] Failed to install router:", error);
294
344
  }
345
+ } else {
346
+ console.info("[iframe] VueRouter not available or createRouter is not a function");
295
347
  }
296
348
 
297
349
  if (!routerInstalled) {
298
350
  // Router was not installed - RouterLink components will fail
299
351
  console.warn("[iframe preview] Router was not installed - RouterLink components will fail");
300
352
  }
353
+
301
354
  // Mount the app
355
+ console.info("[iframe] Mounting app to container...");
356
+ console.info("[iframe] Container before mount:", container.innerHTML);
302
357
  app.mount(container);
358
+ console.info("[iframe] App mounted successfully");
359
+ console.info("[iframe] Container after mount:", container.innerHTML);
303
360
 
304
361
  // Notify parent that iframe is ready
362
+ console.info("[iframe] Sending IFRAME_READY message to parent");
305
363
  sendToParent({ type: "IFRAME_READY" });
306
364
 
307
365
  return app;
@@ -27,23 +27,26 @@ function serializeForPostMessage(data: any): any {
27
27
  * Send a message to the iframe
28
28
  */
29
29
  export function sendToIframe(iframe: HTMLIFrameElement | null, message: IframeMessage): void {
30
+ console.info("[parent] sendToIframe called:", message.type);
30
31
  if (!iframe || !iframe.contentWindow) {
31
- console.warn("Cannot send message: iframe not ready");
32
+ console.warn("[parent] Cannot send message: iframe not ready");
32
33
  return;
33
34
  }
34
35
 
35
36
  // Serialize message to handle Vue reactive proxies
36
37
  const serializedMessage = serializeForPostMessage(message) as IframeMessage;
37
38
  if (!serializedMessage) {
38
- console.error("Failed to serialize message for postMessage");
39
+ console.error("[parent] Failed to serialize message for postMessage");
39
40
  return;
40
41
  }
41
42
 
42
43
  // Security: Only send to same origin
43
44
  try {
45
+ console.info("[parent] Posting message to iframe:", message.type, serializedMessage);
44
46
  iframe.contentWindow.postMessage(serializedMessage, "*");
47
+ console.info("[parent] Message posted successfully");
45
48
  } catch (error) {
46
- console.error("Error sending message to iframe:", error);
49
+ console.error("[parent] Error sending message to iframe:", error);
47
50
  }
48
51
  }
49
52