@upstart.gg/vite-plugins 0.1.29 → 0.1.30

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 (50) hide show
  1. package/dist/upstart-editor-api.d.ts +13 -1
  2. package/dist/upstart-editor-api.d.ts.map +1 -1
  3. package/dist/upstart-editor-api.js +59 -1
  4. package/dist/upstart-editor-api.js.map +1 -1
  5. package/dist/vite-plugin-upstart-attrs.d.ts +12 -7
  6. package/dist/vite-plugin-upstart-attrs.d.ts.map +1 -1
  7. package/dist/vite-plugin-upstart-attrs.js +195 -3
  8. package/dist/vite-plugin-upstart-attrs.js.map +1 -1
  9. package/dist/vite-plugin-upstart-branding/plugin.d.ts +3 -3
  10. package/dist/vite-plugin-upstart-branding/plugin.d.ts.map +1 -1
  11. package/dist/vite-plugin-upstart-branding/plugin.js.map +1 -1
  12. package/dist/vite-plugin-upstart-branding/runtime.js.map +1 -1
  13. package/dist/vite-plugin-upstart-editor/plugin.d.ts +3 -3
  14. package/dist/vite-plugin-upstart-editor/plugin.d.ts.map +1 -1
  15. package/dist/vite-plugin-upstart-editor/plugin.js +4 -1
  16. package/dist/vite-plugin-upstart-editor/plugin.js.map +1 -1
  17. package/dist/vite-plugin-upstart-editor/runtime/click-handler.js +27 -16
  18. package/dist/vite-plugin-upstart-editor/runtime/click-handler.js.map +1 -1
  19. package/dist/vite-plugin-upstart-editor/runtime/error-handler.js.map +1 -1
  20. package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js +1 -1
  21. package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js.map +1 -1
  22. package/dist/vite-plugin-upstart-editor/runtime/index.d.ts +1 -1
  23. package/dist/vite-plugin-upstart-editor/runtime/index.js +14 -2
  24. package/dist/vite-plugin-upstart-editor/runtime/index.js.map +1 -1
  25. package/dist/{src/vite-plugin-upstart-editor → vite-plugin-upstart-editor}/runtime/state.d.ts +1 -1
  26. package/dist/vite-plugin-upstart-editor/runtime/state.d.ts.map +1 -0
  27. package/dist/vite-plugin-upstart-editor/runtime/state.js.map +1 -0
  28. package/dist/vite-plugin-upstart-editor/runtime/text-editor.d.ts.map +1 -1
  29. package/dist/vite-plugin-upstart-editor/runtime/text-editor.js +212 -28
  30. package/dist/vite-plugin-upstart-editor/runtime/text-editor.js.map +1 -1
  31. package/dist/vite-plugin-upstart-editor/runtime/types.d.ts +16 -3
  32. package/dist/vite-plugin-upstart-editor/runtime/types.d.ts.map +1 -1
  33. package/dist/vite-plugin-upstart-editor/runtime/utils.js.map +1 -1
  34. package/dist/vite-plugin-upstart-theme.d.ts +3 -3
  35. package/dist/vite-plugin-upstart-theme.d.ts.map +1 -1
  36. package/dist/vite-plugin-upstart-theme.js +2 -2
  37. package/dist/vite-plugin-upstart-theme.js.map +1 -1
  38. package/package.json +7 -7
  39. package/src/tests/vite-plugin-upstart-attrs.test.ts +298 -37
  40. package/src/upstart-editor-api.ts +71 -0
  41. package/src/vite-plugin-upstart-attrs.ts +293 -5
  42. package/src/vite-plugin-upstart-editor/plugin.ts +11 -1
  43. package/src/vite-plugin-upstart-editor/runtime/click-handler.ts +35 -21
  44. package/src/vite-plugin-upstart-editor/runtime/index.ts +21 -1
  45. package/src/vite-plugin-upstart-editor/runtime/text-editor.ts +260 -41
  46. package/src/vite-plugin-upstart-editor/runtime/types.ts +17 -4
  47. package/src/vite-plugin-upstart-theme.ts +4 -1
  48. package/dist/src/vite-plugin-upstart-editor/runtime/state.d.ts.map +0 -1
  49. package/dist/src/vite-plugin-upstart-editor/runtime/state.js.map +0 -1
  50. /package/dist/{src/vite-plugin-upstart-editor → vite-plugin-upstart-editor}/runtime/state.js +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"text-editor.js","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/text-editor.ts"],"sourcesContent":["import { Editor, Extension, Node } from \"@tiptap/core\";\nimport { BubbleMenu } from \"@tiptap/extension-bubble-menu\";\nimport Placeholder from \"@tiptap/extension-placeholder\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport { sendToParent } from \"./utils.js\";\nimport type { EditorInstance, TextEditorMode, UpstartEditorOptions } from \"./types.js\";\n\n/**\n * Custom Document node for inline elements (e.g. <a>, <span>, headings).\n * Uses `inline*` content instead of the default `block+` to prevent\n * TipTap from wrapping text in <p> tags inside inline elements.\n */\nconst InlineDocument = Node.create({\n name: \"doc\",\n topNode: true,\n content: \"inline*\",\n});\n\n/**\n * Remaps Enter to insert a <br> (hard break) instead of creating a new paragraph.\n * Used in inline-rich mode where block nodes are not allowed.\n */\nconst EnterHardBreak = Extension.create({\n name: \"enterHardBreak\",\n addKeyboardShortcuts() {\n return {\n Enter: () => this.editor.commands.setHardBreak(),\n };\n },\n});\n\nconst DEFAULT_OPTIONS: Required<UpstartEditorOptions> = {\n richTextElements: [\"p\", \"div\", \"article\", \"section\"],\n inlineRichTextElements: [\"a\", \"span\", \"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\"],\n plainTextElements: [\"button\", \"label\"],\n bubbleMenu: true,\n placeholder: \"Start typing...\",\n autoSaveDelay: 1000,\n};\n\nconst activeEditors = new Map<string, EditorInstance>();\nlet i18nSyncInProgress = false;\nconst styleCache = new WeakMap<\n HTMLElement,\n { outline: string; outlineOffset: string; cursor: string; transition: string }\n>();\nlet resolvedOptions: Required<UpstartEditorOptions> = DEFAULT_OPTIONS;\nlet shortcutsRegistered = false;\nlet domObserver: MutationObserver | null = null;\nlet reactivateTimer: number | null = null;\n\n/**\n * Initialize TipTap text editing for elements marked as editable.\n * Activation is deferred to avoid conflicting with React hydration.\n */\nexport function initTextEditor(options: UpstartEditorOptions = {}): void {\n resolvedOptions = { ...DEFAULT_OPTIONS, ...options };\n registerKeyboardShortcuts();\n\n // Defer activation to let React finish hydrating the server-rendered HTML.\n injectEditorStyles();\n // activateAllEditors();\n startDomObserver();\n}\n\n/**\n * Inject CSS rules that visually hide original content when an editor is active.\n * This avoids moving DOM nodes (which breaks React hydration).\n *\n * - `font-size: 0` + `color: transparent` hides direct text nodes\n * - `> *:not(.ProseMirror)` hides child elements\n * - ProseMirror gets explicit inline styles to restore text rendering\n */\nfunction injectEditorStyles(): void {\n if (document.getElementById(\"upstart-editor-styles\")) return;\n\n const style = document.createElement(\"style\");\n style.id = \"upstart-editor-styles\";\n style.textContent = [\n \"[data-upstart-editor-active] {\",\n \" cursor: text;\",\n \"}\",\n // \"[data-upstart-editor-active] > *:not(.ProseMirror) {\",\n // \" display: none !important;\",\n // \"}\",\n \"[data-upstart-editor-active] .ProseMirror {\",\n \" outline: none !important;\",\n \" font-size: inherit !important;\",\n \" line-height: inherit !important;\",\n \" color: inherit !important;\",\n \" letter-spacing: inherit !important;\",\n \" font-weight: inherit !important;\",\n \" white-space: inherit !important;\",\n \"}\",\n \"[data-upstart-editor-active] .ProseMirror:focus {\",\n \" outline: none !important;\",\n \"}\",\n ].join(\"\\n\");\n document.head.appendChild(style);\n}\n\n/**\n * Activate editors on all editable elements. Safe to call multiple times.\n */\nexport function activateAllEditors(): void {\n try {\n cleanupOrphanedEditors();\n const editables = document.querySelectorAll<HTMLElement>('[data-upstart-editable-text=\"true\"]');\n editables.forEach((element) => setupEditableElement(element, resolvedOptions));\n console.log(\"[Upstart Editor] Text editors activated\");\n } catch (error) {\n console.error(\"[Upstart Editor] Failed to activate text editors:\", error);\n sendToParent({\n type: \"editor-error\",\n error: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n}\n\n/**\n * Destroy all active editors.\n */\nexport function destroyAllActiveEditors(): void {\n stopDomObserver();\n for (const hash of activeEditors.keys()) {\n destroyEditor(hash);\n }\n}\n\nfunction setupEditableElement(element: HTMLElement, options: Required<UpstartEditorOptions>): void {\n const hash = getEditableHash(element);\n if (!hash || activeEditors.has(hash)) {\n return;\n }\n\n cacheStyles(element);\n activateEditor(element, hash, options);\n}\n\nfunction activateEditor(element: HTMLElement, hash: string, options: Required<UpstartEditorOptions>): void {\n const mode = getEditorMode(element, options);\n\n let editor: Editor;\n switch (mode) {\n case \"block-rich\":\n editor = createRichTextEditor(element, hash, options);\n break;\n case \"inline-rich\":\n editor = createInlineRichTextEditor(element, hash, options);\n break;\n case \"plain\":\n editor = createPlainTextEditor(element, hash, options);\n break;\n }\n\n // Restore text rendering on ProseMirror (overrides inherited font-size: 0 etc.)\n\n // Mark element — triggers CSS rules that hide original content\n element.dataset.upstartEditorActive = \"true\";\n\n applyActiveStyles(element);\n activeEditors.set(hash, { editor, element, hash, mode });\n}\n\nfunction createPlainTextEditor(\n element: HTMLElement,\n hash: string,\n options: Required<UpstartEditorOptions>,\n): Editor {\n const content = element.textContent ?? \"\";\n element.textContent = \"\"; // Clear the element first! (same as rich text editors)\n let hasChanged = false;\n\n const editor = new Editor({\n element,\n extensions: [\n InlineDocument,\n StarterKit.configure({\n document: false,\n heading: false,\n bold: false,\n italic: false,\n strike: false,\n blockquote: false,\n bulletList: false,\n orderedList: false,\n listItem: false,\n codeBlock: false,\n horizontalRule: false,\n }),\n Placeholder.configure({\n placeholder: \"Click to edit...\",\n }),\n ],\n content,\n editorProps: {\n attributes: {\n class: \"upstart-editor-active\",\n },\n },\n onUpdate: ({ editor: e }) => {\n if (i18nSyncInProgress) return;\n hasChanged = true;\n // const content = e.getText();\n // debouncedSave(hash, content, options.autoSaveDelay);\n syncI18nSiblings(hash);\n },\n onBlur: ({ editor: e }) => {\n if (!hasChanged) return;\n hasChanged = false;\n saveText(hash, e.getText());\n },\n });\n\n return editor;\n}\n\nfunction createRichTextEditor(\n element: HTMLElement,\n hash: string,\n options: Required<UpstartEditorOptions>,\n): Editor {\n const content = element.innerHTML;\n element.innerHTML = \"\"; // Clear the element first!\n let hasChanged = false;\n\n const bubbleMenuElement = createBubbleMenuElement();\n const extensions: Extension[] = [\n StarterKit,\n Placeholder.configure({\n placeholder: options.placeholder,\n }),\n ];\n\n if (options.bubbleMenu) {\n extensions.push(\n BubbleMenu.configure({\n element: bubbleMenuElement,\n }),\n );\n }\n\n const editor = new Editor({\n element,\n extensions,\n content,\n editorProps: {\n attributes: {\n class: \"upstart-editor-active\",\n },\n },\n onUpdate: ({ editor: e }) => {\n if (i18nSyncInProgress) return;\n hasChanged = true;\n // const content = e.getHTML();\n // debouncedSave(hash, content, options.autoSaveDelay);\n syncI18nSiblings(hash);\n },\n onBlur: ({ editor: e }) => {\n if (!hasChanged) return;\n hasChanged = false;\n saveText(hash, e.getHTML());\n },\n });\n\n if (options.bubbleMenu) {\n wireBubbleMenu(bubbleMenuElement, editor);\n }\n\n return editor;\n}\n\nfunction createInlineRichTextEditor(\n element: HTMLElement,\n hash: string,\n options: Required<UpstartEditorOptions>,\n): Editor {\n const content = element.innerHTML;\n element.innerHTML = \"\"; // Clear the element first!\n let hasChanged = false;\n\n const bubbleMenuElement = createBubbleMenuElement(\"inline\");\n const extensions: (Extension | ReturnType<typeof Node.create>)[] = [\n InlineDocument,\n EnterHardBreak,\n StarterKit.configure({\n document: false,\n heading: false,\n blockquote: false,\n bulletList: false,\n orderedList: false,\n listItem: false,\n codeBlock: false,\n horizontalRule: false,\n }),\n Placeholder.configure({\n placeholder: \"Click to edit...\",\n }),\n ];\n\n if (options.bubbleMenu) {\n extensions.push(\n BubbleMenu.configure({\n element: bubbleMenuElement,\n }),\n );\n }\n\n const editor = new Editor({\n element,\n extensions,\n content,\n editorProps: {\n attributes: {\n class: \"upstart-editor-active\",\n },\n },\n onUpdate: ({ editor: e }) => {\n if (i18nSyncInProgress) return;\n hasChanged = true;\n // const content = e.getHTML();\n // debouncedSave(hash, content, options.autoSaveDelay);\n syncI18nSiblings(hash);\n },\n onBlur: ({ editor: e }) => {\n if (!hasChanged) return;\n hasChanged = false;\n saveText(hash, e.getHTML());\n },\n });\n\n if (options.bubbleMenu) {\n wireBubbleMenu(bubbleMenuElement, editor);\n }\n\n return editor;\n}\n\nfunction getEditorMode(element: HTMLElement, options: Required<UpstartEditorOptions>): TextEditorMode {\n const modeOverride = element.dataset.upstartEditableTextMode as TextEditorMode | undefined;\n if (modeOverride === \"plain\" || modeOverride === \"inline-rich\" || modeOverride === \"block-rich\") {\n return modeOverride;\n }\n\n const tagName = element.tagName.toLowerCase();\n\n if (options.plainTextElements.includes(tagName)) {\n return \"plain\";\n }\n\n if (options.inlineRichTextElements.includes(tagName)) {\n return \"inline-rich\";\n }\n\n if (options.richTextElements.includes(tagName)) {\n return \"block-rich\";\n }\n\n return \"block-rich\";\n}\n\nfunction syncI18nSiblings(sourceHash: string): void {\n console.log(\"[Upstart Editor] Syncing i18n siblings for\", sourceHash);\n const instance = activeEditors.get(sourceHash);\n if (!instance) {\n console.warn(\"[Upstart Editor] No editor instance found for hash:\", sourceHash);\n return;\n }\n\n const i18nKey = instance.element.dataset.upstartI18n;\n if (!i18nKey) {\n console.warn(\"[Upstart Editor] No sibling i18n key found for element:\", instance.element);\n return;\n }\n\n const siblings = document.querySelectorAll<HTMLElement>(`[data-upstart-i18n=\"${CSS.escape(i18nKey)}\"]`);\n console.log(`[Upstart Editor] Found ${siblings.length} sibling(s) with i18n key \"${i18nKey}\"`);\n\n // Always use plain text so each sibling's schema can wrap it correctly\n // (e.g. InlineDocument vs block Document have incompatible content rules).\n const plainContent = instance.editor.getText();\n\n i18nSyncInProgress = true;\n try {\n for (const sibling of siblings) {\n if (sibling === instance.element) continue;\n\n const siblingHash = sibling.dataset.upstartHash;\n const siblingInstance = siblingHash ? activeEditors.get(siblingHash) : null;\n\n if (siblingInstance) {\n console.log(\n `[Upstart Editor] Updating sibling editor (hash: ${siblingHash}) with new content`,\n siblingInstance,\n );\n sibling.innerText = plainContent;\n siblingInstance.editor.chain().selectAll().insertContent(plainContent).run();\n } else {\n sibling.textContent = plainContent;\n }\n }\n } finally {\n i18nSyncInProgress = false;\n }\n}\n\nfunction saveText(hash: string, newText: string): void {\n try {\n const instance = activeEditors.get(hash);\n const dataset = instance?.element.dataset ?? {};\n const [namespace, key] = dataset.upstartI18n?.split(\":\") ?? [];\n sendToParent({\n type: \"text-edit\",\n payload: {\n action: \"editText\",\n content: newText,\n namespace,\n key,\n language: document.documentElement.lang,\n },\n });\n\n console.log(\"[Upstart Editor] Text save message sent:\", hash);\n } catch (error) {\n console.error(\"[Upstart Editor] Failed to send save message:\", error);\n sendToParent({\n type: \"editor-error\",\n error: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n}\n\nfunction destroyEditor(hash: string): void {\n const instance = activeEditors.get(hash);\n if (!instance) {\n return;\n }\n\n const finalContent = instance.mode === \"plain\" ? instance.editor.getText() : instance.editor.getHTML();\n\n instance.editor.destroy();\n\n // Remove ProseMirror DOM\n const editorDom = instance.element.querySelector(\".ProseMirror\");\n if (editorDom) editorDom.remove();\n\n // Remove CSS-hiding marker — original content becomes visible again\n delete instance.element.dataset.upstartEditorActive;\n\n // Update element content with the final edited text\n if (instance.mode === \"plain\") {\n instance.element.textContent = finalContent;\n } else {\n instance.element.innerHTML = finalContent;\n }\n\n restoreStyles(instance.element);\n activeEditors.delete(hash);\n}\n\n// ---------------------------------------------------------------------------\n// MutationObserver — re-activates editors after React re-renders\n// ---------------------------------------------------------------------------\n\nfunction startDomObserver(): void {\n if (domObserver) return;\n\n domObserver = new MutationObserver((mutations) => {\n let needsReactivation = false;\n\n for (const mutation of mutations) {\n if (mutation.type !== \"childList\") continue;\n\n for (const node of mutation.addedNodes) {\n if (\n node instanceof HTMLElement &&\n (node.matches?.('[data-upstart-editable-text=\"true\"]') ||\n node.querySelector?.('[data-upstart-editable-text=\"true\"]'))\n ) {\n needsReactivation = true;\n break;\n }\n }\n\n if (!needsReactivation) {\n for (const node of mutation.removedNodes) {\n if (\n node instanceof HTMLElement &&\n (node.matches?.('[data-upstart-editable-text=\"true\"]') ||\n node.querySelector?.('[data-upstart-editable-text=\"true\"]'))\n ) {\n needsReactivation = true;\n break;\n }\n }\n }\n\n if (needsReactivation) break;\n }\n\n if (needsReactivation) {\n scheduleReactivation();\n }\n });\n\n domObserver.observe(document.body, { childList: true, subtree: true });\n}\n\nfunction stopDomObserver(): void {\n if (domObserver) {\n domObserver.disconnect();\n domObserver = null;\n }\n if (reactivateTimer) {\n clearTimeout(reactivateTimer);\n reactivateTimer = null;\n }\n}\n\nfunction scheduleReactivation(): void {\n if (reactivateTimer) clearTimeout(reactivateTimer);\n reactivateTimer = window.setTimeout(() => {\n reactivateTimer = null;\n activateAllEditors();\n }, 50);\n}\n\n/**\n * Remove editors whose host element has been detached from the document\n * (e.g. React replaced the subtree during a re-render).\n */\nfunction cleanupOrphanedEditors(): void {\n for (const [hash, instance] of activeEditors) {\n if (!document.contains(instance.element)) {\n instance.editor.destroy();\n activeEditors.delete(hash);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Keyboard shortcuts\n// ---------------------------------------------------------------------------\n\nfunction registerKeyboardShortcuts(): void {\n if (shortcutsRegistered) {\n return;\n }\n\n document.addEventListener(\"keydown\", (event) => {\n if (event.key === \"Escape\") {\n blurAllEditors();\n }\n\n if ((event.metaKey || event.ctrlKey) && event.key === \"Enter\") {\n blurAllEditors();\n }\n });\n\n shortcutsRegistered = true;\n}\n\nfunction blurAllEditors(): void {\n activeEditors.forEach((instance) => {\n instance.editor.commands.blur();\n });\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction getEditableHash(element: HTMLElement): string | null {\n return element.dataset.upstartHash ?? null;\n}\n\nfunction cacheStyles(element: HTMLElement): void {\n if (styleCache.has(element)) {\n return;\n }\n\n styleCache.set(element, {\n outline: element.style.outline || \"\",\n outlineOffset: element.style.outlineOffset || \"\",\n cursor: element.style.cursor || \"\",\n transition: element.style.transition || \"\",\n });\n}\n\nfunction applyActiveStyles(element: HTMLElement): void {\n cacheStyles(element);\n}\n\nfunction restoreStyles(element: HTMLElement): void {\n const cached = styleCache.get(element);\n if (!cached) {\n element.style.outline = \"\";\n element.style.outlineOffset = \"\";\n element.style.cursor = \"\";\n element.style.transition = \"\";\n return;\n }\n\n element.style.outline = cached.outline;\n element.style.outlineOffset = cached.outlineOffset;\n element.style.cursor = cached.cursor;\n element.style.transition = cached.transition;\n}\n\nconst BUBBLE_MENU_GROUPS: { command: string; icon: string; title: string }[][] = [\n [\n { command: \"bold\", icon: \"format-bold\", title: \"Bold\" },\n { command: \"italic\", icon: \"format-italic\", title: \"Italic\" },\n { command: \"strike\", icon: \"format-strikethrough\", title: \"Strikethrough\" },\n { command: \"code\", icon: \"code-tags\", title: \"Inline Code\" },\n ],\n [\n { command: \"heading\", icon: \"format-header-1\", title: \"Heading\" },\n { command: \"blockquote\", icon: \"format-quote-close\", title: \"Blockquote\" },\n ],\n [\n { command: \"bulletList\", icon: \"format-list-bulleted\", title: \"Bullet List\" },\n { command: \"orderedList\", icon: \"format-list-numbered\", title: \"Ordered List\" },\n ],\n];\n\nfunction getIconifyUrl(iconName: string): string {\n return `https://api.iconify.design/mdi/${iconName}.svg?height=none&color=%23fff`;\n}\n\nfunction createBubbleMenuElement(mode: \"block\" | \"inline\" = \"block\"): HTMLDivElement {\n const groups = mode === \"inline\" ? [BUBBLE_MENU_GROUPS[0]] : BUBBLE_MENU_GROUPS;\n const menu = document.createElement(\"div\");\n menu.className = \"upstart-editor-bubble-menu\";\n menu.style.cssText =\n \"display: flex; align-items: center; gap: 2px; padding: 4px; \" +\n \"background: #111827; color: #ffffff; border-radius: 8px; \" +\n \"font-size: 12px; box-shadow: 0 8px 24px rgba(0,0,0,0.25);\";\n\n groups.forEach((group, groupIndex) => {\n if (groupIndex > 0) {\n const separator = document.createElement(\"div\");\n separator.style.cssText =\n \"width: 1px; height: 16px; background: rgba(255,255,255,0.2); \" + \"margin: 0 4px; flex-shrink: 0;\";\n menu.appendChild(separator);\n }\n\n for (const { command, icon, title } of group) {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.dataset.command = command;\n button.title = title;\n button.style.cssText =\n \"background: transparent; border: none; color: inherit; cursor: pointer; \" +\n \"display: flex; align-items: center; justify-content: center; \" +\n \"width: 28px; height: 28px; padding: 4px; border-radius: 4px; \" +\n \"transition: background 0.15s ease;\";\n\n const img = document.createElement(\"img\");\n img.src = getIconifyUrl(icon);\n img.alt = title;\n img.style.cssText = \"width: 18px; height: 18px; display: block; pointer-events: none;\";\n button.appendChild(img);\n\n button.addEventListener(\"mouseenter\", () => {\n if (!button.dataset.active) {\n button.style.background = \"rgba(255,255,255,0.12)\";\n }\n });\n button.addEventListener(\"mouseleave\", () => {\n button.style.background = button.dataset.active ? \"rgba(255,255,255,0.2)\" : \"transparent\";\n });\n\n menu.appendChild(button);\n }\n });\n\n return menu;\n}\n\nfunction wireBubbleMenu(menu: HTMLDivElement, editor: Editor): void {\n menu.addEventListener(\"mousedown\", (event) => {\n event.preventDefault();\n });\n\n menu.addEventListener(\n \"click\",\n (event) => {\n console.log(\"menu button cliked\");\n event.stopPropagation();\n const target = event.target as HTMLElement | null;\n const command = target?.dataset.command;\n if (!command) return;\n\n const chain = editor.chain().focus();\n\n switch (command) {\n case \"bold\":\n chain.toggleBold().run();\n break;\n case \"italic\":\n chain.toggleItalic().run();\n break;\n case \"strike\":\n chain.toggleStrike().run();\n break;\n case \"code\":\n chain.toggleCode().run();\n break;\n case \"heading\":\n chain.toggleHeading({ level: 1 }).run();\n break;\n case \"blockquote\":\n chain.toggleBlockquote().run();\n break;\n case \"bulletList\":\n chain.toggleBulletList().run();\n break;\n case \"orderedList\":\n chain.toggleOrderedList().run();\n break;\n }\n },\n { capture: true },\n );\n\n const updateActiveStates = (): void => {\n const buttons = menu.querySelectorAll<HTMLButtonElement>(\"button[data-command]\");\n for (const button of buttons) {\n const command = button.dataset.command!;\n const isActive =\n command === \"heading\" ? editor.isActive(\"heading\", { level: 1 }) : editor.isActive(command);\n\n if (isActive) {\n button.dataset.active = \"true\";\n button.style.background = \"rgba(255,255,255,0.2)\";\n } else {\n delete button.dataset.active;\n button.style.background = \"transparent\";\n }\n }\n };\n\n editor.on(\"transaction\", updateActiveStates);\n updateActiveStates();\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"text-editor.js","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/text-editor.ts"],"sourcesContent":["import { Editor, Extension, Node } from \"@tiptap/core\";\nimport { BubbleMenu } from \"@tiptap/extension-bubble-menu\";\nimport Placeholder from \"@tiptap/extension-placeholder\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport { sendToParent } from \"./utils.js\";\nimport type { EditorInstance, TextEditorMode, UpstartEditorOptions } from \"./types.js\";\n\n/**\n * Custom Document node for inline elements (e.g. <a>, <span>, headings).\n * Uses `inline*` content instead of the default `block+` to prevent\n * TipTap from wrapping text in <p> tags inside inline elements.\n */\nconst InlineDocument = Node.create({\n name: \"doc\",\n topNode: true,\n content: \"inline*\",\n});\n\n/**\n * An atomic inline node that represents an i18n template variable like {{year}}.\n * It is displayed as a grayed non-editable chip showing the resolved value.\n * renderText() returns {{varName}} so getText() serialises back to the raw template.\n */\nconst TemplateVariable = Node.create({\n name: \"templateVariable\",\n group: \"inline\",\n inline: true,\n atom: true,\n\n addAttributes() {\n return {\n varName: { default: \"\" },\n value: { default: \"\" },\n };\n },\n\n parseHTML() {\n return [{ tag: \"span[data-tpl-var]\" }];\n },\n\n renderHTML({ node }) {\n return [\n \"span\",\n {\n \"data-tpl-var\": node.attrs.varName,\n contenteditable: \"false\",\n style:\n \"opacity:0.5;background:rgba(0,0,0,0.08);border-radius:3px;padding:0 3px;\" +\n \"font-family:monospace;font-size:0.875em;cursor:default;user-select:none;\",\n },\n node.attrs.value || `{{${node.attrs.varName}}}`,\n ];\n },\n\n renderText({ node }) {\n return `{{${node.attrs.varName}}}`;\n },\n});\n\n/**\n * Remaps Enter to insert a <br> (hard break) instead of creating a new paragraph.\n * Used in inline-rich mode where block nodes are not allowed.\n */\nconst EnterHardBreak = Extension.create({\n name: \"enterHardBreak\",\n addKeyboardShortcuts() {\n return {\n Enter: () => this.editor.commands.setHardBreak(),\n };\n },\n});\n\nconst DEFAULT_OPTIONS: Required<UpstartEditorOptions> = {\n richTextElements: [\"p\", \"div\", \"article\", \"section\"],\n inlineRichTextElements: [\"a\", \"span\", \"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\"],\n plainTextElements: [\"button\", \"label\"],\n bubbleMenu: true,\n placeholder: \"Start typing...\",\n autoSaveDelay: 1000,\n getRawI18nTemplate: () => undefined,\n};\n\n/**\n * Parse a raw i18n template (e.g. \"Copyright {{year}}\") together with the\n * already-rendered text (e.g. \"Copyright 2026\") and produce a TipTap JSON\n * document where variable tokens become TemplateVariable nodes.\n */\nfunction buildI18nContent(rawTemplate: string, renderedText: string): object {\n const VAR_RE = /\\{\\{(\\w+)\\}\\}/g;\n type Segment = { type: \"text\"; text: string } | { type: \"var\"; varName: string };\n const segments: Segment[] = [];\n\n let lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = VAR_RE.exec(rawTemplate)) !== null) {\n if (m.index > lastIndex) segments.push({ type: \"text\", text: rawTemplate.slice(lastIndex, m.index) });\n segments.push({ type: \"var\", varName: m[1] });\n lastIndex = m.index + m[0].length;\n }\n if (lastIndex < rawTemplate.length) segments.push({ type: \"text\", text: rawTemplate.slice(lastIndex) });\n\n // Build a regex over the rendered text to capture variable values\n const escRe = (s: string) => s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const regexParts = segments.map((s) => (s.type === \"text\" ? escRe(s.text) : \"(.+?)\"));\n const fullRegex = new RegExp(\"^\" + regexParts.join(\"\") + \"$\");\n const varSegments = segments.filter((s): s is Extract<Segment, { type: \"var\" }> => s.type === \"var\");\n\n const varValues: Record<string, string> = {};\n const match = renderedText.match(fullRegex);\n if (match) {\n varSegments.forEach((seg, i) => {\n varValues[seg.varName] = match[i + 1] ?? `{{${seg.varName}}}`;\n });\n } else {\n varSegments.forEach((seg) => {\n varValues[seg.varName] = `{{${seg.varName}}}`;\n });\n }\n\n const inlineNodes = segments\n .map((s) =>\n s.type === \"text\"\n ? { type: \"text\", text: s.text }\n : { type: \"templateVariable\", attrs: { varName: s.varName, value: varValues[s.varName] } },\n )\n .filter((n) => n.type !== \"text\" || (n as any).text !== \"\");\n\n return { type: \"doc\", content: [{ type: \"paragraph\", content: inlineNodes }] };\n}\n\nconst activeEditors = new Map<string, EditorInstance>();\nlet i18nSyncInProgress = false;\nconst styleCache = new WeakMap<\n HTMLElement,\n { outline: string; outlineOffset: string; cursor: string; transition: string }\n>();\nlet resolvedOptions: Required<UpstartEditorOptions> = DEFAULT_OPTIONS;\nlet shortcutsRegistered = false;\nlet domObserver: MutationObserver | null = null;\nlet reactivateTimer: number | null = null;\n\n/**\n * Initialize TipTap text editing for elements marked as editable.\n * Activation is deferred to avoid conflicting with React hydration.\n */\nexport function initTextEditor(options: UpstartEditorOptions = {}): void {\n resolvedOptions = { ...DEFAULT_OPTIONS, ...options };\n registerKeyboardShortcuts();\n\n // Defer activation to let React finish hydrating the server-rendered HTML.\n injectEditorStyles();\n // activateAllEditors();\n startDomObserver();\n}\n\n/**\n * Inject CSS rules that visually hide original content when an editor is active.\n * This avoids moving DOM nodes (which breaks React hydration).\n *\n * - `font-size: 0` + `color: transparent` hides direct text nodes\n * - `> *:not(.ProseMirror)` hides child elements\n * - ProseMirror gets explicit inline styles to restore text rendering\n */\nfunction injectEditorStyles(): void {\n if (document.getElementById(\"upstart-editor-styles\")) return;\n\n const style = document.createElement(\"style\");\n style.id = \"upstart-editor-styles\";\n style.textContent = [\n \"[data-upstart-editor-active] {\",\n \" cursor: text;\",\n \"}\",\n // \"[data-upstart-editor-active] > *:not(.ProseMirror) {\",\n // \" display: none !important;\",\n // \"}\",\n \"[data-upstart-editor-active] .ProseMirror {\",\n \" outline: none !important;\",\n \" font-size: inherit !important;\",\n \" line-height: inherit !important;\",\n \" color: inherit !important;\",\n \" letter-spacing: inherit !important;\",\n \" font-weight: inherit !important;\",\n \" white-space: inherit !important;\",\n \"}\",\n \"[data-upstart-editor-active] .ProseMirror:focus {\",\n \" outline: none !important;\",\n \"}\",\n ].join(\"\\n\");\n document.head.appendChild(style);\n}\n\n/**\n * Activate editors on all editable elements. Safe to call multiple times.\n */\nexport function activateAllEditors(): void {\n cleanupOrphanedEditors();\n const editables = document.querySelectorAll<HTMLElement>('[data-upstart-editable-text=\"true\"]');\n editables.forEach((element) => {\n try {\n setupEditableElement(element, resolvedOptions);\n } catch (error) {\n console.error(\"[Upstart Editor] Failed to activate element:\", element.dataset.upstartHash, error);\n }\n });\n console.log(\"[Upstart Editor] Text editors activated\");\n}\n\n/**\n * Destroy all active editors.\n */\nexport function destroyAllActiveEditors(): void {\n stopDomObserver();\n for (const hash of activeEditors.keys()) {\n destroyEditor(hash);\n }\n}\n\nfunction setupEditableElement(element: HTMLElement, options: Required<UpstartEditorOptions>): void {\n const hash = getEditableHash(element);\n if (!hash || activeEditors.has(hash)) {\n return;\n }\n\n cacheStyles(element);\n activateEditor(element, hash, options);\n}\n\nfunction activateEditor(element: HTMLElement, hash: string, options: Required<UpstartEditorOptions>): void {\n const mode = getEditorMode(element, options);\n\n let editor: Editor;\n\n switch (mode) {\n case \"direct\":\n editor = createDirectEditor(element, hash, options);\n break;\n case \"rich-panel\":\n editor = createRichPanelEditor(element, hash, options);\n break;\n case \"block-rich\":\n editor = createRichTextEditor(element, hash, options);\n break;\n case \"inline-rich\":\n editor = createInlineRichTextEditor(element, hash, options);\n break;\n case \"plain\":\n editor = createPlainTextEditor(element, hash, options);\n break;\n }\n\n // Mark element — triggers CSS rules that hide original content\n element.dataset.upstartEditorActive = \"true\";\n\n applyActiveStyles(element);\n activeEditors.set(hash, { editor, element, hash, mode });\n}\n\nfunction createPlainTextEditor(\n element: HTMLElement,\n hash: string,\n options: Required<UpstartEditorOptions>,\n): Editor {\n const renderedText = element.textContent ?? \"\";\n\n let content: string | object = renderedText;\n const extraExtensions: ReturnType<typeof Node.create>[] = [];\n\n // Mixed-text: JSXText + expression chips (e.g. © {year} Some text)\n const mixedTemplate = element.dataset.upstartMixedTemplate;\n if (mixedTemplate) {\n content = buildI18nContent(mixedTemplate, renderedText);\n extraExtensions.push(TemplateVariable);\n } else {\n // Check for i18n template variables (e.g. data-i18n-values=\"year,month\")\n const i18nValueKeys = element.dataset.i18nValues?.split(\",\").filter(Boolean) ?? [];\n if (i18nValueKeys.length > 0) {\n const i18nAttr = element.dataset.upstartI18n;\n if (i18nAttr) {\n const colonIdx = i18nAttr.indexOf(\":\");\n const namespace = colonIdx >= 0 ? i18nAttr.slice(0, colonIdx) : \"translation\";\n const key = colonIdx >= 0 ? i18nAttr.slice(colonIdx + 1) : i18nAttr;\n const rawTemplate = options.getRawI18nTemplate(namespace, key);\n if (rawTemplate) {\n content = buildI18nContent(rawTemplate, renderedText);\n extraExtensions.push(TemplateVariable);\n }\n }\n }\n }\n\n element.textContent = \"\";\n let hasChanged = false;\n\n const editor = new Editor({\n element,\n extensions: [\n ...extraExtensions,\n StarterKit.configure({\n heading: false,\n bold: false,\n italic: false,\n strike: false,\n blockquote: false,\n bulletList: false,\n orderedList: false,\n listItem: false,\n codeBlock: false,\n horizontalRule: false,\n }),\n Placeholder.configure({\n placeholder: \"Click to edit...\",\n }),\n ],\n content,\n editorProps: {\n attributes: {\n class: \"upstart-editor-active\",\n },\n },\n onUpdate: () => {\n if (i18nSyncInProgress) return;\n hasChanged = true;\n syncI18nSiblings(hash);\n },\n onBlur: ({ editor: e }) => {\n if (!hasChanged) return;\n hasChanged = false;\n saveText(hash, e.getText());\n },\n });\n\n return editor;\n}\n\nfunction createRichTextEditor(\n element: HTMLElement,\n hash: string,\n options: Required<UpstartEditorOptions>,\n): Editor {\n const content = element.innerHTML;\n element.innerHTML = \"\"; // Clear the element first!\n let hasChanged = false;\n\n const bubbleMenuElement = createBubbleMenuElement();\n const extensions: Extension[] = [\n StarterKit,\n Placeholder.configure({\n placeholder: options.placeholder,\n }),\n ];\n\n if (options.bubbleMenu) {\n extensions.push(\n BubbleMenu.configure({\n element: bubbleMenuElement,\n }),\n );\n }\n\n const editor = new Editor({\n element,\n extensions,\n content,\n editorProps: {\n attributes: {\n class: \"upstart-editor-active\",\n },\n },\n onUpdate: () => {\n if (i18nSyncInProgress) return;\n hasChanged = true;\n syncI18nSiblings(hash);\n },\n onBlur: ({ editor: e }) => {\n if (!hasChanged) return;\n hasChanged = false;\n saveText(hash, e.getHTML());\n },\n });\n\n if (options.bubbleMenu) {\n wireBubbleMenu(bubbleMenuElement, editor);\n }\n\n return editor;\n}\n\nfunction createInlineRichTextEditor(\n element: HTMLElement,\n hash: string,\n options: Required<UpstartEditorOptions>,\n): Editor {\n const content = element.innerHTML;\n element.innerHTML = \"\"; // Clear the element first!\n let hasChanged = false;\n\n const bubbleMenuElement = createBubbleMenuElement(\"inline\");\n const extensions: (Extension | ReturnType<typeof Node.create>)[] = [\n InlineDocument,\n EnterHardBreak,\n StarterKit.configure({\n document: false,\n heading: false,\n blockquote: false,\n bulletList: false,\n orderedList: false,\n listItem: false,\n codeBlock: false,\n horizontalRule: false,\n }),\n Placeholder.configure({\n placeholder: \"Click to edit...\",\n }),\n ];\n\n if (options.bubbleMenu) {\n extensions.push(\n BubbleMenu.configure({\n element: bubbleMenuElement,\n }),\n );\n }\n\n const editor = new Editor({\n element,\n extensions,\n content,\n editorProps: {\n attributes: {\n class: \"upstart-editor-active\",\n },\n },\n onUpdate: () => {\n if (i18nSyncInProgress) return;\n hasChanged = true;\n syncI18nSiblings(hash);\n },\n onBlur: ({ editor: e }) => {\n if (!hasChanged) return;\n hasChanged = false;\n saveText(hash, e.getHTML());\n },\n });\n\n if (options.bubbleMenu) {\n wireBubbleMenu(bubbleMenuElement, editor);\n }\n\n return editor;\n}\n\nfunction createDirectEditor(\n element: HTMLElement,\n hash: string,\n options: Required<UpstartEditorOptions>,\n): Editor {\n const content = element.innerHTML;\n element.innerHTML = \"\";\n let hasChanged = false;\n\n const bubbleMenuElement = createBubbleMenuElement(\"inline\");\n const extensions: (Extension | ReturnType<typeof Node.create>)[] = [\n EnterHardBreak,\n StarterKit.configure({\n heading: false,\n blockquote: false,\n bulletList: false,\n orderedList: false,\n listItem: false,\n codeBlock: false,\n horizontalRule: false,\n }),\n Placeholder.configure({ placeholder: \"Click to edit...\" }),\n ];\n\n if (options.bubbleMenu) {\n extensions.push(BubbleMenu.configure({ element: bubbleMenuElement }));\n }\n\n const editor = new Editor({\n element,\n extensions,\n content,\n editorProps: { attributes: { class: \"upstart-editor-active\" } },\n onUpdate: () => {\n hasChanged = true;\n },\n onBlur: ({ editor: e }) => {\n if (!hasChanged) return;\n hasChanged = false;\n // Strip the outer <p> that TipTap adds when using default document schema\n saveText(hash, e.getHTML().replace(/^<p>([\\s\\S]*)<\\/p>$/, \"$1\"));\n },\n });\n\n if (options.bubbleMenu) {\n wireBubbleMenu(bubbleMenuElement, editor);\n }\n\n return editor;\n}\n\nfunction createRichPanelEditor(\n element: HTMLElement,\n hash: string,\n options: Required<UpstartEditorOptions>,\n): Editor {\n const content = element.innerHTML;\n element.innerHTML = \"\";\n let hasChanged = false;\n\n const bubbleMenuElement = createBubbleMenuElement(\"inline\");\n const extensions: Extension[] = [StarterKit, Placeholder.configure({ placeholder: options.placeholder })];\n\n if (options.bubbleMenu) {\n extensions.push(BubbleMenu.configure({ element: bubbleMenuElement }));\n }\n\n const editor = new Editor({\n element,\n extensions,\n content,\n editorProps: { attributes: { class: \"upstart-editor-active\" } },\n onUpdate: () => {\n hasChanged = true;\n },\n onBlur: ({ editor: e }) => {\n if (!hasChanged) return;\n hasChanged = false;\n saveText(hash, e.getHTML());\n },\n });\n\n if (options.bubbleMenu) {\n wireBubbleMenu(bubbleMenuElement, editor);\n }\n\n return editor;\n}\n\nfunction getEditorMode(element: HTMLElement, options: Required<UpstartEditorOptions>): TextEditorMode {\n const modeOverride = element.dataset.upstartEditableTextMode as TextEditorMode | undefined;\n if (\n modeOverride === \"plain\" ||\n modeOverride === \"inline-rich\" ||\n modeOverride === \"block-rich\" ||\n modeOverride === \"direct\" ||\n modeOverride === \"rich-panel\"\n ) {\n return modeOverride;\n }\n\n const tagName = element.tagName.toLowerCase();\n\n if (options.plainTextElements.includes(tagName)) {\n return \"plain\";\n }\n\n if (options.inlineRichTextElements.includes(tagName)) {\n return \"inline-rich\";\n }\n\n if (options.richTextElements.includes(tagName)) {\n return \"block-rich\";\n }\n\n return \"block-rich\";\n}\n\nfunction syncI18nSiblings(sourceHash: string): void {\n console.log(\"[Upstart Editor] Syncing i18n siblings for\", sourceHash);\n const instance = activeEditors.get(sourceHash);\n if (!instance) {\n console.warn(\"[Upstart Editor] No editor instance found for hash:\", sourceHash);\n return;\n }\n\n const i18nKey = instance.element.dataset.upstartI18n;\n if (!i18nKey) {\n console.warn(\"[Upstart Editor] No sibling i18n key found for element:\", instance.element);\n return;\n }\n\n const siblings = document.querySelectorAll<HTMLElement>(`[data-upstart-i18n=\"${CSS.escape(i18nKey)}\"]`);\n console.log(`[Upstart Editor] Found ${siblings.length} sibling(s) with i18n key \"${i18nKey}\"`);\n\n // Always use plain text so each sibling's schema can wrap it correctly\n // (e.g. InlineDocument vs block Document have incompatible content rules).\n const plainContent = instance.editor.getText();\n\n i18nSyncInProgress = true;\n try {\n for (const sibling of siblings) {\n if (sibling === instance.element) continue;\n\n const siblingHash = sibling.dataset.upstartHash;\n const siblingInstance = siblingHash ? activeEditors.get(siblingHash) : null;\n\n if (siblingInstance) {\n console.log(\n `[Upstart Editor] Updating sibling editor (hash: ${siblingHash}) with new content`,\n siblingInstance,\n );\n sibling.innerText = plainContent;\n siblingInstance.editor.chain().selectAll().insertContent(plainContent).run();\n } else {\n sibling.textContent = plainContent;\n }\n }\n } finally {\n i18nSyncInProgress = false;\n }\n}\n\nfunction saveText(hash: string, newText: string): void {\n try {\n const instance = activeEditors.get(hash);\n const dataset = instance?.element.dataset ?? {};\n\n // direct/rich-panel always go via editTextDirect; plain goes via editTextDirect when a\n // registry id is present (mixed-text), and via editText (i18n) otherwise.\n if (instance?.mode === \"direct\" || instance?.mode === \"rich-panel\" || dataset.upstartId) {\n sendToParent({\n type: \"text-edit\",\n payload: { action: \"editTextDirect\", id: dataset.upstartId!, content: newText },\n });\n } else {\n const [namespace, key] = dataset.upstartI18n?.split(\":\") ?? [];\n sendToParent({\n type: \"text-edit\",\n payload: {\n action: \"editText\",\n content: newText,\n namespace,\n key,\n language: document.documentElement.lang,\n },\n });\n }\n\n console.log(\"[Upstart Editor] Text save message sent:\", hash);\n } catch (error) {\n console.error(\"[Upstart Editor] Failed to send save message:\", error);\n sendToParent({\n type: \"editor-error\",\n error: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n}\n\nfunction destroyEditor(hash: string): void {\n const instance = activeEditors.get(hash);\n if (!instance) {\n return;\n }\n\n const isPlainMode = instance.mode === \"plain\";\n const finalContent = isPlainMode ? instance.editor.getText() : instance.editor.getHTML();\n\n instance.editor.destroy();\n\n // Remove ProseMirror DOM\n const editorDom = instance.element.querySelector(\".ProseMirror\");\n if (editorDom) editorDom.remove();\n\n // Remove CSS-hiding marker — original content becomes visible again\n delete instance.element.dataset.upstartEditorActive;\n\n // Update element content with the final edited text\n if (isPlainMode) {\n instance.element.textContent = finalContent;\n } else {\n instance.element.innerHTML = finalContent;\n }\n\n restoreStyles(instance.element);\n activeEditors.delete(hash);\n}\n\n// ---------------------------------------------------------------------------\n// MutationObserver — re-activates editors after React re-renders\n// ---------------------------------------------------------------------------\n\nfunction startDomObserver(): void {\n if (domObserver) return;\n\n domObserver = new MutationObserver((mutations) => {\n let needsReactivation = false;\n\n for (const mutation of mutations) {\n if (mutation.type !== \"childList\") continue;\n\n for (const node of mutation.addedNodes) {\n if (\n node instanceof HTMLElement &&\n (node.matches?.('[data-upstart-editable-text=\"true\"]') ||\n node.querySelector?.('[data-upstart-editable-text=\"true\"]'))\n ) {\n needsReactivation = true;\n break;\n }\n }\n\n if (!needsReactivation) {\n for (const node of mutation.removedNodes) {\n if (\n node instanceof HTMLElement &&\n (node.matches?.('[data-upstart-editable-text=\"true\"]') ||\n node.querySelector?.('[data-upstart-editable-text=\"true\"]'))\n ) {\n needsReactivation = true;\n break;\n }\n }\n }\n\n if (needsReactivation) break;\n }\n\n if (needsReactivation) {\n scheduleReactivation();\n }\n });\n\n domObserver.observe(document.body, { childList: true, subtree: true });\n}\n\nfunction stopDomObserver(): void {\n if (domObserver) {\n domObserver.disconnect();\n domObserver = null;\n }\n if (reactivateTimer) {\n clearTimeout(reactivateTimer);\n reactivateTimer = null;\n }\n}\n\nfunction scheduleReactivation(): void {\n if (reactivateTimer) clearTimeout(reactivateTimer);\n reactivateTimer = window.setTimeout(() => {\n reactivateTimer = null;\n activateAllEditors();\n }, 50);\n}\n\n/**\n * Remove editors whose host element has been detached from the document\n * (e.g. React replaced the subtree during a re-render).\n */\nfunction cleanupOrphanedEditors(): void {\n for (const [hash, instance] of activeEditors) {\n if (!document.contains(instance.element)) {\n instance.editor.destroy();\n activeEditors.delete(hash);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Keyboard shortcuts\n// ---------------------------------------------------------------------------\n\nfunction registerKeyboardShortcuts(): void {\n if (shortcutsRegistered) {\n return;\n }\n\n document.addEventListener(\"keydown\", (event) => {\n if (event.key === \"Escape\") {\n blurAllEditors();\n }\n\n if ((event.metaKey || event.ctrlKey) && event.key === \"Enter\") {\n blurAllEditors();\n }\n });\n\n shortcutsRegistered = true;\n}\n\nfunction blurAllEditors(): void {\n activeEditors.forEach((instance) => {\n instance.editor.commands.blur();\n });\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction getEditableHash(element: HTMLElement): string | null {\n return element.dataset.upstartHash ?? null;\n}\n\nfunction cacheStyles(element: HTMLElement): void {\n if (styleCache.has(element)) {\n return;\n }\n\n styleCache.set(element, {\n outline: element.style.outline || \"\",\n outlineOffset: element.style.outlineOffset || \"\",\n cursor: element.style.cursor || \"\",\n transition: element.style.transition || \"\",\n });\n}\n\nfunction applyActiveStyles(element: HTMLElement): void {\n cacheStyles(element);\n}\n\nfunction restoreStyles(element: HTMLElement): void {\n const cached = styleCache.get(element);\n if (!cached) {\n element.style.outline = \"\";\n element.style.outlineOffset = \"\";\n element.style.cursor = \"\";\n element.style.transition = \"\";\n return;\n }\n\n element.style.outline = cached.outline;\n element.style.outlineOffset = cached.outlineOffset;\n element.style.cursor = cached.cursor;\n element.style.transition = cached.transition;\n}\n\nconst BUBBLE_MENU_GROUPS: { command: string; icon: string; title: string }[][] = [\n [\n { command: \"bold\", icon: \"format-bold\", title: \"Bold\" },\n { command: \"italic\", icon: \"format-italic\", title: \"Italic\" },\n { command: \"strike\", icon: \"format-strikethrough\", title: \"Strikethrough\" },\n { command: \"code\", icon: \"code-tags\", title: \"Inline Code\" },\n ],\n [\n { command: \"heading\", icon: \"format-header-1\", title: \"Heading\" },\n { command: \"blockquote\", icon: \"format-quote-close\", title: \"Blockquote\" },\n ],\n [\n { command: \"bulletList\", icon: \"format-list-bulleted\", title: \"Bullet List\" },\n { command: \"orderedList\", icon: \"format-list-numbered\", title: \"Ordered List\" },\n ],\n];\n\nfunction getIconifyUrl(iconName: string): string {\n return `https://api.iconify.design/mdi/${iconName}.svg?height=none&color=%23fff`;\n}\n\nfunction createBubbleMenuElement(mode: \"block\" | \"inline\" = \"block\"): HTMLDivElement {\n const groups = mode === \"inline\" ? [BUBBLE_MENU_GROUPS[0]] : BUBBLE_MENU_GROUPS;\n const menu = document.createElement(\"div\");\n menu.className = \"upstart-editor-bubble-menu\";\n menu.style.cssText =\n \"display: flex; align-items: center; gap: 2px; padding: 4px; \" +\n \"background: #111827; color: #ffffff; border-radius: 8px; \" +\n \"font-size: 12px; box-shadow: 0 8px 24px rgba(0,0,0,0.25);\";\n\n groups.forEach((group, groupIndex) => {\n if (groupIndex > 0) {\n const separator = document.createElement(\"div\");\n separator.style.cssText =\n \"width: 1px; height: 16px; background: rgba(255,255,255,0.2); \" + \"margin: 0 4px; flex-shrink: 0;\";\n menu.appendChild(separator);\n }\n\n for (const { command, icon, title } of group) {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.dataset.command = command;\n button.title = title;\n button.style.cssText =\n \"background: transparent; border: none; color: inherit; cursor: pointer; \" +\n \"display: flex; align-items: center; justify-content: center; \" +\n \"width: 28px; height: 28px; padding: 4px; border-radius: 4px; \" +\n \"transition: background 0.15s ease;\";\n\n const img = document.createElement(\"img\");\n img.src = getIconifyUrl(icon);\n img.alt = title;\n img.style.cssText = \"width: 18px; height: 18px; display: block; pointer-events: none;\";\n button.appendChild(img);\n\n button.addEventListener(\"mouseenter\", () => {\n if (!button.dataset.active) {\n button.style.background = \"rgba(255,255,255,0.12)\";\n }\n });\n button.addEventListener(\"mouseleave\", () => {\n button.style.background = button.dataset.active ? \"rgba(255,255,255,0.2)\" : \"transparent\";\n });\n\n menu.appendChild(button);\n }\n });\n\n return menu;\n}\n\nfunction wireBubbleMenu(menu: HTMLDivElement, editor: Editor): void {\n menu.addEventListener(\"mousedown\", (event) => {\n event.preventDefault();\n });\n\n menu.addEventListener(\n \"click\",\n (event) => {\n console.log(\"menu button cliked\");\n event.stopPropagation();\n const target = event.target as HTMLElement | null;\n const command = target?.dataset.command;\n if (!command) return;\n\n const chain = editor.chain().focus();\n\n switch (command) {\n case \"bold\":\n chain.toggleBold().run();\n break;\n case \"italic\":\n chain.toggleItalic().run();\n break;\n case \"strike\":\n chain.toggleStrike().run();\n break;\n case \"code\":\n chain.toggleCode().run();\n break;\n case \"heading\":\n chain.toggleHeading({ level: 1 }).run();\n break;\n case \"blockquote\":\n chain.toggleBlockquote().run();\n break;\n case \"bulletList\":\n chain.toggleBulletList().run();\n break;\n case \"orderedList\":\n chain.toggleOrderedList().run();\n break;\n }\n },\n { capture: true },\n );\n\n const updateActiveStates = (): void => {\n const buttons = menu.querySelectorAll<HTMLButtonElement>(\"button[data-command]\");\n for (const button of buttons) {\n const command = button.dataset.command!;\n const isActive =\n command === \"heading\" ? editor.isActive(\"heading\", { level: 1 }) : editor.isActive(command);\n\n if (isActive) {\n button.dataset.active = \"true\";\n button.style.background = \"rgba(255,255,255,0.2)\";\n } else {\n delete button.dataset.active;\n button.style.background = \"transparent\";\n }\n }\n };\n\n editor.on(\"transaction\", updateActiveStates);\n updateActiveStates();\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { PayloadEditText } from "../../upstart-editor-api.js";
1
+ import { PayloadEditText, PayloadEditTextDirect } from "../../upstart-editor-api.js";
2
2
  import { Editor } from "@tiptap/core";
3
3
 
4
4
  //#region src/vite-plugin-upstart-editor/runtime/types.d.ts
@@ -8,8 +8,13 @@ import { Editor } from "@tiptap/core";
8
8
  type EditorMode = "preview" | "edit";
9
9
  /**
10
10
  * Text editor mode: determines schema constraints and bubble menu options.
11
+ * - block-rich: inline TipTap rich text (block-level elements: p, div, article…)
12
+ * - inline-rich: inline TipTap rich text constrained to inline nodes (spans, headings…)
13
+ * - plain: inline TipTap plain text, no formatting
14
+ * - direct: inline TipTap plain text, saved directly into the TSX source
15
+ * - rich-panel: inline TipTap rich text with bubble menu (block-level, like block-rich)
11
16
  */
12
- type TextEditorMode = "block-rich" | "inline-rich" | "plain";
17
+ type TextEditorMode = "block-rich" | "inline-rich" | "plain" | "direct" | "rich-panel";
13
18
  /**
14
19
  * Element bounds used for UI positioning.
15
20
  */
@@ -35,7 +40,7 @@ interface EditorInstance {
35
40
  */
36
41
  type EditorMessage = {
37
42
  type: "text-edit";
38
- payload: PayloadEditText;
43
+ payload: PayloadEditText | PayloadEditTextDirect;
39
44
  } | {
40
45
  type: "editor-ready";
41
46
  } | {
@@ -58,6 +63,7 @@ type EditorMessage = {
58
63
  recordId?: string;
59
64
  themeColors?: Record<string, string>;
60
65
  bounds: ElementBounds;
66
+ viewportWidth?: number;
61
67
  };
62
68
  /**
63
69
  * Messages sent from parent to iframe.
@@ -69,6 +75,7 @@ type ParentMessage = {
69
75
  type: "preview-classname";
70
76
  classNameId: string;
71
77
  className: string;
78
+ previewCSS?: string;
72
79
  } | {
73
80
  type: "request-scroll-position";
74
81
  } | {
@@ -127,6 +134,12 @@ interface UpstartEditorOptions {
127
134
  * @default 1000
128
135
  */
129
136
  autoSaveDelay?: number;
137
+ /**
138
+ * Callback to retrieve the raw (un-interpolated) i18n template string for a given
139
+ * namespace + key, e.g. "Copyright {{year}}". Used to render template variables as
140
+ * non-editable atoms inside i18n text editors.
141
+ */
142
+ getRawI18nTemplate?: (namespace: string, key: string) => string | undefined;
130
143
  }
131
144
  /**
132
145
  * Build-time plugin options.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/types.ts"],"mappings":";;;;;;AAMA;KAAY,UAAA;;;;KAKA,cAAA;;;;UAKK,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;;;;KAMI,aAAA;EACN,IAAA;EAAmB,OAAA,EAAS,eAAA;AAAA;EAC5B,IAAA;AAAA;EACA,IAAA;AAAA;EACA,IAAA;EAAyB,CAAA;EAAW,CAAA;AAAA;EACpC,IAAA;EAAsB,KAAA;AAAA;EAEtB,IAAA;EACA,IAAA;EACA,aAAA;EACA,QAAA;EACA,gBAAA;EACA,WAAA;EACA,YAAA;EACA,QAAA;EACA,WAAA,GAAc,MAAA;EACd,MAAA,EAAQ,aAAA;AAAA;;;;KAMF,aAAA;EACN,IAAA;EAAkB,IAAA,EAAM,UAAA;AAAA;EACxB,IAAA;EAA2B,WAAA;EAAqB,SAAA;AAAA;EAChD,IAAA;AAAA;EACA,IAAA;EAAiC,CAAA;EAAW,CAAA;AAAA;;;;UAKjC,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;EAhBf;;;;EAqBA,gBAAA;EAde;;;;;EAqBf,sBAAA;EAnBM;;;;EAyBN,iBAAA;EAlBmC;;;;EAwBnC,UAAA;EANA;;;;EAYA,WAAA;EAMa;AAMf;;;EANE,aAAA;AAAA;;;;UAMe,0BAAA;;;;;EAKf,OAAA;;;;;EAMA,UAAA;AAAA"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/types.ts"],"mappings":";;;;;;AAMA;KAAY,UAAA;;;;AAUZ;;;;;KAAY,cAAA;;;;UAKK,aAAA;EACf,GAAA;EACA,IAAA;EACA,KAAA;EACA,MAAA;EACA,KAAA;EACA,MAAA;AAAA;AAMF;;;AAAA,UAAiB,cAAA;EACf,MAAA,EAAQ,MAAA;EACR,OAAA,EAAS,WAAA;EACT,IAAA;EACA,IAAA,EAAM,cAAA;AAAA;;;;KAMI,aAAA;EACN,IAAA;EAAmB,OAAA,EAAS,eAAA,GAAkB,qBAAA;AAAA;EAC9C,IAAA;AAAA;EACA,IAAA;AAAA;EACA,IAAA;EAAyB,CAAA;EAAW,CAAA;AAAA;EACpC,IAAA;EAAsB,KAAA;AAAA;EAEtB,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;AAAA;;;;KAMM,aAAA;EACN,IAAA;EAAkB,IAAA,EAAM,UAAA;AAAA;EACxB,IAAA;EAA2B,WAAA;EAAqB,SAAA;EAAmB,UAAA;AAAA;EACnE,IAAA;AAAA;EACA,IAAA;EAAiC,CAAA;EAAW,CAAA;AAAA;;;;UAKjC,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;EAfH;;AAMd;;EAcE,gBAAA;EAZmB;;;;;EAmBnB,sBAAA;EAlBY;AAMd;;;EAkBE,iBAAA;EAbA;;;;EAmBA,UAAA;EAYA;;;;EANA,WAAA;EAaoD;AAMtD;;;EAbE,aAAA;EAwBU;;;;;EAjBV,kBAAA,IAAsB,SAAA,UAAmB,GAAA;AAAA;;;;UAM1B,0BAAA;;;;;EAKf,OAAA;;;;;EAMA,UAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/utils.ts"],"sourcesContent":["import type { EditorMessage } from \"./types\";\n\n/**\n * Send a message to the parent window.\n */\nexport function sendToParent(message: EditorMessage): void {\n if (typeof window === \"undefined\") {\n return;\n }\n\n if (window.parent === window) {\n console.warn(\"[Upstart Editor] Not running in iframe, cannot send message:\", message);\n return;\n }\n\n window.parent.postMessage(\n {\n source: \"upstart-editor\",\n ...message,\n },\n \"*\",\n );\n}\n\n/**\n * Check if running inside an iframe.\n */\nexport function isInIframe(): boolean {\n if (typeof window === \"undefined\") {\n return false;\n }\n\n return window.parent !== window;\n}\n"],"mappings":";;;;AAKA,SAAgB,aAAa,SAA8B;AACzD,KAAI,OAAO,WAAW,YACpB;AAGF,KAAI,OAAO,WAAW,QAAQ;AAC5B,UAAQ,KAAK,gEAAgE,QAAQ;AACrF;;AAGF,QAAO,OAAO,YACZ;EACE,QAAQ;EACR,GAAG;EACJ,EACD,IACD;;;;;AAMH,SAAgB,aAAsB;AACpC,KAAI,OAAO,WAAW,YACpB,QAAO;AAGT,QAAO,OAAO,WAAW"}
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/utils.ts"],"sourcesContent":["import type { EditorMessage } from \"./types\";\n\n/**\n * Send a message to the parent window.\n */\nexport function sendToParent(message: EditorMessage): void {\n if (typeof window === \"undefined\") {\n return;\n }\n\n if (window.parent === window) {\n console.warn(\"[Upstart Editor] Not running in iframe, cannot send message:\", message);\n return;\n }\n\n window.parent.postMessage(\n {\n source: \"upstart-editor\",\n ...message,\n },\n \"*\",\n );\n}\n\n/**\n * Check if running inside an iframe.\n */\nexport function isInIframe(): boolean {\n if (typeof window === \"undefined\") {\n return false;\n }\n\n return window.parent !== window;\n}\n"],"mappings":";;;;AAKA,SAAgB,aAAa,SAA8B;CACzD,IAAI,OAAO,WAAW,aACpB;CAGF,IAAI,OAAO,WAAW,QAAQ;EAC5B,QAAQ,KAAK,gEAAgE,QAAQ;EACrF;;CAGF,OAAO,OAAO,YACZ;EACE,QAAQ;EACR,GAAG;EACJ,EACD,IACD;;;;;AAMH,SAAgB,aAAsB;CACpC,IAAI,OAAO,WAAW,aACpB,OAAO;CAGT,OAAO,OAAO,WAAW"}
@@ -1,4 +1,4 @@
1
- import * as unplugin from "unplugin";
1
+ import * as _$unplugin from "unplugin";
2
2
  import { Theme } from "@upstart.gg/sdk/themes/theme";
