@upstart.gg/vite-plugins 0.1.5 → 0.1.7

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.
Files changed (62) hide show
  1. package/dist/src/vite-plugin-upstart-editor/runtime/state.d.ts +10 -0
  2. package/dist/src/vite-plugin-upstart-editor/runtime/state.d.ts.map +1 -0
  3. package/dist/src/vite-plugin-upstart-editor/runtime/state.js +18 -0
  4. package/dist/src/vite-plugin-upstart-editor/runtime/state.js.map +1 -0
  5. package/dist/upstart-editor-api.d.ts +0 -3
  6. package/dist/upstart-editor-api.d.ts.map +1 -1
  7. package/dist/upstart-editor-api.js +1 -2
  8. package/dist/upstart-editor-api.js.map +1 -1
  9. package/dist/vite-plugin-upstart-attrs.d.ts +3 -3
  10. package/dist/vite-plugin-upstart-attrs.d.ts.map +1 -1
  11. package/dist/vite-plugin-upstart-attrs.js +22 -22
  12. package/dist/vite-plugin-upstart-attrs.js.map +1 -1
  13. package/dist/vite-plugin-upstart-branding/plugin.d.ts +3 -4
  14. package/dist/vite-plugin-upstart-branding/plugin.d.ts.map +1 -1
  15. package/dist/vite-plugin-upstart-branding/plugin.js +1 -2
  16. package/dist/vite-plugin-upstart-branding/plugin.js.map +1 -1
  17. package/dist/vite-plugin-upstart-branding/runtime.d.ts.map +1 -1
  18. package/dist/vite-plugin-upstart-branding/runtime.js +2 -2
  19. package/dist/vite-plugin-upstart-branding/runtime.js.map +1 -1
  20. package/dist/vite-plugin-upstart-branding/types.d.ts.map +1 -1
  21. package/dist/vite-plugin-upstart-branding/types.js +1 -1
  22. package/dist/vite-plugin-upstart-editor/plugin.d.ts +3 -4
  23. package/dist/vite-plugin-upstart-editor/plugin.d.ts.map +1 -1
  24. package/dist/vite-plugin-upstart-editor/plugin.js +2 -3
  25. package/dist/vite-plugin-upstart-editor/plugin.js.map +1 -1
  26. package/dist/vite-plugin-upstart-editor/runtime/click-handler.d.ts.map +1 -1
  27. package/dist/vite-plugin-upstart-editor/runtime/click-handler.js +84 -16
  28. package/dist/vite-plugin-upstart-editor/runtime/click-handler.js.map +1 -1
  29. package/dist/vite-plugin-upstart-editor/runtime/error-handler.d.ts.map +1 -1
  30. package/dist/vite-plugin-upstart-editor/runtime/error-handler.js +1 -2
  31. package/dist/vite-plugin-upstart-editor/runtime/error-handler.js.map +1 -1
  32. package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.d.ts.map +1 -1
  33. package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js +78 -21
  34. package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js.map +1 -1
  35. package/dist/vite-plugin-upstart-editor/runtime/index.d.ts +3 -5
  36. package/dist/vite-plugin-upstart-editor/runtime/index.d.ts.map +1 -1
  37. package/dist/vite-plugin-upstart-editor/runtime/index.js +30 -15
  38. package/dist/vite-plugin-upstart-editor/runtime/index.js.map +1 -1
  39. package/dist/vite-plugin-upstart-editor/runtime/text-editor.d.ts +0 -1
  40. package/dist/vite-plugin-upstart-editor/runtime/text-editor.d.ts.map +1 -1
  41. package/dist/vite-plugin-upstart-editor/runtime/text-editor.js +9 -37
  42. package/dist/vite-plugin-upstart-editor/runtime/text-editor.js.map +1 -1
  43. package/dist/vite-plugin-upstart-editor/runtime/types.d.ts +7 -9
  44. package/dist/vite-plugin-upstart-editor/runtime/types.d.ts.map +1 -1
  45. package/dist/vite-plugin-upstart-editor/runtime/types.js +1 -1
  46. package/dist/vite-plugin-upstart-editor/runtime/utils.d.ts +0 -1
  47. package/dist/vite-plugin-upstart-editor/runtime/utils.d.ts.map +1 -1
  48. package/dist/vite-plugin-upstart-editor/runtime/utils.js +1 -1
  49. package/dist/vite-plugin-upstart-theme.d.ts +3 -3
  50. package/dist/vite-plugin-upstart-theme.d.ts.map +1 -1
  51. package/dist/vite-plugin-upstart-theme.js +1 -2
  52. package/dist/vite-plugin-upstart-theme.js.map +1 -1
  53. package/package.json +14 -13
  54. package/src/vite-plugin-upstart-attrs.ts +1 -0
  55. package/src/vite-plugin-upstart-branding/runtime.ts +2 -3
  56. package/src/vite-plugin-upstart-editor/plugin.ts +2 -2
  57. package/src/vite-plugin-upstart-editor/runtime/click-handler.ts +94 -18
  58. package/src/vite-plugin-upstart-editor/runtime/hover-overlay.ts +94 -20
  59. package/src/vite-plugin-upstart-editor/runtime/index.ts +42 -13
  60. package/src/vite-plugin-upstart-editor/runtime/state.ts +17 -0
  61. package/src/vite-plugin-upstart-editor/runtime/text-editor.ts +49 -81
  62. package/src/vite-plugin-upstart-editor/runtime/types.ts +6 -3
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.js","names":[],"sources":["../../src/vite-plugin-upstart-branding/runtime.ts"],"sourcesContent":["const UPSTART_ICON_SVG = `<svg width=\"18\" height=\"18\" viewBox=\"0 0 512 512\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<g clip-path=\"url(#clip0_upstart_branding)\">\n<path d=\"M452 0H60C26.8629 0 0 26.8629 0 60V452C0 485.137 26.8629 512 60 512H452C485.137 512 512 485.137 512 452V60C512 26.8629 485.137 0 452 0Z\" fill=\"#817FCC\"/>\n<path d=\"M346.505 112H410L377.739 307.306C374.122 329.235 365.764 348.423 352.664 364.87C339.564 381.316 322.896 394.141 302.66 403.343C282.424 412.448 259.841 417 234.913 417C209.984 417 188.966 412.448 171.858 403.343C154.75 394.141 142.383 381.316 134.758 364.87C127.133 348.423 125.129 329.235 128.746 307.306L161.006 112H224.501L192.974 301.872C191.214 313.326 192.094 323.508 195.613 332.416C199.23 341.325 205.194 348.325 213.503 353.416C221.813 358.506 232.078 361.052 244.297 361.052C256.615 361.052 267.76 358.506 277.731 353.416C287.801 348.325 296.061 341.325 302.513 332.416C309.063 323.508 313.218 313.326 314.978 301.872L346.505 112Z\" fill=\"white\"/>\n<path d=\"M101 119C101 115.134 104.134 112 108 112H164C167.866 112 171 115.134 171 119V160C171 163.866 167.866 167 164 167H108C104.134 167 101 163.866 101 160V119Z\" fill=\"white\"/>\n</g>\n<defs><clipPath id=\"clip0_upstart_branding\"><rect width=\"512\" height=\"512\" fill=\"white\"/></clipPath></defs>\n</svg>`;\n\nconst BADGE_ID = \"upstart-branding-badge\";\nconst DISMISS_DELAY_MS = 4000;\n\nlet isInitialized = false;\n\n/**\n * Initialize the Upstart branding badge.\n * Injects a fixed-position badge at the bottom center of the viewport.\n * The badge slides up and fades in, then dismisses after ~4 seconds or on scroll.\n */\nexport function initUpstartBranding(): void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return;\n }\n\n if (isInitialized) {\n return;\n }\n\n isInitialized = true;\n\n // Delay initialization to wait for React hydration\n if (\"scheduler\" in globalThis) {\n // @ts-expect-error not yet in TS types\n globalThis.scheduler.postTask(() => injectBranding(), { delay: 250, priority: \"background\" });\n } else {\n setTimeout(() => injectBranding(), 250);\n }\n}\n\nfunction injectBranding(): void {\n const style = document.createElement(\"style\");\n style.textContent = `\n @keyframes upstart-branding-slide-in {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n }\n\n #${BADGE_ID} {\n position: fixed;\n bottom: 16px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 2147483647;\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 14px;\n background: rgba(30, 30, 30, 0.75);\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n border-radius: 9999px;\n box-shadow: 0 4px 24px rgba(0, 0, 0, 0.18), 0 1px 4px rgba(0, 0, 0, 0.1);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n font-size: 13px;\n font-weight: 500;\n color: rgba(255, 255, 255, 0.9);\n text-decoration: none;\n cursor: pointer;\n pointer-events: auto;\n animation: upstart-branding-slide-in 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;\n transition: opacity 0.4s ease, transform 0.4s ease;\n white-space: nowrap;\n line-height: 1;\n }\n\n #${BADGE_ID}:hover {\n background: rgba(30, 30, 30, 0.9);\n color: #fff;\n }\n\n #${BADGE_ID}.upstart-branding-hiding {\n opacity: 0;\n transform: translateX(-50%) translateY(12px);\n pointer-events: none;\n }\n\n #${BADGE_ID} svg {\n flex-shrink: 0;\n border-radius: 3px;\n }\n `;\n document.head.appendChild(style);\n\n const badge = document.createElement(\"a\");\n badge.id = BADGE_ID;\n badge.href = \"https://upstart.gg\";\n badge.target = \"_blank\";\n badge.rel = \"noopener noreferrer\";\n badge.innerHTML = `${UPSTART_ICON_SVG}<span>Made with Upstart</span>`;\n\n document.body.appendChild(badge);\n\n let dismissed = false;\n function dismiss() {\n if (dismissed) return;\n dismissed = true;\n badge.classList.add(\"upstart-branding-hiding\");\n setTimeout(() => {\n badge.remove();\n style.remove();\n }, 500);\n window.removeEventListener(\"scroll\", onScroll);\n clearTimeout(timer);\n }\n\n const timer = setTimeout(dismiss, DISMISS_DELAY_MS);\n\n function onScroll() {\n dismiss();\n }\n window.addEventListener(\"scroll\", onScroll, { passive: true, once: true });\n}\n"],"mappings":";AAAA,MAAM,mBAAmB;;;;;;;;AASzB,MAAM,WAAW;AACjB,MAAM,mBAAmB;AAEzB,IAAI,gBAAgB;;;;;;AAOpB,SAAgB,sBAA4B;AAC1C,KAAI,OAAO,WAAW,eAAe,OAAO,aAAa,YACvD;AAGF,KAAI,cACF;AAGF,iBAAgB;AAGhB,KAAI,eAAe,WAEjB,YAAW,UAAU,eAAe,gBAAgB,EAAE;EAAE,OAAO;EAAK,UAAU;EAAc,CAAC;KAE7F,kBAAiB,gBAAgB,EAAE,IAAI;;AAI3C,SAAS,iBAAuB;CAC9B,MAAM,QAAQ,SAAS,cAAc,QAAQ;AAC7C,OAAM,cAAc;;;;;;;;;;;;OAYf,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BT,SAAS;;;;;OAKT,SAAS;;;;;;OAMT,SAAS;;;;;AAKd,UAAS,KAAK,YAAY,MAAM;CAEhC,MAAM,QAAQ,SAAS,cAAc,IAAI;AACzC,OAAM,KAAK;AACX,OAAM,OAAO;AACb,OAAM,SAAS;AACf,OAAM,MAAM;AACZ,OAAM,YAAY,GAAG,iBAAiB;AAEtC,UAAS,KAAK,YAAY,MAAM;CAEhC,IAAI,YAAY;CAChB,SAAS,UAAU;AACjB,MAAI,UAAW;AACf,cAAY;AACZ,QAAM,UAAU,IAAI,0BAA0B;AAC9C,mBAAiB;AACf,SAAM,QAAQ;AACd,SAAM,QAAQ;KACb,IAAI;AACP,SAAO,oBAAoB,UAAU,SAAS;AAC9C,eAAa,MAAM;;CAGrB,MAAM,QAAQ,WAAW,SAAS,iBAAiB;CAEnD,SAAS,WAAW;AAClB,WAAS;;AAEX,QAAO,iBAAiB,UAAU,UAAU;EAAE,SAAS;EAAM,MAAM;EAAM,CAAC"}
1
+ {"version":3,"file":"runtime.js","names":[],"sources":["../../src/vite-plugin-upstart-branding/runtime.ts"],"sourcesContent":["const UPSTART_ICON_SVG = `<svg width=\"18\" height=\"18\" viewBox=\"0 0 512 512\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<g clip-path=\"url(#clip0_upstart_branding)\">\n<path d=\"M452 0H60C26.8629 0 0 26.8629 0 60V452C0 485.137 26.8629 512 60 512H452C485.137 512 512 485.137 512 452V60C512 26.8629 485.137 0 452 0Z\" fill=\"#817FCC\"/>\n<path d=\"M346.505 112H410L377.739 307.306C374.122 329.235 365.764 348.423 352.664 364.87C339.564 381.316 322.896 394.141 302.66 403.343C282.424 412.448 259.841 417 234.913 417C209.984 417 188.966 412.448 171.858 403.343C154.75 394.141 142.383 381.316 134.758 364.87C127.133 348.423 125.129 329.235 128.746 307.306L161.006 112H224.501L192.974 301.872C191.214 313.326 192.094 323.508 195.613 332.416C199.23 341.325 205.194 348.325 213.503 353.416C221.813 358.506 232.078 361.052 244.297 361.052C256.615 361.052 267.76 358.506 277.731 353.416C287.801 348.325 296.061 341.325 302.513 332.416C309.063 323.508 313.218 313.326 314.978 301.872L346.505 112Z\" fill=\"white\"/>\n<path d=\"M101 119C101 115.134 104.134 112 108 112H164C167.866 112 171 115.134 171 119V160C171 163.866 167.866 167 164 167H108C104.134 167 101 163.866 101 160V119Z\" fill=\"white\"/>\n</g>\n<defs><clipPath id=\"clip0_upstart_branding\"><rect width=\"512\" height=\"512\" fill=\"white\"/></clipPath></defs>\n</svg>`;\n\nconst BADGE_ID = \"upstart-branding-badge\";\nconst DISMISS_DELAY_MS = 4000;\n\nlet isInitialized = false;\n\n/**\n * Initialize the Upstart branding badge.\n * Injects a fixed-position badge at the bottom center of the viewport.\n * The badge slides up and fades in, then dismisses after ~4 seconds or on scroll.\n */\nexport function initUpstartBranding(): void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return;\n }\n\n if (isInitialized) {\n return;\n }\n\n isInitialized = true;\n\n // Delay initialization to wait for React hydration\n if (\"scheduler\" in window) {\n window.scheduler.postTask(() => injectBranding(), { delay: 250, priority: \"background\" });\n } else {\n setTimeout(() => injectBranding(), 250);\n }\n}\n\nfunction injectBranding(): void {\n const style = document.createElement(\"style\");\n style.textContent = `\n @keyframes upstart-branding-slide-in {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n }\n\n #${BADGE_ID} {\n position: fixed;\n bottom: 16px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 2147483647;\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 14px;\n background: rgba(30, 30, 30, 0.75);\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n border-radius: 9999px;\n box-shadow: 0 4px 24px rgba(0, 0, 0, 0.18), 0 1px 4px rgba(0, 0, 0, 0.1);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n font-size: 13px;\n font-weight: 500;\n color: rgba(255, 255, 255, 0.9);\n text-decoration: none;\n cursor: pointer;\n pointer-events: auto;\n animation: upstart-branding-slide-in 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;\n transition: opacity 0.4s ease, transform 0.4s ease;\n white-space: nowrap;\n line-height: 1;\n }\n\n #${BADGE_ID}:hover {\n background: rgba(30, 30, 30, 0.9);\n color: #fff;\n }\n\n #${BADGE_ID}.upstart-branding-hiding {\n opacity: 0;\n transform: translateX(-50%) translateY(12px);\n pointer-events: none;\n }\n\n #${BADGE_ID} svg {\n flex-shrink: 0;\n border-radius: 3px;\n }\n `;\n document.head.appendChild(style);\n\n const badge = document.createElement(\"a\");\n badge.id = BADGE_ID;\n badge.href = \"https://upstart.gg\";\n badge.target = \"_blank\";\n badge.rel = \"noopener noreferrer\";\n badge.innerHTML = `${UPSTART_ICON_SVG}<span>Made with Upstart</span>`;\n\n document.body.appendChild(badge);\n\n let dismissed = false;\n function dismiss() {\n if (dismissed) return;\n dismissed = true;\n badge.classList.add(\"upstart-branding-hiding\");\n setTimeout(() => {\n badge.remove();\n style.remove();\n }, 500);\n window.removeEventListener(\"scroll\", onScroll);\n clearTimeout(timer);\n }\n\n const timer = setTimeout(dismiss, DISMISS_DELAY_MS);\n\n function onScroll() {\n dismiss();\n }\n window.addEventListener(\"scroll\", onScroll, { passive: true, once: true });\n}\n"],"mappings":";AAAA,MAAM,mBAAmB;;;;;;;;AASzB,MAAM,WAAW;AACjB,MAAM,mBAAmB;AAEzB,IAAI,gBAAgB;;;;;;AAOpB,SAAgB,sBAA4B;AAC1C,KAAI,OAAO,WAAW,eAAe,OAAO,aAAa,YACvD;AAGF,KAAI,cACF;AAGF,iBAAgB;AAGhB,KAAI,eAAe,OACjB,QAAO,UAAU,eAAe,gBAAgB,EAAE;EAAE,OAAO;EAAK,UAAU;EAAc,CAAC;KAEzF,kBAAiB,gBAAgB,EAAE,IAAI;;AAI3C,SAAS,iBAAuB;CAC9B,MAAM,QAAQ,SAAS,cAAc,QAAQ;AAC7C,OAAM,cAAc;;;;;;;;;;;;OAYf,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BT,SAAS;;;;;OAKT,SAAS;;;;;;OAMT,SAAS;;;;;AAKd,UAAS,KAAK,YAAY,MAAM;CAEhC,MAAM,QAAQ,SAAS,cAAc,IAAI;AACzC,OAAM,KAAK;AACX,OAAM,OAAO;AACb,OAAM,SAAS;AACf,OAAM,MAAM;AACZ,OAAM,YAAY,GAAG,iBAAiB;AAEtC,UAAS,KAAK,YAAY,MAAM;CAEhC,IAAI,YAAY;CAChB,SAAS,UAAU;AACjB,MAAI,UAAW;AACf,cAAY;AACZ,QAAM,UAAU,IAAI,0BAA0B;AAC9C,mBAAiB;AACf,SAAM,QAAQ;AACd,SAAM,QAAQ;KACb,IAAI;AACP,SAAO,oBAAoB,UAAU,SAAS;AAC9C,eAAa,MAAM;;CAGrB,MAAM,QAAQ,WAAW,SAAS,iBAAiB;CAEnD,SAAS,WAAW;AAClB,WAAS;;AAEX,QAAO,iBAAiB,UAAU,UAAU;EAAE,SAAS;EAAM,MAAM;EAAM,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/vite-plugin-upstart-branding/types.ts"],"sourcesContent":[],"mappings":";;AAGA;;UAAiB,4BAAA"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/vite-plugin-upstart-branding/types.ts"],"mappings":";;AAGA;;UAAiB,4BAAA;EAKf;;;;EAAA,OAAA;AAAA"}
@@ -1 +1 @@
1
- export { };
1
+ export {};
@@ -1,15 +1,14 @@
1
1
  import { UpstartEditorPluginOptions } from "./runtime/types.js";
