@upstart.gg/vite-plugins 0.1.40 → 0.1.42

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 (37) hide show
  1. package/dist/upstart-editor-api.d.ts +59 -1
  2. package/dist/upstart-editor-api.d.ts.map +1 -1
  3. package/dist/upstart-editor-api.js +273 -4
  4. package/dist/upstart-editor-api.js.map +1 -1
  5. package/dist/vite-plugin-upstart-attrs.d.ts +2 -1
  6. package/dist/vite-plugin-upstart-attrs.d.ts.map +1 -1
  7. package/dist/vite-plugin-upstart-attrs.js +91 -4
  8. package/dist/vite-plugin-upstart-attrs.js.map +1 -1
  9. package/dist/vite-plugin-upstart-editor/runtime/array-controls.js +342 -0
  10. package/dist/vite-plugin-upstart-editor/runtime/array-controls.js.map +1 -0
  11. package/dist/vite-plugin-upstart-editor/runtime/click-handler.d.ts.map +1 -1
  12. package/dist/vite-plugin-upstart-editor/runtime/click-handler.js +29 -2
  13. package/dist/vite-plugin-upstart-editor/runtime/click-handler.js.map +1 -1
  14. package/dist/vite-plugin-upstart-editor/runtime/form-guard.js +80 -0
  15. package/dist/vite-plugin-upstart-editor/runtime/form-guard.js.map +1 -0
  16. package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.d.ts.map +1 -1
  17. package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js +2 -1
  18. package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js.map +1 -1
  19. package/dist/vite-plugin-upstart-editor/runtime/index.d.ts.map +1 -1
  20. package/dist/vite-plugin-upstart-editor/runtime/index.js +26 -4
  21. package/dist/vite-plugin-upstart-editor/runtime/index.js.map +1 -1
  22. package/dist/vite-plugin-upstart-editor/runtime/text-editor.d.ts.map +1 -1
  23. package/dist/vite-plugin-upstart-editor/runtime/text-editor.js +40 -36
  24. package/dist/vite-plugin-upstart-editor/runtime/text-editor.js.map +1 -1
  25. package/dist/vite-plugin-upstart-editor/runtime/types.d.ts +20 -4
  26. package/dist/vite-plugin-upstart-editor/runtime/types.d.ts.map +1 -1
  27. package/package.json +3 -3
  28. package/src/tests/vite-plugin-upstart-attrs.test.ts +412 -0
  29. package/src/upstart-editor-api.ts +314 -5
  30. package/src/vite-plugin-upstart-attrs.ts +154 -4
  31. package/src/vite-plugin-upstart-editor/runtime/array-controls.ts +478 -0
  32. package/src/vite-plugin-upstart-editor/runtime/click-handler.ts +43 -0
  33. package/src/vite-plugin-upstart-editor/runtime/form-guard.ts +121 -0
  34. package/src/vite-plugin-upstart-editor/runtime/hover-overlay.ts +6 -1
  35. package/src/vite-plugin-upstart-editor/runtime/index.ts +20 -4
  36. package/src/vite-plugin-upstart-editor/runtime/text-editor.ts +49 -58
  37. package/src/vite-plugin-upstart-editor/runtime/types.ts +31 -4
@@ -1 +1 @@
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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"}
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-image-id],\",\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 !!(\n el.dataset.upstartClassnameId ||\n el.dataset.upstartImageId ||\n (el.dataset.upstartDatasource && el.dataset.upstartRecordId)\n );\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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"}
@@ -1 +1 @@
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
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/index.ts"],"mappings":";;;;;;;;;;;;iBAiBgB,OAAA,CAAQ,IAAA,EAAM,UAAA;AAAA,iBAUd,gBAAA,CAAiB,QAAA;;AAVjC;;iBA6CgB,iBAAA,CAAA"}
@@ -4,6 +4,8 @@ import { initClickHandler } from "./click-handler.js";
4
4
  import { hideOverlays, initHoverOverlay } from "./hover-overlay.js";
5
5
  import { initErrorHandler } from "./error-handler.js";
6
6
  import { activateAllEditors, destroyAllActiveEditors, initTextEditor } from "./text-editor.js";
