@upstart.gg/vite-plugins 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/src/vite-plugin-upstart-editor/runtime/state.d.ts +10 -0
  2. package/dist/src/vite-plugin-upstart-editor/runtime/state.d.ts.map +1 -0
  3. package/dist/src/vite-plugin-upstart-editor/runtime/state.js +18 -0
  4. package/dist/src/vite-plugin-upstart-editor/runtime/state.js.map +1 -0
  5. package/dist/upstart-editor-api.d.ts +0 -3
  6. package/dist/upstart-editor-api.d.ts.map +1 -1
  7. package/dist/upstart-editor-api.js +1 -2
  8. package/dist/upstart-editor-api.js.map +1 -1
  9. package/dist/vite-plugin-upstart-attrs.d.ts +3 -3
  10. package/dist/vite-plugin-upstart-attrs.d.ts.map +1 -1
  11. package/dist/vite-plugin-upstart-attrs.js +22 -22
  12. package/dist/vite-plugin-upstart-attrs.js.map +1 -1
  13. package/dist/vite-plugin-upstart-branding/plugin.d.ts +3 -4
  14. package/dist/vite-plugin-upstart-branding/plugin.d.ts.map +1 -1
  15. package/dist/vite-plugin-upstart-branding/plugin.js +1 -2
  16. package/dist/vite-plugin-upstart-branding/plugin.js.map +1 -1
  17. package/dist/vite-plugin-upstart-branding/runtime.d.ts.map +1 -1
  18. package/dist/vite-plugin-upstart-branding/runtime.js +2 -2
  19. package/dist/vite-plugin-upstart-branding/runtime.js.map +1 -1
  20. package/dist/vite-plugin-upstart-branding/types.d.ts.map +1 -1
  21. package/dist/vite-plugin-upstart-branding/types.js +1 -1
  22. package/dist/vite-plugin-upstart-editor/plugin.d.ts +3 -4
  23. package/dist/vite-plugin-upstart-editor/plugin.d.ts.map +1 -1
  24. package/dist/vite-plugin-upstart-editor/plugin.js +2 -3
  25. package/dist/vite-plugin-upstart-editor/plugin.js.map +1 -1
  26. package/dist/vite-plugin-upstart-editor/runtime/click-handler.d.ts.map +1 -1
  27. package/dist/vite-plugin-upstart-editor/runtime/click-handler.js +84 -16
  28. package/dist/vite-plugin-upstart-editor/runtime/click-handler.js.map +1 -1
  29. package/dist/vite-plugin-upstart-editor/runtime/error-handler.d.ts.map +1 -1
  30. package/dist/vite-plugin-upstart-editor/runtime/error-handler.js +1 -2
  31. package/dist/vite-plugin-upstart-editor/runtime/error-handler.js.map +1 -1
  32. package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.d.ts.map +1 -1
  33. package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js +78 -21
  34. package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js.map +1 -1
  35. package/dist/vite-plugin-upstart-editor/runtime/index.d.ts +3 -5
  36. package/dist/vite-plugin-upstart-editor/runtime/index.d.ts.map +1 -1
  37. package/dist/vite-plugin-upstart-editor/runtime/index.js +30 -15
  38. package/dist/vite-plugin-upstart-editor/runtime/index.js.map +1 -1
  39. package/dist/vite-plugin-upstart-editor/runtime/text-editor.d.ts +0 -1
  40. package/dist/vite-plugin-upstart-editor/runtime/text-editor.d.ts.map +1 -1
  41. package/dist/vite-plugin-upstart-editor/runtime/text-editor.js +9 -37
  42. package/dist/vite-plugin-upstart-editor/runtime/text-editor.js.map +1 -1
  43. package/dist/vite-plugin-upstart-editor/runtime/types.d.ts +7 -9
  44. package/dist/vite-plugin-upstart-editor/runtime/types.d.ts.map +1 -1
  45. package/dist/vite-plugin-upstart-editor/runtime/types.js +1 -1
  46. package/dist/vite-plugin-upstart-editor/runtime/utils.d.ts +0 -1
  47. package/dist/vite-plugin-upstart-editor/runtime/utils.d.ts.map +1 -1
  48. package/dist/vite-plugin-upstart-editor/runtime/utils.js +1 -1
  49. package/dist/vite-plugin-upstart-theme.d.ts +3 -3
  50. package/dist/vite-plugin-upstart-theme.d.ts.map +1 -1
  51. package/dist/vite-plugin-upstart-theme.js +1 -2
  52. package/dist/vite-plugin-upstart-theme.js.map +1 -1
  53. package/package.json +14 -13
  54. package/src/vite-plugin-upstart-attrs.ts +1 -0
  55. package/src/vite-plugin-upstart-branding/runtime.ts +2 -3
  56. package/src/vite-plugin-upstart-editor/plugin.ts +2 -2
  57. package/src/vite-plugin-upstart-editor/runtime/click-handler.ts +94 -18
  58. package/src/vite-plugin-upstart-editor/runtime/hover-overlay.ts +94 -20
  59. package/src/vite-plugin-upstart-editor/runtime/index.ts +42 -13
  60. package/src/vite-plugin-upstart-editor/runtime/state.ts +17 -0
  61. package/src/vite-plugin-upstart-editor/runtime/text-editor.ts +49 -81
  62. package/src/vite-plugin-upstart-editor/runtime/types.ts +6 -3
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["currentMode: EditorMode"],"sources":["../../../src/vite-plugin-upstart-editor/runtime/index.ts"],"sourcesContent":["import { initClickHandler } from \"./click-handler.js\";\nimport { initHoverOverlay, hideOverlays } from \"./hover-overlay.js\";\nimport { initErrorHandler } from \"./error-handler.js\";\nimport { initTextEditor, activateAllEditors, destroyAllActiveEditors } from \"./text-editor.js\";\nimport { sendToParent } from \"./utils.js\";\nimport type { EditorMode, UpstartParentMessage } from \"./types.js\";\n\nlet currentMode: EditorMode = \"preview\";\nlet isInitialized = false;\n\n/**\n * Get the current editor mode.\n */\nexport function getCurrentMode(): EditorMode {\n return currentMode;\n}\n\n/**\n * Set the current editor mode.\n */\nexport function setMode(mode: EditorMode): void {\n currentMode = mode;\n console.log(`[Upstart Editor] Setting mode to: ${mode}`);\n if (mode === \"edit\") {\n enableEditMode();\n } else {\n disableEditMode();\n }\n}\n\n/**\n * Initialize the Upstart editor runtime.\n */\nexport function initUpstartEditor(): void {\n if (typeof window === \"undefined\") {\n console.warn(\"[Upstart Editor] Cannot initialize editor: not running in a browser environment\");\n return;\n }\n\n if (isInitialized) {\n console.log(\"[Upstart Editor] Editor is already initialized\");\n return;\n }\n\n try {\n console.log(\"[Upstart Editor] Initializing...\");\n\n isInitialized = true;\n\n window.addEventListener(\"message\", handleParentMessage);\n\n // Notify parent on SPA navigation so it can resend the current editMode\n window.addEventListener(\"popstate\", () => {\n sendToParent({ type: \"editor-navigated\" });\n });\n const originalPushState = history.pushState.bind(history);\n history.pushState = (...args) => {\n originalPushState(...args);\n sendToParent({ type: \"editor-navigated\" });\n };\n const originalReplaceState = history.replaceState.bind(history);\n history.replaceState = (...args) => {\n originalReplaceState(...args);\n sendToParent({ type: \"editor-navigated\" });\n };\n\n initTextEditor();\n initClickHandler();\n initHoverOverlay();\n initErrorHandler();\n\n sendToParent({ type: \"editor-ready\" });\n } catch (error) {\n console.error(\"[Upstart Editor] Initialization failed:\", error);\n sendToParent({\n type: \"editor-error\",\n error: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n}\n\nconst ALLOWED_ORIGINS = [\"http://localhost:8080\", /upstart.gg$/];\n\nconst matchAllowedOrigins = (origin: string) => {\n return ALLOWED_ORIGINS.some((allowedOrigin) => {\n if (typeof allowedOrigin === \"string\") {\n return origin === allowedOrigin;\n } else if (allowedOrigin instanceof RegExp) {\n return allowedOrigin.test(origin);\n }\n return false;\n });\n};\n\nfunction handleParentMessage(event: MessageEvent): void {\n const message = event.data as UpstartParentMessage | undefined;\n\n console.log(\"[Upstart Editor] Received message from parent:\", { event, message });\n\n if (!message || !matchAllowedOrigins(event.origin)) {\n console.warn(\"[Upstart Editor] Ignoring message from unknown source:\", event.origin);\n return;\n }\n\n if (message.type === \"set-mode\") {\n console.log(\"Setting editor mode to:\", message.mode);\n setMode(message.mode);\n }\n}\n\nfunction enableEditMode(): void {\n console.log(\"[Upstart Editor] Edit mode enabled\");\n activateAllEditors();\n}\n\nfunction disableEditMode(): void {\n console.log(\"[Upstart Editor] Preview mode enabled\");\n destroyAllActiveEditors();\n hideOverlays();\n}\n\nexport { initTextEditor } from \"./text-editor.js\";\nexport { initClickHandler } from \"./click-handler.js\";\nexport { initErrorHandler } from \"./error-handler.js\";\nexport { initHoverOverlay } from \"./hover-overlay.js\";\nexport { sendToParent } from \"./utils.js\";\nexport type { EditorMessage, UpstartEditorMessage } from \"./types.js\";\n"],"mappings":";;;;;;;AAOA,IAAIA,cAA0B;AAC9B,IAAI,gBAAgB;;;;AAKpB,SAAgB,iBAA6B;AAC3C,QAAO;;;;;AAMT,SAAgB,QAAQ,MAAwB;AAC9C,eAAc;AACd,SAAQ,IAAI,qCAAqC,OAAO;AACxD,KAAI,SAAS,OACX,iBAAgB;KAEhB,kBAAiB;;;;;AAOrB,SAAgB,oBAA0B;AACxC,KAAI,OAAO,WAAW,aAAa;AACjC,UAAQ,KAAK,kFAAkF;AAC/F;;AAGF,KAAI,eAAe;AACjB,UAAQ,IAAI,iDAAiD;AAC7D;;AAGF,KAAI;AACF,UAAQ,IAAI,mCAAmC;AAE/C,kBAAgB;AAEhB,SAAO,iBAAiB,WAAW,oBAAoB;AAGvD,SAAO,iBAAiB,kBAAkB;AACxC,gBAAa,EAAE,MAAM,oBAAoB,CAAC;IAC1C;EACF,MAAM,oBAAoB,QAAQ,UAAU,KAAK,QAAQ;AACzD,UAAQ,aAAa,GAAG,SAAS;AAC/B,qBAAkB,GAAG,KAAK;AAC1B,gBAAa,EAAE,MAAM,oBAAoB,CAAC;;EAE5C,MAAM,uBAAuB,QAAQ,aAAa,KAAK,QAAQ;AAC/D,UAAQ,gBAAgB,GAAG,SAAS;AAClC,wBAAqB,GAAG,KAAK;AAC7B,gBAAa,EAAE,MAAM,oBAAoB,CAAC;;AAG5C,kBAAgB;AAChB,oBAAkB;AAClB,oBAAkB;AAClB,oBAAkB;AAElB,eAAa,EAAE,MAAM,gBAAgB,CAAC;UAC/B,OAAO;AACd,UAAQ,MAAM,2CAA2C,MAAM;AAC/D,eAAa;GACX,MAAM;GACN,OAAO,iBAAiB,QAAQ,MAAM,UAAU;GACjD,CAAC;;;AAIN,MAAM,kBAAkB,CAAC,yBAAyB,cAAc;AAEhE,MAAM,uBAAuB,WAAmB;AAC9C,QAAO,gBAAgB,MAAM,kBAAkB;AAC7C,MAAI,OAAO,kBAAkB,SAC3B,QAAO,WAAW;WACT,yBAAyB,OAClC,QAAO,cAAc,KAAK,OAAO;AAEnC,SAAO;GACP;;AAGJ,SAAS,oBAAoB,OAA2B;CACtD,MAAM,UAAU,MAAM;AAEtB,SAAQ,IAAI,kDAAkD;EAAE;EAAO;EAAS,CAAC;AAEjF,KAAI,CAAC,WAAW,CAAC,oBAAoB,MAAM,OAAO,EAAE;AAClD,UAAQ,KAAK,0DAA0D,MAAM,OAAO;AACpF;;AAGF,KAAI,QAAQ,SAAS,YAAY;AAC/B,UAAQ,IAAI,2BAA2B,QAAQ,KAAK;AACpD,UAAQ,QAAQ,KAAK;;;AAIzB,SAAS,iBAAuB;AAC9B,SAAQ,IAAI,qCAAqC;AACjD,qBAAoB;;AAGtB,SAAS,kBAAwB;AAC/B,SAAQ,IAAI,wCAAwC;AACpD,0BAAyB;AACzB,eAAc"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/index.ts"],"sourcesContent":["import { initClickHandler } from \"./click-handler.js\";\nimport { initHoverOverlay, hideOverlays } from \"./hover-overlay.js\";\nimport { initErrorHandler } from \"./error-handler.js\";\nimport { initTextEditor, activateAllEditors, destroyAllActiveEditors } from \"./text-editor.js\";\nimport { sendToParent } from \"./utils.js\";\nimport { getCurrentMode, setCurrentMode } from \"./state.js\";\nimport type { EditorMode, UpstartParentMessage } from \"./types.js\";\n\nlet isInitialized = false;\n\nexport { getCurrentMode };\n\n/**\n * Set the current editor mode.\n */\nexport function setMode(mode: EditorMode): void {\n setCurrentMode(mode);\n console.log(`[Upstart Editor] Setting mode to: ${mode}`);\n if (mode === \"edit\") {\n enableEditMode();\n } else {\n disableEditMode();\n }\n}\n\nexport function waitForHydration(callback: () => void): void {\n // Remix/React 18 wraps hydrateRoot in startTransition, making hydration a\n // concurrent (low-priority) operation that can span many frames after the\n // load event. Instead of guessing a delay, we wait for the DOM to stabilise:\n // once 200 ms pass without any childList mutations, hydration is done.\n const STABILITY_MS = 200;\n\n const onReady = () => {\n let timer: number | null = null;\n\n const settle = () => {\n if (timer) clearTimeout(timer);\n timer = window.setTimeout(() => {\n observer.disconnect();\n callback();\n }, STABILITY_MS);\n };\n\n const observer = new MutationObserver(settle);\n observer.observe(document.documentElement, { childList: true, subtree: true });\n\n // Kick off the first timer (covers case where no mutations occur after load)\n settle();\n };\n\n if (document.readyState === \"complete\") {\n onReady();\n } else {\n window.addEventListener(\"load\", onReady, { once: true });\n }\n}\n\n/**\n * Initialize the Upstart editor runtime.\n */\nexport function initUpstartEditor(): void {\n if (isInitialized) {\n console.log(\"[Upstart Editor] Editor is already initialized\");\n return;\n }\n\n try {\n console.log(\"[Upstart Editor] Initializing...\");\n\n isInitialized = true;\n\n window.addEventListener(\"message\", handleParentMessage);\n\n // Notify parent on SPA navigation so it can resend the current editMode\n window.addEventListener(\"popstate\", () => {\n sendToParent({ type: \"editor-navigated\" });\n });\n const originalPushState = history.pushState.bind(history);\n history.pushState = (...args) => {\n originalPushState(...args);\n sendToParent({ type: \"editor-navigated\" });\n };\n const originalReplaceState = history.replaceState.bind(history);\n history.replaceState = (...args) => {\n originalReplaceState(...args);\n sendToParent({ type: \"editor-navigated\" });\n };\n\n initTextEditor();\n initClickHandler();\n initHoverOverlay();\n initErrorHandler();\n\n sendToParent({ type: \"editor-ready\" });\n } catch (error) {\n console.error(\"[Upstart Editor] Initialization failed:\", error);\n sendToParent({\n type: \"editor-error\",\n error: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n}\n\nconst ALLOWED_ORIGINS = [\"http://localhost:8080\", /upstart.gg$/];\n\nconst matchAllowedOrigins = (origin: string) => {\n return ALLOWED_ORIGINS.some((allowedOrigin) => {\n if (typeof allowedOrigin === \"string\") {\n return origin === allowedOrigin;\n } else if (allowedOrigin instanceof RegExp) {\n return allowedOrigin.test(origin);\n }\n return false;\n });\n};\n\nfunction handleParentMessage(event: MessageEvent): void {\n const message = event.data as UpstartParentMessage | undefined;\n\n console.log(\"[Upstart Editor] Received message from parent:\", { event, message });\n\n if (!message || !matchAllowedOrigins(event.origin)) {\n console.warn(\"[Upstart Editor] Ignoring message from unknown source:\", event.origin);\n return;\n }\n\n if (message.type === \"set-mode\") {\n console.log(\"Setting editor mode to:\", message.mode);\n setMode(message.mode);\n } else if (message.type === \"preview-classname\") {\n const el = document.querySelector<HTMLElement>(`[data-upstart-classname-id=\"${message.classNameId}\"]`);\n if (el) {\n el.className = message.className;\n }\n }\n}\n\nfunction enableEditMode(): void {\n console.log(\"[Upstart Editor] Edit mode enabled\");\n document.documentElement.setAttribute(\"data-upstart-edit-mode\", \"\");\n activateAllEditors();\n}\n\nfunction disableEditMode(): void {\n console.log(\"[Upstart Editor] Preview mode enabled\");\n document.documentElement.removeAttribute(\"data-upstart-edit-mode\");\n destroyAllActiveEditors();\n hideOverlays();\n}\n\nexport { initTextEditor } from \"./text-editor.js\";\nexport { initClickHandler } from \"./click-handler.js\";\nexport { initErrorHandler } from \"./error-handler.js\";\nexport { initHoverOverlay } from \"./hover-overlay.js\";\nexport { sendToParent } from \"./utils.js\";\nexport type { EditorMessage, UpstartEditorMessage } from \"./types.js\";\n"],"mappings":";;;;;;;AAQA,IAAI,gBAAgB;;;;AAOpB,SAAgB,QAAQ,MAAwB;AAC9C,gBAAe,KAAK;AACpB,SAAQ,IAAI,qCAAqC,OAAO;AACxD,KAAI,SAAS,OACX,iBAAgB;KAEhB,kBAAiB;;AAIrB,SAAgB,iBAAiB,UAA4B;CAK3D,MAAM,eAAe;CAErB,MAAM,gBAAgB;EACpB,IAAI,QAAuB;EAE3B,MAAM,eAAe;AACnB,OAAI,MAAO,cAAa,MAAM;AAC9B,WAAQ,OAAO,iBAAiB;AAC9B,aAAS,YAAY;AACrB,cAAU;MACT,aAAa;;EAGlB,MAAM,WAAW,IAAI,iBAAiB,OAAO;AAC7C,WAAS,QAAQ,SAAS,iBAAiB;GAAE,WAAW;GAAM,SAAS;GAAM,CAAC;AAG9E,UAAQ;;AAGV,KAAI,SAAS,eAAe,WAC1B,UAAS;KAET,QAAO,iBAAiB,QAAQ,SAAS,EAAE,MAAM,MAAM,CAAC;;;;;AAO5D,SAAgB,oBAA0B;AACxC,KAAI,eAAe;AACjB,UAAQ,IAAI,iDAAiD;AAC7D;;AAGF,KAAI;AACF,UAAQ,IAAI,mCAAmC;AAE/C,kBAAgB;AAEhB,SAAO,iBAAiB,WAAW,oBAAoB;AAGvD,SAAO,iBAAiB,kBAAkB;AACxC,gBAAa,EAAE,MAAM,oBAAoB,CAAC;IAC1C;EACF,MAAM,oBAAoB,QAAQ,UAAU,KAAK,QAAQ;AACzD,UAAQ,aAAa,GAAG,SAAS;AAC/B,qBAAkB,GAAG,KAAK;AAC1B,gBAAa,EAAE,MAAM,oBAAoB,CAAC;;EAE5C,MAAM,uBAAuB,QAAQ,aAAa,KAAK,QAAQ;AAC/D,UAAQ,gBAAgB,GAAG,SAAS;AAClC,wBAAqB,GAAG,KAAK;AAC7B,gBAAa,EAAE,MAAM,oBAAoB,CAAC;;AAG5C,kBAAgB;AAChB,oBAAkB;AAClB,oBAAkB;AAClB,oBAAkB;AAElB,eAAa,EAAE,MAAM,gBAAgB,CAAC;UAC/B,OAAO;AACd,UAAQ,MAAM,2CAA2C,MAAM;AAC/D,eAAa;GACX,MAAM;GACN,OAAO,iBAAiB,QAAQ,MAAM,UAAU;GACjD,CAAC;;;AAIN,MAAM,kBAAkB,CAAC,yBAAyB,cAAc;AAEhE,MAAM,uBAAuB,WAAmB;AAC9C,QAAO,gBAAgB,MAAM,kBAAkB;AAC7C,MAAI,OAAO,kBAAkB,SAC3B,QAAO,WAAW;WACT,yBAAyB,OAClC,QAAO,cAAc,KAAK,OAAO;AAEnC,SAAO;GACP;;AAGJ,SAAS,oBAAoB,OAA2B;CACtD,MAAM,UAAU,MAAM;AAEtB,SAAQ,IAAI,kDAAkD;EAAE;EAAO;EAAS,CAAC;AAEjF,KAAI,CAAC,WAAW,CAAC,oBAAoB,MAAM,OAAO,EAAE;AAClD,UAAQ,KAAK,0DAA0D,MAAM,OAAO;AACpF;;AAGF,KAAI,QAAQ,SAAS,YAAY;AAC/B,UAAQ,IAAI,2BAA2B,QAAQ,KAAK;AACpD,UAAQ,QAAQ,KAAK;YACZ,QAAQ,SAAS,qBAAqB;EAC/C,MAAM,KAAK,SAAS,cAA2B,+BAA+B,QAAQ,YAAY,IAAI;AACtG,MAAI,GACF,IAAG,YAAY,QAAQ;;;AAK7B,SAAS,iBAAuB;AAC9B,SAAQ,IAAI,qCAAqC;AACjD,UAAS,gBAAgB,aAAa,0BAA0B,GAAG;AACnE,qBAAoB;;AAGtB,SAAS,kBAAwB;AAC/B,SAAQ,IAAI,wCAAwC;AACpD,UAAS,gBAAgB,gBAAgB,yBAAyB;AAClE,0BAAyB;AACzB,eAAc"}
@@ -1,7 +1,6 @@
1
1
  import { UpstartEditorOptions } from "./types.js";