2
- import * as unplugin5 from "unplugin";
2
+ import * as unplugin from "unplugin";
3
3
 
4
4
  //#region src/vite-plugin-upstart-editor/plugin.d.ts
5
-
6
5
  /**
7
6
  * Upstart Visual Editor Vite plugin (build-time)
8
7
  *
9
8
  * Injects the editor runtime into the app entry during build.
10
9
  */
11
- declare const upstartEditor: unplugin5.UnpluginInstance<UpstartEditorPluginOptions, boolean>;
12
- declare const _default: (options: UpstartEditorPluginOptions) => unplugin5.VitePlugin<any> | unplugin5.VitePlugin<any>[];
10
+ declare const upstartEditor: unplugin.UnpluginInstance<UpstartEditorPluginOptions, boolean>;
11
+ declare const _default: (options: UpstartEditorPluginOptions) => unplugin.VitePlugin<any>[] | unplugin.VitePlugin<any>;
13
12
  //#endregion
14
13
  export { _default as default, upstartEditor };
15
14
  //# sourceMappingURL=plugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","names":[],"sources":["../../src/vite-plugin-upstart-editor/plugin.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAeA;AAwCG;cAxCU,eAAa,SAAA,CAAA,iBAAA;cAwCvB"}
1
+ {"version":3,"file":"plugin.d.ts","names":[],"sources":["../../src/vite-plugin-upstart-editor/plugin.ts"],"mappings":";;;;;;;AAeA;;cAAa,aAAA,EAAa,QAAA,CAAA,gBAAA,CAAA,0BAAA;AAAA,cAwCvB,QAAA"}
@@ -1,7 +1,6 @@
1
1
  import path from "node:path";
