@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.
- package/dist/src/vite-plugin-upstart-editor/runtime/state.d.ts +10 -0
- package/dist/src/vite-plugin-upstart-editor/runtime/state.d.ts.map +1 -0
- package/dist/src/vite-plugin-upstart-editor/runtime/state.js +18 -0
- package/dist/src/vite-plugin-upstart-editor/runtime/state.js.map +1 -0
- package/dist/upstart-editor-api.d.ts +0 -3
- package/dist/upstart-editor-api.d.ts.map +1 -1
- package/dist/upstart-editor-api.js +1 -2
- package/dist/upstart-editor-api.js.map +1 -1
- package/dist/vite-plugin-upstart-attrs.d.ts +3 -3
- package/dist/vite-plugin-upstart-attrs.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-attrs.js +22 -22
- package/dist/vite-plugin-upstart-attrs.js.map +1 -1
- package/dist/vite-plugin-upstart-branding/plugin.d.ts +3 -4
- package/dist/vite-plugin-upstart-branding/plugin.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-branding/plugin.js +1 -2
- package/dist/vite-plugin-upstart-branding/plugin.js.map +1 -1
- package/dist/vite-plugin-upstart-branding/runtime.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-branding/runtime.js +2 -2
- package/dist/vite-plugin-upstart-branding/runtime.js.map +1 -1
- package/dist/vite-plugin-upstart-branding/types.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-branding/types.js +1 -1
- package/dist/vite-plugin-upstart-editor/plugin.d.ts +3 -4
- package/dist/vite-plugin-upstart-editor/plugin.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/plugin.js +2 -3
- package/dist/vite-plugin-upstart-editor/plugin.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/click-handler.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/click-handler.js +84 -16
- package/dist/vite-plugin-upstart-editor/runtime/click-handler.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/error-handler.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/error-handler.js +1 -2
- package/dist/vite-plugin-upstart-editor/runtime/error-handler.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js +78 -21
- package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/index.d.ts +3 -5
- package/dist/vite-plugin-upstart-editor/runtime/index.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/index.js +30 -15
- package/dist/vite-plugin-upstart-editor/runtime/index.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/text-editor.d.ts +0 -1
- package/dist/vite-plugin-upstart-editor/runtime/text-editor.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/text-editor.js +9 -37
- package/dist/vite-plugin-upstart-editor/runtime/text-editor.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/types.d.ts +7 -9
- package/dist/vite-plugin-upstart-editor/runtime/types.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/types.js +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/utils.d.ts +0 -1
- package/dist/vite-plugin-upstart-editor/runtime/utils.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/utils.js +1 -1
- package/dist/vite-plugin-upstart-theme.d.ts +3 -3
- package/dist/vite-plugin-upstart-theme.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-theme.js +1 -2
- package/dist/vite-plugin-upstart-theme.js.map +1 -1
- package/package.json +14 -13
- package/src/vite-plugin-upstart-attrs.ts +1 -0
- package/src/vite-plugin-upstart-branding/runtime.ts +2 -3
- package/src/vite-plugin-upstart-editor/plugin.ts +2 -2
- package/src/vite-plugin-upstart-editor/runtime/click-handler.ts +94 -18
- package/src/vite-plugin-upstart-editor/runtime/hover-overlay.ts +94 -20
- package/src/vite-plugin-upstart-editor/runtime/index.ts +42 -13
- package/src/vite-plugin-upstart-editor/runtime/state.ts +17 -0
- package/src/vite-plugin-upstart-editor/runtime/text-editor.ts +49 -81
- package/src/vite-plugin-upstart-editor/runtime/types.ts +6 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[
|
|
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text-editor.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/text-editor.ts"],"
|
|
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
|
-
|
|
72
|
-
|
|
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"],"
|
|
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/utils.ts"],"
|
|
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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
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:
|
|
19
|
-
declare const _default: (options: PluginOptions) =>
|
|
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"],"
|
|
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":";;;;;;;;;AAyBA,SAAS,mBAAmB,OAAc;CACxC,MAAM,EAAE,eAAe;AACvB,KAAI,CAAC,WAAY,QAAO,EAAE;CAE1B,MAAMA,cAAiC,EAAE;CACzC,MAAM,uBAAO,IAAI,KAAa;CAE9B,MAAM,WAAW,SAAuB;AACtC,MAAI,MAAM,SAAS,YAAY,CAAC,KAAK,IAAI,KAAK,OAAO,EAAE;AACrD,QAAK,IAAI,KAAK,OAAO;AACrB,eAAY,KAAK,KAAK;;;AAI1B,SAAQ,WAAW,KAAK;AACxB,SAAQ,WAAW,MAAM;AACzB,KAAI,WAAW,OACb,QAAO,OAAO,WAAW,OAAO,CAAC,QAAQ,QAAQ;AAGnD,QAAO;;;;;AAMT,SAAS,iBACP,YACA,YAA0B,MAC1B,UAA+D,QACvD;CAER,MAAM,2BAAW,IAAI,KAAyB;AAE9C,MAAK,MAAM,QAAQ,mBAAmB,WAAW,CAC/C,UAAS,IAAI,KAAK,QAAQ,KAAK;AAGjC,KAAI,UACF,MAAK,MAAM,QAAQ,mBAAmB,UAAU,CAC9C,UAAS,IAAI,KAAK,QAAQ,KAAK;AAInC,KAAI,SAAS,SAAS,EACpB,QAAO;AAcT,QAAO,kFAFK,qCARK,MAAM,KAAK,SAAS,QAAQ,CAAC,CAAC,KAAK,SAAS;EAC3D,MAAM,OAAO,KAAK,OAAO,QAAQ,MAAM,IAAI;AAC3C,MAAI,KAAK,WAAW,KAAK,QAAQ,SAAS,KAAK,KAAK,QAAQ,OAAO,KAAK,QAAQ,GAAG,GAAG,CACpF,QAAO,UAAU,KAAK,QAAQ,KAAK,QAAQ,GAAG,IAAI,KAAK,QAAQ,GAAG,GAAG;AAEvE,SAAO,UAAU;GACjB,CAEwD,KAAK,IAAI,CAAC,WAAW,UAEc;;;;;AAM/F,SAAS,UAAU,MAA2C;AAC5D,KAAI,CAAC,KAAM,QAAO;AAElB,KAAI,KAAK,SAAS,QAChB,QAAO,gBAAgB,KAAK;AAG9B,KAAI,KAAK,SAAS,SAGhB,QAAO,IAAI,KAAK,OAAO;AAGzB,QAAO;;;;;AAMT,SAAS,aAAa,KAAqB;AACzC,QAAO,IAAI,QAAQ,sBAAsB,QAAQ,CAAC,aAAa;;;;;AAMjE,SAAS,mBACP,YACA,YAA0B,MAC1B,eAAiC,SACzB;CACR,MAAM,kCAAkB,IAAI,KAAqB;CAGjD,MAAM,kBAAkB,WAAW;AAGnC,KAAI,gBAAgB,MAAM;EACxB,MAAM,UAAU,UAAU,gBAAgB,KAAK;AAC/C,MAAI,QACF,iBAAgB,IAAI,QAAQ,QAAQ;;AAKxC,KAAI,gBAAgB,OAAO;EACzB,MAAM,UAAU,UAAU,gBAAgB,MAAM;AAChD,MAAI,QACF,iBAAgB,IAAI,SAAS,QAAQ;;AAKzC,KAAI,gBAAgB,OAClB,MAAK,MAAM,CAAC,MAAM,SAAS,OAAO,QAAQ,gBAAgB,OAAO,EAAE;EACjE,MAAM,UAAU,UAAU,KAAK;AAC/B,MAAI,QACF,iBAAgB,IAAI,MAAM,QAAQ;;AAMxC,KAAI,WAAW;EACb,MAAM,iBAAiB,UAAU;AAEjC,MAAI,eAAe,MAAM;GACvB,MAAM,UAAU,UAAU,eAAe,KAAK;AAC9C,OAAI,QACF,iBAAgB,IAAI,QAAQ,QAAQ;;AAIxC,MAAI,eAAe,OAAO;GACxB,MAAM,UAAU,UAAU,eAAe,MAAM;AAC/C,OAAI,QACF,iBAAgB,IAAI,SAAS,QAAQ;;AAIzC,MAAI,eAAe,OACjB,MAAK,MAAM,CAAC,MAAM,SAAS,OAAO,QAAQ,eAAe,OAAO,EAAE;GAChE,MAAM,UAAU,UAAU,KAAK;AAC/B,OAAI,QACF,iBAAgB,IAAI,MAAM,QAAQ;;;AAU1C,QAAO;;MAJW,MAAM,KAAK,gBAAgB,SAAS,CAAC,CACpD,KAAK,CAAC,MAAM,WAAW,YAAY,KAAK,IAAI,MAAM,GAAG,CACrD,KAAK,KAAK,CAIC;;;;;;YAMJ,WAAW,GAAG,GAAG,iBAAiB,WAAW,CAAC,YAAY,cAAc,KAAK,WAAW,MAAM,WAAW,OAAO,WAAW,KAAK,KAAK,UAAU,GAAG,GAAG,iBAAiB,SAAS,cAAc,oBAAoB,GAAG;;;;;;;;AAWhO,SAAS,uBACP,OACA,MACA,cACQ;CACR,MAAM,YAAY,aAAa,MAAM;CACrC,MAAMC,QAAkB;EACtB,YAAY,UAAU,GAAG;EACzB,mBAAmB,UAAU,mBAAmB;EAChD,GAAK,iBAAiB,UAAU,MAAM,uBAAuB,UAC5D,iBAAiB,WAAW,MAAM,uBAAuB,UACtD,CAAC,mBAAmB,GACpB,EAAE;EAEP;AAGD,MAAK,MAAM,CAAC,UAAU,eAAe,OAAO,QAAQ,UAAU,OAAO,EAAE;EACrE,MAAM,WAAW;GAAC;GAAW;GAAW;GAAU,CAAC,SAAS,SAAS,GACjE,SAAS,QAAQ,QAAQ,QAAQ,GACjC,aAAa,SAAS;AAC1B,QAAM,KAAK,aAAa,SAAS,IAAI,WAAW,GAAG;;AAGrD,OAAM,KAAK,GAAG;AAGd,OAAM,KAAK,wBAAwB,UAAU,eAAe,GAAG;AAC/D,OAAM,KAAK,qBAAqB,UAAU,YAAY,GAAG;AACzD,OAAM,KAAK,mBAAmB,UAAU,UAAU,GAAG;AACrD,OAAM,KAAK,sBAAsB,UAAU,aAAa,GAAG;AAC3D,OAAM,KAAK,mBAAmB,UAAU,UAAU,GAAG;AACrD,OAAM,KAAK,eAAe,UAAU,OAAO,GAAG;AAC9C,OAAM,KAAK,cAAc,UAAU,QAAQ,IAAI,EAAE,GAAG;AACpD,OAAM,KAAK,gBAAgB;AAG3B,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,MAAa,eAAe,gBAA+B,SAAS;CAClE,IAAI,YAAY;AAEhB,QAAO;EACL,MAAM;EACN,OAAO;EAEP,MAAM,EACJ,iBAAiB;AAEf,OAAI,UAAW;AACf,eAAY;AAEZ,OAAI,CAAC,MAAM,QAAQ,OAAO;AACxB,YAAQ,KAAK,kFAAkF;AAC/F;;GAGF,MAAM,YAAY,KAAK,QAAQ,KAAK,WAAW;AAC/C,OAAI,CAAC,GAAG,WAAW,UAAU,CAC3B,IAAG,UAAU,WAAW,EAAE,WAAW,MAAM,CAAC;GAG9C,MAAM,EAAE,QAAQ,eAAe,WAAW;GAG1C,MAAM,WAAW,iBAAiB,OAAO,OAAO,OAAO,MAAM,aAAa;GAC1E,MAAM,eAAe,KAAK,KAAK,WAAW,YAAY;AACtD,MAAG,cAAc,cAAc,SAAS;GAExC,MAAMC,SAAmB,EAAE;AAG3B,UAAO,KAAK,mBAAmB,OAAO,OAAO,OAAO,KAAK,CAAC;AAC1D,UAAO,KAAK,GAAG;AAKf,OAAI,OAAO,MAAM;AACf,WAAO,KAAK,6BAA6B;AACzC,WAAO,KAAK,8BAA4B;AACxC,WAAO,KAAK,uBAAuB,OAAO,MAAM,QAAQ,OAAO,QAAQ,CAAC;AACxE,WAAO,KAAK,IAAI;AAChB,WAAO,KAAK,GAAG;;AAIjB,OAAI,OAAO,OAAO,OAAO,OAAO,MAAM,IAAI;AACxC,WAAO,KAAK,8BAA8B;AAC1C,WAAO,KAAK,8BAA4B;AACxC,WAAO,KAAK,uBAAuB,OAAO,OAAO,SAAS,OAAO,QAAQ,CAAC;AAC1E,WAAO,KAAK,IAAI;AAChB,WAAO,KAAK,GAAG;;GAIjB,MAAM,aAAa,OAAO,KAAK,KAAK;AACpC,MAAG,cAAc,KAAK,YAAY,WAAW;KAEhD;EACF;EACD;AAEF,wCAAe,aAAa"}
|
|
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.
|
|
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": "
|
|
17
|
-
"@oxc-parser/binding-linux-x64-gnu": "
|
|
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": "
|
|
23
|
+
"oxc-parser": "0.101.0",
|
|
24
24
|
"unplugin": "^2.3.11",
|
|
25
25
|
"zimmerframe": "^1.1.4",
|
|
26
|
-
"@upstart.gg/sdk": "^0.1.
|
|
26
|
+
"@upstart.gg/sdk": "^0.1.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
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
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.
|
|
114
|
-
"@upstart.gg/sdk": "^0.1.
|
|
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 &&
|
|
125
|
-
"ci:lint": "biome check . &&
|
|
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
|
|
33
|
-
|
|
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 = "
|
|
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}`,
|