2
2
 
3
3
  //#region src/vite-plugin-upstart-editor/runtime/text-editor.d.ts
4
-
5
4
  /**
6
5
  * Initialize TipTap text editing for elements marked as editable.
7
6
  * Activation is deferred to avoid conflicting with React hydration.
@@ -1 +1 @@
1
- {"version":3,"file":"text-editor.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/text-editor.ts"],"sourcesContent":[],"mappings":";;;;;;AAuDA;AA6FA;AAkBgB,iBA/GA,cAAA,CA+GuB,OAAA,CAAA,EA/GC,oBA+GD,CAAA,EAAA,IAAA;;;;iBAlBvB,kBAAA,CAAA;;;;iBAkBA,uBAAA,CAAA"}
1
+ {"version":3,"file":"text-editor.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/text-editor.ts"],"mappings":";;;;;AAuDA;;iBAAgB,cAAA,CAAe,OAAA,GAAS,oBAAA;;;AAiDxC;iBAAgB,kBAAA,CAAA;;;;iBAkBA,uBAAA,CAAA"}
@@ -3,7 +3,6 @@ import { Editor, Extension, Node } from "@tiptap/core";
3
3
  import { BubbleMenu } from "@tiptap/extension-bubble-menu";
4
4
  import Placeholder from "@tiptap/extension-placeholder";
5
5
  import StarterKit from "@tiptap/starter-kit";
6
-
7
6
  //#region src/vite-plugin-upstart-editor/runtime/text-editor.ts
8
7
  /**
9
8
  * Custom Document node for inline elements (e.g. <a>, <span>, headings).
@@ -59,40 +58,13 @@ let reactivateTimer = null;
59
58
  * Activation is deferred to avoid conflicting with React hydration.
60
59
  */