2
2
  import { createUnplugin } from "unplugin";
3
3
  import { fileURLToPath } from "node:url";
4
-
5
4
  //#region src/vite-plugin-upstart-editor/plugin.ts
6
5
  const DEFAULT_OPTIONS = {
7
6
  enabled: false,
@@ -29,14 +28,14 @@ const upstartEditor = createUnplugin((options = {}) => {
29
28
  if (!cleanId.includes("entry.client.tsx")) return null;
30
29
  if (code.includes("initUpstartEditor")) return null;
31
30
  return {
32
- code: `${`import { initUpstartEditor } from ${JSON.stringify(runtimePath)};`}${code}requestIdleCallback(initUpstartEditor);`,
31
+ code: `${`import { initUpstartEditor, waitForHydration } from ${JSON.stringify(runtimePath)};`}${code}waitForHydration(initUpstartEditor);`,
33
32
  map: null
34
33
  };
35
34
  }
36
35
  };
37
36
  });
38
37
  var plugin_default = upstartEditor.vite;
39
-
40
38
  //#endregion
41
39
  export { plugin_default as default, upstartEditor };
40
+
42
41
  //# sourceMappingURL=plugin.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":["DEFAULT_OPTIONS: Required<UpstartEditorPluginOptions>"],"sources":["../../src/vite-plugin-upstart-editor/plugin.ts"],"sourcesContent":["import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createUnplugin } from \"unplugin\";\nimport type { UpstartEditorPluginOptions } from \"./runtime/types.js\";\n\nconst DEFAULT_OPTIONS: Required<UpstartEditorPluginOptions> = {\n enabled: false,\n autoInject: true,\n};\n\n/**\n * Upstart Visual Editor Vite plugin (build-time)\n *\n * Injects the editor runtime into the app entry during build.\n */\nexport const upstartEditor = createUnplugin<UpstartEditorPluginOptions>((options = {}) => {\n const { enabled, autoInject } = { ...DEFAULT_OPTIONS, ...options };\n\n if (!enabled) {\n return { name: \"upstart-editor-disabled\" };\n }\n\n const runtimePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), \"./runtime/index\");\n\n return {\n name: \"upstart-editor\",\n enforce: \"pre\",\n\n transform(code, id) {\n if (!autoInject) {\n return null;\n }\n\n const [cleanId] = id.split(\"?\");\n if (!cleanId || cleanId.includes(\"node_modules\")) {\n return null;\n }\n\n if (!cleanId.includes(\"entry.client.tsx\")) {\n return null;\n }\n\n if (code.includes(\"initUpstartEditor\")) {\n return null;\n }\n\n const imports = `import { initUpstartEditor } from ${JSON.stringify(runtimePath)};`;\n const injection = \"requestIdleCallback(initUpstartEditor);\";\n\n return {\n code: `${imports}${code}${injection}`,\n map: null,\n };\n },\n };\n});\n\nexport default upstartEditor.vite;\n"],"mappings":";;;;;AAKA,MAAMA,kBAAwD;CAC5D,SAAS;CACT,YAAY;CACb;;;;;;AAOD,MAAa,gBAAgB,gBAA4C,UAAU,EAAE,KAAK;CACxF,MAAM,EAAE,SAAS,eAAe;EAAE,GAAG;EAAiB,GAAG;EAAS;AAElE,KAAI,CAAC,QACH,QAAO,EAAE,MAAM,2BAA2B;CAG5C,MAAM,cAAc,KAAK,QAAQ,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC,EAAE,kBAAkB;AAEjG,QAAO;EACL,MAAM;EACN,SAAS;EAET,UAAU,MAAM,IAAI;AAClB,OAAI,CAAC,WACH,QAAO;GAGT,MAAM,CAAC,WAAW,GAAG,MAAM,IAAI;AAC/B,OAAI,CAAC,WAAW,QAAQ,SAAS,eAAe,CAC9C,QAAO;AAGT,OAAI,CAAC,QAAQ,SAAS,mBAAmB,CACvC,QAAO;AAGT,OAAI,KAAK,SAAS,oBAAoB,CACpC,QAAO;AAMT,UAAO;IACL,MAAM,GAJQ,qCAAqC,KAAK,UAAU,YAAY,CAAC,KAI5D;IACnB,KAAK;IACN;;EAEJ;EACD;AAEF,qBAAe,cAAc"}
1
+ {"version":3,"file":"plugin.js","names":[],"sources":["../../src/vite-plugin-upstart-editor/plugin.ts"],"sourcesContent":["import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createUnplugin } from \"unplugin\";\nimport type { UpstartEditorPluginOptions } from \"./runtime/types.js\";\n\nconst DEFAULT_OPTIONS: Required<UpstartEditorPluginOptions> = {\n enabled: false,\n autoInject: true,\n};\n\n/**\n * Upstart Visual Editor Vite plugin (build-time)\n *\n * Injects the editor runtime into the app entry during build.\n */\nexport const upstartEditor = createUnplugin<UpstartEditorPluginOptions>((options = {}) => {\n const { enabled, autoInject } = { ...DEFAULT_OPTIONS, ...options };\n\n if (!enabled) {\n return { name: \"upstart-editor-disabled\" };\n }\n\n const runtimePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), \"./runtime/index\");\n\n return {\n name: \"upstart-editor\",\n enforce: \"pre\",\n\n transform(code, id) {\n if (!autoInject) {\n return null;\n }\n\n const [cleanId] = id.split(\"?\");\n if (!cleanId || cleanId.includes(\"node_modules\")) {\n return null;\n }\n\n if (!cleanId.includes(\"entry.client.tsx\")) {\n return null;\n }\n\n if (code.includes(\"initUpstartEditor\")) {\n return null;\n }\n\n const imports = `import { initUpstartEditor, waitForHydration } from ${JSON.stringify(runtimePath)};`;\n const injection = \"waitForHydration(initUpstartEditor);\";\n\n return {\n code: `${imports}${code}${injection}`,\n map: null,\n };\n },\n };\n});\n\nexport default upstartEditor.vite;\n"],"mappings":";;;;AAKA,MAAM,kBAAwD;CAC5D,SAAS;CACT,YAAY;CACb;;;;;;AAOD,MAAa,gBAAgB,gBAA4C,UAAU,EAAE,KAAK;CACxF,MAAM,EAAE,SAAS,eAAe;EAAE,GAAG;EAAiB,GAAG;EAAS;AAElE,KAAI,CAAC,QACH,QAAO,EAAE,MAAM,2BAA2B;CAG5C,MAAM,cAAc,KAAK,QAAQ,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC,EAAE,kBAAkB;AAEjG,QAAO;EACL,MAAM;EACN,SAAS;EAET,UAAU,MAAM,IAAI;AAClB,OAAI,CAAC,WACH,QAAO;GAGT,MAAM,CAAC,WAAW,GAAG,MAAM,IAAI;AAC/B,OAAI,CAAC,WAAW,QAAQ,SAAS,eAAe,CAC9C,QAAO;AAGT,OAAI,CAAC,QAAQ,SAAS,mBAAmB,CACvC,QAAO;AAGT,OAAI,KAAK,SAAS,oBAAoB,CACpC,QAAO;AAMT,UAAO;IACL,MAAM,GAJQ,uDAAuD,KAAK,UAAU,YAAY,CAAC,KAI9E,KAAA;IACnB,KAAK;IACN;;EAEJ;EACD;AAEF,IAAA,iBAAe,cAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"click-handler.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/click-handler.ts"],"sourcesContent":[],"mappings":";;AAQA;AAiBA;iBAjBgB,gBAAA,CAAA;;;;iBAiBA,mBAAA,CAAA"}
1
+ {"version":3,"file":"click-handler.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/click-handler.ts"],"mappings":";;AA+BA;;iBAAgB,gBAAA,CAAA;;;AAiBhB;iBAAgB,mBAAA,CAAA"}
@@ -1,8 +1,29 @@
1
+ import { getCurrentMode } from "../../src/vite-plugin-upstart-editor/runtime/state.js";
1
2
  import { sendToParent } from "./utils.js";
