@upstart.gg/vite-plugins 0.1.41 → 0.1.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/upstart-editor-api.d.ts +59 -1
- package/dist/upstart-editor-api.d.ts.map +1 -1
- package/dist/upstart-editor-api.js +273 -4
- package/dist/upstart-editor-api.js.map +1 -1
- package/dist/vite-plugin-upstart-attrs.d.ts +2 -1
- package/dist/vite-plugin-upstart-attrs.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-attrs.js +91 -4
- package/dist/vite-plugin-upstart-attrs.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/array-controls.js +342 -0
- package/dist/vite-plugin-upstart-editor/runtime/array-controls.js.map +1 -0
- package/dist/vite-plugin-upstart-editor/runtime/click-handler.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/click-handler.js +29 -2
- package/dist/vite-plugin-upstart-editor/runtime/click-handler.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/form-guard.js +80 -0
- package/dist/vite-plugin-upstart-editor/runtime/form-guard.js.map +1 -0
- package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js +2 -1
- package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/index.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/index.js +26 -4
- package/dist/vite-plugin-upstart-editor/runtime/index.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/text-editor.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/text-editor.js +40 -36
- package/dist/vite-plugin-upstart-editor/runtime/text-editor.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/types.d.ts +20 -4
- package/dist/vite-plugin-upstart-editor/runtime/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/tests/vite-plugin-upstart-attrs.test.ts +412 -0
- package/src/upstart-editor-api.ts +314 -5
- package/src/vite-plugin-upstart-attrs.ts +154 -4
- package/src/vite-plugin-upstart-editor/runtime/array-controls.ts +478 -0
- package/src/vite-plugin-upstart-editor/runtime/click-handler.ts +43 -0
- package/src/vite-plugin-upstart-editor/runtime/form-guard.ts +121 -0
- package/src/vite-plugin-upstart-editor/runtime/hover-overlay.ts +6 -1
- package/src/vite-plugin-upstart-editor/runtime/index.ts +20 -4
- package/src/vite-plugin-upstart-editor/runtime/text-editor.ts +49 -58
- 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
|
|
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":";;;;;;;;;;;;
|
|
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({
|
|
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({
|
|
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({
|
|
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({
|
|
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":"
|
|
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":";;;;;
|
|
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
|
|
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(
|
|
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,
|
|
226
|
+
editor = createDirectEditor(element, options);
|
|
222
227
|
break;
|
|
223
228
|
case "rich-panel":
|
|
224
|
-
editor = createRichPanelEditor(element,
|
|
229
|
+
editor = createRichPanelEditor(element, options);
|
|
225
230
|
break;
|
|
226
231
|
case "block-rich":
|
|
227
|
-
editor = createRichTextEditor(element,
|
|
232
|
+
editor = createRichTextEditor(element, options);
|
|
228
233
|
break;
|
|
229
234
|
case "inline-rich":
|
|
230
|
-
editor = createInlineRichTextEditor(element,
|
|
235
|
+
editor = createInlineRichTextEditor(element, options);
|
|
231
236
|
break;
|
|
232
237
|
case "plain":
|
|
233
|
-
editor = createPlainTextEditor(element,
|
|
238
|
+
editor = createPlainTextEditor(element, options);
|
|
234
239
|
break;
|
|
235
240
|
}
|
|
236
241
|
element.dataset.upstartEditorActive = "true";
|
|
237
242
|
applyActiveStyles(element);
|
|
238
|
-
activeEditors.set(
|
|
243
|
+
activeEditors.set(element, {
|
|
239
244
|
editor,
|
|
240
245
|
element,
|
|
241
246
|
hash,
|
|
242
247
|
mode
|
|
243
248
|
});
|
|
244
249
|
}
|
|
245
|
-
function createPlainTextEditor(element,
|
|
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(
|
|
296
|
+
syncI18nSiblings(element);
|
|
292
297
|
},
|
|
293
298
|
onBlur: ({ editor: e }) => {
|
|
294
299
|
if (!hasChanged) return;
|
|
295
300
|
hasChanged = false;
|
|
296
|
-
saveText(
|
|
301
|
+
saveText(element, e.getText());
|
|
297
302
|
}
|
|
298
303
|
});
|
|
299
304
|
}
|
|
300
|
-
function createRichTextEditor(element,
|
|
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(
|
|
320
|
+
syncI18nSiblings(element);
|
|
316
321
|
},
|
|
317
322
|
onBlur: ({ editor: e }) => {
|
|
318
323
|
if (!hasChanged) return;
|
|
319
324
|
hasChanged = false;
|
|
320
|
-
saveText(
|
|
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,
|
|
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(
|
|
360
|
+
syncI18nSiblings(element);
|
|
356
361
|
},
|
|
357
362
|
onBlur: ({ editor: e }) => {
|
|
358
363
|
if (!hasChanged) return;
|
|
359
364
|
hasChanged = false;
|
|
360
|
-
saveText(
|
|
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,
|
|
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(
|
|
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,
|
|
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(
|
|
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(
|
|
436
|
-
|
|
437
|
-
|
|
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
|
|
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
|
|
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: ${
|
|
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(
|
|
470
|
+
function saveText(element, newText) {
|
|
467
471
|
try {
|
|
468
|
-
const instance = activeEditors.get(
|
|
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(
|
|
501
|
-
const instance = activeEditors.get(
|
|
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(
|
|
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 [
|
|
565
|
+
for (const [element, instance] of activeEditors) if (!document.contains(instance.element)) {
|
|
562
566
|
instance.editor.destroy();
|
|
563
|
-
activeEditors.delete(
|
|
567
|
+
activeEditors.delete(element);
|
|
564
568
|
}
|
|
565
569
|
}
|
|
566
570
|
function registerKeyboardShortcuts() {
|