61
60
  function initTextEditor(options = {}) {
62
- if (typeof window === "undefined") {
63
- console.warn("[Upstart Editor] Cannot initialize text editor: not running in a browser environment");
64
- return;
65
- }
66
61
  resolvedOptions = {
67
62
  ...DEFAULT_OPTIONS,
68
63
  ...options
69
64
  };
70
65
  registerKeyboardShortcuts();
71
- waitForHydration(() => {
72
- injectEditorStyles();
73
- startDomObserver();
74
- });
75
- }
76
- function waitForHydration(callback) {
77
- const STABILITY_MS = 200;
78
- const onReady = () => {
79
- let timer = null;
80
- const settle = () => {
81
- if (timer) clearTimeout(timer);
82
- timer = window.setTimeout(() => {
83
- observer.disconnect();
84
- callback();
85
- }, STABILITY_MS);
86
- };
87
- const observer = new MutationObserver(settle);
88
- observer.observe(document.documentElement, {
89
- childList: true,
90
- subtree: true
91
- });
92
- settle();
93
- };
94
- if (document.readyState === "complete") onReady();
95
- else window.addEventListener("load", onReady, { once: true });
66
+ injectEditorStyles();
67
+ startDomObserver();
96
68
  }
97
69
  /**
98
70
  * Inject CSS rules that visually hide original content when an editor is active.
@@ -109,11 +81,6 @@ function injectEditorStyles() {
109
81
  style.textContent = [
110
82
  "[data-upstart-editor-active] {",
111
83
  " cursor: text;",
112
- " transition: outline 100ms;",
113
- "}",
114
- "[data-upstart-editor-active]:hover {",
115
- " outline: 1px solid #7270c6;",
116
- " outline-offset: 4px;",
117
84
  "}",
118
85
  "[data-upstart-editor-active] .ProseMirror {",
119
86
  " outline: none !important;",
@@ -184,6 +151,7 @@ function activateEditor(element, hash, options) {
184
151
  }
185
152
  function createPlainTextEditor(element, hash, options) {
186
153
  const content = element.textContent ?? "";
154
+ element.textContent = "";
187
155
  let hasChanged = false;
188
156
  return new Editor({
189
157
  element,
@@ -285,6 +253,8 @@ function createInlineRichTextEditor(element, hash, options) {
285
253
  return editor;
286
254
  }
287
255
  function getEditorMode(element, options) {
256
+ const modeOverride = element.dataset.upstartEditableTextMode;
257
+ if (modeOverride === "plain" || modeOverride === "inline-rich" || modeOverride === "block-rich") return modeOverride;
288
258
  const tagName = element.tagName.toLowerCase();
289
259
  if (options.plainTextElements.includes(tagName)) return "plain";
290
260
  if (options.inlineRichTextElements.includes(tagName)) return "inline-rich";
@@ -534,6 +504,8 @@ function wireBubbleMenu(menu, editor) {
534
504
  event.preventDefault();
535
505
  });
536
506
  menu.addEventListener("click", (event) => {
507
+ console.log("menu button cliked");
508
+ event.stopPropagation();
537
509
  const command = event.target?.dataset.command;
538
510
  if (!command) return;
539
511
  const chain = editor.chain().focus();
@@ -563,7 +535,7 @@ function wireBubbleMenu(menu, editor) {
563
535
  chain.toggleOrderedList().run();
564
536
  break;
565
537
  }
566
- });
538
+ }, { capture: true });
567
539
  const updateActiveStates = () => {
568
540
  const buttons = menu.querySelectorAll("button[data-command]");
569
541
  for (const button of buttons) {
@@ -580,7 +552,7 @@ function wireBubbleMenu(menu, editor) {
580
552
  editor.on("transaction", updateActiveStates);
581
553
  updateActiveStates();
582
554
  }
583
-
584
555
  //#endregion
585
556
  export { activateAllEditors, destroyAllActiveEditors, initTextEditor };
557
+
586
558
  //# sourceMappingURL=text-editor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"text-editor.js","names":["DEFAULT_OPTIONS: Required<UpstartEditorOptions>","resolvedOptions: Required<UpstartEditorOptions>","domObserver: MutationObserver | null","reactivateTimer: number | null","timer: number | null","editor: Editor","extensions: Extension[]","extensions: (Extension | ReturnType<typeof Node.create>)[]","BUBBLE_MENU_GROUPS: { command: string; icon: string; title: string }[][]"],"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 if (typeof window === \"undefined\") {\n console.warn(\"[Upstart Editor] Cannot initialize text editor: not running in a browser environment\");\n return;\n }\n\n resolvedOptions = { ...DEFAULT_OPTIONS, ...options };\n registerKeyboardShortcuts();\n\n // Defer activation to let React finish hydrating the server-rendered HTML.\n waitForHydration(() => {\n injectEditorStyles();\n // activateAllEditors();\n startDomObserver();\n });\n}\n\nfunction waitForHydration(callback: () => void): void {\n // Remix/React 18 wraps hydrateRoot in startTransition, making hydration a\n // concurrent (low-priority) operation that can span many frames after the\n // load event. Instead of guessing a delay, we wait for the DOM to stabilise:\n // once 200 ms pass without any childList mutations, hydration is done.\n const STABILITY_MS = 200;\n\n const onReady = () => {\n let timer: number | null = null;\n\n const settle = () => {\n if (timer) clearTimeout(timer);\n timer = window.setTimeout(() => {\n observer.disconnect();\n callback();\n }, STABILITY_MS);\n };\n\n const observer = new MutationObserver(settle);\n observer.observe(document.documentElement, { childList: true, subtree: true });\n\n // Kick off the first timer (covers case where no mutations occur after load)\n settle();\n };\n\n if (document.readyState === \"complete\") {\n onReady();\n } else {\n window.addEventListener(\"load\", onReady, { once: true });\n }\n}\n\n/**\n * 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 \" transition: outline 100ms;\",\n \"}\",\n \"[data-upstart-editor-active]:hover {\",\n \" outline: 1px solid #7270c6;\",\n \" outline-offset: 4px;\",\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 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 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(\"click\", (event) => {\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\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 * 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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"}
@@ -2,7 +2,6 @@ import { PayloadEditText } 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
5
-
6
5
  /**
7
6
  * Editor mode
8
7
  */