2
- import { getCurrentMode } from "./index.js";
3
-
4
3
  //#region src/vite-plugin-upstart-editor/runtime/click-handler.ts
5
4
  let isInitialized = false;
5
+ const DAISY_VAR_NAMES = [
6
+ "base-100",
7
+ "base-200",
8
+ "base-300",
9
+ "base-content",
10
+ "primary",
11
+ "primary-content",
12
+ "secondary",
13
+ "secondary-content",
14
+ "accent",
15
+ "accent-content",
16
+ "neutral",
17
+ "neutral-content",
18
+ "info",
19
+ "info-content",
20
+ "success",
21
+ "success-content",
22
+ "warning",
23
+ "warning-content",
24
+ "error",
25
+ "error-content"
26
+ ];
6
27
  /**
7
28
  * Initialize click handler for className editing.
8
29
  */
@@ -28,21 +49,44 @@ function handleClick(event) {
28
49
  console.warn("[Upstart Editor] Click target is not an HTMLElement");
29
50
  return;
30
51
  }
52
+ if (target.closest(".upstart-editor-bubble-menu")) {
53
+ console.info("[Upstart Editor] Click ignored: target is inside the bubble menu");
54
+ return;
55
+ }
31
56
  const activeLink = target.closest("a[data-upstart-editor-active]");
32
57
  const isFormControlClick = Boolean(target?.closest("input, textarea, select, button"));
33
58
  if (activeLink || !isFormControlClick) event.preventDefault();
34
59
  if (target.closest("[contenteditable='true']")) {
35
- console.info("[Upstart Editor] Click ignored: target is inside a contenteditable element");
36
- return;
37
- }
38
- const datasourceEl = target.closest("[data-upstart-datasource][data-upstart-record-id]");
39
- if (datasourceEl) {
40
- event.stopPropagation();
41
- sendToParent({
42
- type: "datasource-item-clicked",
43
- datasourceId: datasourceEl.dataset.upstartDatasource,
44
- recordId: datasourceEl.dataset.upstartRecordId
45
- });
60
+ const editableEl = target.closest("[data-upstart-editable-text='true']");
61
+ if (editableEl) {
62
+ const classNameId = editableEl.dataset.upstartClassnameId || editableEl.closest("[data-upstart-classname-id]")?.dataset.upstartClassnameId || "";
63
+ if (classNameId) {
64
+ const computedStyle = getComputedStyle(document.documentElement);
65
+ const themeColors = {};
66
+ for (const name of DAISY_VAR_NAMES) {
67
+ const val = computedStyle.getPropertyValue(`--color-${name}`).trim();
68
+ if (val) themeColors[name] = val;
69
+ }
70
+ sendToParent({
71
+ type: "element-clicked",
72
+ hash: editableEl.dataset.upstartHash ?? "",
73
+ componentName: editableEl.dataset.upstartComponent,
74
+ filePath: editableEl.dataset.upstartFile ?? "",
75
+ classNameId,
76
+ currentClassName: editableEl.className,
77
+ themeColors,
78
+ bounds: {
79
+ top: 0,
80
+ left: 0,
81
+ width: 0,
82
+ height: 0,
83
+ right: 0,
84
+ bottom: 0
85
+ }
86
+ });
87
+ }
88
+ }
89
+ console.info("[Upstart Editor] Click inside contenteditable: TipTap handles text editing");
46
90
  return;
47
91
  }
48
92
  const element = target.closest("[data-upstart-hash]");