3
3
 
4
4
  //#region src/vite-plugin-upstart-theme.d.ts
@@ -17,8 +17,8 @@ interface PluginOptions {
17
17
  /**
18
18
  * Unplugin-based theme generator
19
19
  */
20
- declare const upstartTheme: unplugin.UnpluginInstance<PluginOptions, boolean>;
21
- declare const _default: (options: PluginOptions) => unplugin.VitePlugin<any>[] | unplugin.VitePlugin<any>;
20
+ declare const upstartTheme: _$unplugin.UnpluginInstance<PluginOptions, boolean>;
21
+ declare const _default: (options: PluginOptions) => _$unplugin.VitePlugin<any>[] | _$unplugin.VitePlugin<any>;
22
22
  //#endregion
23
23
  export { _default as default, upstartTheme };
24
24
  //# sourceMappingURL=vite-plugin-upstart-theme.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin-upstart-theme.d.ts","names":[],"sources":["../src/vite-plugin-upstart-theme.ts"],"mappings":";;;;UAWU,aAAA;EACR,MAAA;IACE,KAAA,EAAO,KAAA;IACP,IAAA,GAAO,KAAA;IACP,OAAA;EAAA;EAEF,UAAA;EALA;EAOA,YAAA;EANS;EAQT,YAAA;AAAA;;;;cA4VW,YAAA,EAAY,QAAA,CAAA,gBAAA,CAAA,aAAA;AAAA,cAoEtB,QAAA"}
1
+ {"version":3,"file":"vite-plugin-upstart-theme.d.ts","names":[],"sources":["../src/vite-plugin-upstart-theme.ts"],"mappings":";;;;UAWU,aAAA;EACR,MAAA;IACE,KAAA,EAAO,KAAA;IACP,IAAA,GAAO,KAAA;IACP,OAAA;EAAA;EAEF,UAAA;EALA;EAOA,YAAA;EANS;EAQT,YAAA;AAAA;;;;cA+VW,YAAA,EAAY,UAAA,CAAA,gBAAA,CAAA,aAAA;AAAA,cAoEtB,QAAA"}
@@ -1,7 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { createUnplugin } from "unplugin";
3
- import { fontStacksFonts, processTheme } from "@upstart.gg/sdk/themes/theme";
4
3
  import fs from "node:fs";
4
+ import { fontStacksFonts, processTheme } from "@upstart.gg/sdk/themes/theme";
5
5
  //#region src/vite-plugin-upstart-theme.ts
6
6
  /**
7
7
  * Collects Google Fonts from a theme's typography configuration
@@ -32,7 +32,7 @@ function generateFontsCSS(lightTheme, darkTheme = null, display = "swap") {
32
32
  if (allFonts.size === 0) return "/* No Google Fonts to import */\n";
33
33
  return `/* Google Fonts - Auto-generated by vite-plugin-upstart-theme */\n@import url("${`https://fonts.googleapis.com/css2?${Array.from(allFonts.values()).map((font) => {
34
34
  const name = font.family.replace(/ /g, "+");
35
- if (font.weights && font.weights.length > 1 && font.weights[0] !== font.weights.at(-1)) return `family=${name}:wght@${font.weights[0]}..${font.weights.at(-1)}`;
35
+ if (font.weights && font.weights.length > 1 && font.weights[0] !== font.weights.at(-1)) return `family=${name}:wght@${Math.min(...font.weights)}..${Math.max(...font.weights)}`;
36
36
  return `family=${name}`;
37
37
  }).join("&")}&display=${display}`}");\n`;
