@upstart.gg/vite-plugins 0.1.72 → 0.1.74
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/vite-plugin-upstart-editor/runtime/click-handler.js +4 -2
- package/dist/vite-plugin-upstart-editor/runtime/click-handler.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/types.d.ts +6 -0
- package/dist/vite-plugin-upstart-editor/runtime/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/vite-plugin-upstart-editor/runtime/click-handler.ts +2 -0
- package/src/vite-plugin-upstart-editor/runtime/types.ts +6 -0
|
@@ -216,7 +216,8 @@ function handleClick(event) {
|
|
|
216
216
|
bottom: 0
|
|
217
217
|
},
|
|
218
218
|
tagName: editableEl.tagName.toLowerCase(),
|
|
219
|
-
images: collectImages(editableEl, clickPoint)
|
|
219
|
+
images: collectImages(editableEl, clickPoint),
|
|
220
|
+
editableText: true
|
|
220
221
|
});
|
|
221
222
|
}
|
|
222
223
|
}
|
|
@@ -304,7 +305,8 @@ function handleClick(event) {
|
|
|
304
305
|
bounds,
|
|
305
306
|
viewportWidth: window.innerWidth,
|
|
306
307
|
tagName: element.tagName.toLowerCase(),
|
|
307
|
-
images: collectImages(element, clickPoint)
|
|
308
|
+
images: collectImages(element, clickPoint),
|
|
309
|
+
editableText: element.dataset.upstartEditableText === "true"
|
|
308
310
|
});
|
|
309
311
|
console.log("[Upstart Editor] Element clicked:", componentName, hash);
|
|
310
312
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"click-handler.js","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/click-handler.ts"],"sourcesContent":["import { getCurrentMode, getSelection, toggleSelection } from \"./state.js\";\nimport { pickImagesForClick, type Point } from \"./image-pick.js\";\nimport { sendToParent } from \"./utils.js\";\nimport type { EditableImageRef, SelectionRef } from \"./types.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 * Whether any ancestor (up to <body>) clips its overflow, so a scaled-up image\n * stays visually contained.\n */\nfunction isClipped(node: HTMLElement): boolean {\n for (let el = node.parentElement; el && el !== document.body; el = el.parentElement) {\n const style = getComputedStyle(el);\n if (style.overflowX !== \"visible\" || style.overflowY !== \"visible\") return true;\n }\n return false;\n}\n\nfunction toImageRef(node: HTMLElement): EditableImageRef {\n const img = node as HTMLImageElement;\n const rect = node.getBoundingClientRect();\n const parent = node.parentElement;\n return {\n id: node.dataset.upstartImageId as string,\n src: img.currentSrc || img.src || \"\",\n alt: img.alt || \"\",\n classNameId: node.dataset.upstartClassnameId || undefined,\n currentClassName: node.dataset.upstartClassnameId ? node.className : undefined,\n width: Math.round(rect.width),\n height: Math.round(rect.height),\n naturalWidth: img.naturalWidth || undefined,\n naturalHeight: img.naturalHeight || undefined,\n clipped: isClipped(node),\n parentClassNameId: parent?.dataset.upstartClassnameId || undefined,\n parentClassName: parent?.dataset.upstartClassnameId ? parent.className : undefined,\n };\n}\n\nfunction editableImagesIn(el: HTMLElement): HTMLElement[] {\n const found = new Set<HTMLElement>();\n if (el.dataset.upstartImageId) found.add(el);\n for (const node of el.querySelectorAll<HTMLElement>(\"[data-upstart-image-id]\")) {\n found.add(node);\n }\n return Array.from(found);\n}\n\n/**\n * Collect the editable images (elements carrying data-upstart-image-id) relevant to a click\n * on `start`: the images inside the clicked element, or, when it has none, an ancestor's image\n * lying under the click point (a hero <img> covered by an overlay). See pickImagesForClick for\n * the exact rules — in particular an ancestor's images are never surfaced when the click is\n * not visually on them, so clicking a title never opens the logo next to it.\n */\nfunction collectImages(start: HTMLElement, point?: Point): EditableImageRef[] {\n const withRect = (node: HTMLElement) => ({ node, rect: node.getBoundingClientRect() });\n const own = editableImagesIn(start).map(withRect);\n const ancestorLevels: { node: HTMLElement; rect: DOMRect }[][] = [];\n if (own.length === 0) {\n let el = start.parentElement;\n for (let depth = 0; el && el !== document.body && depth < 8; depth++, el = el.parentElement) {\n ancestorLevels.push(editableImagesIn(el).map(withRect));\n }\n }\n return pickImagesForClick(own, ancestorLevels, point).map(({ node }) => toImageRef(node));\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\n/**\n * Build a SelectionRef from a clicked element, walking up to the nearest\n * component (data-upstart-file) for the source file / component name and\n * collecting i18n keys from the element's subtree.\n */\nfunction buildSelectionRef(element: HTMLElement): SelectionRef | null {\n const hash = element.dataset.upstartHash ?? \"\";\n\n const fileEl = element.closest<HTMLElement>(\"[data-upstart-file]\");\n const filePath = fileEl?.dataset.upstartFile ?? element.dataset.upstartFile ?? \"\";\n const componentName = fileEl?.dataset.upstartComponent ?? element.dataset.upstartComponent;\n\n // A stable identity for toggling. Prefer filePath+hash; fall back so distinct\n // elements without a hash don't collapse onto the same key.\n const key = `${filePath}:${hash}` !== \":\" ? `${filePath}:${hash}` : \"\";\n if (!key) {\n return null;\n }\n\n const i18nKeys = new Set<string>();\n const collectKeys = (el: HTMLElement) => {\n const raw = el.dataset.upstartI18n;\n if (raw) {\n for (const k of raw.split(/[\\s,]+/).filter(Boolean)) i18nKeys.add(k);\n }\n };\n collectKeys(element);\n for (const node of element.querySelectorAll<HTMLElement>(\"[data-upstart-i18n]\")) {\n collectKeys(node);\n }\n\n const text = (element.innerText ?? \"\").trim().slice(0, 200) || undefined;\n\n // Whether THIS element's content is derived from database data (element-level marker).\n // This is what gates content edits — its content can't be edited from the editor, only\n // its presentation. i18n/translated content is editable (it lives in translation files),\n // so i18n keys on the element take precedence and suppress the data-bound flag even if the\n // element was wrongly marked.\n const dataBoundEl = element.closest<HTMLElement>(\"[data-upstart-data-bound]\");\n const dataBound = !!dataBoundEl && dataBoundEl.dataset.upstartDataBound !== \"false\" && i18nKeys.size === 0;\n\n return {\n key,\n hash,\n filePath,\n componentName,\n i18nKeys: i18nKeys.size > 0 ? Array.from(i18nKeys) : undefined,\n text,\n dataBound: dataBound || undefined,\n };\n}\n\n/**\n * \"Select\" mode click: toggle the clicked component in/out of the selection,\n * mark it visually with data-upstart-selected, and report the new selection\n * to the parent. Never edits or navigates.\n */\nfunction handleSelectClick(event: MouseEvent, target: HTMLElement): void {\n const rawElement = target.closest<HTMLElement>(\"[data-upstart-hash]\");\n if (!rawElement) {\n console.info(\"[Upstart Editor] Select click ignored: no data-upstart-hash ancestor\");\n return;\n }\n\n // Bubble up to a rich-panel editable ancestor so selecting an inline child\n // (e.g. <strong>) selects the whole editable region — mirrors edit-mode resolution.\n const richPanelAncestor = rawElement.parentElement?.closest<HTMLElement>(\n '[data-upstart-editable-text-mode=\"rich-panel\"]',\n );\n const element = richPanelAncestor ?? rawElement;\n\n event.preventDefault();\n event.stopPropagation();\n\n const ref = buildSelectionRef(element);\n if (!ref) {\n return;\n }\n\n const nowSelected = toggleSelection(ref, element);\n if (nowSelected) {\n element.setAttribute(\"data-upstart-selected\", \"\");\n } else {\n element.removeAttribute(\"data-upstart-selected\");\n }\n\n sendToParent({ type: \"selection-changed\", selection: getSelection() });\n console.log(\"[Upstart Editor] Selection toggled:\", ref.key, nowSelected);\n}\n\nfunction handleClick(event: MouseEvent): void {\n const mode = getCurrentMode();\n if (mode !== \"edit\" && mode !== \"select\") {\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 // Viewport coordinates of the click, to tell which image (if any) was visually hit.\n const clickPoint: Point = { x: event.clientX, y: event.clientY };\n\n if (mode === \"select\") {\n handleSelectClick(event, target);\n return;\n }\n\n // Editor-only chrome (the array ×/+/✓ controls) and locally-staged draft list\n // items handle their own clicks and must never be treated as element selection.\n if (target.closest(\"#upstart-array-controls, [data-upstart-draft-item]\")) {\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 let classNameId = editableEl.dataset.upstartClassnameId ?? \"\";\n let currentClassName = editableEl.className;\n if (!classNameId) {\n const parentWithClassname = editableEl.closest<HTMLElement>(\"[data-upstart-classname-id]\");\n if (parentWithClassname) {\n classNameId = parentWithClassname.dataset.upstartClassnameId ?? \"\";\n currentClassName = parentWithClassname.className;\n }\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,\n themeColors,\n bounds: { top: 0, left: 0, width: 0, height: 0, right: 0, bottom: 0 },\n tagName: editableEl.tagName.toLowerCase(),\n images: collectImages(editableEl, clickPoint),\n });\n }\n }\n console.info(\"[Upstart Editor] Click inside contenteditable: TipTap handles text editing\");\n return;\n }\n\n // Detect a datasource record under the click. We check this BEFORE the\n // data-upstart-hash gate because a record may be rendered without that\n // attribute (e.g. an <a> from <Link to={...}>) and we still want to open\n // the inline datasource editor instead of letting the browser navigate.\n const datasourceEl = target.closest<HTMLElement>(\"[data-upstart-datasource][data-upstart-record-id]\");\n\n // Find the closest element with data-upstart-hash\n const rawElement = target.closest<HTMLElement>(\"[data-upstart-hash]\");\n if (!rawElement) {\n if (datasourceEl) {\n // Datasource record click without a data-upstart-hash ancestor: stop\n // navigation, surface the datasource event so the editor can open the\n // inline datasource item inspector.\n event.preventDefault();\n event.stopPropagation();\n sendToParent({\n type: \"element-clicked\",\n hash: \"\",\n componentName: undefined,\n filePath: \"\",\n classNameId: \"\",\n currentClassName: \"\",\n datasourceId: datasourceEl.dataset.upstartDatasource,\n recordId: datasourceEl.dataset.upstartRecordId,\n themeColors: {},\n bounds: { top: 0, left: 0, width: 0, height: 0, right: 0, bottom: 0 },\n tagName: datasourceEl.tagName.toLowerCase(),\n images: collectImages(datasourceEl, clickPoint),\n });\n return;\n }\n console.info(\"[Upstart Editor] Click ignored: no ancestral element with data-upstart-hash found\");\n return;\n }\n\n // If the clicked element is nested inside a rich-panel editable ancestor, bubble up to it\n // so that clicking any inline child (e.g. <strong>, <em>) edits the whole rich-panel region.\n const richPanelAncestor = rawElement.parentElement?.closest<HTMLElement>(\n '[data-upstart-editable-text-mode=\"rich-panel\"]',\n );\n const element = richPanelAncestor ?? rawElement;\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, focus the TipTap editor (all modes now use inline TipTap)\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 // 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 const 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 // datasourceEl was resolved before the data-upstart-hash gate so we could\n // intercept datasource clicks even without a hash ancestor (see above).\n const datasourceId = datasourceEl?.dataset.upstartDatasource;\n const recordId = datasourceEl?.dataset.upstartRecordId;\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 viewportWidth: window.innerWidth,\n tagName: element.tagName.toLowerCase(),\n images: collectImages(element, clickPoint),\n });\n\n console.log(\"[Upstart Editor] Element clicked:\", componentName, hash);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"click-handler.js","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/click-handler.ts"],"sourcesContent":["import { getCurrentMode, getSelection, toggleSelection } from \"./state.js\";\nimport { pickImagesForClick, type Point } from \"./image-pick.js\";\nimport { sendToParent } from \"./utils.js\";\nimport type { EditableImageRef, SelectionRef } from \"./types.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 * Whether any ancestor (up to <body>) clips its overflow, so a scaled-up image\n * stays visually contained.\n */\nfunction isClipped(node: HTMLElement): boolean {\n for (let el = node.parentElement; el && el !== document.body; el = el.parentElement) {\n const style = getComputedStyle(el);\n if (style.overflowX !== \"visible\" || style.overflowY !== \"visible\") return true;\n }\n return false;\n}\n\nfunction toImageRef(node: HTMLElement): EditableImageRef {\n const img = node as HTMLImageElement;\n const rect = node.getBoundingClientRect();\n const parent = node.parentElement;\n return {\n id: node.dataset.upstartImageId as string,\n src: img.currentSrc || img.src || \"\",\n alt: img.alt || \"\",\n classNameId: node.dataset.upstartClassnameId || undefined,\n currentClassName: node.dataset.upstartClassnameId ? node.className : undefined,\n width: Math.round(rect.width),\n height: Math.round(rect.height),\n naturalWidth: img.naturalWidth || undefined,\n naturalHeight: img.naturalHeight || undefined,\n clipped: isClipped(node),\n parentClassNameId: parent?.dataset.upstartClassnameId || undefined,\n parentClassName: parent?.dataset.upstartClassnameId ? parent.className : undefined,\n };\n}\n\nfunction editableImagesIn(el: HTMLElement): HTMLElement[] {\n const found = new Set<HTMLElement>();\n if (el.dataset.upstartImageId) found.add(el);\n for (const node of el.querySelectorAll<HTMLElement>(\"[data-upstart-image-id]\")) {\n found.add(node);\n }\n return Array.from(found);\n}\n\n/**\n * Collect the editable images (elements carrying data-upstart-image-id) relevant to a click\n * on `start`: the images inside the clicked element, or, when it has none, an ancestor's image\n * lying under the click point (a hero <img> covered by an overlay). See pickImagesForClick for\n * the exact rules — in particular an ancestor's images are never surfaced when the click is\n * not visually on them, so clicking a title never opens the logo next to it.\n */\nfunction collectImages(start: HTMLElement, point?: Point): EditableImageRef[] {\n const withRect = (node: HTMLElement) => ({ node, rect: node.getBoundingClientRect() });\n const own = editableImagesIn(start).map(withRect);\n const ancestorLevels: { node: HTMLElement; rect: DOMRect }[][] = [];\n if (own.length === 0) {\n let el = start.parentElement;\n for (let depth = 0; el && el !== document.body && depth < 8; depth++, el = el.parentElement) {\n ancestorLevels.push(editableImagesIn(el).map(withRect));\n }\n }\n return pickImagesForClick(own, ancestorLevels, point).map(({ node }) => toImageRef(node));\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\n/**\n * Build a SelectionRef from a clicked element, walking up to the nearest\n * component (data-upstart-file) for the source file / component name and\n * collecting i18n keys from the element's subtree.\n */\nfunction buildSelectionRef(element: HTMLElement): SelectionRef | null {\n const hash = element.dataset.upstartHash ?? \"\";\n\n const fileEl = element.closest<HTMLElement>(\"[data-upstart-file]\");\n const filePath = fileEl?.dataset.upstartFile ?? element.dataset.upstartFile ?? \"\";\n const componentName = fileEl?.dataset.upstartComponent ?? element.dataset.upstartComponent;\n\n // A stable identity for toggling. Prefer filePath+hash; fall back so distinct\n // elements without a hash don't collapse onto the same key.\n const key = `${filePath}:${hash}` !== \":\" ? `${filePath}:${hash}` : \"\";\n if (!key) {\n return null;\n }\n\n const i18nKeys = new Set<string>();\n const collectKeys = (el: HTMLElement) => {\n const raw = el.dataset.upstartI18n;\n if (raw) {\n for (const k of raw.split(/[\\s,]+/).filter(Boolean)) i18nKeys.add(k);\n }\n };\n collectKeys(element);\n for (const node of element.querySelectorAll<HTMLElement>(\"[data-upstart-i18n]\")) {\n collectKeys(node);\n }\n\n const text = (element.innerText ?? \"\").trim().slice(0, 200) || undefined;\n\n // Whether THIS element's content is derived from database data (element-level marker).\n // This is what gates content edits — its content can't be edited from the editor, only\n // its presentation. i18n/translated content is editable (it lives in translation files),\n // so i18n keys on the element take precedence and suppress the data-bound flag even if the\n // element was wrongly marked.\n const dataBoundEl = element.closest<HTMLElement>(\"[data-upstart-data-bound]\");\n const dataBound = !!dataBoundEl && dataBoundEl.dataset.upstartDataBound !== \"false\" && i18nKeys.size === 0;\n\n return {\n key,\n hash,\n filePath,\n componentName,\n i18nKeys: i18nKeys.size > 0 ? Array.from(i18nKeys) : undefined,\n text,\n dataBound: dataBound || undefined,\n };\n}\n\n/**\n * \"Select\" mode click: toggle the clicked component in/out of the selection,\n * mark it visually with data-upstart-selected, and report the new selection\n * to the parent. Never edits or navigates.\n */\nfunction handleSelectClick(event: MouseEvent, target: HTMLElement): void {\n const rawElement = target.closest<HTMLElement>(\"[data-upstart-hash]\");\n if (!rawElement) {\n console.info(\"[Upstart Editor] Select click ignored: no data-upstart-hash ancestor\");\n return;\n }\n\n // Bubble up to a rich-panel editable ancestor so selecting an inline child\n // (e.g. <strong>) selects the whole editable region — mirrors edit-mode resolution.\n const richPanelAncestor = rawElement.parentElement?.closest<HTMLElement>(\n '[data-upstart-editable-text-mode=\"rich-panel\"]',\n );\n const element = richPanelAncestor ?? rawElement;\n\n event.preventDefault();\n event.stopPropagation();\n\n const ref = buildSelectionRef(element);\n if (!ref) {\n return;\n }\n\n const nowSelected = toggleSelection(ref, element);\n if (nowSelected) {\n element.setAttribute(\"data-upstart-selected\", \"\");\n } else {\n element.removeAttribute(\"data-upstart-selected\");\n }\n\n sendToParent({ type: \"selection-changed\", selection: getSelection() });\n console.log(\"[Upstart Editor] Selection toggled:\", ref.key, nowSelected);\n}\n\nfunction handleClick(event: MouseEvent): void {\n const mode = getCurrentMode();\n if (mode !== \"edit\" && mode !== \"select\") {\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 // Viewport coordinates of the click, to tell which image (if any) was visually hit.\n const clickPoint: Point = { x: event.clientX, y: event.clientY };\n\n if (mode === \"select\") {\n handleSelectClick(event, target);\n return;\n }\n\n // Editor-only chrome (the array ×/+/✓ controls) and locally-staged draft list\n // items handle their own clicks and must never be treated as element selection.\n if (target.closest(\"#upstart-array-controls, [data-upstart-draft-item]\")) {\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 let classNameId = editableEl.dataset.upstartClassnameId ?? \"\";\n let currentClassName = editableEl.className;\n if (!classNameId) {\n const parentWithClassname = editableEl.closest<HTMLElement>(\"[data-upstart-classname-id]\");\n if (parentWithClassname) {\n classNameId = parentWithClassname.dataset.upstartClassnameId ?? \"\";\n currentClassName = parentWithClassname.className;\n }\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,\n themeColors,\n bounds: { top: 0, left: 0, width: 0, height: 0, right: 0, bottom: 0 },\n tagName: editableEl.tagName.toLowerCase(),\n images: collectImages(editableEl, clickPoint),\n editableText: true,\n });\n }\n }\n console.info(\"[Upstart Editor] Click inside contenteditable: TipTap handles text editing\");\n return;\n }\n\n // Detect a datasource record under the click. We check this BEFORE the\n // data-upstart-hash gate because a record may be rendered without that\n // attribute (e.g. an <a> from <Link to={...}>) and we still want to open\n // the inline datasource editor instead of letting the browser navigate.\n const datasourceEl = target.closest<HTMLElement>(\"[data-upstart-datasource][data-upstart-record-id]\");\n\n // Find the closest element with data-upstart-hash\n const rawElement = target.closest<HTMLElement>(\"[data-upstart-hash]\");\n if (!rawElement) {\n if (datasourceEl) {\n // Datasource record click without a data-upstart-hash ancestor: stop\n // navigation, surface the datasource event so the editor can open the\n // inline datasource item inspector.\n event.preventDefault();\n event.stopPropagation();\n sendToParent({\n type: \"element-clicked\",\n hash: \"\",\n componentName: undefined,\n filePath: \"\",\n classNameId: \"\",\n currentClassName: \"\",\n datasourceId: datasourceEl.dataset.upstartDatasource,\n recordId: datasourceEl.dataset.upstartRecordId,\n themeColors: {},\n bounds: { top: 0, left: 0, width: 0, height: 0, right: 0, bottom: 0 },\n tagName: datasourceEl.tagName.toLowerCase(),\n images: collectImages(datasourceEl, clickPoint),\n });\n return;\n }\n console.info(\"[Upstart Editor] Click ignored: no ancestral element with data-upstart-hash found\");\n return;\n }\n\n // If the clicked element is nested inside a rich-panel editable ancestor, bubble up to it\n // so that clicking any inline child (e.g. <strong>, <em>) edits the whole rich-panel region.\n const richPanelAncestor = rawElement.parentElement?.closest<HTMLElement>(\n '[data-upstart-editable-text-mode=\"rich-panel\"]',\n );\n const element = richPanelAncestor ?? rawElement;\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, focus the TipTap editor (all modes now use inline TipTap)\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 // 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 const 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 // datasourceEl was resolved before the data-upstart-hash gate so we could\n // intercept datasource clicks even without a hash ancestor (see above).\n const datasourceId = datasourceEl?.dataset.upstartDatasource;\n const recordId = datasourceEl?.dataset.upstartRecordId;\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 viewportWidth: window.innerWidth,\n tagName: element.tagName.toLowerCase(),\n images: collectImages(element, clickPoint),\n editableText: element.dataset.upstartEditableText === \"true\",\n });\n\n console.log(\"[Upstart Editor] Element clicked:\", componentName, hash);\n}\n"],"mappings":"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"}
|
|
@@ -128,6 +128,12 @@ type EditorMessage = {
|
|
|
128
128
|
viewportWidth?: number; /** Lower-case tag name of the clicked element (e.g. "img"). */
|
|
129
129
|
tagName?: string; /** Editable images found in the clicked element's nearest subtree. */
|
|
130
130
|
images?: EditableImageRef[];
|
|
131
|
+
/**
|
|
132
|
+
* The clicked element hosts an inline text editor (`data-upstart-editable-text`): the
|
|
133
|
+
* text is edited in place, so the host may keep the site visible instead of opening
|
|
134
|
+
* its inspector (phone layout).
|
|
135
|
+
*/
|
|
136
|
+
editableText?: boolean;
|
|
131
137
|
};
|
|
132
138
|
/**
|
|
133
139
|
* Messages sent from parent to iframe.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/types.ts"],"mappings":";;;;;;AAkBA;;;;;KAAY,UAAA;;;;;;UAOK,YAAA;EACf,GAAA;EACA,IAAA;EACA,QAAA;EACA,aAAA;EACA,QAAA;EACA,IAAA;EAiBU;;;;;EAXV,SAAA;AAAA;;;;;;;;;KAWU,cAAA;;AAiBZ;;UAZiB,aAAA;EACf,GAAA;EACA,IAAA;EACA,KAAA;EACA,MAAA;EACA,KAAA;EACA,MAAA;AAAA;;;;UAMe,cAAA;EACf,MAAA,EAAQ,MAAA;EACR,OAAA,EAAS,WAAA;EACT,IAAA;EACA,IAAA,EAAM,cAAA;AAAA;;;;;UAOS,gBAAA;EACf,EAAA;EACA,GAAA;EACA,GAAA;EAMA;EAJA,WAAA;EACA,gBAAA;EAOA;EALA,KAAA;EACA,MAAA;EACA,YAAA;EACA,aAAA;EAWU;EATV,OAAA;;EAEA,iBAAA;EACA,eAAA;AAAA;;;;KAMU,aAAA;EAEN,IAAA;EACA,OAAA,EACI,eAAA,GACA,qBAAA,GACA,mBAAA,GACA,sBAAA,GACA,eAAA;AAAA;EAEJ,IAAA;EAAsB,IAAA;AAAA;EACtB,IAAA;EAA0B,IAAA;AAAA;EAE1B,IAAA;EAAsB,OAAA;AAAA;EACtB,IAAA;EAAyB,CAAA;EAAW,CAAA;AAAA;EACpC,IAAA;EAAsB,KAAA;AAAA;;;;;;;EAMtB,IAAA;EAAuB,KAAA;AAAA;EACvB,IAAA;EAA2B,SAAA,EAAW,YAAA;AAAA;EAEtC,IAAA;EACA,IAAA;EACA,aAAA;EACA,QAAA;EACA,gBAAA;EACA,WAAA;EACA,YAAA;EACA,QAAA;EACA,WAAA,GAAc,MAAA;EACd,MAAA,EAAQ,aAAA;EACR,aAAA,
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/types.ts"],"mappings":";;;;;;AAkBA;;;;;KAAY,UAAA;;;;;;UAOK,YAAA;EACf,GAAA;EACA,IAAA;EACA,QAAA;EACA,aAAA;EACA,QAAA;EACA,IAAA;EAiBU;;;;;EAXV,SAAA;AAAA;;;;;;;;;KAWU,cAAA;;AAiBZ;;UAZiB,aAAA;EACf,GAAA;EACA,IAAA;EACA,KAAA;EACA,MAAA;EACA,KAAA;EACA,MAAA;AAAA;;;;UAMe,cAAA;EACf,MAAA,EAAQ,MAAA;EACR,OAAA,EAAS,WAAA;EACT,IAAA;EACA,IAAA,EAAM,cAAA;AAAA;;;;;UAOS,gBAAA;EACf,EAAA;EACA,GAAA;EACA,GAAA;EAMA;EAJA,WAAA;EACA,gBAAA;EAOA;EALA,KAAA;EACA,MAAA;EACA,YAAA;EACA,aAAA;EAWU;EATV,OAAA;;EAEA,iBAAA;EACA,eAAA;AAAA;;;;KAMU,aAAA;EAEN,IAAA;EACA,OAAA,EACI,eAAA,GACA,qBAAA,GACA,mBAAA,GACA,sBAAA,GACA,eAAA;AAAA;EAEJ,IAAA;EAAsB,IAAA;AAAA;EACtB,IAAA;EAA0B,IAAA;AAAA;EAE1B,IAAA;EAAsB,OAAA;AAAA;EACtB,IAAA;EAAyB,CAAA;EAAW,CAAA;AAAA;EACpC,IAAA;EAAsB,KAAA;AAAA;;;;;;;EAMtB,IAAA;EAAuB,KAAA;AAAA;EACvB,IAAA;EAA2B,SAAA,EAAW,YAAA;AAAA;EAEtC,IAAA;EACA,IAAA;EACA,aAAA;EACA,QAAA;EACA,gBAAA;EACA,WAAA;EACA,YAAA;EACA,QAAA;EACA,WAAA,GAAc,MAAA;EACd,MAAA,EAAQ,aAAA;EACR,aAAA,WAUY;EARZ,OAAA,WAcM;EAZN,MAAA,GAAS,gBAAA;;;;;;EAMT,YAAA;AAAA;;;;KAMM,aAAA;EACN,IAAA;EAAkB,IAAA,EAAM,UAAA;AAAA;EACxB,IAAA;EAA2B,WAAA;EAAqB,SAAA;EAAmB,UAAA;AAAA;EACnE,IAAA;EAAuB,OAAA;EAAiB,GAAA;AAAA;EAExC,IAAA;EAA6B,OAAA;EAAiB,GAAA;AAAA;EAC9C,IAAA;AAAA;EACA,IAAA;EAAiC,CAAA;EAAW,CAAA;AAAA;EAC5C,IAAA;AAAA;EACA,IAAA;EAAkB,GAAA;AAAA;EAElB,IAAA;EAAoB,MAAA,EAAQ,OAAA,CAAQ,aAAA;AAAA;;;;UAKzB,oBAAA;EACf,MAAA;EACA,IAAA,EAAM,aAAA;EAAA,CACL,GAAA;AAAA;;;;UAMc,oBAAA;EACf,MAAA;EACA,IAAA,EAAM,aAAA;EAAA,CACL,GAAA;AAAA;;;;UAMc,oBAAA;EA2CqC;;AAMtD;;EA5CE,gBAAA;EAiDA;;;;;EA1CA,sBAAA;;;;;EAMA,iBAAA;;;;;EAMA,UAAA;;;;;EAMA,WAAA;;;;;EAMA,aAAA;;;;;;EAOA,kBAAA,IAAsB,SAAA,UAAmB,GAAA;AAAA;;;;UAM1B,0BAAA;;;;;EAKf,OAAA;;;;;EAMA,UAAA;AAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@upstart.gg/vite-plugins",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.74",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"oxc-parser": "0.101.0",
|
|
24
24
|
"unplugin": "^2.3.11",
|
|
25
25
|
"zimmerframe": "^1.1.4",
|
|
26
|
-
"@upstart.gg/sdk": "^0.1.
|
|
26
|
+
"@upstart.gg/sdk": "^0.1.74"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@rolldown/binding-linux-arm64-gnu": "1.0.0",
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
},
|
|
119
119
|
"peerDependencies": {
|
|
120
120
|
"zod": "4.3.6",
|
|
121
|
-
"@upstart.gg/sdk": "^0.1.
|
|
121
|
+
"@upstart.gg/sdk": "^0.1.74"
|
|
122
122
|
},
|
|
123
123
|
"author": "Upstart",
|
|
124
124
|
"publishConfig": {
|
|
@@ -275,6 +275,7 @@ function handleClick(event: MouseEvent): void {
|
|
|
275
275
|
bounds: { top: 0, left: 0, width: 0, height: 0, right: 0, bottom: 0 },
|
|
276
276
|
tagName: editableEl.tagName.toLowerCase(),
|
|
277
277
|
images: collectImages(editableEl, clickPoint),
|
|
278
|
+
editableText: true,
|
|
278
279
|
});
|
|
279
280
|
}
|
|
280
281
|
}
|
|
@@ -388,6 +389,7 @@ function handleClick(event: MouseEvent): void {
|
|
|
388
389
|
viewportWidth: window.innerWidth,
|
|
389
390
|
tagName: element.tagName.toLowerCase(),
|
|
390
391
|
images: collectImages(element, clickPoint),
|
|
392
|
+
editableText: element.dataset.upstartEditableText === "true",
|
|
391
393
|
});
|
|
392
394
|
|
|
393
395
|
console.log("[Upstart Editor] Element clicked:", componentName, hash);
|
|
@@ -135,6 +135,12 @@ export type EditorMessage =
|
|
|
135
135
|
tagName?: string;
|
|
136
136
|
/** Editable images found in the clicked element's nearest subtree. */
|
|
137
137
|
images?: EditableImageRef[];
|
|
138
|
+
/**
|
|
139
|
+
* The clicked element hosts an inline text editor (`data-upstart-editable-text`): the
|
|
140
|
+
* text is edited in place, so the host may keep the site visible instead of opening
|
|
141
|
+
* its inspector (phone layout).
|
|
142
|
+
*/
|
|
143
|
+
editableText?: boolean;
|
|
138
144
|
};
|
|
139
145
|
|
|
140
146
|
/**
|