@@ -55,7 +99,28 @@ function handleClick(event) {
55
99
  const hash = element.dataset.upstartHash;
56
100
  const componentName = element.dataset.upstartComponent;
57
101
  const filePath = element.dataset.upstartFile ?? "";
58
- const classNameId = element.dataset.upstartClassnameId ?? "";
102
+ if (element.dataset.upstartEditableText === "true") {
103
+ const proseMirror = element.querySelector(".ProseMirror");
104
+ if (proseMirror) proseMirror.focus();
105
+ }
106
+ let classNameId = element.dataset.upstartClassnameId ?? "";
107
+ let currentClassName = element.className;
108
+ if (!classNameId) {
109
+ const parentWithClassname = element.closest("[data-upstart-classname-id]");
110
+ if (parentWithClassname) {
111
+ classNameId = parentWithClassname.dataset.upstartClassnameId ?? "";
112
+ currentClassName = parentWithClassname.className;
113
+ }
114
+ }
115
+ const datasourceEl = target.closest("[data-upstart-datasource][data-upstart-record-id]");
116
+ const datasourceId = datasourceEl?.dataset.upstartDatasource;
117
+ const recordId = datasourceEl?.dataset.upstartRecordId;
118
+ const computedStyle = getComputedStyle(document.documentElement);
119
+ const themeColors = {};
120
+ for (const name of DAISY_VAR_NAMES) {
121
+ const val = computedStyle.getPropertyValue(`--color-${name}`).trim();
122
+ if (val) themeColors[name] = val;
123
+ }
59
124
  const rect = element.getBoundingClientRect();
60
125
  sendToParent({
61
126
  type: "element-clicked",
@@ -63,7 +128,10 @@ function handleClick(event) {
63
128
  componentName,
64
129
  filePath,
65
130
  classNameId,
66
- currentClassName: element.className,
131
+ currentClassName,
132
+ datasourceId,
133
+ recordId,
134
+ themeColors,
67
135
  bounds: {
68
136
  top: rect.top,
69
137
  left: rect.left,
@@ -75,7 +143,7 @@ function handleClick(event) {
75
143
  });
76
144
  console.log("[Upstart Editor] Element clicked:", componentName, hash);
77
145
  }
78
-
79
146
  //#endregion
80
147
  export { cleanupClickHandler, initClickHandler };
148
+
81
149
  //# sourceMappingURL=click-handler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"click-handler.js","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/click-handler.ts"],"sourcesContent":["import { getCurrentMode } from \"./index.js\";\nimport { sendToParent } from \"./utils.js\";\n\nlet isInitialized = false;\n\n/**\n * Initialize click handler for className editing.\n */\nexport function initClickHandler(): void {\n if (typeof document === \"undefined\") {\n return;\n }\n\n if (isInitialized) {\n return;\n }\n\n console.log(\"[Upstart Editor] Initializing click handler...\");\n document.addEventListener(\"click\", handleClick, true);\n isInitialized = true;\n}\n\n/**\n * Cleanup click handler.\n */\nexport function cleanupClickHandler(): void {\n document.removeEventListener(\"click\", handleClick, true);\n isInitialized = false;\n}\n\nfunction handleClick(event: MouseEvent): void {\n if (getCurrentMode() !== \"edit\") {\n return;\n }\n\n console.debug(\"[Upstart Editor] Click event:\", event);\n\n const target = event.target as HTMLElement | null;\n if (!target) {\n console.warn(\"[Upstart Editor] Click target is not an HTMLElement\");\n return;\n }\n\n const activeLink = target.closest(\"a[data-upstart-editor-active]\");\n const isFormControlClick = Boolean(\n (target as HTMLElement | null)?.closest(\"input, textarea, select, button\"),\n );\n if (activeLink || !isFormControlClick) {\n event.preventDefault();\n }\n\n if (target.closest(\"[contenteditable='true']\")) {\n console.info(\"[Upstart Editor] Click ignored: target is inside a contenteditable element\");\n return;\n }\n\n // Check if the click targets a datasource record (takes priority over class editing)\n const datasourceEl = target.closest<HTMLElement>(\"[data-upstart-datasource][data-upstart-record-id]\");\n if (datasourceEl) {\n event.stopPropagation();\n sendToParent({\n type: \"datasource-item-clicked\",\n datasourceId: datasourceEl.dataset.upstartDatasource as string,\n recordId: datasourceEl.dataset.upstartRecordId as string,\n });\n return;\n }\n\n const element = target.closest<HTMLElement>(\"[data-upstart-hash]\");\n // const i18nKey = target.closest<HTMLElement>(\"[data-upstart-i18n]\");\n\n if (!element) {\n console.info(\"[Upstart Editor] Click ignored: no ancestral element with data-upstart-hash found\");\n return;\n }\n\n event.stopPropagation();\n\n console.log(\"Element clicked dataset:\", element.dataset);\n\n const hash = element.dataset.upstartHash as string;\n const componentName = element.dataset.upstartComponent;\n const filePath = element.dataset.upstartFile ?? \"\";\n const classNameId = element.dataset.upstartClassnameId ?? \"\";\n const rect = element.getBoundingClientRect();\n\n sendToParent({\n type: \"element-clicked\",\n hash,\n componentName,\n filePath,\n classNameId,\n currentClassName: element.className,\n bounds: {\n top: rect.top,\n left: rect.left,\n width: rect.width,\n height: rect.height,\n right: rect.right,\n bottom: rect.bottom,\n },\n });\n\n console.log(\"[Upstart Editor] Element clicked:\", componentName, hash);\n}\n"],"mappings":";;;;AAGA,IAAI,gBAAgB;;;;AAKpB,SAAgB,mBAAyB;AACvC,KAAI,OAAO,aAAa,YACtB;AAGF,KAAI,cACF;AAGF,SAAQ,IAAI,iDAAiD;AAC7D,UAAS,iBAAiB,SAAS,aAAa,KAAK;AACrD,iBAAgB;;;;;AAMlB,SAAgB,sBAA4B;AAC1C,UAAS,oBAAoB,SAAS,aAAa,KAAK;AACxD,iBAAgB;;AAGlB,SAAS,YAAY,OAAyB;AAC5C,KAAI,gBAAgB,KAAK,OACvB;AAGF,SAAQ,MAAM,iCAAiC,MAAM;CAErD,MAAM,SAAS,MAAM;AACrB,KAAI,CAAC,QAAQ;AACX,UAAQ,KAAK,sDAAsD;AACnE;;CAGF,MAAM,aAAa,OAAO,QAAQ,gCAAgC;CAClE,MAAM,qBAAqB,QACxB,QAA+B,QAAQ,kCAAkC,CAC3E;AACD,KAAI,cAAc,CAAC,mBACjB,OAAM,gBAAgB;AAGxB,KAAI,OAAO,QAAQ,2BAA2B,EAAE;AAC9C,UAAQ,KAAK,6EAA6E;AAC1F;;CAIF,MAAM,eAAe,OAAO,QAAqB,oDAAoD;AACrG,KAAI,cAAc;AAChB,QAAM,iBAAiB;AACvB,eAAa;GACX,MAAM;GACN,cAAc,aAAa,QAAQ;GACnC,UAAU,aAAa,QAAQ;GAChC,CAAC;AACF;;CAGF,MAAM,UAAU,OAAO,QAAqB,sBAAsB;AAGlE,KAAI,CAAC,SAAS;AACZ,UAAQ,KAAK,oFAAoF;AACjG;;AAGF,OAAM,iBAAiB;AAEvB,SAAQ,IAAI,4BAA4B,QAAQ,QAAQ;CAExD,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,gBAAgB,QAAQ,QAAQ;CACtC,MAAM,WAAW,QAAQ,QAAQ,eAAe;CAChD,MAAM,cAAc,QAAQ,QAAQ,sBAAsB;CAC1D,MAAM,OAAO,QAAQ,uBAAuB;AAE5C,cAAa;EACX,MAAM;EACN;EACA;EACA;EACA;EACA,kBAAkB,QAAQ;EAC1B,QAAQ;GACN,KAAK,KAAK;GACV,MAAM,KAAK;GACX,OAAO,KAAK;GACZ,QAAQ,KAAK;GACb,OAAO,KAAK;GACZ,QAAQ,KAAK;GACd;EACF,CAAC;AAEF,SAAQ,IAAI,qCAAqC,eAAe,KAAK"}
1
+ {"version":3,"file":"click-handler.js","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/click-handler.ts"],"sourcesContent":["import { getCurrentMode } from \"./state.js\";\nimport { sendToParent } from \"./utils.js\";\n\nlet isInitialized = false;\n\nconst DAISY_VAR_NAMES = [\n \"base-100\",\n \"base-200\",\n \"base-300\",\n \"base-content\",\n \"primary\",\n \"primary-content\",\n \"secondary\",\n \"secondary-content\",\n \"accent\",\n \"accent-content\",\n \"neutral\",\n \"neutral-content\",\n \"info\",\n \"info-content\",\n \"success\",\n \"success-content\",\n \"warning\",\n \"warning-content\",\n \"error\",\n \"error-content\",\n];\n\n/**\n * Initialize click handler for className editing.\n */\nexport function initClickHandler(): void {\n if (typeof document === \"undefined\") {\n return;\n }\n\n if (isInitialized) {\n return;\n }\n\n console.log(\"[Upstart Editor] Initializing click handler...\");\n document.addEventListener(\"click\", handleClick, true);\n isInitialized = true;\n}\n\n/**\n * Cleanup click handler.\n */\nexport function cleanupClickHandler(): void {\n document.removeEventListener(\"click\", handleClick, true);\n isInitialized = false;\n}\n\nfunction handleClick(event: MouseEvent): void {\n if (getCurrentMode() !== \"edit\") {\n return;\n }\n\n console.debug(\"[Upstart Editor] Click event:\", event);\n\n const target = event.target as HTMLElement | null;\n if (!target) {\n console.warn(\"[Upstart Editor] Click target is not an HTMLElement\");\n return;\n }\n\n if (target.closest(\".upstart-editor-bubble-menu\")) {\n console.info(\"[Upstart Editor] Click ignored: target is inside the bubble menu\");\n return;\n }\n\n const activeLink = target.closest(\"a[data-upstart-editor-active]\");\n const isFormControlClick = Boolean(\n (target as HTMLElement | null)?.closest(\"input, textarea, select, button\"),\n );\n if (activeLink || !isFormControlClick) {\n event.preventDefault();\n }\n\n // Clicks inside contenteditable (TipTap active) — still notify style panel if applicable\n if (target.closest(\"[contenteditable='true']\")) {\n const editableEl = target.closest<HTMLElement>(\"[data-upstart-editable-text='true']\");\n if (editableEl) {\n const classNameId =\n editableEl.dataset.upstartClassnameId ||\n editableEl.closest<HTMLElement>(\"[data-upstart-classname-id]\")?.dataset.upstartClassnameId ||\n \"\";\n if (classNameId) {\n const computedStyle = getComputedStyle(document.documentElement);\n const themeColors: Record<string, string> = {};\n for (const name of DAISY_VAR_NAMES) {\n const val = computedStyle.getPropertyValue(`--color-${name}`).trim();\n if (val) themeColors[name] = val;\n }\n sendToParent({\n type: \"element-clicked\",\n hash: editableEl.dataset.upstartHash ?? \"\",\n componentName: editableEl.dataset.upstartComponent,\n filePath: editableEl.dataset.upstartFile ?? \"\",\n classNameId,\n currentClassName: editableEl.className,\n themeColors,\n bounds: { top: 0, left: 0, width: 0, height: 0, right: 0, bottom: 0 },\n });\n }\n }\n console.info(\"[Upstart Editor] Click inside contenteditable: TipTap handles text editing\");\n return;\n }\n\n // Find the closest element with data-upstart-hash\n const element = target.closest<HTMLElement>(\"[data-upstart-hash]\");\n if (!element) {\n console.info(\"[Upstart Editor] Click ignored: no ancestral element with data-upstart-hash found\");\n return;\n }\n\n event.stopPropagation();\n\n console.log(\"Element clicked dataset:\", element.dataset);\n\n const hash = element.dataset.upstartHash as string;\n const componentName = element.dataset.upstartComponent;\n const filePath = element.dataset.upstartFile ?? \"\";\n\n // If this element has editable-text=\"true\", let TipTap handle text editing\n if (element.dataset.upstartEditableText === \"true\") {\n const proseMirror = element.querySelector<HTMLElement>(\".ProseMirror\");\n if (proseMirror) {\n proseMirror.focus();\n }\n }\n\n // Find classNameId on the element itself, or walk up to find it on a parent\n let classNameId = element.dataset.upstartClassnameId ?? \"\";\n let currentClassName = element.className;\n if (!classNameId) {\n const parentWithClassname = element.closest<HTMLElement>(\"[data-upstart-classname-id]\");\n if (parentWithClassname) {\n classNameId = parentWithClassname.dataset.upstartClassnameId ?? \"\";\n currentClassName = parentWithClassname.className;\n }\n }\n\n // Check if this element (or a nearby ancestor) is a datasource record\n const datasourceEl = target.closest<HTMLElement>(\"[data-upstart-datasource][data-upstart-record-id]\");\n const datasourceId = datasourceEl?.dataset.upstartDatasource;\n const recordId = datasourceEl?.dataset.upstartRecordId;\n\n // Read DaisyUI CSS custom property values from the iframe's document\n const computedStyle = getComputedStyle(document.documentElement);\n const themeColors: Record<string, string> = {};\n for (const name of DAISY_VAR_NAMES) {\n const val = computedStyle.getPropertyValue(`--color-${name}`).trim();\n if (val) themeColors[name] = val;\n }\n\n const rect = element.getBoundingClientRect();\n\n sendToParent({\n type: \"element-clicked\",\n hash,\n componentName,\n filePath,\n classNameId,\n currentClassName,\n datasourceId,\n recordId,\n themeColors,\n bounds: {\n top: rect.top,\n left: rect.left,\n width: rect.width,\n height: rect.height,\n right: rect.right,\n bottom: rect.bottom,\n },\n });\n\n console.log(\"[Upstart Editor] Element clicked:\", componentName, hash);\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"error-handler.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/error-handler.ts"],"sourcesContent":[],"mappings":";iBAEgB,gBAAA,CAAA"}
1
+ {"version":3,"file":"error-handler.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/error-handler.ts"],"mappings":";iBAEgB,gBAAA,CAAA"}
@@ -1,5 +1,4 @@
1
1
  import { sendToParent } from "./utils.js";
2
-
3
2
  //#region src/vite-plugin-upstart-editor/runtime/error-handler.ts
4
3
  function initErrorHandler() {
5
4
  window.addEventListener("error", (event) => {
@@ -10,7 +9,7 @@ function initErrorHandler() {
10
9
  });
11
10
  });
12
11
  }
13
-
14
12
  //#endregion
15
13
  export { initErrorHandler };
14
+
16
15
  //# sourceMappingURL=error-handler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"error-handler.js","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/error-handler.ts"],"sourcesContent":["import { sendToParent } from \"./utils.js\";\n\nexport function initErrorHandler() {\n // Global error handler for uncaught errors in the editor\n window.addEventListener(\"error\", (event) => {\n console.error(\"[Upstart Editor] Uncaught error in editor:\", event.error);\n sendToParent({\n type: \"editor-error\",\n error: event.error,\n });\n });\n}\n"],"mappings":";;;AAEA,SAAgB,mBAAmB;AAEjC,QAAO,iBAAiB,UAAU,UAAU;AAC1C,UAAQ,MAAM,8CAA8C,MAAM,MAAM;AACxE,eAAa;GACX,MAAM;GACN,OAAO,MAAM;GACd,CAAC;GACF"}
1
+ {"version":3,"file":"error-handler.js","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/error-handler.ts"],"sourcesContent":["import { sendToParent } from \"./utils.js\";\n\nexport function initErrorHandler() {\n // Global error handler for uncaught errors in the editor\n window.addEventListener(\"error\", (event) => {\n console.error(\"[Upstart Editor] Uncaught error in editor:\", event.error);\n sendToParent({\n type: \"editor-error\",\n error: event.error,\n });\n });\n}\n"],"mappings":";;AAEA,SAAgB,mBAAmB;AAEjC,QAAO,iBAAiB,UAAU,UAAU;AAC1C,UAAQ,MAAM,8CAA8C,MAAM,MAAM;AACxE,eAAa;GACX,MAAM;GACN,OAAO,MAAM;GACd,CAAC;GACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"hover-overlay.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/hover-overlay.ts"],"sourcesContent":[],"mappings":";;AAWA;AAsBA;iBAtBgB,gBAAA,CAAA;;;;iBAsBA,YAAA,CAAA"}
1
+ {"version":3,"file":"hover-overlay.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/hover-overlay.ts"],"mappings":";;AAYA;;iBAAgB,gBAAA,CAAA;;;AAiDhB;iBAAgB,YAAA,CAAA"}
@@ -1,9 +1,8 @@
1
- import { sendToParent } from "./utils.js";
2
- import { getCurrentMode } from "./index.js";
3
-
1
+ import { getCurrentMode } from "../../src/vite-plugin-upstart-editor/runtime/state.js";
4
2
  //#region src/vite-plugin-upstart-editor/runtime/hover-overlay.ts
5
3
  let overlay = null;
6
4
  let currentTarget = null;
5
+ let hoveredEl = null;
7
6
  let isInitialized = false;
8
7
  let rafId = null;
9
8
  /**
@@ -13,8 +12,34 @@ function initHoverOverlay() {
13
12
  if (typeof document === "undefined") return;
14
13
  if (isInitialized) return;
15
14
  console.log("[Upstart Editor] Initializing hover overlay...");
15
+ const style = document.createElement("style");
16
+ style.id = "upstart-hover-edit-styles";
17
+ style.textContent = [
18
+ ":root[data-upstart-edit-mode] [data-upstart-classname-id]:not([data-upstart-editable-text='true']),",
19
+ ":root[data-upstart-edit-mode] [data-upstart-datasource][data-upstart-record-id]:not([data-upstart-editable-text='true']),",
20
+ ":root[data-upstart-edit-mode] [data-upstart-editor-active] {",
21
+ " transition: outline 150ms, outline-offset 150ms;",
22
+ "}",
23
+ ":root[data-upstart-edit-mode] [data-upstart-hovered] {",
24
+ " outline: 2px solid rgba(114, 112, 198, 0.6);",
25
+ " outline-offset: 4px;",
26
+ " cursor: pointer;",
27
+ "}",
28
+ ":root[data-upstart-edit-mode] [data-upstart-editor-active][data-upstart-hovered] {",
29
+ " outline: 2px solid rgba(114, 112, 198, 0.6);",
30
+ " outline-offset: 4px;",
31
+ " cursor: text;",
32
+ "}",
33
+ ":root[data-upstart-edit-mode] [data-upstart-hovered-ancestor] {",
34
+ " outline: 1px dashed rgba(114, 112, 198, 0.6);",
35
+ " cursor: pointer;",
36
+ "}"
37
+ ].join("\n");
38
+ document.head.appendChild(style);
16
39
  document.addEventListener("mouseover", handleMouseOver);
17
40
  document.addEventListener("mouseout", handleMouseOut);
41
+ document.addEventListener("keydown", handleKeyChange);
42
+ document.addEventListener("keyup", handleKeyChange);
18
43
  window.addEventListener("scroll", scheduleOverlayUpdate, { passive: true });
19
44
  window.addEventListener("resize", scheduleOverlayUpdate, { passive: true });
20
45
  isInitialized = true;
@@ -27,32 +52,59 @@ function hideOverlays() {
27
52
  overlay.style.display = "none";
28
53
  currentTarget = null;
29
54
  }
55
+ clearEditableHover();
56
+ }
57
+ function isEligible(el) {
58
+ if (el.dataset.upstartEditableText === "true") return false;
59
+ return !!(el.dataset.upstartClassnameId || el.dataset.upstartDatasource && el.dataset.upstartRecordId);
60
+ }
61
+ function clearEditableHover() {
62
+ for (const el of document.querySelectorAll("[data-upstart-hovered],[data-upstart-hovered-ancestor]")) {
63
+ el.removeAttribute("data-upstart-hovered");
64
+ el.removeAttribute("data-upstart-hovered-ancestor");
65
+ }
66
+ hoveredEl = null;
67
+ }
68
+ function applyTextHover(el) {
69
+ clearEditableHover();
70
+ el.setAttribute("data-upstart-hovered", "");
71
+ hoveredEl = el;
72
+ }
73
+ function applyEditableHover(target, withAncestors) {
74
+ clearEditableHover();
75
+ let el = target;
76
+ while (el && el !== document.documentElement) {
77
+ if (isEligible(el)) break;
78
+ el = el.parentElement;
79
+ }
80
+ if (!el || el === document.documentElement) return;
81
+ el.setAttribute("data-upstart-hovered", "");
82
+ hoveredEl = el;
83
+ if (withAncestors) {
84
+ let ancestor = el.parentElement;
85
+ while (ancestor && ancestor !== document.documentElement) {
86
+ if (isEligible(ancestor)) ancestor.setAttribute("data-upstart-hovered-ancestor", "");
87
+ ancestor = ancestor.parentElement;
88
+ }
89
+ }
30
90
  }
31
91
  function handleMouseOver(event) {
32
92
  if (getCurrentMode() !== "edit") return;
33
93
  const target = event.target;
34
94
  if (!target) return;
95
+ const textEl = target.closest("[data-upstart-editor-active]");
96
+ if (textEl) {
97
+ applyTextHover(textEl);
98
+ if (overlay) overlay.style.display = "none";
99
+ currentTarget = null;
100
+ return;
101
+ }
102
+ applyEditableHover(target, event.metaKey || event.ctrlKey);
35
103
  const component = target.closest("[data-upstart-component]");
36
104
  if (!component || component.dataset.upstartEditorActive) return;
37
105
  if (!overlay) createOverlay();
38
106
  currentTarget = component;
39
107
  positionOverlay(component);
40
- const hash = component.dataset.upstartHash;
41
- if (hash) {
42
- const rect = component.getBoundingClientRect();
43
- sendToParent({
44
- type: "element-hovered",
45
- hash,
46
- bounds: {
47
- top: rect.top,
48
- left: rect.left,
49
- width: rect.width,
50
- height: rect.height,
51
- right: rect.right,
52
- bottom: rect.bottom
53
- }
54
- });
55
- }
56
108
  }
57
109
  function handleMouseOut(event) {
58
110
  const target = event.target;
@@ -63,10 +115,15 @@ function handleMouseOut(event) {
63
115
  if (relatedTarget && component.contains(relatedTarget)) return;
64
116
  hideOverlays();
65
117
  }
118
+ function handleKeyChange(event) {
119
+ if (getCurrentMode() !== "edit" || !hoveredEl) return;
120
+ if (event.key !== "Meta" && event.key !== "Control") return;
121
+ applyEditableHover(hoveredEl, event.type === "keydown" && (event.metaKey || event.ctrlKey));
122
+ }
66
123
  function createOverlay() {
67
124
  overlay = document.createElement("div");
68
125
  overlay.id = "upstart-hover-overlay";
69
- overlay.style.cssText = "position: absolute; pointer-events: none; border: 2px solid #3b82f6; background: rgba(59, 130, 246, 0.05); border-radius: 4px; z-index: 9999; transition: all 0.1s ease; display: none;";
126
+ overlay.style.cssText = "position: absolute; pointer-events: none; border: 2px solid rgba(114, 112, 198, 0.6); background: rgba(114, 112, 198, 0.05); border-radius: 4px; z-index: 9999; transition: all 0.1s ease; display: none;";
70
127
  document.body.appendChild(overlay);
71
128
  }
72
129
  function positionOverlay(element) {
@@ -85,7 +142,7 @@ function scheduleOverlayUpdate() {
85
142
  if (currentTarget && overlay && overlay.style.display === "block") positionOverlay(currentTarget);
86
143
  });
87
144
  }
88
-
89
145
  //#endregion
90
146
  export { hideOverlays, initHoverOverlay };
147
+
91
148
  //# sourceMappingURL=hover-overlay.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hover-overlay.js","names":["overlay: HTMLDivElement | null","currentTarget: HTMLElement | null","rafId: number | null"],"sources":["../../../src/vite-plugin-upstart-editor/runtime/hover-overlay.ts"],"sourcesContent":["import { getCurrentMode } from \"./index.js\";\nimport { sendToParent } from \"./utils.js\";\n\nlet overlay: HTMLDivElement | null = null;\nlet currentTarget: HTMLElement | null = null;\nlet isInitialized = false;\nlet rafId: number | null = null;\n\n/**\n * Initialize hover overlay.\n */\nexport function initHoverOverlay(): void {\n if (typeof document === \"undefined\") {\n return;\n }\n\n if (isInitialized) {\n return;\n }\n\n console.log(\"[Upstart Editor] Initializing hover overlay...\");\n\n document.addEventListener(\"mouseover\", handleMouseOver);\n document.addEventListener(\"mouseout\", handleMouseOut);\n window.addEventListener(\"scroll\", scheduleOverlayUpdate, { passive: true });\n window.addEventListener(\"resize\", scheduleOverlayUpdate, { passive: true });\n\n isInitialized = true;\n}\n\n/**\n * Hide all overlays.\n */\nexport function hideOverlays(): void {\n if (overlay) {\n overlay.style.display = \"none\";\n currentTarget = null;\n }\n}\n\nfunction handleMouseOver(event: MouseEvent): void {\n if (getCurrentMode() !== \"edit\") {\n return;\n }\n\n const target = event.target as HTMLElement | null;\n if (!target) {\n return;\n }\n\n const component = target.closest<HTMLElement>(\"[data-upstart-component]\");\n if (!component || component.dataset.upstartEditorActive) {\n return;\n }\n\n if (!overlay) {\n createOverlay();\n }\n\n currentTarget = component;\n positionOverlay(component);\n\n const hash = component.dataset.upstartHash;\n if (hash) {\n const rect = component.getBoundingClientRect();\n sendToParent({\n type: \"element-hovered\",\n hash,\n bounds: {\n top: rect.top,\n left: rect.left,\n width: rect.width,\n height: rect.height,\n right: rect.right,\n bottom: rect.bottom,\n },\n });\n }\n}\n\nfunction handleMouseOut(event: MouseEvent): void {\n const target = event.target as HTMLElement | null;\n const relatedTarget = event.relatedTarget as HTMLElement | null;\n\n if (!target) {\n return;\n }\n\n const component = target.closest<HTMLElement>(\"[data-upstart-component]\");\n if (!component) {\n return;\n }\n\n if (relatedTarget && component.contains(relatedTarget)) {\n return;\n }\n\n hideOverlays();\n}\n\nfunction createOverlay(): void {\n overlay = document.createElement(\"div\");\n overlay.id = \"upstart-hover-overlay\";\n overlay.style.cssText =\n \"position: absolute; pointer-events: none; border: 2px solid #3b82f6; \" +\n \"background: rgba(59, 130, 246, 0.05); border-radius: 4px; z-index: 9999; \" +\n \"transition: all 0.1s ease; display: none;\";\n document.body.appendChild(overlay);\n}\n\nfunction positionOverlay(element: HTMLElement): void {\n if (!overlay) {\n return;\n }\n\n const rect = element.getBoundingClientRect();\n overlay.style.top = `${rect.top + window.scrollY}px`;\n overlay.style.left = `${rect.left + window.scrollX}px`;\n overlay.style.width = `${rect.width}px`;\n overlay.style.height = `${rect.height}px`;\n overlay.style.display = \"block\";\n}\n\nfunction scheduleOverlayUpdate(): void {\n if (rafId !== null) {\n return;\n }\n\n rafId = requestAnimationFrame(() => {\n rafId = null;\n if (currentTarget && overlay && overlay.style.display === \"block\") {\n positionOverlay(currentTarget);\n }\n });\n}\n"],"mappings":";;;;AAGA,IAAIA,UAAiC;AACrC,IAAIC,gBAAoC;AACxC,IAAI,gBAAgB;AACpB,IAAIC,QAAuB;;;;AAK3B,SAAgB,mBAAyB;AACvC,KAAI,OAAO,aAAa,YACtB;AAGF,KAAI,cACF;AAGF,SAAQ,IAAI,iDAAiD;AAE7D,UAAS,iBAAiB,aAAa,gBAAgB;AACvD,UAAS,iBAAiB,YAAY,eAAe;AACrD,QAAO,iBAAiB,UAAU,uBAAuB,EAAE,SAAS,MAAM,CAAC;AAC3E,QAAO,iBAAiB,UAAU,uBAAuB,EAAE,SAAS,MAAM,CAAC;AAE3E,iBAAgB;;;;;AAMlB,SAAgB,eAAqB;AACnC,KAAI,SAAS;AACX,UAAQ,MAAM,UAAU;AACxB,kBAAgB;;;AAIpB,SAAS,gBAAgB,OAAyB;AAChD,KAAI,gBAAgB,KAAK,OACvB;CAGF,MAAM,SAAS,MAAM;AACrB,KAAI,CAAC,OACH;CAGF,MAAM,YAAY,OAAO,QAAqB,2BAA2B;AACzE,KAAI,CAAC,aAAa,UAAU,QAAQ,oBAClC;AAGF,KAAI,CAAC,QACH,gBAAe;AAGjB,iBAAgB;AAChB,iBAAgB,UAAU;CAE1B,MAAM,OAAO,UAAU,QAAQ;AAC/B,KAAI,MAAM;EACR,MAAM,OAAO,UAAU,uBAAuB;AAC9C,eAAa;GACX,MAAM;GACN;GACA,QAAQ;IACN,KAAK,KAAK;IACV,MAAM,KAAK;IACX,OAAO,KAAK;IACZ,QAAQ,KAAK;IACb,OAAO,KAAK;IACZ,QAAQ,KAAK;IACd;GACF,CAAC;;;AAIN,SAAS,eAAe,OAAyB;CAC/C,MAAM,SAAS,MAAM;CACrB,MAAM,gBAAgB,MAAM;AAE5B,KAAI,CAAC,OACH;CAGF,MAAM,YAAY,OAAO,QAAqB,2BAA2B;AACzE,KAAI,CAAC,UACH;AAGF,KAAI,iBAAiB,UAAU,SAAS,cAAc,CACpD;AAGF,eAAc;;AAGhB,SAAS,gBAAsB;AAC7B,WAAU,SAAS,cAAc,MAAM;AACvC,SAAQ,KAAK;AACb,SAAQ,MAAM,UACZ;AAGF,UAAS,KAAK,YAAY,QAAQ;;AAGpC,SAAS,gBAAgB,SAA4B;AACnD,KAAI,CAAC,QACH;CAGF,MAAM,OAAO,QAAQ,uBAAuB;AAC5C,SAAQ,MAAM,MAAM,GAAG,KAAK,MAAM,OAAO,QAAQ;AACjD,SAAQ,MAAM,OAAO,GAAG,KAAK,OAAO,OAAO,QAAQ;AACnD,SAAQ,MAAM,QAAQ,GAAG,KAAK,MAAM;AACpC,SAAQ,MAAM,SAAS,GAAG,KAAK,OAAO;AACtC,SAAQ,MAAM,UAAU;;AAG1B,SAAS,wBAA8B;AACrC,KAAI,UAAU,KACZ;AAGF,SAAQ,4BAA4B;AAClC,UAAQ;AACR,MAAI,iBAAiB,WAAW,QAAQ,MAAM,YAAY,QACxD,iBAAgB,cAAc;GAEhC"}
1
+ {"version":3,"file":"hover-overlay.js","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/hover-overlay.ts"],"sourcesContent":["import { getCurrentMode } from \"./state.js\";\nimport { sendToParent } from \"./utils.js\";\n\nlet overlay: HTMLDivElement | null = null;\nlet currentTarget: HTMLElement | null = null;\nlet hoveredEl: HTMLElement | null = null;\nlet isInitialized = false;\nlet rafId: number | null = null;\n\n/**\n * Initialize hover overlay.\n */\nexport function initHoverOverlay(): void {\n if (typeof document === \"undefined\") {\n return;\n }\n\n if (isInitialized) {\n return;\n }\n\n console.log(\"[Upstart Editor] Initializing hover overlay...\");\n\n const style = document.createElement(\"style\");\n style.id = \"upstart-hover-edit-styles\";\n style.textContent = [\n \":root[data-upstart-edit-mode] [data-upstart-classname-id]:not([data-upstart-editable-text='true']),\",\n \":root[data-upstart-edit-mode] [data-upstart-datasource][data-upstart-record-id]:not([data-upstart-editable-text='true']),\",\n \":root[data-upstart-edit-mode] [data-upstart-editor-active] {\",\n \" transition: outline 150ms, outline-offset 150ms;\",\n \"}\",\n \":root[data-upstart-edit-mode] [data-upstart-hovered] {\",\n \" outline: 2px solid rgba(114, 112, 198, 0.6);\",\n \" outline-offset: 4px;\",\n \" cursor: pointer;\",\n \"}\",\n \":root[data-upstart-edit-mode] [data-upstart-editor-active][data-upstart-hovered] {\",\n \" outline: 2px solid rgba(114, 112, 198, 0.6);\",\n \" outline-offset: 4px;\",\n \" cursor: text;\",\n \"}\",\n \":root[data-upstart-edit-mode] [data-upstart-hovered-ancestor] {\",\n \" outline: 1px dashed rgba(114, 112, 198, 0.6);\",\n \" cursor: pointer;\",\n \"}\",\n ].join(\"\\n\");\n document.head.appendChild(style);\n\n document.addEventListener(\"mouseover\", handleMouseOver);\n document.addEventListener(\"mouseout\", handleMouseOut);\n document.addEventListener(\"keydown\", handleKeyChange);\n document.addEventListener(\"keyup\", handleKeyChange);\n window.addEventListener(\"scroll\", scheduleOverlayUpdate, { passive: true });\n window.addEventListener(\"resize\", scheduleOverlayUpdate, { passive: true });\n\n isInitialized = true;\n}\n\n/**\n * Hide all overlays.\n */\nexport function hideOverlays(): void {\n if (overlay) {\n overlay.style.display = \"none\";\n currentTarget = null;\n }\n clearEditableHover();\n}\n\nfunction isEligible(el: HTMLElement): boolean {\n if (el.dataset.upstartEditableText === \"true\") return false;\n return !!(el.dataset.upstartClassnameId || (el.dataset.upstartDatasource && el.dataset.upstartRecordId));\n}\n\nfunction clearEditableHover(): void {\n for (const el of document.querySelectorAll(\"[data-upstart-hovered],[data-upstart-hovered-ancestor]\")) {\n (el as HTMLElement).removeAttribute(\"data-upstart-hovered\");\n (el as HTMLElement).removeAttribute(\"data-upstart-hovered-ancestor\");\n }\n hoveredEl = null;\n}\n\nfunction applyTextHover(el: HTMLElement): void {\n clearEditableHover();\n el.setAttribute(\"data-upstart-hovered\", \"\");\n hoveredEl = el;\n}\n\nfunction applyEditableHover(target: HTMLElement, withAncestors: boolean): void {\n clearEditableHover();\n\n // Find closest eligible element\n let el: HTMLElement | null = target;\n while (el && el !== document.documentElement) {\n if (isEligible(el)) break;\n el = el.parentElement;\n }\n if (!el || el === document.documentElement) return;\n\n el.setAttribute(\"data-upstart-hovered\", \"\");\n hoveredEl = el;\n\n if (withAncestors) {\n let ancestor = el.parentElement;\n while (ancestor && ancestor !== document.documentElement) {\n if (isEligible(ancestor)) {\n ancestor.setAttribute(\"data-upstart-hovered-ancestor\", \"\");\n }\n ancestor = ancestor.parentElement;\n }\n }\n}\n\nfunction handleMouseOver(event: MouseEvent): void {\n if (getCurrentMode() !== \"edit\") {\n return;\n }\n\n const target = event.target as HTMLElement | null;\n if (!target) {\n return;\n }\n\n // If hovering inside an active text editor, highlight only that element\n // and suppress the component overlay box so parents don't get hover effects.\n const textEl = target.closest<HTMLElement>(\"[data-upstart-editor-active]\");\n if (textEl) {\n applyTextHover(textEl);\n if (overlay) overlay.style.display = \"none\";\n currentTarget = null;\n return;\n }\n\n applyEditableHover(target, event.metaKey || event.ctrlKey);\n\n const component = target.closest<HTMLElement>(\"[data-upstart-component]\");\n if (!component || component.dataset.upstartEditorActive) {\n return;\n }\n\n if (!overlay) {\n createOverlay();\n }\n\n currentTarget = component;\n positionOverlay(component);\n}\n\nfunction handleMouseOut(event: MouseEvent): void {\n const target = event.target as HTMLElement | null;\n const relatedTarget = event.relatedTarget as HTMLElement | null;\n\n if (!target) {\n return;\n }\n\n const component = target.closest<HTMLElement>(\"[data-upstart-component]\");\n if (!component) {\n return;\n }\n\n if (relatedTarget && component.contains(relatedTarget)) {\n return;\n }\n\n hideOverlays();\n}\n\nfunction handleKeyChange(event: KeyboardEvent): void {\n if (getCurrentMode() !== \"edit\" || !hoveredEl) return;\n if (event.key !== \"Meta\" && event.key !== \"Control\") return;\n applyEditableHover(hoveredEl, event.type === \"keydown\" && (event.metaKey || event.ctrlKey));\n}\n\nfunction createOverlay(): void {\n overlay = document.createElement(\"div\");\n overlay.id = \"upstart-hover-overlay\";\n overlay.style.cssText =\n \"position: absolute; pointer-events: none; border: 2px solid rgba(114, 112, 198, 0.6); \" +\n \"background: rgba(114, 112, 198, 0.05); border-radius: 4px; z-index: 9999; \" +\n \"transition: all 0.1s ease; display: none;\";\n document.body.appendChild(overlay);\n}\n\nfunction positionOverlay(element: HTMLElement): void {\n if (!overlay) {\n return;\n }\n\n const rect = element.getBoundingClientRect();\n overlay.style.top = `${rect.top + window.scrollY}px`;\n overlay.style.left = `${rect.left + window.scrollX}px`;\n overlay.style.width = `${rect.width}px`;\n overlay.style.height = `${rect.height}px`;\n overlay.style.display = \"block\";\n}\n\nfunction scheduleOverlayUpdate(): void {\n if (rafId !== null) {\n return;\n }\n\n rafId = requestAnimationFrame(() => {\n rafId = null;\n if (currentTarget && overlay && overlay.style.display === \"block\") {\n positionOverlay(currentTarget);\n }\n });\n}\n"],"mappings":"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"}
@@ -2,22 +2,20 @@ import { EditorMessage, EditorMode, UpstartEditorMessage } from "./types.js";
2
2
  import { initClickHandler } from "./click-handler.js";