7
+ import { hideArrayControls, initArrayControls, refreshArrayControls } from "./array-controls.js";
8
+ import { initFormGuard } from "./form-guard.js";
7
9
  //#region src/vite-plugin-upstart-editor/runtime/index.ts
8
10
  let isInitialized = false;
9
11
  /**
@@ -48,26 +50,41 @@ function initUpstartEditor() {
48
50
  console.log("[Upstart Editor] Initializing...");
49
51
  isInitialized = true;
50
52
  window.addEventListener("message", handleParentMessage);
53
+ const currentPath = () => location.pathname + location.search;
51
54
  window.addEventListener("popstate", () => {
52
- sendToParent({ type: "editor-navigated" });
55
+ sendToParent({
56
+ type: "editor-navigated",
57
+ path: currentPath()
58
+ });
53
59
  });
54
60
  const originalPushState = history.pushState.bind(history);
55
61
  history.pushState = (...args) => {
56
62
  originalPushState(...args);
57
- sendToParent({ type: "editor-navigated" });
63
+ sendToParent({
64
+ type: "editor-navigated",
65
+ path: currentPath()
66
+ });
58
67
  };
59
68
  const originalReplaceState = history.replaceState.bind(history);
60
69
  history.replaceState = (...args) => {
61
70
  originalReplaceState(...args);
62
- sendToParent({ type: "editor-navigated" });
71
+ sendToParent({
72
+ type: "editor-navigated",
73
+ path: currentPath()
74
+ });
63
75
  };
64
76
  const i18next = globalThis.__i18next;
65
77
  const getRawI18nTemplate = i18next ? (namespace, key) => i18next.getResource(i18next.language, namespace, key) : void 0;
66
78
  initTextEditor(getRawI18nTemplate ? { getRawI18nTemplate } : {});
67
79
  initClickHandler();
68
80
  initHoverOverlay();
81
+ initArrayControls();
82
+ initFormGuard();
69
83
  initErrorHandler();
70
- sendToParent({ type: "editor-ready" });
84
+ sendToParent({
85
+ type: "editor-ready",
86
+ path: currentPath()
87
+ });
71
88
  } catch (error) {
72
89
  console.error("[Upstart Editor] Initialization failed:", error);
73
90
  sendToParent({
@@ -110,6 +127,9 @@ function handleParentMessage(event) {
110
127
  }
111
128
  styleEl.textContent = message.previewCSS;
112
129
  } else if (styleEl) styleEl.textContent = "";
130
+ } else if (message.type === "preview-image") {
131
+ const el = document.querySelector(`[data-upstart-image-id="${message.imageId}"]`);
132
+ if (el) el.src = message.src;
113
133
  } else if (message.type === "request-scroll-position") sendToParent({
114
134
  type: "scroll-position",
115
135
  x: window.scrollX,
@@ -125,12 +145,14 @@ function enableEditMode() {
125
145
  console.log("[Upstart Editor] Edit mode enabled");
126
146
  document.documentElement.setAttribute("data-upstart-edit-mode", "");
127
147
  activateAllEditors();
148
+ refreshArrayControls();
128
149
  }
129
150
  function disableEditMode() {
130
151
  console.log("[Upstart Editor] Preview mode enabled");
131
152
  document.documentElement.removeAttribute("data-upstart-edit-mode");
132
153
  destroyAllActiveEditors();
133
154
  hideOverlays();
155
+ hideArrayControls();
134
156
  }
135
157
  //#endregion
136
158
  export { getCurrentMode, initClickHandler, initErrorHandler, initHoverOverlay, initTextEditor, initUpstartEditor, sendToParent, setMode, waitForHydration };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/index.ts"],"sourcesContent":["import { initClickHandler } from \"./click-handler.js\";\nimport { initHoverOverlay, hideOverlays } from \"./hover-overlay.js\";\nimport { initErrorHandler } from \"./error-handler.js\";\nimport { initTextEditor, activateAllEditors, destroyAllActiveEditors } from \"./text-editor.js\";\nimport { sendToParent } from \"./utils.js\";\nimport { getCurrentMode, setCurrentMode } from \"./state.js\";\nimport type { EditorMode, UpstartParentMessage } from \"./types.js\";\n\nlet isInitialized = false;\n\nexport { getCurrentMode };\n\n/**\n * Set the current editor mode.\n */\nexport function setMode(mode: EditorMode): void {\n setCurrentMode(mode);\n console.log(`[Upstart Editor] Setting mode to: ${mode}`);\n if (mode === \"edit\") {\n enableEditMode();\n } else {\n disableEditMode();\n }\n}\n\nexport function waitForHydration(callback: () => void): void {\n // Remix/React 18 wraps hydrateRoot in startTransition, making hydration a\n // concurrent (low-priority) operation that can span many frames after the\n // load event. Instead of guessing a delay, we wait for the DOM to stabilise:\n // once 200 ms pass without any childList mutations, hydration is done.\n const STABILITY_MS = 200;\n\n const onReady = () => {\n let timer: number | null = null;\n\n const settle = () => {\n if (timer) clearTimeout(timer);\n timer = window.setTimeout(() => {\n observer.disconnect();\n callback();\n }, STABILITY_MS);\n };\n\n const observer = new MutationObserver(settle);\n observer.observe(document.documentElement, { childList: true, subtree: true });\n\n // Kick off the first timer (covers case where no mutations occur after load)\n settle();\n };\n\n if (document.readyState === \"complete\") {\n onReady();\n } else {\n window.addEventListener(\"load\", onReady, { once: true });\n }\n}\n\n/**\n * Initialize the Upstart editor runtime.\n */\nexport function initUpstartEditor(): void {\n if (isInitialized) {\n console.log(\"[Upstart Editor] Editor is already initialized\");\n return;\n }\n\n try {\n console.log(\"[Upstart Editor] Initializing...\");\n\n isInitialized = true;\n\n window.addEventListener(\"message\", handleParentMessage);\n\n // Notify parent on SPA navigation so it can resend the current editMode\n window.addEventListener(\"popstate\", () => {\n sendToParent({ type: \"editor-navigated\" });\n });\n const originalPushState = history.pushState.bind(history);\n history.pushState = (...args) => {\n originalPushState(...args);\n sendToParent({ type: \"editor-navigated\" });\n };\n const originalReplaceState = history.replaceState.bind(history);\n history.replaceState = (...args) => {\n originalReplaceState(...args);\n sendToParent({ type: \"editor-navigated\" });\n };\n\n // i18next integration: if the app exposes it on window.__i18next (set before hydration),\n // wire it up so template variables (e.g. {{year}}) render as non-editable atoms.\n const i18next = (globalThis as any).__i18next;\n const getRawI18nTemplate = i18next\n ? (namespace: string, key: string) =>\n i18next.getResource(i18next.language, namespace, key) as string | undefined\n : undefined;\n\n initTextEditor(getRawI18nTemplate ? { getRawI18nTemplate } : {});\n initClickHandler();\n initHoverOverlay();\n initErrorHandler();\n\n sendToParent({ type: \"editor-ready\" });\n } catch (error) {\n console.error(\"[Upstart Editor] Initialization failed:\", error);\n sendToParent({\n type: \"editor-error\",\n error: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n}\n\nconst ALLOWED_ORIGINS = [\"http://localhost:8080\", /upstart.gg$/];\n\nconst matchAllowedOrigins = (origin: string) => {\n return ALLOWED_ORIGINS.some((allowedOrigin) => {\n if (typeof allowedOrigin === \"string\") {\n return origin === allowedOrigin;\n } else if (allowedOrigin instanceof RegExp) {\n return allowedOrigin.test(origin);\n }\n return false;\n });\n};\n\nfunction handleParentMessage(event: MessageEvent): void {\n const message = event.data as UpstartParentMessage | undefined;\n\n console.log(\"[Upstart Editor] Received message from parent:\", { event, message });\n\n if (!message || !matchAllowedOrigins(event.origin)) {\n console.warn(\"[Upstart Editor] Ignoring message from unknown source:\", event.origin);\n return;\n }\n\n if (message.type === \"set-mode\") {\n console.log(\"Setting editor mode to:\", message.mode);\n setMode(message.mode);\n } else if (message.type === \"preview-classname\") {\n const el = document.querySelector<HTMLElement>(`[data-upstart-classname-id=\"${message.classNameId}\"]`);\n if (el) {\n el.className = message.className;\n }\n const STYLE_ID = \"upstart-preview-style\";\n let styleEl = document.getElementById(STYLE_ID) as HTMLStyleElement | null;\n if (message.previewCSS) {\n if (!styleEl) {\n styleEl = document.createElement(\"style\");\n styleEl.id = STYLE_ID;\n document.head.appendChild(styleEl);\n }\n styleEl.textContent = message.previewCSS;\n } else if (styleEl) {\n styleEl.textContent = \"\";\n }\n } else if (message.type === \"request-scroll-position\") {\n sendToParent({ type: \"scroll-position\", x: window.scrollX, y: window.scrollY });\n } else if (message.type === \"restore-scroll-position\") {\n window.scrollTo({ left: message.x, top: message.y, behavior: \"auto\" });\n }\n}\n\nfunction enableEditMode(): void {\n console.log(\"[Upstart Editor] Edit mode enabled\");\n document.documentElement.setAttribute(\"data-upstart-edit-mode\", \"\");\n activateAllEditors();\n}\n\nfunction disableEditMode(): void {\n console.log(\"[Upstart Editor] Preview mode enabled\");\n document.documentElement.removeAttribute(\"data-upstart-edit-mode\");\n destroyAllActiveEditors();\n hideOverlays();\n}\n\nexport { initTextEditor } from \"./text-editor.js\";\nexport { initClickHandler } from \"./click-handler.js\";\nexport { initErrorHandler } from \"./error-handler.js\";\nexport { initHoverOverlay } from \"./hover-overlay.js\";\nexport { sendToParent } from \"./utils.js\";\nexport type { EditorMessage, UpstartEditorMessage } from \"./types.js\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/index.ts"],"sourcesContent":["import { initClickHandler } from \"./click-handler.js\";\nimport { initHoverOverlay, hideOverlays } from \"./hover-overlay.js\";\nimport { initErrorHandler } from \"./error-handler.js\";\nimport { initTextEditor, activateAllEditors, destroyAllActiveEditors } from \"./text-editor.js\";\nimport { initArrayControls, refreshArrayControls, hideArrayControls } from \"./array-controls.js\";\nimport { initFormGuard } from \"./form-guard.js\";\nimport { sendToParent } from \"./utils.js\";\nimport { getCurrentMode, setCurrentMode } from \"./state.js\";\nimport type { EditorMode, UpstartParentMessage } from \"./types.js\";\n\nlet isInitialized = false;\n\nexport { getCurrentMode };\n\n/**\n * Set the current editor mode.\n */\nexport function setMode(mode: EditorMode): void {\n setCurrentMode(mode);\n console.log(`[Upstart Editor] Setting mode to: ${mode}`);\n if (mode === \"edit\") {\n enableEditMode();\n } else {\n disableEditMode();\n }\n}\n\nexport function waitForHydration(callback: () => void): void {\n // Remix/React 18 wraps hydrateRoot in startTransition, making hydration a\n // concurrent (low-priority) operation that can span many frames after the\n // load event. Instead of guessing a delay, we wait for the DOM to stabilise:\n // once 200 ms pass without any childList mutations, hydration is done.\n const STABILITY_MS = 200;\n\n const onReady = () => {\n let timer: number | null = null;\n\n const settle = () => {\n if (timer) clearTimeout(timer);\n timer = window.setTimeout(() => {\n observer.disconnect();\n callback();\n }, STABILITY_MS);\n };\n\n const observer = new MutationObserver(settle);\n observer.observe(document.documentElement, { childList: true, subtree: true });\n\n // Kick off the first timer (covers case where no mutations occur after load)\n settle();\n };\n\n if (document.readyState === \"complete\") {\n onReady();\n } else {\n window.addEventListener(\"load\", onReady, { once: true });\n }\n}\n\n/**\n * Initialize the Upstart editor runtime.\n */\nexport function initUpstartEditor(): void {\n if (isInitialized) {\n console.log(\"[Upstart Editor] Editor is already initialized\");\n return;\n }\n\n try {\n console.log(\"[Upstart Editor] Initializing...\");\n\n isInitialized = true;\n\n window.addEventListener(\"message\", handleParentMessage);\n\n // Current path (pathname + search), reported to the parent so it can reload\n // the iframe on the page being viewed instead of always the home page.\n const currentPath = () => location.pathname + location.search;\n\n // Notify parent on SPA navigation so it can resend the current editMode\n // and track the current page for the next reload.\n window.addEventListener(\"popstate\", () => {\n sendToParent({ type: \"editor-navigated\", path: currentPath() });\n });\n const originalPushState = history.pushState.bind(history);\n history.pushState = (...args) => {\n originalPushState(...args);\n sendToParent({ type: \"editor-navigated\", path: currentPath() });\n };\n const originalReplaceState = history.replaceState.bind(history);\n history.replaceState = (...args) => {\n originalReplaceState(...args);\n sendToParent({ type: \"editor-navigated\", path: currentPath() });\n };\n\n // i18next integration: if the app exposes it on window.__i18next (set before hydration),\n // wire it up so template variables (e.g. {{year}}) render as non-editable atoms.\n const i18next = (globalThis as any).__i18next;\n const getRawI18nTemplate = i18next\n ? (namespace: string, key: string) =>\n i18next.getResource(i18next.language, namespace, key) as string | undefined\n : undefined;\n\n initTextEditor(getRawI18nTemplate ? { getRawI18nTemplate } : {});\n initClickHandler();\n initHoverOverlay();\n initArrayControls();\n initFormGuard();\n initErrorHandler();\n\n sendToParent({ type: \"editor-ready\", path: currentPath() });\n } catch (error) {\n console.error(\"[Upstart Editor] Initialization failed:\", error);\n sendToParent({\n type: \"editor-error\",\n error: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n}\n\nconst ALLOWED_ORIGINS = [\"http://localhost:8080\", /upstart.gg$/];\n\nconst matchAllowedOrigins = (origin: string) => {\n return ALLOWED_ORIGINS.some((allowedOrigin) => {\n if (typeof allowedOrigin === \"string\") {\n return origin === allowedOrigin;\n } else if (allowedOrigin instanceof RegExp) {\n return allowedOrigin.test(origin);\n }\n return false;\n });\n};\n\nfunction handleParentMessage(event: MessageEvent): void {\n const message = event.data as UpstartParentMessage | undefined;\n\n console.log(\"[Upstart Editor] Received message from parent:\", { event, message });\n\n if (!message || !matchAllowedOrigins(event.origin)) {\n console.warn(\"[Upstart Editor] Ignoring message from unknown source:\", event.origin);\n return;\n }\n\n if (message.type === \"set-mode\") {\n console.log(\"Setting editor mode to:\", message.mode);\n setMode(message.mode);\n } else if (message.type === \"preview-classname\") {\n const el = document.querySelector<HTMLElement>(`[data-upstart-classname-id=\"${message.classNameId}\"]`);\n if (el) {\n el.className = message.className;\n }\n const STYLE_ID = \"upstart-preview-style\";\n let styleEl = document.getElementById(STYLE_ID) as HTMLStyleElement | null;\n if (message.previewCSS) {\n if (!styleEl) {\n styleEl = document.createElement(\"style\");\n styleEl.id = STYLE_ID;\n document.head.appendChild(styleEl);\n }\n styleEl.textContent = message.previewCSS;\n } else if (styleEl) {\n styleEl.textContent = \"\";\n }\n } else if (message.type === \"preview-image\") {\n const el = document.querySelector<HTMLImageElement>(`[data-upstart-image-id=\"${message.imageId}\"]`);\n if (el) {\n el.src = message.src;\n }\n } else if (message.type === \"request-scroll-position\") {\n sendToParent({ type: \"scroll-position\", x: window.scrollX, y: window.scrollY });\n } else if (message.type === \"restore-scroll-position\") {\n window.scrollTo({ left: message.x, top: message.y, behavior: \"auto\" });\n }\n}\n\nfunction enableEditMode(): void {\n console.log(\"[Upstart Editor] Edit mode enabled\");\n document.documentElement.setAttribute(\"data-upstart-edit-mode\", \"\");\n activateAllEditors();\n refreshArrayControls();\n}\n\nfunction disableEditMode(): void {\n console.log(\"[Upstart Editor] Preview mode enabled\");\n document.documentElement.removeAttribute(\"data-upstart-edit-mode\");\n destroyAllActiveEditors();\n hideOverlays();\n hideArrayControls();\n}\n\nexport { initTextEditor } from \"./text-editor.js\";\nexport { initClickHandler } from \"./click-handler.js\";\nexport { initErrorHandler } from \"./error-handler.js\";\nexport { initHoverOverlay } from \"./hover-overlay.js\";\nexport { sendToParent } from \"./utils.js\";\nexport type { EditorMessage, UpstartEditorMessage } from \"./types.js\";\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"text-editor.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/text-editor.ts"],"mappings":";;;;;AAiJA;;iBAAgB,cAAA,CAAe,OAAA,GAAS,oBAAA;;;AAiDxC;iBAAgB,kBAAA,CAAA;;;;iBAgBA,uBAAA,CAAA"}
1
+ {"version":3,"file":"text-editor.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/text-editor.ts"],"mappings":";;;;;AAqJA;;iBAAgB,cAAA,CAAe,OAAA,GAAS,oBAAA;;;AAyDxC;iBAAgB,kBAAA,CAAA;;;;iBAgBA,uBAAA,CAAA"}
@@ -182,6 +182,11 @@ function injectEditorStyles() {
182
182
  "}",
183
183
  "[data-upstart-editor-active] .ProseMirror:focus {",
184
184
  " outline: none !important;",
185
+ "}",
186
+ "[data-upstart-editor-active][data-upstart-editable-text-mode='plain'] .ProseMirror p,",
187
+ "[data-upstart-editor-active][data-upstart-editable-text-mode='direct'] .ProseMirror p {",
188
+ " margin: 0 !important;",
189
+ " padding: 0 !important;",
185
190
  "}"
186
191
  ].join("\n");
187
192
  document.head.appendChild(style);
@@ -205,11 +210,11 @@ function activateAllEditors() {
205
210
  */
206
211
  function destroyAllActiveEditors() {
207
212
  stopDomObserver();
208
- for (const hash of activeEditors.keys()) destroyEditor(hash);
213
+ for (const element of activeEditors.keys()) destroyEditor(element);
209
214
  }
210
215
  function setupEditableElement(element, options) {
211
216
  const hash = getEditableHash(element);
212
- if (!hash || activeEditors.has(hash)) return;
217
+ if (!hash || activeEditors.has(element)) return;
213
218
  cacheStyles(element);
214
219
  activateEditor(element, hash, options);
215
220
  }
@@ -218,31 +223,31 @@ function activateEditor(element, hash, options) {
218
223
  let editor;
219
224
  switch (mode) {
220
225
  case "direct":
221
- editor = createDirectEditor(element, hash, options);
226
+ editor = createDirectEditor(element, options);
222
227
  break;
223
228
  case "rich-panel":
224
- editor = createRichPanelEditor(element, hash, options);
229
+ editor = createRichPanelEditor(element, options);
225
230
  break;
226
231
  case "block-rich":
227
- editor = createRichTextEditor(element, hash, options);
232
+ editor = createRichTextEditor(element, options);
228
233
  break;
229
234
  case "inline-rich":
230
- editor = createInlineRichTextEditor(element, hash, options);
235
+ editor = createInlineRichTextEditor(element, options);
231
236
  break;
232
237
  case "plain":
233
- editor = createPlainTextEditor(element, hash, options);
238
+ editor = createPlainTextEditor(element, options);
234
239
  break;
235
240
  }
236
241
  element.dataset.upstartEditorActive = "true";
237
242
  applyActiveStyles(element);
238
- activeEditors.set(hash, {
243
+ activeEditors.set(element, {
239
244
  editor,
240
245
  element,
241
246
  hash,
242
247
  mode
243
248
  });
244
249
  }
245
- function createPlainTextEditor(element, hash, options) {
250
+ function createPlainTextEditor(element, options) {
246
251
  const renderedText = element.textContent ?? "";
247
252
  let content = renderedText;
248
253
  const extraExtensions = [];
@@ -288,16 +293,16 @@ function createPlainTextEditor(element, hash, options) {
288
293
  onUpdate: () => {
289
294
  if (i18nSyncInProgress) return;
290
295
  hasChanged = true;
291
- syncI18nSiblings(hash);
296
+ syncI18nSiblings(element);
292
297
  },
293
298
  onBlur: ({ editor: e }) => {
294
299
  if (!hasChanged) return;
295
300
  hasChanged = false;
296
- saveText(hash, e.getText());
301
+ saveText(element, e.getText());
297
302
  }
298
303
  });
299
304
  }
300
- function createRichTextEditor(element, hash, options) {
305
+ function createRichTextEditor(element, options) {
301
306
  const content = element.innerHTML;
302
307
  element.innerHTML = "";
303
308
  let hasChanged = false;
@@ -312,18 +317,18 @@ function createRichTextEditor(element, hash, options) {
312
317
  onUpdate: () => {
313
318
  if (i18nSyncInProgress) return;
314
319
  hasChanged = true;
315
- syncI18nSiblings(hash);
320
+ syncI18nSiblings(element);
316
321
  },
317
322
  onBlur: ({ editor: e }) => {
318
323
  if (!hasChanged) return;
319
324
  hasChanged = false;
320
- saveText(hash, e.getHTML());
325
+ saveText(element, e.getHTML());
321
326
  }
322
327
  });
323
328
  if (options.bubbleMenu) wireBubbleMenu(bubbleMenuElement, editor);
324
329
  return editor;
325
330
  }
326
- function createInlineRichTextEditor(element, hash, options) {
331
+ function createInlineRichTextEditor(element, options) {
327
332
  const content = element.innerHTML;
328
333
  element.innerHTML = "";
329
334
  let hasChanged = false;
@@ -352,18 +357,18 @@ function createInlineRichTextEditor(element, hash, options) {
352
357
  onUpdate: () => {
353
358
  if (i18nSyncInProgress) return;
354
359
  hasChanged = true;
355
- syncI18nSiblings(hash);
360
+ syncI18nSiblings(element);
356
361
  },
357
362
  onBlur: ({ editor: e }) => {
358
363
  if (!hasChanged) return;
359
364
  hasChanged = false;
360
- saveText(hash, e.getHTML());
365
+ saveText(element, e.getHTML());
361
366
  }
362
367
  });
363
368
  if (options.bubbleMenu) wireBubbleMenu(bubbleMenuElement, editor);
364
369
  return editor;
365
370
  }
366
- function createDirectEditor(element, hash, options) {
371
+ function createDirectEditor(element, options) {
367
372
  const content = element.innerHTML;
368
373
  element.innerHTML = "";
369
374
  let hasChanged = false;
@@ -393,13 +398,13 @@ function createDirectEditor(element, hash, options) {
393
398
  onBlur: ({ editor: e }) => {
394
399
  if (!hasChanged) return;
395
400
  hasChanged = false;
396
- saveText(hash, e.getHTML().replace(/^<p>([\s\S]*)<\/p>$/, "$1"));
401
+ saveText(element, e.getHTML().replace(/^<p>([\s\S]*)<\/p>$/, "$1"));
397
402
  }
398
403
  });
399
404
  if (options.bubbleMenu) wireBubbleMenu(bubbleMenuElement, editor);
400
405
  return editor;
401
406
  }
402
- function createRichPanelEditor(element, hash, options) {
407
+ function createRichPanelEditor(element, options) {
403
408
  const content = element.innerHTML;
404
409
  element.innerHTML = "";
405
410
  let hasChanged = false;
@@ -417,7 +422,7 @@ function createRichPanelEditor(element, hash, options) {
417
422
  onBlur: ({ editor: e }) => {
418
423
  if (!hasChanged) return;
419
424
  hasChanged = false;
420
- saveText(hash, e.getHTML());
425
+ saveText(element, e.getHTML());
421
426
  }
422
427
  });
423
428
  if (options.bubbleMenu) wireBubbleMenu(bubbleMenuElement, editor);
@@ -432,11 +437,11 @@ function getEditorMode(element, options) {
432
437
  if (options.richTextElements.includes(tagName)) return "block-rich";
433
438
  return "block-rich";
434
439
  }
435
- function syncI18nSiblings(sourceHash) {
436
- console.log("[Upstart Editor] Syncing i18n siblings for", sourceHash);
437
- const instance = activeEditors.get(sourceHash);
440
+ function syncI18nSiblings(sourceElement) {
441
+ const instance = activeEditors.get(sourceElement);
442
+ console.log("[Upstart Editor] Syncing i18n siblings for", instance?.hash);
438
443
  if (!instance) {
439
- console.warn("[Upstart Editor] No editor instance found for hash:", sourceHash);
444
+ console.warn("[Upstart Editor] No editor instance found for element:", sourceElement);
440
445
  return;
441
446
  }
442
447
  const i18nKey = instance.element.dataset.upstartI18n;
@@ -451,10 +456,9 @@ function syncI18nSiblings(sourceHash) {
451
456
  try {
452
457
  for (const sibling of siblings) {
453
458
  if (sibling === instance.element) continue;
454
- const siblingHash = sibling.dataset.upstartHash;
455
- const siblingInstance = siblingHash ? activeEditors.get(siblingHash) : null;
459
+ const siblingInstance = activeEditors.get(sibling);
456
460
  if (siblingInstance) {
457
- console.log(`[Upstart Editor] Updating sibling editor (hash: ${siblingHash}) with new content`, siblingInstance);
461
+ console.log(`[Upstart Editor] Updating sibling editor (hash: ${siblingInstance.hash}) with new content`, siblingInstance);
458
462
  sibling.innerText = plainContent;
459
463
  siblingInstance.editor.chain().selectAll().insertContent(plainContent).run();
460
464
  } else sibling.textContent = plainContent;
@@ -463,9 +467,9 @@ function syncI18nSiblings(sourceHash) {
463
467
  i18nSyncInProgress = false;
464
468
  }
465
469
  }
466
- function saveText(hash, newText) {
470
+ function saveText(element, newText) {
467
471
  try {
468
- const instance = activeEditors.get(hash);
472
+ const instance = activeEditors.get(element);
469
473
  const dataset = instance?.element.dataset ?? {};
470
474
  if (instance?.mode === "direct" || instance?.mode === "rich-panel" || dataset.upstartId) sendToParent({
471
475
  type: "text-edit",
@@ -488,7 +492,7 @@ function saveText(hash, newText) {
488
492
  }
489
493
  });
490
494
  }
491
- console.log("[Upstart Editor] Text save message sent:", hash);
495
+ console.log("[Upstart Editor] Text save message sent:", instance?.hash);
492
496
  } catch (error) {
493
497
  console.error("[Upstart Editor] Failed to send save message:", error);
494
498
  sendToParent({
@@ -497,8 +501,8 @@ function saveText(hash, newText) {
497
501
  });
498
502
  }
499
503
  }
500
- function destroyEditor(hash) {
501
- const instance = activeEditors.get(hash);
504
+ function destroyEditor(element) {
505
+ const instance = activeEditors.get(element);
502
506
  if (!instance) return;
503
507
  const isPlainMode = instance.mode === "plain";
504
508
  const finalContent = isPlainMode ? instance.editor.getText() : instance.editor.getHTML();
@@ -509,7 +513,7 @@ function destroyEditor(hash) {
509
513
  if (isPlainMode) instance.element.textContent = finalContent;
510
514
  else instance.element.innerHTML = finalContent;
511
515
  restoreStyles(instance.element);
512
- activeEditors.delete(hash);
516
+ activeEditors.delete(element);
513
517
  }
514
518
  function startDomObserver() {
515
519
  if (domObserver) return;
@@ -558,9 +562,9 @@ function scheduleReactivation() {
558
562
  * (e.g. React replaced the subtree during a re-render).
559
563
  */
560
564
  function cleanupOrphanedEditors() {
561
- for (const [hash, instance] of activeEditors) if (!document.contains(instance.element)) {
565
+ for (const [element, instance] of activeEditors) if (!document.contains(instance.element)) {
562
566
  instance.editor.destroy();
563
- activeEditors.delete(hash);
567
+ activeEditors.delete(element);
564
568
  }
565
569
  }
566
570
  function registerKeyboardShortcuts() {