@yoamigo.com/core 0.3.8 → 0.3.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.
- package/dist/index.js +223 -8
- package/dist/lib.js +223 -8
- package/dist/plugin.js +4 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -181,9 +181,13 @@ var BuilderSelectionManager = class {
|
|
|
181
181
|
page: window.location.pathname
|
|
182
182
|
});
|
|
183
183
|
}
|
|
184
|
-
sendToParent(message) {
|
|
184
|
+
sendToParent(message, transfer) {
|
|
185
185
|
try {
|
|
186
|
-
|
|
186
|
+
if (transfer && transfer.length > 0) {
|
|
187
|
+
window.parent.postMessage(message, "*", transfer);
|
|
188
|
+
} else {
|
|
189
|
+
window.parent.postMessage(message, "*");
|
|
190
|
+
}
|
|
187
191
|
} catch (error) {
|
|
188
192
|
console.error("[BuilderSelection] Failed to send message to parent:", error);
|
|
189
193
|
}
|
|
@@ -210,6 +214,25 @@ var BuilderSelectionManager = class {
|
|
|
210
214
|
this.captureRegionScreenshot(data.region);
|
|
211
215
|
}
|
|
212
216
|
break;
|
|
217
|
+
case "NAVIGATE":
|
|
218
|
+
if (data.path) {
|
|
219
|
+
this.handleNavigate(data.path);
|
|
220
|
+
}
|
|
221
|
+
break;
|
|
222
|
+
case "CAPTURE_ALL_PAGES":
|
|
223
|
+
if (data.checkpointId && data.viewport) {
|
|
224
|
+
this.captureAllPagesScreenshots(data.checkpointId, data.viewport);
|
|
225
|
+
}
|
|
226
|
+
break;
|
|
227
|
+
case "GET_SCROLL_POSITION":
|
|
228
|
+
this.sendToParent({ type: "SCROLL_POSITION", scrollY: window.scrollY });
|
|
229
|
+
break;
|
|
230
|
+
case "GET_ALL_ROUTES":
|
|
231
|
+
this.sendToParent({ type: "ALL_ROUTES", routes: this.getAllRoutes() });
|
|
232
|
+
break;
|
|
233
|
+
case "GET_CURRENT_PAGE":
|
|
234
|
+
this.sendToParent({ type: "CURRENT_PAGE", pagePath: window.location.pathname });
|
|
235
|
+
break;
|
|
213
236
|
}
|
|
214
237
|
});
|
|
215
238
|
}
|
|
@@ -285,6 +308,25 @@ var BuilderSelectionManager = class {
|
|
|
285
308
|
handleKeyDown = (e) => {
|
|
286
309
|
const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0;
|
|
287
310
|
const modifier = isMac ? e.metaKey : e.ctrlKey;
|
|
311
|
+
const activeEl = document.activeElement;
|
|
312
|
+
const isEditing = activeEl?.tagName === "INPUT" || activeEl?.tagName === "TEXTAREA" || activeEl?.getAttribute("contenteditable") === "true" || activeEl?.closest(".ya-text-editing") || activeEl?.closest(".ya-link-editing");
|
|
313
|
+
if (!isEditing && modifier) {
|
|
314
|
+
if (e.key === "z" && !e.shiftKey) {
|
|
315
|
+
e.preventDefault();
|
|
316
|
+
this.sendToParent({ type: "IFRAME_UNDO" });
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
if (e.key === "z" && e.shiftKey) {
|
|
320
|
+
e.preventDefault();
|
|
321
|
+
this.sendToParent({ type: "IFRAME_REDO" });
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (!isEditing && e.ctrlKey && e.key === "y") {
|
|
326
|
+
e.preventDefault();
|
|
327
|
+
this.sendToParent({ type: "IFRAME_REDO" });
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
288
330
|
if (modifier) {
|
|
289
331
|
if (e.key === "=" || e.key === "+" || e.key === "-" || e.key === "0" || e.key === "1") {
|
|
290
332
|
e.preventDefault();
|
|
@@ -293,12 +335,8 @@ var BuilderSelectionManager = class {
|
|
|
293
335
|
return;
|
|
294
336
|
}
|
|
295
337
|
}
|
|
296
|
-
if (e.key === "Shift") {
|
|
297
|
-
|
|
298
|
-
const isEditing = activeElement?.closest(".ya-text-editing") || activeElement?.closest(".ya-link-editing");
|
|
299
|
-
if (!isEditing) {
|
|
300
|
-
this.sendToParent({ type: "SHIFT_KEY_PRESSED" });
|
|
301
|
-
}
|
|
338
|
+
if (e.key === "Shift" && !isEditing) {
|
|
339
|
+
this.sendToParent({ type: "SHIFT_KEY_PRESSED" });
|
|
302
340
|
}
|
|
303
341
|
if (e.key === "Escape") {
|
|
304
342
|
this.sendToParent({ type: "ESCAPE_KEY_PRESSED" });
|
|
@@ -517,6 +555,35 @@ var BuilderSelectionManager = class {
|
|
|
517
555
|
this.notifyPageReady();
|
|
518
556
|
};
|
|
519
557
|
}
|
|
558
|
+
/**
|
|
559
|
+
* Navigate to a new route with retry logic for HMR timing
|
|
560
|
+
* Used by AI agent to navigate after creating new pages
|
|
561
|
+
* Retries navigation if route doesn't exist yet (new page being created)
|
|
562
|
+
*/
|
|
563
|
+
handleNavigate(path) {
|
|
564
|
+
console.log("[BuilderSelection] Navigate request:", path);
|
|
565
|
+
const MAX_RETRIES = 10;
|
|
566
|
+
const RETRY_DELAY_MS = 300;
|
|
567
|
+
let retries = 0;
|
|
568
|
+
const attemptNavigation = () => {
|
|
569
|
+
history.pushState({}, "", path);
|
|
570
|
+
window.dispatchEvent(new PopStateEvent("popstate"));
|
|
571
|
+
setTimeout(() => {
|
|
572
|
+
if (window.location.pathname === path) {
|
|
573
|
+
console.log("[BuilderSelection] Navigation successful:", path);
|
|
574
|
+
this.notifyPageReady();
|
|
575
|
+
} else if (retries < MAX_RETRIES) {
|
|
576
|
+
retries++;
|
|
577
|
+
console.log(`[BuilderSelection] Navigation retry ${retries}/${MAX_RETRIES} for path: ${path}`);
|
|
578
|
+
setTimeout(attemptNavigation, RETRY_DELAY_MS);
|
|
579
|
+
} else {
|
|
580
|
+
console.warn("[BuilderSelection] Navigation failed after max retries:", path);
|
|
581
|
+
this.notifyPageReady();
|
|
582
|
+
}
|
|
583
|
+
}, 100);
|
|
584
|
+
};
|
|
585
|
+
attemptNavigation();
|
|
586
|
+
}
|
|
520
587
|
showHoverOverlay(element) {
|
|
521
588
|
if (!this.hoverOverlay) return;
|
|
522
589
|
const rect = element.getBoundingClientRect();
|
|
@@ -757,6 +824,154 @@ var BuilderSelectionManager = class {
|
|
|
757
824
|
document.body.appendChild(container);
|
|
758
825
|
this.selections.set(selectionId, { element, container, badge, border });
|
|
759
826
|
}
|
|
827
|
+
/**
|
|
828
|
+
* Get all available routes from the page
|
|
829
|
+
* Looks for routes registered via wouter or standard link patterns
|
|
830
|
+
*/
|
|
831
|
+
getAllRoutes() {
|
|
832
|
+
const wouterRoutes = window.__wouterRoutes;
|
|
833
|
+
if (wouterRoutes && Array.isArray(wouterRoutes)) {
|
|
834
|
+
return wouterRoutes;
|
|
835
|
+
}
|
|
836
|
+
const links = document.querySelectorAll('a[href^="/"]');
|
|
837
|
+
const routes = /* @__PURE__ */ new Set(["/"]);
|
|
838
|
+
links.forEach((link) => {
|
|
839
|
+
const href = link.getAttribute("href");
|
|
840
|
+
if (href && href.startsWith("/") && !href.includes("http")) {
|
|
841
|
+
const path = href.split("?")[0].replace(/\/$/, "") || "/";
|
|
842
|
+
routes.add(path);
|
|
843
|
+
}
|
|
844
|
+
});
|
|
845
|
+
const navLinks = document.querySelectorAll("nav a[href]");
|
|
846
|
+
navLinks.forEach((link) => {
|
|
847
|
+
const href = link.getAttribute("href");
|
|
848
|
+
if (href && href.startsWith("/")) {
|
|
849
|
+
const path = href.split("?")[0].replace(/\/$/, "") || "/";
|
|
850
|
+
routes.add(path);
|
|
851
|
+
}
|
|
852
|
+
});
|
|
853
|
+
return Array.from(routes).sort();
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Capture full-page screenshots for all pages
|
|
857
|
+
* Non-blocking: uses requestIdleCallback between pages
|
|
858
|
+
* Transfers ArrayBuffer directly (zero-copy) for performance
|
|
859
|
+
*/
|
|
860
|
+
async captureAllPagesScreenshots(checkpointId, viewport) {
|
|
861
|
+
console.log("[BuilderSelection] Starting multi-page capture for checkpoint:", checkpointId);
|
|
862
|
+
const routes = this.getAllRoutes();
|
|
863
|
+
const originalPath = window.location.pathname;
|
|
864
|
+
const total = routes.length;
|
|
865
|
+
this.sendToParent({
|
|
866
|
+
type: "CAPTURE_PROGRESS",
|
|
867
|
+
checkpointId,
|
|
868
|
+
completed: 0,
|
|
869
|
+
total
|
|
870
|
+
});
|
|
871
|
+
for (let i = 0; i < routes.length; i++) {
|
|
872
|
+
const pagePath = routes[i];
|
|
873
|
+
try {
|
|
874
|
+
await new Promise((resolve) => {
|
|
875
|
+
if ("requestIdleCallback" in window) {
|
|
876
|
+
requestIdleCallback(() => resolve(), { timeout: 1e3 });
|
|
877
|
+
} else {
|
|
878
|
+
setTimeout(resolve, 50);
|
|
879
|
+
}
|
|
880
|
+
});
|
|
881
|
+
if (window.location.pathname !== pagePath) {
|
|
882
|
+
await this.navigateAndWait(pagePath);
|
|
883
|
+
}
|
|
884
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
885
|
+
const buffer = await this.capturePageAsArrayBuffer();
|
|
886
|
+
if (buffer) {
|
|
887
|
+
this.sendToParent(
|
|
888
|
+
{
|
|
889
|
+
type: "PAGE_SCREENSHOT_READY",
|
|
890
|
+
checkpointId,
|
|
891
|
+
pagePath,
|
|
892
|
+
buffer
|
|
893
|
+
},
|
|
894
|
+
[buffer]
|
|
895
|
+
// Transfer list - moves ownership, no copy
|
|
896
|
+
);
|
|
897
|
+
}
|
|
898
|
+
this.sendToParent({
|
|
899
|
+
type: "CAPTURE_PROGRESS",
|
|
900
|
+
checkpointId,
|
|
901
|
+
completed: i + 1,
|
|
902
|
+
total
|
|
903
|
+
});
|
|
904
|
+
} catch (error) {
|
|
905
|
+
console.error(`[BuilderSelection] Failed to capture page ${pagePath}:`, error);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
if (window.location.pathname !== originalPath) {
|
|
909
|
+
await this.navigateAndWait(originalPath);
|
|
910
|
+
}
|
|
911
|
+
this.sendToParent({
|
|
912
|
+
type: "ALL_SCREENSHOTS_COMPLETE",
|
|
913
|
+
checkpointId
|
|
914
|
+
});
|
|
915
|
+
console.log("[BuilderSelection] Multi-page capture complete:", checkpointId);
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Navigate to a path and wait for page ready
|
|
919
|
+
*/
|
|
920
|
+
async navigateAndWait(path) {
|
|
921
|
+
return new Promise((resolve) => {
|
|
922
|
+
history.pushState({}, "", path);
|
|
923
|
+
window.dispatchEvent(new PopStateEvent("popstate"));
|
|
924
|
+
requestAnimationFrame(() => {
|
|
925
|
+
requestAnimationFrame(() => {
|
|
926
|
+
resolve();
|
|
927
|
+
});
|
|
928
|
+
});
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Capture current page as ArrayBuffer (for zero-copy transfer)
|
|
933
|
+
*/
|
|
934
|
+
async capturePageAsArrayBuffer() {
|
|
935
|
+
try {
|
|
936
|
+
const html2canvas = (await import("html2canvas-pro")).default;
|
|
937
|
+
const overlays = document.querySelectorAll(".builder-selection-container, #builder-hover-overlay");
|
|
938
|
+
overlays.forEach((el) => {
|
|
939
|
+
;
|
|
940
|
+
el.style.display = "none";
|
|
941
|
+
});
|
|
942
|
+
const originalScrollY = window.scrollY;
|
|
943
|
+
window.scrollTo(0, 0);
|
|
944
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
945
|
+
const canvas = await html2canvas(document.body, {
|
|
946
|
+
scale: 0.5,
|
|
947
|
+
// Lower resolution for reasonable file size
|
|
948
|
+
logging: false,
|
|
949
|
+
useCORS: true,
|
|
950
|
+
allowTaint: true,
|
|
951
|
+
backgroundColor: "#ffffff",
|
|
952
|
+
windowHeight: document.body.scrollHeight,
|
|
953
|
+
// Full page height
|
|
954
|
+
height: document.body.scrollHeight
|
|
955
|
+
});
|
|
956
|
+
window.scrollTo(0, originalScrollY);
|
|
957
|
+
overlays.forEach((el) => {
|
|
958
|
+
;
|
|
959
|
+
el.style.display = "";
|
|
960
|
+
});
|
|
961
|
+
const blob = await new Promise((resolve) => {
|
|
962
|
+
canvas.toBlob((b) => resolve(b), "image/jpeg", 0.6);
|
|
963
|
+
});
|
|
964
|
+
if (!blob) {
|
|
965
|
+
console.error("[BuilderSelection] Failed to create blob from canvas");
|
|
966
|
+
return null;
|
|
967
|
+
}
|
|
968
|
+
const buffer = await blob.arrayBuffer();
|
|
969
|
+
return buffer;
|
|
970
|
+
} catch (error) {
|
|
971
|
+
console.error("[BuilderSelection] Page capture failed:", error);
|
|
972
|
+
return null;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
760
975
|
};
|
|
761
976
|
var instance = null;
|
|
762
977
|
function initBuilderSelection() {
|
package/dist/lib.js
CHANGED
|
@@ -140,9 +140,13 @@ var BuilderSelectionManager = class {
|
|
|
140
140
|
page: window.location.pathname
|
|
141
141
|
});
|
|
142
142
|
}
|
|
143
|
-
sendToParent(message) {
|
|
143
|
+
sendToParent(message, transfer) {
|
|
144
144
|
try {
|
|
145
|
-
|
|
145
|
+
if (transfer && transfer.length > 0) {
|
|
146
|
+
window.parent.postMessage(message, "*", transfer);
|
|
147
|
+
} else {
|
|
148
|
+
window.parent.postMessage(message, "*");
|
|
149
|
+
}
|
|
146
150
|
} catch (error) {
|
|
147
151
|
console.error("[BuilderSelection] Failed to send message to parent:", error);
|
|
148
152
|
}
|
|
@@ -169,6 +173,25 @@ var BuilderSelectionManager = class {
|
|
|
169
173
|
this.captureRegionScreenshot(data.region);
|
|
170
174
|
}
|
|
171
175
|
break;
|
|
176
|
+
case "NAVIGATE":
|
|
177
|
+
if (data.path) {
|
|
178
|
+
this.handleNavigate(data.path);
|
|
179
|
+
}
|
|
180
|
+
break;
|
|
181
|
+
case "CAPTURE_ALL_PAGES":
|
|
182
|
+
if (data.checkpointId && data.viewport) {
|
|
183
|
+
this.captureAllPagesScreenshots(data.checkpointId, data.viewport);
|
|
184
|
+
}
|
|
185
|
+
break;
|
|
186
|
+
case "GET_SCROLL_POSITION":
|
|
187
|
+
this.sendToParent({ type: "SCROLL_POSITION", scrollY: window.scrollY });
|
|
188
|
+
break;
|
|
189
|
+
case "GET_ALL_ROUTES":
|
|
190
|
+
this.sendToParent({ type: "ALL_ROUTES", routes: this.getAllRoutes() });
|
|
191
|
+
break;
|
|
192
|
+
case "GET_CURRENT_PAGE":
|
|
193
|
+
this.sendToParent({ type: "CURRENT_PAGE", pagePath: window.location.pathname });
|
|
194
|
+
break;
|
|
172
195
|
}
|
|
173
196
|
});
|
|
174
197
|
}
|
|
@@ -244,6 +267,25 @@ var BuilderSelectionManager = class {
|
|
|
244
267
|
handleKeyDown = (e) => {
|
|
245
268
|
const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0;
|
|
246
269
|
const modifier = isMac ? e.metaKey : e.ctrlKey;
|
|
270
|
+
const activeEl = document.activeElement;
|
|
271
|
+
const isEditing = activeEl?.tagName === "INPUT" || activeEl?.tagName === "TEXTAREA" || activeEl?.getAttribute("contenteditable") === "true" || activeEl?.closest(".ya-text-editing") || activeEl?.closest(".ya-link-editing");
|
|
272
|
+
if (!isEditing && modifier) {
|
|
273
|
+
if (e.key === "z" && !e.shiftKey) {
|
|
274
|
+
e.preventDefault();
|
|
275
|
+
this.sendToParent({ type: "IFRAME_UNDO" });
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
if (e.key === "z" && e.shiftKey) {
|
|
279
|
+
e.preventDefault();
|
|
280
|
+
this.sendToParent({ type: "IFRAME_REDO" });
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (!isEditing && e.ctrlKey && e.key === "y") {
|
|
285
|
+
e.preventDefault();
|
|
286
|
+
this.sendToParent({ type: "IFRAME_REDO" });
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
247
289
|
if (modifier) {
|
|
248
290
|
if (e.key === "=" || e.key === "+" || e.key === "-" || e.key === "0" || e.key === "1") {
|
|
249
291
|
e.preventDefault();
|
|
@@ -252,12 +294,8 @@ var BuilderSelectionManager = class {
|
|
|
252
294
|
return;
|
|
253
295
|
}
|
|
254
296
|
}
|
|
255
|
-
if (e.key === "Shift") {
|
|
256
|
-
|
|
257
|
-
const isEditing = activeElement?.closest(".ya-text-editing") || activeElement?.closest(".ya-link-editing");
|
|
258
|
-
if (!isEditing) {
|
|
259
|
-
this.sendToParent({ type: "SHIFT_KEY_PRESSED" });
|
|
260
|
-
}
|
|
297
|
+
if (e.key === "Shift" && !isEditing) {
|
|
298
|
+
this.sendToParent({ type: "SHIFT_KEY_PRESSED" });
|
|
261
299
|
}
|
|
262
300
|
if (e.key === "Escape") {
|
|
263
301
|
this.sendToParent({ type: "ESCAPE_KEY_PRESSED" });
|
|
@@ -476,6 +514,35 @@ var BuilderSelectionManager = class {
|
|
|
476
514
|
this.notifyPageReady();
|
|
477
515
|
};
|
|
478
516
|
}
|
|
517
|
+
/**
|
|
518
|
+
* Navigate to a new route with retry logic for HMR timing
|
|
519
|
+
* Used by AI agent to navigate after creating new pages
|
|
520
|
+
* Retries navigation if route doesn't exist yet (new page being created)
|
|
521
|
+
*/
|
|
522
|
+
handleNavigate(path) {
|
|
523
|
+
console.log("[BuilderSelection] Navigate request:", path);
|
|
524
|
+
const MAX_RETRIES = 10;
|
|
525
|
+
const RETRY_DELAY_MS = 300;
|
|
526
|
+
let retries = 0;
|
|
527
|
+
const attemptNavigation = () => {
|
|
528
|
+
history.pushState({}, "", path);
|
|
529
|
+
window.dispatchEvent(new PopStateEvent("popstate"));
|
|
530
|
+
setTimeout(() => {
|
|
531
|
+
if (window.location.pathname === path) {
|
|
532
|
+
console.log("[BuilderSelection] Navigation successful:", path);
|
|
533
|
+
this.notifyPageReady();
|
|
534
|
+
} else if (retries < MAX_RETRIES) {
|
|
535
|
+
retries++;
|
|
536
|
+
console.log(`[BuilderSelection] Navigation retry ${retries}/${MAX_RETRIES} for path: ${path}`);
|
|
537
|
+
setTimeout(attemptNavigation, RETRY_DELAY_MS);
|
|
538
|
+
} else {
|
|
539
|
+
console.warn("[BuilderSelection] Navigation failed after max retries:", path);
|
|
540
|
+
this.notifyPageReady();
|
|
541
|
+
}
|
|
542
|
+
}, 100);
|
|
543
|
+
};
|
|
544
|
+
attemptNavigation();
|
|
545
|
+
}
|
|
479
546
|
showHoverOverlay(element) {
|
|
480
547
|
if (!this.hoverOverlay) return;
|
|
481
548
|
const rect = element.getBoundingClientRect();
|
|
@@ -716,6 +783,154 @@ var BuilderSelectionManager = class {
|
|
|
716
783
|
document.body.appendChild(container);
|
|
717
784
|
this.selections.set(selectionId, { element, container, badge, border });
|
|
718
785
|
}
|
|
786
|
+
/**
|
|
787
|
+
* Get all available routes from the page
|
|
788
|
+
* Looks for routes registered via wouter or standard link patterns
|
|
789
|
+
*/
|
|
790
|
+
getAllRoutes() {
|
|
791
|
+
const wouterRoutes = window.__wouterRoutes;
|
|
792
|
+
if (wouterRoutes && Array.isArray(wouterRoutes)) {
|
|
793
|
+
return wouterRoutes;
|
|
794
|
+
}
|
|
795
|
+
const links = document.querySelectorAll('a[href^="/"]');
|
|
796
|
+
const routes = /* @__PURE__ */ new Set(["/"]);
|
|
797
|
+
links.forEach((link) => {
|
|
798
|
+
const href = link.getAttribute("href");
|
|
799
|
+
if (href && href.startsWith("/") && !href.includes("http")) {
|
|
800
|
+
const path = href.split("?")[0].replace(/\/$/, "") || "/";
|
|
801
|
+
routes.add(path);
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
const navLinks = document.querySelectorAll("nav a[href]");
|
|
805
|
+
navLinks.forEach((link) => {
|
|
806
|
+
const href = link.getAttribute("href");
|
|
807
|
+
if (href && href.startsWith("/")) {
|
|
808
|
+
const path = href.split("?")[0].replace(/\/$/, "") || "/";
|
|
809
|
+
routes.add(path);
|
|
810
|
+
}
|
|
811
|
+
});
|
|
812
|
+
return Array.from(routes).sort();
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
815
|
+
* Capture full-page screenshots for all pages
|
|
816
|
+
* Non-blocking: uses requestIdleCallback between pages
|
|
817
|
+
* Transfers ArrayBuffer directly (zero-copy) for performance
|
|
818
|
+
*/
|
|
819
|
+
async captureAllPagesScreenshots(checkpointId, viewport) {
|
|
820
|
+
console.log("[BuilderSelection] Starting multi-page capture for checkpoint:", checkpointId);
|
|
821
|
+
const routes = this.getAllRoutes();
|
|
822
|
+
const originalPath = window.location.pathname;
|
|
823
|
+
const total = routes.length;
|
|
824
|
+
this.sendToParent({
|
|
825
|
+
type: "CAPTURE_PROGRESS",
|
|
826
|
+
checkpointId,
|
|
827
|
+
completed: 0,
|
|
828
|
+
total
|
|
829
|
+
});
|
|
830
|
+
for (let i = 0; i < routes.length; i++) {
|
|
831
|
+
const pagePath = routes[i];
|
|
832
|
+
try {
|
|
833
|
+
await new Promise((resolve) => {
|
|
834
|
+
if ("requestIdleCallback" in window) {
|
|
835
|
+
requestIdleCallback(() => resolve(), { timeout: 1e3 });
|
|
836
|
+
} else {
|
|
837
|
+
setTimeout(resolve, 50);
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
if (window.location.pathname !== pagePath) {
|
|
841
|
+
await this.navigateAndWait(pagePath);
|
|
842
|
+
}
|
|
843
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
844
|
+
const buffer = await this.capturePageAsArrayBuffer();
|
|
845
|
+
if (buffer) {
|
|
846
|
+
this.sendToParent(
|
|
847
|
+
{
|
|
848
|
+
type: "PAGE_SCREENSHOT_READY",
|
|
849
|
+
checkpointId,
|
|
850
|
+
pagePath,
|
|
851
|
+
buffer
|
|
852
|
+
},
|
|
853
|
+
[buffer]
|
|
854
|
+
// Transfer list - moves ownership, no copy
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
this.sendToParent({
|
|
858
|
+
type: "CAPTURE_PROGRESS",
|
|
859
|
+
checkpointId,
|
|
860
|
+
completed: i + 1,
|
|
861
|
+
total
|
|
862
|
+
});
|
|
863
|
+
} catch (error) {
|
|
864
|
+
console.error(`[BuilderSelection] Failed to capture page ${pagePath}:`, error);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
if (window.location.pathname !== originalPath) {
|
|
868
|
+
await this.navigateAndWait(originalPath);
|
|
869
|
+
}
|
|
870
|
+
this.sendToParent({
|
|
871
|
+
type: "ALL_SCREENSHOTS_COMPLETE",
|
|
872
|
+
checkpointId
|
|
873
|
+
});
|
|
874
|
+
console.log("[BuilderSelection] Multi-page capture complete:", checkpointId);
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Navigate to a path and wait for page ready
|
|
878
|
+
*/
|
|
879
|
+
async navigateAndWait(path) {
|
|
880
|
+
return new Promise((resolve) => {
|
|
881
|
+
history.pushState({}, "", path);
|
|
882
|
+
window.dispatchEvent(new PopStateEvent("popstate"));
|
|
883
|
+
requestAnimationFrame(() => {
|
|
884
|
+
requestAnimationFrame(() => {
|
|
885
|
+
resolve();
|
|
886
|
+
});
|
|
887
|
+
});
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* Capture current page as ArrayBuffer (for zero-copy transfer)
|
|
892
|
+
*/
|
|
893
|
+
async capturePageAsArrayBuffer() {
|
|
894
|
+
try {
|
|
895
|
+
const html2canvas = (await import("html2canvas-pro")).default;
|
|
896
|
+
const overlays = document.querySelectorAll(".builder-selection-container, #builder-hover-overlay");
|
|
897
|
+
overlays.forEach((el) => {
|
|
898
|
+
;
|
|
899
|
+
el.style.display = "none";
|
|
900
|
+
});
|
|
901
|
+
const originalScrollY = window.scrollY;
|
|
902
|
+
window.scrollTo(0, 0);
|
|
903
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
904
|
+
const canvas = await html2canvas(document.body, {
|
|
905
|
+
scale: 0.5,
|
|
906
|
+
// Lower resolution for reasonable file size
|
|
907
|
+
logging: false,
|
|
908
|
+
useCORS: true,
|
|
909
|
+
allowTaint: true,
|
|
910
|
+
backgroundColor: "#ffffff",
|
|
911
|
+
windowHeight: document.body.scrollHeight,
|
|
912
|
+
// Full page height
|
|
913
|
+
height: document.body.scrollHeight
|
|
914
|
+
});
|
|
915
|
+
window.scrollTo(0, originalScrollY);
|
|
916
|
+
overlays.forEach((el) => {
|
|
917
|
+
;
|
|
918
|
+
el.style.display = "";
|
|
919
|
+
});
|
|
920
|
+
const blob = await new Promise((resolve) => {
|
|
921
|
+
canvas.toBlob((b) => resolve(b), "image/jpeg", 0.6);
|
|
922
|
+
});
|
|
923
|
+
if (!blob) {
|
|
924
|
+
console.error("[BuilderSelection] Failed to create blob from canvas");
|
|
925
|
+
return null;
|
|
926
|
+
}
|
|
927
|
+
const buffer = await blob.arrayBuffer();
|
|
928
|
+
return buffer;
|
|
929
|
+
} catch (error) {
|
|
930
|
+
console.error("[BuilderSelection] Page capture failed:", error);
|
|
931
|
+
return null;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
719
934
|
};
|
|
720
935
|
var instance = null;
|
|
721
936
|
function initBuilderSelection() {
|
package/dist/plugin.js
CHANGED
|
@@ -10,6 +10,7 @@ function yoamigoPlugin(options = {}) {
|
|
|
10
10
|
name: "yoamigo:content-hmr",
|
|
11
11
|
handleHotUpdate({ file, server }) {
|
|
12
12
|
if (!file.endsWith("/content.ts") && !file.endsWith("\\content.ts")) return;
|
|
13
|
+
console.log("[yoamigo:content-hmr] content.ts changed, sending HMR event to clients");
|
|
13
14
|
server.ws.send({
|
|
14
15
|
type: "custom",
|
|
15
16
|
event: "yoamigo:content-update"
|
|
@@ -21,12 +22,15 @@ function yoamigoPlugin(options = {}) {
|
|
|
21
22
|
const hmrCode = `
|
|
22
23
|
// YoAmigo Content HMR - injected by yoamigo:content-hmr plugin
|
|
23
24
|
if (import.meta.hot) {
|
|
25
|
+
console.log('[yoamigo:content-hmr] HMR listener registered')
|
|
24
26
|
import.meta.hot.on('yoamigo:content-update', async () => {
|
|
27
|
+
console.log('[yoamigo:content-hmr] Received content update event')
|
|
25
28
|
try {
|
|
26
29
|
const { default: newContent } = await import(/* @vite-ignore */ './content?t=' + Date.now())
|
|
27
30
|
const { registerContent } = await import('@yoamigo.com/core')
|
|
28
31
|
registerContent(newContent)
|
|
29
32
|
window.dispatchEvent(new CustomEvent('content-updated'))
|
|
33
|
+
console.log('[yoamigo:content-hmr] Content updated successfully')
|
|
30
34
|
} catch (err) {
|
|
31
35
|
console.error('[yoamigo:content-hmr] Failed to hot reload content:', err)
|
|
32
36
|
}
|