@@ -44,14 +43,6 @@ type EditorMessage = {
44
43
  } | {
45
44
  type: "editor-error";
46
45
  error: string;
47
- } | {
48
- type: "element-hovered";
49
- hash: string;
50
- bounds: ElementBounds;
51
- } | {
52
- type: "datasource-item-clicked";
53
- datasourceId: string;
54
- recordId: string;
55
46
  } | {
56
47
  type: "element-clicked";
57
48
  hash: string;
@@ -59,6 +50,9 @@ type EditorMessage = {
59
50
  filePath: string;
60
51
  currentClassName: string;
61
52
  classNameId: string;
53
+ datasourceId?: string;
54
+ recordId?: string;
55
+ themeColors?: Record<string, string>;
62
56
  bounds: ElementBounds;
63
57
  };
64
58
  /**
@@ -67,6 +61,10 @@ type EditorMessage = {
67
61
  type ParentMessage = {
68
62
  type: "set-mode";
69
63
  mode: EditorMode;
64
+ } | {
65
+ type: "preview-classname";
66
+ classNameId: string;
67
+ className: string;
70
68
  };
71
69
  /**
72
70
  * Message wrapper sent via postMessage from iframe.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/types.ts"],"sourcesContent":[],"mappings":";;;;;;;AAMA;AAKY,KALA,UAAA,GAKc,SAAA,GAAA,MAAA;AAK1B;AAYA;;AAEW,KAnBC,cAAA,GAmBD,YAAA,GAAA,aAAA,GAAA,OAAA;;;AAQX;AACkC,UAvBjB,aAAA,CAuBiB;EAImB,GAAA,EAAA,MAAA;EASvC,IAAA,EAAA,MAAA;EAAa,KAAA,EAAA,MAAA;EAMf,MAAA,EAAA,MAAA;EAKK,KAAA,EAAA,MAAA;EASA,MAAA,EAAA,MAAA;AASjB;AA0CA;;;UA/FiB,cAAA;UACP;WACC;;QAEH;;;;;KAMI,aAAA;;WACsB;;;;;;;;;;;UAImB;;;;;;;;;;;;UASvC;;;;;KAMF,aAAA;;QAA0C;;;;;UAKrC,oBAAA;;QAET;;;;;;UAOS,oBAAA;;QAET;;;;;;UAOS,oBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA0CA,0BAAA"}
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;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;;;;UAKrC,oBAAA;EACf,MAAA;EACA,IAAA,EAAM,aAAA;EAAA,CACL,GAAA;AAAA;;;;UAMc,oBAAA;EACf,MAAA;EACA,IAAA,EAAM,aAAA;EAAA,CACL,GAAA;AAAA;;;AAZH;UAkBiB,oBAAA;;;;;EAKf,gBAAA;EApBC;;;AAMH;;EAqBE,sBAAA;EAnBmB;;;;EAyBnB,iBAAA;EAxBY;;AAMd;;EAwBE,UAAA;EAxBmC;;;;EA8BnC,WAAA;EAAA;;;;EAMA,aAAA;AAAA;;;;UAMe,0BAAA;;;;;EAKf,OAAA;;;;;EAMA,UAAA;AAAA"}
@@ -1 +1 @@
1
- export { };
1
+ export {};
@@ -1,7 +1,6 @@
1
1
  import { EditorMessage } from "./types.js";
2
2
 
3
3
  //#region src/vite-plugin-upstart-editor/runtime/utils.d.ts
4
-
5
4
  /**
6
5
  * Send a message to the parent window.
7
6
  */
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/utils.ts"],"sourcesContent":[],"mappings":";;;;;;AAKA;AAsBgB,iBAtBA,YAAA,CAsBU,OAAA,EAtBY,aAsBZ,CAAA,EAAA,IAAA;;;;iBAAV,UAAA,CAAA"}
1
+ {"version":3,"file":"utils.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/utils.ts"],"mappings":";;;;;AAKA;iBAAgB,YAAA,CAAa,OAAA,EAAS,aAAA;;;;iBAsBtB,UAAA,CAAA"}
@@ -20,7 +20,7 @@ function isInIframe() {
20
20
  if (typeof window === "undefined") return false;
21
21
  return window.parent !== window;
22
22
  }