38
38
  }
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin-upstart-theme.js","names":[],"sources":["../src/vite-plugin-upstart-theme.ts"],"sourcesContent":["import { createUnplugin } from \"unplugin\";\nimport {\n GenericFont,\n GoogleFont,\n type Theme,\n fontStacksFonts,\n processTheme,\n} from \"@upstart.gg/sdk/themes/theme\";\nimport fs from \"fs\";\nimport path from \"path\";\n\ninterface PluginOptions {\n themes: {\n light: Theme;\n dark?: Theme;\n default: \"light\" | \"dark\";\n };\n outputPath: string;\n /** Google Fonts display parameter (default: 'swap') */\n fontsDisplay?: \"auto\" | \"block\" | \"swap\" | \"fallback\" | \"optional\";\n /** CSS framework to target (default: 'daisy-ui') */\n cssFramework?: \"daisy-ui\" | \"shadcn-ui\";\n}\n\n/**\n * Collects Google Fonts from a theme's typography configuration\n */\nfunction collectGoogleFonts(theme: Theme) {\n const { typography } = theme;\n if (!typography) return [];\n\n const googleFonts: Array<GoogleFont> = [];\n const seen = new Set<string>();\n\n const addFont = (font?: GenericFont) => {\n if (font?.type === \"google\" && !seen.has(font.family)) {\n seen.add(font.family);\n googleFonts.push(font);\n }\n };\n\n addFont(typography.sans);\n addFont(typography.serif);\n if (typography.others) {\n Object.values(typography.others).forEach(addFont);\n }\n\n return googleFonts;\n}\n\n/**\n * Generates the fonts.css file content with Google Fonts imports\n */\nfunction generateFontsCSS(\n lightTheme: Theme,\n darkTheme: Theme | null = null,\n display: \"auto\" | \"block\" | \"swap\" | \"fallback\" | \"optional\" = \"swap\",\n): string {\n // Collect fonts from both themes\n const allFonts = new Map<string, GoogleFont>();\n\n for (const font of collectGoogleFonts(lightTheme)) {\n allFonts.set(font.family, font);\n }\n\n if (darkTheme) {\n for (const font of collectGoogleFonts(darkTheme)) {\n allFonts.set(font.family, font);\n }\n }\n\n if (allFonts.size === 0) {\n return \"/* No Google Fonts to import */\\n\";\n }\n\n // Build the Google Fonts URL\n const families = Array.from(allFonts.values()).map((font) => {\n const name = font.family.replace(/ /g, \"+\");\n if (font.weights && font.weights.length > 1 && font.weights[0] !== font.weights.at(-1)) {\n return `family=${name}:wght@${font.weights[0]}..${font.weights.at(-1)}`;\n }\n return `family=${name}`;\n });\n\n const url = `https://fonts.googleapis.com/css2?${families.join(\"&\")}&display=${display}`;\n\n return `/* Google Fonts - Auto-generated by vite-plugin-upstart-theme */\\n@import url(\"${url}\");\\n`;\n}\n\n/**\n * Converts a font definition to a CSS font-family string\n */\nfunction fontToCss(font: Theme[\"typography\"][\"sans\"]): string {\n if (!font) return \"\";\n\n if (font.type === \"stack\") {\n return fontStacksFonts[font.family];\n }\n\n if (font.type === \"google\") {\n // For Google fonts, we just use the family name\n // The actual font loading will be handled separately\n return `\"${font.family}\", system-ui, sans-serif`;\n }\n\n return \"\";\n}\n\n/**\n * Converts camelCase to kebab-case\n */\nfunction camelToKebab(str: string): string {\n return str.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase();\n}\n\n/**\n * Generates @theme block with font definitions\n */\nfunction generateThemeBlock(\n lightTheme: Theme,\n darkTheme: Theme | null = null,\n defaultTheme: \"light\" | \"dark\" = \"light\",\n): string {\n const fontDefinitions = new Map<string, string>();\n\n // Process light theme fonts\n const lightTypography = lightTheme.typography;\n\n // Add sans font\n if (lightTypography.sans) {\n const fontCss = fontToCss(lightTypography.sans);\n if (fontCss) {\n fontDefinitions.set(\"sans\", fontCss);\n }\n }\n\n // Add serif font\n if (lightTypography.serif) {\n const fontCss = fontToCss(lightTypography.serif);\n if (fontCss) {\n fontDefinitions.set(\"serif\", fontCss);\n }\n }\n\n // Add other fonts\n if (lightTypography.others) {\n for (const [name, font] of Object.entries(lightTypography.others)) {\n const fontCss = fontToCss(font);\n if (fontCss) {\n fontDefinitions.set(name, fontCss);\n }\n }\n }\n\n // Process dark theme fonts if provided\n if (darkTheme) {\n const darkTypography = darkTheme.typography;\n\n if (darkTypography.sans) {\n const fontCss = fontToCss(darkTypography.sans);\n if (fontCss) {\n fontDefinitions.set(\"sans\", fontCss);\n }\n }\n\n if (darkTypography.serif) {\n const fontCss = fontToCss(darkTypography.serif);\n if (fontCss) {\n fontDefinitions.set(\"serif\", fontCss);\n }\n }\n\n if (darkTypography.others) {\n for (const [name, font] of Object.entries(darkTypography.others)) {\n const fontCss = fontToCss(font);\n if (fontCss) {\n fontDefinitions.set(name, fontCss);\n }\n }\n }\n }\n\n const fontLines = Array.from(fontDefinitions.entries())\n .map(([name, value]) => ` --font-${name}: ${value};`)\n .join(\"\\n\");\n\n return `/* Fonts in the tailwind @theme block */\n@theme {\n ${fontLines}\n}\n\n/* Exclude properties since it produce warnings and is not important */\n@plugin \"daisyui\" {\n exclude: properties;\n themes: ${lightTheme.id} ${defaultTheme === \"light\" || !darkTheme ? \"--default\" : \"\"}${darkTheme?.id && darkTheme?.id !== lightTheme.id ? `, ${darkTheme.id} ${defaultTheme === \"dark\" ? \"--default\" : \"--prefersdark\"}` : \"\"};\n logs: true;\n}\n`;\n}\n\n/**\n * Generates @theme block for a single theme with colors and layout variables\n */\nfunction generateThemeVariables(theme: Theme, defaultTheme: \"light\" | \"dark\"): string {\n const processed = processTheme(theme);\n const lines: string[] = [\n ` name: \"${processed.id}\";`,\n ` color-scheme: ${processed.browserColorScheme};`,\n ...((defaultTheme === \"dark\" && theme.browserColorScheme === \"dark\") ||\n (defaultTheme === \"light\" && theme.browserColorScheme === \"light\")\n ? [` default: true;`]\n : []),\n // ...(type === \"dark\" && defaultTheme !== \"dark\" ? [` prefersdark: true;`] : [` prefersdark: false;`]),\n ];\n\n // Color variables\n for (const [colorKey, colorValue] of Object.entries(processed.colors)) {\n const kebabKey = [\"base100\", \"base200\", \"base300\"].includes(colorKey)\n ? colorKey.replace(\"base\", \"base-\")\n : camelToKebab(colorKey);\n lines.push(` --color-${kebabKey}: ${colorValue};`);\n }\n\n lines.push(\"\");\n\n // Layout variables\n lines.push(` --radius-selector: ${processed.radiusSelector};`);\n lines.push(` --radius-field: ${processed.radiusField};`);\n lines.push(` --radius-box: ${processed.radiusBox};`);\n lines.push(` --size-selector: ${processed.sizeSelector};`);\n lines.push(` --size-field: ${processed.sizeField};`);\n lines.push(` --border: ${processed.border};`);\n lines.push(` --depth: ${processed.depth ? 1 : 0};`);\n lines.push(` --noise: 0;`);\n\n // return the generated lines\n return lines.join(\"\\n\");\n}\n\n/**\n * Builds the CSS variable block for a single shadcn theme (`:root` or `.dark`)\n */\nfunction buildShadcnVars(theme: Theme): string {\n const processed = processTheme(theme);\n const { colors } = processed;\n\n const monoFont = theme.typography?.others?.mono;\n const monoFamilyValue = monoFont ? fontToCss(monoFont) : \"ui-monospace, monospace\";\n const sansFamilyValue = theme.typography?.sans\n ? fontToCss(theme.typography.sans)\n : \"ui-sans-serif, system-ui, sans-serif\";\n\n const vars: [string, string][] = [\n [\"--radius\", processed.radiusField],\n [\"--background\", colors.base100],\n [\"--foreground\", colors.baseContent],\n [\"--card\", colors.base200],\n [\"--card-foreground\", colors.baseContent],\n [\"--popover\", colors.base200],\n [\"--popover-foreground\", colors.baseContent],\n [\"--primary\", colors.primary],\n [\"--primary-foreground\", colors.primaryContent],\n [\"--secondary\", colors.secondary],\n [\"--secondary-foreground\", colors.secondaryContent],\n [\"--muted\", colors.neutral],\n [\"--muted-foreground\", colors.neutralContent],\n // shadcn --accent = hover surface, NOT the vibrant accent color\n [\"--accent\", colors.base300],\n [\"--accent-foreground\", colors.baseContent],\n [\"--destructive\", colors.error],\n [\"--border\", colors.base300],\n [\"--input\", colors.base300],\n [\"--ring\", colors.primary],\n [\"--chart-1\", colors.primary],\n [\"--chart-2\", colors.secondary],\n // vibrant accent goes in chart-3\n [\"--chart-3\", colors.accent],\n [\"--chart-4\", colors.success],\n [\"--chart-5\", colors.warning],\n [\"--sidebar\", colors.base200],\n [\"--sidebar-foreground\", colors.baseContent],\n [\"--sidebar-primary\", colors.primary],\n [\"--sidebar-primary-foreground\", colors.primaryContent],\n [\"--sidebar-accent\", colors.base300],\n [\"--sidebar-accent-foreground\", colors.baseContent],\n [\"--sidebar-border\", colors.base300],\n [\"--sidebar-ring\", colors.primary],\n [\"--font-sans\", sansFamilyValue],\n [\"--font-mono\", monoFamilyValue],\n ];\n\n return vars.map(([k, v]) => ` ${k}: ${v};`).join(\"\\n\");\n}\n\n/**\n * Generates the full shadcn-compatible CSS output (no DaisyUI, no @plugin blocks).\n * Uses :root for light theme and .dark for dark theme.\n */\nfunction generateShadcnThemeCSS(lightTheme: Theme, darkTheme: Theme | null = null): string {\n const output: string[] = [];\n\n // Tailwind v4 @theme inline — wires CSS vars to Tailwind utility classes\n output.push(`@theme inline {\n --radius-sm: calc(var(--radius) - 4px);\n --radius-md: calc(var(--radius) - 2px);\n --radius-lg: var(--radius);\n --radius-xl: calc(var(--radius) + 4px);\n --color-background: var(--background);\n --color-foreground: var(--foreground);\n --color-card: var(--card);\n --color-card-foreground: var(--card-foreground);\n --color-popover: var(--popover);\n --color-popover-foreground: var(--popover-foreground);\n --color-primary: var(--primary);\n --color-primary-foreground: var(--primary-foreground);\n --color-secondary: var(--secondary);\n --color-secondary-foreground: var(--secondary-foreground);\n --color-muted: var(--muted);\n --color-muted-foreground: var(--muted-foreground);\n --color-accent: var(--accent);\n --color-accent-foreground: var(--accent-foreground);\n --color-destructive: var(--destructive);\n --color-border: var(--border);\n --color-input: var(--input);\n --color-ring: var(--ring);\n --color-chart-1: var(--chart-1);\n --color-chart-2: var(--chart-2);\n --color-chart-3: var(--chart-3);\n --color-chart-4: var(--chart-4);\n --color-chart-5: var(--chart-5);\n --color-sidebar: var(--sidebar);\n --color-sidebar-foreground: var(--sidebar-foreground);\n --color-sidebar-primary: var(--sidebar-primary);\n --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);\n --color-sidebar-accent: var(--sidebar-accent);\n --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);\n --color-sidebar-border: var(--sidebar-border);\n --color-sidebar-ring: var(--sidebar-ring);\n --font-sans: var(--font-sans);\n --font-mono: var(--font-mono);\n}`);\n\n output.push(\"\");\n\n // Light theme — always :root\n // If only a dark theme is provided (no separate light), use :root, .dark\n const isDarkOnly = !darkTheme && lightTheme.browserColorScheme === \"dark\";\n const lightSelector = isDarkOnly ? \":root, .dark\" : \":root\";\n\n output.push(`/* ${lightTheme.name} theme */`);\n output.push(`${lightSelector} {`);\n output.push(buildShadcnVars(lightTheme));\n output.push(\"}\");\n\n // Dark theme — .dark selector\n if (darkTheme && darkTheme.id !== lightTheme.id) {\n output.push(\"\");\n output.push(`/* ${darkTheme.name} theme (dark) */`);\n output.push(\".dark {\");\n output.push(buildShadcnVars(darkTheme));\n output.push(\"}\");\n }\n\n return output.join(\"\\n\");\n}\n\n/**\n * Unplugin-based theme generator\n */\nexport const upstartTheme = createUnplugin<PluginOptions>((opts) => {\n let generated = false;\n\n return {\n name: \"unplugin-theme-generator\",\n apply: \"serve\",\n\n vite: {\n configResolved() {\n // Generate the CSS file once when config is resolved\n if (generated) return;\n generated = true;\n\n if (!opts?.themes?.light) {\n console.warn(\"No light theme provided to unplugin-theme-generator. A light theme is required.\");\n return;\n }\n\n const outputDir = path.dirname(opts.outputPath);\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true });\n }\n\n const { themes, fontsDisplay = \"swap\", cssFramework = \"daisy-ui\" } = opts;\n\n // Generate fonts.css file (shared by both frameworks)\n const fontsCSS = generateFontsCSS(themes.light, themes.dark, fontsDisplay);\n const fontsCSSPath = path.join(outputDir, \"fonts.css\");\n fs.writeFileSync(fontsCSSPath, fontsCSS);\n\n let cssContent: string;\n\n if (cssFramework === \"shadcn-ui\") {\n cssContent = generateShadcnThemeCSS(themes.light, themes.dark ?? null);\n } else {\n const output: string[] = [];\n\n // Generate @theme block with fonts from both themes\n output.push(generateThemeBlock(themes.light, themes.dark));\n output.push(\"\");\n\n // IMPORTANT; Place dark theme first OTHERWISE daisyUI will not apply it correctly\n // @see https://github.com/saadeghi/daisyui/issues/3921#issuecomment-3059524563\n // Generate @theme block for dark theme if present\n if (themes.dark) {\n output.push(\"/* Dark theme variables */\");\n output.push('@plugin \"daisyui/theme\" {');\n output.push(generateThemeVariables(themes.dark, themes.default));\n output.push(\"}\");\n output.push(\"\");\n }\n\n // Generate @theme block for light theme\n if (themes.light?.id !== themes.dark?.id) {\n output.push(\"/* Light theme variables */\");\n output.push('@plugin \"daisyui/theme\" {');\n output.push(generateThemeVariables(themes.light, themes.default));\n output.push(\"}\");\n output.push(\"\");\n }\n\n cssContent = output.join(\"\\n\");\n }\n\n fs.writeFileSync(opts.outputPath, cssContent);\n },\n },\n };\n});\n\nexport default upstartTheme.vite;\n"],"mappings":"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"}
1
+ {"version":3,"file":"vite-plugin-upstart-theme.js","names":[],"sources":["../src/vite-plugin-upstart-theme.ts"],"sourcesContent":["import { createUnplugin } from \"unplugin\";\nimport {\n GenericFont,\n GoogleFont,\n type Theme,\n fontStacksFonts,\n processTheme,\n} from \"@upstart.gg/sdk/themes/theme\";\nimport fs from \"fs\";\nimport path from \"path\";\n\ninterface PluginOptions {\n themes: {\n light: Theme;\n dark?: Theme;\n default: \"light\" | \"dark\";\n };\n outputPath: string;\n /** Google Fonts display parameter (default: 'swap') */\n fontsDisplay?: \"auto\" | \"block\" | \"swap\" | \"fallback\" | \"optional\";\n /** CSS framework to target (default: 'daisy-ui') */\n cssFramework?: \"daisy-ui\" | \"shadcn-ui\";\n}\n\n/**\n * Collects Google Fonts from a theme's typography configuration\n */\nfunction collectGoogleFonts(theme: Theme) {\n const { typography } = theme;\n if (!typography) return [];\n\n const googleFonts: Array<GoogleFont> = [];\n const seen = new Set<string>();\n\n const addFont = (font?: GenericFont) => {\n if (font?.type === \"google\" && !seen.has(font.family)) {\n seen.add(font.family);\n googleFonts.push(font);\n }\n };\n\n addFont(typography.sans);\n addFont(typography.serif);\n if (typography.others) {\n Object.values(typography.others).forEach(addFont);\n }\n\n return googleFonts;\n}\n\n/**\n * Generates the fonts.css file content with Google Fonts imports\n */\nfunction generateFontsCSS(\n lightTheme: Theme,\n darkTheme: Theme | null = null,\n display: \"auto\" | \"block\" | \"swap\" | \"fallback\" | \"optional\" = \"swap\",\n): string {\n // Collect fonts from both themes\n const allFonts = new Map<string, GoogleFont>();\n\n for (const font of collectGoogleFonts(lightTheme)) {\n allFonts.set(font.family, font);\n }\n\n if (darkTheme) {\n for (const font of collectGoogleFonts(darkTheme)) {\n allFonts.set(font.family, font);\n }\n }\n\n if (allFonts.size === 0) {\n return \"/* No Google Fonts to import */\\n\";\n }\n\n // Build the Google Fonts URL\n const families = Array.from(allFonts.values()).map((font) => {\n const name = font.family.replace(/ /g, \"+\");\n if (font.weights && font.weights.length > 1 && font.weights[0] !== font.weights.at(-1)) {\n // Take min weight and max weight\n const minWeight = Math.min(...font.weights);\n const maxWeight = Math.max(...font.weights);\n return `family=${name}:wght@${minWeight}..${maxWeight}`;\n }\n return `family=${name}`;\n });\n\n const url = `https://fonts.googleapis.com/css2?${families.join(\"&\")}&display=${display}`;\n\n return `/* Google Fonts - Auto-generated by vite-plugin-upstart-theme */\\n@import url(\"${url}\");\\n`;\n}\n\n/**\n * Converts a font definition to a CSS font-family string\n */\nfunction fontToCss(font: Theme[\"typography\"][\"sans\"]): string {\n if (!font) return \"\";\n\n if (font.type === \"stack\") {\n return fontStacksFonts[font.family];\n }\n\n if (font.type === \"google\") {\n // For Google fonts, we just use the family name\n // The actual font loading will be handled separately\n return `\"${font.family}\", system-ui, sans-serif`;\n }\n\n return \"\";\n}\n\n/**\n * Converts camelCase to kebab-case\n */\nfunction camelToKebab(str: string): string {\n return str.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase();\n}\n\n/**\n * Generates @theme block with font definitions\n */\nfunction generateThemeBlock(\n lightTheme: Theme,\n darkTheme: Theme | null = null,\n defaultTheme: \"light\" | \"dark\" = \"light\",\n): string {\n const fontDefinitions = new Map<string, string>();\n\n // Process light theme fonts\n const lightTypography = lightTheme.typography;\n\n // Add sans font\n if (lightTypography.sans) {\n const fontCss = fontToCss(lightTypography.sans);\n if (fontCss) {\n fontDefinitions.set(\"sans\", fontCss);\n }\n }\n\n // Add serif font\n if (lightTypography.serif) {\n const fontCss = fontToCss(lightTypography.serif);\n if (fontCss) {\n fontDefinitions.set(\"serif\", fontCss);\n }\n }\n\n // Add other fonts\n if (lightTypography.others) {\n for (const [name, font] of Object.entries(lightTypography.others)) {\n const fontCss = fontToCss(font);\n if (fontCss) {\n fontDefinitions.set(name, fontCss);\n }\n }\n }\n\n // Process dark theme fonts if provided\n if (darkTheme) {\n const darkTypography = darkTheme.typography;\n\n if (darkTypography.sans) {\n const fontCss = fontToCss(darkTypography.sans);\n if (fontCss) {\n fontDefinitions.set(\"sans\", fontCss);\n }\n }\n\n if (darkTypography.serif) {\n const fontCss = fontToCss(darkTypography.serif);\n if (fontCss) {\n fontDefinitions.set(\"serif\", fontCss);\n }\n }\n\n if (darkTypography.others) {\n for (const [name, font] of Object.entries(darkTypography.others)) {\n const fontCss = fontToCss(font);\n if (fontCss) {\n fontDefinitions.set(name, fontCss);\n }\n }\n }\n }\n\n const fontLines = Array.from(fontDefinitions.entries())\n .map(([name, value]) => ` --font-${name}: ${value};`)\n .join(\"\\n\");\n\n return `/* Fonts in the tailwind @theme block */\n@theme {\n ${fontLines}\n}\n\n/* Exclude properties since it produce warnings and is not important */\n@plugin \"daisyui\" {\n exclude: properties;\n themes: ${lightTheme.id} ${defaultTheme === \"light\" || !darkTheme ? \"--default\" : \"\"}${darkTheme?.id && darkTheme?.id !== lightTheme.id ? `, ${darkTheme.id} ${defaultTheme === \"dark\" ? \"--default\" : \"--prefersdark\"}` : \"\"};\n logs: true;\n}\n`;\n}\n\n/**\n * Generates @theme block for a single theme with colors and layout variables\n */\nfunction generateThemeVariables(theme: Theme, defaultTheme: \"light\" | \"dark\"): string {\n const processed = processTheme(theme);\n const lines: string[] = [\n ` name: \"${processed.id}\";`,\n ` color-scheme: ${processed.browserColorScheme};`,\n ...((defaultTheme === \"dark\" && theme.browserColorScheme === \"dark\") ||\n (defaultTheme === \"light\" && theme.browserColorScheme === \"light\")\n ? [` default: true;`]\n : []),\n // ...(type === \"dark\" && defaultTheme !== \"dark\" ? [` prefersdark: true;`] : [` prefersdark: false;`]),\n ];\n\n // Color variables\n for (const [colorKey, colorValue] of Object.entries(processed.colors)) {\n const kebabKey = [\"base100\", \"base200\", \"base300\"].includes(colorKey)\n ? colorKey.replace(\"base\", \"base-\")\n : camelToKebab(colorKey);\n lines.push(` --color-${kebabKey}: ${colorValue};`);\n }\n\n lines.push(\"\");\n\n // Layout variables\n lines.push(` --radius-selector: ${processed.radiusSelector};`);\n lines.push(` --radius-field: ${processed.radiusField};`);\n lines.push(` --radius-box: ${processed.radiusBox};`);\n lines.push(` --size-selector: ${processed.sizeSelector};`);\n lines.push(` --size-field: ${processed.sizeField};`);\n lines.push(` --border: ${processed.border};`);\n lines.push(` --depth: ${processed.depth ? 1 : 0};`);\n lines.push(` --noise: 0;`);\n\n // return the generated lines\n return lines.join(\"\\n\");\n}\n\n/**\n * Builds the CSS variable block for a single shadcn theme (`:root` or `.dark`)\n */\nfunction buildShadcnVars(theme: Theme): string {\n const processed = processTheme(theme);\n const { colors } = processed;\n\n const monoFont = theme.typography?.others?.mono;\n const monoFamilyValue = monoFont ? fontToCss(monoFont) : \"ui-monospace, monospace\";\n const sansFamilyValue = theme.typography?.sans\n ? fontToCss(theme.typography.sans)\n : \"ui-sans-serif, system-ui, sans-serif\";\n\n const vars: [string, string][] = [\n [\"--radius\", processed.radiusField],\n [\"--background\", colors.base100],\n [\"--foreground\", colors.baseContent],\n [\"--card\", colors.base200],\n [\"--card-foreground\", colors.baseContent],\n [\"--popover\", colors.base200],\n [\"--popover-foreground\", colors.baseContent],\n [\"--primary\", colors.primary],\n [\"--primary-foreground\", colors.primaryContent],\n [\"--secondary\", colors.secondary],\n [\"--secondary-foreground\", colors.secondaryContent],\n [\"--muted\", colors.neutral],\n [\"--muted-foreground\", colors.neutralContent],\n // shadcn --accent = hover surface, NOT the vibrant accent color\n [\"--accent\", colors.base300],\n [\"--accent-foreground\", colors.baseContent],\n [\"--destructive\", colors.error],\n [\"--border\", colors.base300],\n [\"--input\", colors.base300],\n [\"--ring\", colors.primary],\n [\"--chart-1\", colors.primary],\n [\"--chart-2\", colors.secondary],\n // vibrant accent goes in chart-3\n [\"--chart-3\", colors.accent],\n [\"--chart-4\", colors.success],\n [\"--chart-5\", colors.warning],\n [\"--sidebar\", colors.base200],\n [\"--sidebar-foreground\", colors.baseContent],\n [\"--sidebar-primary\", colors.primary],\n [\"--sidebar-primary-foreground\", colors.primaryContent],\n [\"--sidebar-accent\", colors.base300],\n [\"--sidebar-accent-foreground\", colors.baseContent],\n [\"--sidebar-border\", colors.base300],\n [\"--sidebar-ring\", colors.primary],\n [\"--font-sans\", sansFamilyValue],\n [\"--font-mono\", monoFamilyValue],\n ];\n\n return vars.map(([k, v]) => ` ${k}: ${v};`).join(\"\\n\");\n}\n\n/**\n * Generates the full shadcn-compatible CSS output (no DaisyUI, no @plugin blocks).\n * Uses :root for light theme and .dark for dark theme.\n */\nfunction generateShadcnThemeCSS(lightTheme: Theme, darkTheme: Theme | null = null): string {\n const output: string[] = [];\n\n // Tailwind v4 @theme inline — wires CSS vars to Tailwind utility classes\n output.push(`@theme inline {\n --radius-sm: calc(var(--radius) - 4px);\n --radius-md: calc(var(--radius) - 2px);\n --radius-lg: var(--radius);\n --radius-xl: calc(var(--radius) + 4px);\n --color-background: var(--background);\n --color-foreground: var(--foreground);\n --color-card: var(--card);\n --color-card-foreground: var(--card-foreground);\n --color-popover: var(--popover);\n --color-popover-foreground: var(--popover-foreground);\n --color-primary: var(--primary);\n --color-primary-foreground: var(--primary-foreground);\n --color-secondary: var(--secondary);\n --color-secondary-foreground: var(--secondary-foreground);\n --color-muted: var(--muted);\n --color-muted-foreground: var(--muted-foreground);\n --color-accent: var(--accent);\n --color-accent-foreground: var(--accent-foreground);\n --color-destructive: var(--destructive);\n --color-border: var(--border);\n --color-input: var(--input);\n --color-ring: var(--ring);\n --color-chart-1: var(--chart-1);\n --color-chart-2: var(--chart-2);\n --color-chart-3: var(--chart-3);\n --color-chart-4: var(--chart-4);\n --color-chart-5: var(--chart-5);\n --color-sidebar: var(--sidebar);\n --color-sidebar-foreground: var(--sidebar-foreground);\n --color-sidebar-primary: var(--sidebar-primary);\n --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);\n --color-sidebar-accent: var(--sidebar-accent);\n --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);\n --color-sidebar-border: var(--sidebar-border);\n --color-sidebar-ring: var(--sidebar-ring);\n --font-sans: var(--font-sans);\n --font-mono: var(--font-mono);\n}`);\n\n output.push(\"\");\n\n // Light theme — always :root\n // If only a dark theme is provided (no separate light), use :root, .dark\n const isDarkOnly = !darkTheme && lightTheme.browserColorScheme === \"dark\";\n const lightSelector = isDarkOnly ? \":root, .dark\" : \":root\";\n\n output.push(`/* ${lightTheme.name} theme */`);\n output.push(`${lightSelector} {`);\n output.push(buildShadcnVars(lightTheme));\n output.push(\"}\");\n\n // Dark theme — .dark selector\n if (darkTheme && darkTheme.id !== lightTheme.id) {\n output.push(\"\");\n output.push(`/* ${darkTheme.name} theme (dark) */`);\n output.push(\".dark {\");\n output.push(buildShadcnVars(darkTheme));\n output.push(\"}\");\n }\n\n return output.join(\"\\n\");\n}\n\n/**\n * Unplugin-based theme generator\n */\nexport const upstartTheme = createUnplugin<PluginOptions>((opts) => {\n let generated = false;\n\n return {\n name: \"unplugin-theme-generator\",\n apply: \"serve\",\n\n vite: {\n configResolved() {\n // Generate the CSS file once when config is resolved\n if (generated) return;\n generated = true;\n\n if (!opts?.themes?.light) {\n console.warn(\"No light theme provided to unplugin-theme-generator. A light theme is required.\");\n return;\n }\n\n const outputDir = path.dirname(opts.outputPath);\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true });\n }\n\n const { themes, fontsDisplay = \"swap\", cssFramework = \"daisy-ui\" } = opts;\n\n // Generate fonts.css file (shared by both frameworks)\n const fontsCSS = generateFontsCSS(themes.light, themes.dark, fontsDisplay);\n const fontsCSSPath = path.join(outputDir, \"fonts.css\");\n fs.writeFileSync(fontsCSSPath, fontsCSS);\n\n let cssContent: string;\n\n if (cssFramework === \"shadcn-ui\") {\n cssContent = generateShadcnThemeCSS(themes.light, themes.dark ?? null);\n } else {\n const output: string[] = [];\n\n // Generate @theme block with fonts from both themes\n output.push(generateThemeBlock(themes.light, themes.dark));\n output.push(\"\");\n\n // IMPORTANT; Place dark theme first OTHERWISE daisyUI will not apply it correctly\n // @see https://github.com/saadeghi/daisyui/issues/3921#issuecomment-3059524563\n // Generate @theme block for dark theme if present\n if (themes.dark) {\n output.push(\"/* Dark theme variables */\");\n output.push('@plugin \"daisyui/theme\" {');\n output.push(generateThemeVariables(themes.dark, themes.default));\n output.push(\"}\");\n output.push(\"\");\n }\n\n // Generate @theme block for light theme\n if (themes.light?.id !== themes.dark?.id) {\n output.push(\"/* Light theme variables */\");\n output.push('@plugin \"daisyui/theme\" {');\n output.push(generateThemeVariables(themes.light, themes.default));\n output.push(\"}\");\n output.push(\"\");\n }\n\n cssContent = output.join(\"\\n\");\n }\n\n fs.writeFileSync(opts.outputPath, cssContent);\n },\n },\n };\n});\n\nexport default upstartTheme.vite;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@upstart.gg/vite-plugins",
3
- "version": "0.1.29",
3
+ "version": "0.1.30",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -23,19 +23,19 @@
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.29"
26
+ "@upstart.gg/sdk": "^0.1.30"
27
27
  },
28
28
  "devDependencies": {
29
- "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.9",
30
- "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.9",
31
- "@typescript/native-preview": "latest",
29
+ "@rolldown/binding-linux-arm64-gnu": "1.0.0",
30
+ "@rolldown/binding-linux-x64-gnu": "1.0.0",
31
+ "@typescript/native-preview": "7.0.0-dev.20260424.2",
32
32
  "@types/bun": "1.3.5",
33
33
  "@types/estree": "^1.0.8",
34
34
  "@types/estree-jsx": "1.0.5",
35
35
  "@types/node": "^24.10.0",
36
36
  "@types/react": "19.2.14",
37
37
  "@types/web": "0.0.342",
38
- "tsdown": "^0.21.2",
38
+ "tsdown": "0.22.0",
39
39
  "vitest": "4.1.4"
40
40
  },
41
41
  "exports": {
@@ -113,7 +113,7 @@
113
113
  },
114
114
  "peerDependencies": {
115
115
  "zod": "4.3.6",
116
- "@upstart.gg/sdk": "^0.1.29"
116
+ "@upstart.gg/sdk": "^0.1.30"
117
117
  },
118
118
  "author": "Upstart",
119
119
  "publishConfig": {