3
3
  import { initErrorHandler } from "./error-handler.js";
4
4
  import { initHoverOverlay } from "./hover-overlay.js";
5
+ import { getCurrentMode } from "../../src/vite-plugin-upstart-editor/runtime/state.js";
5
6
  import { initTextEditor } from "./text-editor.js";
6
7
  import { sendToParent } from "./utils.js";
7
8
 
8
9
  //#region src/vite-plugin-upstart-editor/runtime/index.d.ts
9
- /**
10
- * Get the current editor mode.
11
- */
12
- declare function getCurrentMode(): EditorMode;
13
10
  /**
14
11
  * Set the current editor mode.
15
12
  */
16
13
  declare function setMode(mode: EditorMode): void;
14
+ declare function waitForHydration(callback: () => void): void;
17
15
  /**
18
16
  * Initialize the Upstart editor runtime.
19
17
  */
20
18
  declare function initUpstartEditor(): void;
21
19
  //#endregion
22
- export { type EditorMessage, type UpstartEditorMessage, getCurrentMode, initClickHandler, initErrorHandler, initHoverOverlay, initTextEditor, initUpstartEditor, sendToParent, setMode };
20
+ export { type EditorMessage, type UpstartEditorMessage, getCurrentMode, initClickHandler, initErrorHandler, initHoverOverlay, initTextEditor, initUpstartEditor, sendToParent, setMode, waitForHydration };
23
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;iBAagB,cAAA,CAAA,GAAkB;;;;AAAlB,iBAOA,OAAA,CAPc,IAAI,EAOJ,UAPc,CAAA,EAAA,IAAA;AAO5C;AAaA;;iBAAgB,iBAAA,CAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/index.ts"],"mappings":";;;;;;;;;;;;iBAegB,OAAA,CAAQ,IAAA,EAAM,UAAA;AAAA,iBAUd,gBAAA,CAAiB,QAAA;;AAVjC;;iBA6CgB,iBAAA,CAAA"}
@@ -1,35 +1,45 @@
1
+ import { getCurrentMode, setCurrentMode } from "../../src/vite-plugin-upstart-editor/runtime/state.js";
1
2
  import { sendToParent } from "./utils.js";