23
-
24
23
  //#endregion
25
24
  export { isInIframe, sendToParent };
25
+
26
26
  //# sourceMappingURL=utils.js.map
@@ -1,4 +1,4 @@
1
- import * as unplugin2 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
@@ -15,8 +15,8 @@ interface PluginOptions {
15
15
  /**
16
16
  * Unplugin-based theme generator
17
17
  */
18
- declare const upstartTheme: unplugin2.UnpluginInstance<PluginOptions, boolean>;
19
- declare const _default: (options: PluginOptions) => unplugin2.VitePlugin<any> | unplugin2.VitePlugin<any>[];
18
+ declare const upstartTheme: unplugin.UnpluginInstance<PluginOptions, boolean>;
19
+ declare const _default: (options: PluginOptions) => unplugin.VitePlugin<any>[] | unplugin.VitePlugin<any>;
20
20
  //#endregion
21
21
  export { _default as default, upstartTheme };
22
22
  //# 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"],"sourcesContent":[],"mappings":";;;;UAWU,aAAA;;WAEC;IAFD,IAAA,CAAA,EAGC,KAHY;IA2OV,OAAA,EAAA,OA8DX,GAAA,MA9DuB;EA8DtB,CAAA;;;;;;;;cA9DU,cAAY,SAAA,CAAA,iBAAA;cA8DtB"}
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;AAAA;;;;cAmOW,YAAA,EAAY,QAAA,CAAA,gBAAA,CAAA,aAAA;AAAA,cA8DtB,QAAA"}
@@ -2,7 +2,6 @@ import path from "node:path";
2
2
  import { createUnplugin } from "unplugin";
3
3
  import { fontStacksFonts, processTheme } from "@upstart.gg/sdk/themes/theme";
4
4
  import fs from "node:fs";
5
-
6
5
  //#region src/vite-plugin-upstart-theme.ts
7
6
  /**
8
7
  * Collects Google Fonts from a theme's typography configuration
@@ -171,7 +170,7 @@ const upstartTheme = createUnplugin((opts) => {
171
170
  };
172
171
  });
173
172
  var vite_plugin_upstart_theme_default = upstartTheme.vite;
174
-
175
173
  //#endregion
176
174
  export { vite_plugin_upstart_theme_default as default, upstartTheme };
175
+
177
176
  //# sourceMappingURL=vite-plugin-upstart-theme.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin-upstart-theme.js","names":["googleFonts: Array<GoogleFont>","lines: string[]","output: string[]"],"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}\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\n/**\n * Generates @theme block for a single theme with colors and layout variables\n */\nfunction generateThemeVariables(\n theme: Theme,\n type: \"light\" | \"dark\",\n defaultTheme: \"light\" | \"dark\",\n): 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 * 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\" } = opts;\n\n // Generate fonts.css file\n const fontsCSS = generateFontsCSS(themes.light, themes.dark, fontsDisplay);\n const fontsCSSPath = path.join(outputDir, \"fonts.css\");\n fs.writeFileSync(fontsCSSPath, fontsCSS);\n\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, \"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, \"light\", themes.default));\n output.push(\"}\");\n output.push(\"\");\n }\n\n // Write the generated CSS file\n const cssContent = output.join(\"\\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}\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\n/**\n * Generates @theme block for a single theme with colors and layout variables\n */\nfunction generateThemeVariables(\n theme: Theme,\n type: \"light\" | \"dark\",\n defaultTheme: \"light\" | \"dark\",\n): 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 * 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\" } = opts;\n\n // Generate fonts.css file\n const fontsCSS = generateFontsCSS(themes.light, themes.dark, fontsDisplay);\n const fontsCSSPath = path.join(outputDir, \"fonts.css\");\n fs.writeFileSync(fontsCSSPath, fontsCSS);\n\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, \"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, \"light\", themes.default));\n output.push(\"}\");\n output.push(\"\");\n }\n\n // Write the generated CSS file\n const cssContent = output.join(\"\\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.5",
3
+ "version": "0.1.7",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -13,28 +13,29 @@
13
13
  },
14
14
  "sideEffects": false,
15
15
  "dependencies": {
16
- "@oxc-parser/binding-linux-arm64-gnu": "^0.101.0",
17
- "@oxc-parser/binding-linux-x64-gnu": "^0.101.0",
16
+ "@oxc-parser/binding-linux-arm64-gnu": "0.118.0",
17
+ "@oxc-parser/binding-linux-x64-gnu": "0.118.0",
18
18
  "@tiptap/core": "^2.27.1",
19
19
  "@tiptap/extension-bubble-menu": "^2.27.1",
20
20
  "@tiptap/extension-placeholder": "^2.27.2",
21
21
  "@tiptap/starter-kit": "^2.27.1",
22
22
  "magic-string": "^0.30.21",
23
- "oxc-parser": "^0.101.0",
23
+ "oxc-parser": "0.101.0",
24
24
  "unplugin": "^2.3.11",
25
25
  "zimmerframe": "^1.1.4",
26
- "@upstart.gg/sdk": "^0.1.5"
26
+ "@upstart.gg/sdk": "^0.1.7"
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",
29
31
  "@types/bun": "1.3.5",
30
32
  "@types/estree": "^1.0.8",
31
33
  "@types/estree-jsx": "1.0.5",
32
34
  "@types/node": "^24.10.0",
33
35
  "@types/react": "19.2.14",
34
- "tsdown": "0.18.3",
35
- "vitest": "4.0.16",
36
- "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.53",
37
- "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.53"
36
+ "@types/web": "0.0.342",
37
+ "tsdown": "^0.21.2",
38
+ "vitest": "^4.0.16"
38
39
  },
39
40
  "exports": {
40
41
  "./package.json": "./package.json",
@@ -110,8 +111,8 @@
110
111
  }
111
112
  },
