@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 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
- window.parent.postMessage(message, "*");
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
- const activeElement = document.activeElement;
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
- window.parent.postMessage(message, "*");
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
- const activeElement = document.activeElement;
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yoamigo.com/core",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
4
4
  "description": "Core components, router, and utilities for YoAmigo templates",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",