2
3
  import { initClickHandler } from "./click-handler.js";
3
4
  import { hideOverlays, initHoverOverlay } from "./hover-overlay.js";
4
5
  import { initErrorHandler } from "./error-handler.js";
5
6
  import { activateAllEditors, destroyAllActiveEditors, initTextEditor } from "./text-editor.js";
6
-
7
7
  //#region src/vite-plugin-upstart-editor/runtime/index.ts
8
- let currentMode = "preview";
9
8
  let isInitialized = false;
10
9
  /**
11
- * Get the current editor mode.
12
- */
13
- function getCurrentMode() {
14
- return currentMode;
15
- }
16
- /**
17
10
  * Set the current editor mode.
18
11
  */
19
12
  function setMode(mode) {
20
- currentMode = mode;
13
+ setCurrentMode(mode);
21
14
  console.log(`[Upstart Editor] Setting mode to: ${mode}`);
22
15
  if (mode === "edit") enableEditMode();
23
16
  else disableEditMode();
24
17
  }
18
+ function waitForHydration(callback) {
19
+ const STABILITY_MS = 200;
20
+ const onReady = () => {
21
+ let timer = null;
22
+ const settle = () => {
23
+ if (timer) clearTimeout(timer);
24
+ timer = window.setTimeout(() => {
25
+ observer.disconnect();
26
+ callback();
27
+ }, STABILITY_MS);
28
+ };
29
+ const observer = new MutationObserver(settle);
30
+ observer.observe(document.documentElement, {
31
+ childList: true,
32
+ subtree: true
33
+ });
34
+ settle();
35
+ };
36
+ if (document.readyState === "complete") onReady();
37
+ else window.addEventListener("load", onReady, { once: true });
38
+ }
25
39
  /**
26
40
  * Initialize the Upstart editor runtime.
27
41
  */