112
113
  "peerDependencies": {
113
- "zod": "4.2.1",
114
- "@upstart.gg/sdk": "^0.1.5"
114
+ "zod": "4.3.6",
115
+ "@upstart.gg/sdk": "^0.1.7"
115
116
  },
116
117
  "author": "Upstart",
117
118
  "publishConfig": {
@@ -121,7 +122,7 @@
121
122
  "test": "bun run --bun vitest --run",
122
123
  "test:bun": "bun test",
123
124
  "build": "tsdown",
124
- "lint": "biome check . --write && tsc --noEmit",
125
- "ci:lint": "biome check . && tsc --noEmit"
125
+ "lint": "biome check . --write && tsgo --noEmit",
126
+ "ci:lint": "biome check . && tsgo --noEmit"
126
127
  }
127
128
  }
@@ -270,6 +270,7 @@ export function transformWithOxc(code: string, filePath: string) {
270
270
 
271
271
  if (hasI18n) {
272
272
  attributes.push('data-upstart-editable-text="true"');
273
+ attributes.push('data-upstart-editable-text-mode="plain"');
273
274
 
274
275
  const hasDynamic = allI18nKeys.some((t) => t.keyExpr || t.nsExpr);
275
276
  if (hasDynamic) {
@@ -29,9 +29,8 @@ export function initUpstartBranding(): void {
29
29
  isInitialized = true;
30
30
 
31
31
  // Delay initialization to wait for React hydration
32
- if ("scheduler" in globalThis) {
33
- // @ts-expect-error not yet in TS types
34
- globalThis.scheduler.postTask(() => injectBranding(), { delay: 250, priority: "background" });
32
+ if ("scheduler" in window) {
33
+ window.scheduler.postTask(() => injectBranding(), { delay: 250, priority: "background" });
35
34
  } else {
36
35
  setTimeout(() => injectBranding(), 250);
37
36
  }
@@ -44,8 +44,8 @@ export const upstartEditor = createUnplugin<UpstartEditorPluginOptions>((options
44
44
  return null;
45
45
  }
46
46
 
47
- const imports = `import { initUpstartEditor } from ${JSON.stringify(runtimePath)};`;
48
- const injection = "requestIdleCallback(initUpstartEditor);";
47
+ const imports = `import { initUpstartEditor, waitForHydration } from ${JSON.stringify(runtimePath)};`;
48
+ const injection = "waitForHydration(initUpstartEditor);";
49
49
 
50
50
  return {
51
51
  code: `${imports}${code}${injection}`,