28
42
  function initUpstartEditor() {
29
- if (typeof window === "undefined") {
30
- console.warn("[Upstart Editor] Cannot initialize editor: not running in a browser environment");
31
- return;
32
- }
33
43
  if (isInitialized) {
34
44
  console.log("[Upstart Editor] Editor is already initialized");
35
45
  return;
@@ -85,18 +95,23 @@ function handleParentMessage(event) {
85
95
  if (message.type === "set-mode") {
86
96
  console.log("Setting editor mode to:", message.mode);
87
97
  setMode(message.mode);
98
+ } else if (message.type === "preview-classname") {
99
+ const el = document.querySelector(`[data-upstart-classname-id="${message.classNameId}"]`);
100
+ if (el) el.className = message.className;
88
101
  }
89
102
  }
90
103
  function enableEditMode() {
91
104
  console.log("[Upstart Editor] Edit mode enabled");
105
+ document.documentElement.setAttribute("data-upstart-edit-mode", "");
92
106
  activateAllEditors();
93
107
  }
94
108
  function disableEditMode() {
95
109
  console.log("[Upstart Editor] Preview mode enabled");
110
+ document.documentElement.removeAttribute("data-upstart-edit-mode");
96
111
  destroyAllActiveEditors();
97
112
  hideOverlays();
98
113
  }
99
-
100
114
  //#endregion
101
- export { getCurrentMode, initClickHandler, initErrorHandler, initHoverOverlay, initTextEditor, initUpstartEditor, sendToParent, setMode };
115
+ export { getCurrentMode, initClickHandler, initErrorHandler, initHoverOverlay, initTextEditor, initUpstartEditor, sendToParent, setMode, waitForHydration };
116
+
102
117
  //# sourceMappingURL=index.js.map