@upstart.gg/vite-plugins 0.1.29 → 0.1.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/upstart-editor-api.d.ts +13 -1
- package/dist/upstart-editor-api.d.ts.map +1 -1
- package/dist/upstart-editor-api.js +59 -1
- package/dist/upstart-editor-api.js.map +1 -1
- package/dist/vite-plugin-upstart-attrs.d.ts +12 -7
- package/dist/vite-plugin-upstart-attrs.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-attrs.js +195 -3
- package/dist/vite-plugin-upstart-attrs.js.map +1 -1
- package/dist/vite-plugin-upstart-branding/plugin.d.ts +3 -3
- package/dist/vite-plugin-upstart-branding/plugin.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-branding/plugin.js.map +1 -1
- package/dist/vite-plugin-upstart-branding/runtime.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/plugin.d.ts +3 -3
- package/dist/vite-plugin-upstart-editor/plugin.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/plugin.js +4 -1
- package/dist/vite-plugin-upstart-editor/plugin.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/click-handler.js +27 -16
- package/dist/vite-plugin-upstart-editor/runtime/click-handler.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/error-handler.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/index.d.ts +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/index.js +14 -2
- package/dist/vite-plugin-upstart-editor/runtime/index.js.map +1 -1
- package/dist/{src/vite-plugin-upstart-editor → vite-plugin-upstart-editor}/runtime/state.d.ts +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/state.d.ts.map +1 -0
- package/dist/vite-plugin-upstart-editor/runtime/state.js.map +1 -0
- package/dist/vite-plugin-upstart-editor/runtime/text-editor.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/text-editor.js +212 -28
- package/dist/vite-plugin-upstart-editor/runtime/text-editor.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/types.d.ts +16 -3
- package/dist/vite-plugin-upstart-editor/runtime/types.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/utils.js.map +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 +2 -2
- package/dist/vite-plugin-upstart-theme.js.map +1 -1
- package/package.json +7 -7
- package/src/tests/vite-plugin-upstart-attrs.test.ts +298 -37
- package/src/upstart-editor-api.ts +71 -0
- package/src/vite-plugin-upstart-attrs.ts +293 -5
- package/src/vite-plugin-upstart-editor/plugin.ts +11 -1
- package/src/vite-plugin-upstart-editor/runtime/click-handler.ts +35 -21
- package/src/vite-plugin-upstart-editor/runtime/index.ts +21 -1
- package/src/vite-plugin-upstart-editor/runtime/text-editor.ts +260 -41
- package/src/vite-plugin-upstart-editor/runtime/types.ts +17 -4
- package/src/vite-plugin-upstart-theme.ts +4 -1
- package/dist/src/vite-plugin-upstart-editor/runtime/state.d.ts.map +0 -1
- package/dist/src/vite-plugin-upstart-editor/runtime/state.js.map +0 -1
- /package/dist/{src/vite-plugin-upstart-editor → vite-plugin-upstart-editor}/runtime/state.js +0 -0
|
@@ -16,6 +16,47 @@ const InlineDocument = Node.create({
|
|
|
16
16
|
content: "inline*",
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* An atomic inline node that represents an i18n template variable like {{year}}.
|
|
21
|
+
* It is displayed as a grayed non-editable chip showing the resolved value.
|
|
22
|
+
* renderText() returns {{varName}} so getText() serialises back to the raw template.
|
|
23
|
+
*/
|
|
24
|
+
const TemplateVariable = Node.create({
|
|
25
|
+
name: "templateVariable",
|
|
26
|
+
group: "inline",
|
|
27
|
+
inline: true,
|
|
28
|
+
atom: true,
|
|
29
|
+
|
|
30
|
+
addAttributes() {
|
|
31
|
+
return {
|
|
32
|
+
varName: { default: "" },
|
|
33
|
+
value: { default: "" },
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
parseHTML() {
|
|
38
|
+
return [{ tag: "span[data-tpl-var]" }];
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
renderHTML({ node }) {
|
|
42
|
+
return [
|
|
43
|
+
"span",
|
|
44
|
+
{
|
|
45
|
+
"data-tpl-var": node.attrs.varName,
|
|
46
|
+
contenteditable: "false",
|
|
47
|
+
style:
|
|
48
|
+
"opacity:0.5;background:rgba(0,0,0,0.08);border-radius:3px;padding:0 3px;" +
|
|
49
|
+
"font-family:monospace;font-size:0.875em;cursor:default;user-select:none;",
|
|
50
|
+
},
|
|
51
|
+
node.attrs.value || `{{${node.attrs.varName}}}`,
|
|
52
|
+
];
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
renderText({ node }) {
|
|
56
|
+
return `{{${node.attrs.varName}}}`;
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
19
60
|
/**
|
|
20
61
|
* Remaps Enter to insert a <br> (hard break) instead of creating a new paragraph.
|
|
21
62
|
* Used in inline-rich mode where block nodes are not allowed.
|
|
@@ -36,8 +77,57 @@ const DEFAULT_OPTIONS: Required<UpstartEditorOptions> = {
|
|
|
36
77
|
bubbleMenu: true,
|
|
37
78
|
placeholder: "Start typing...",
|
|
38
79
|
autoSaveDelay: 1000,
|
|
80
|
+
getRawI18nTemplate: () => undefined,
|
|
39
81
|
};
|
|
40
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Parse a raw i18n template (e.g. "Copyright {{year}}") together with the
|
|
85
|
+
* already-rendered text (e.g. "Copyright 2026") and produce a TipTap JSON
|
|
86
|
+
* document where variable tokens become TemplateVariable nodes.
|
|
87
|
+
*/
|
|
88
|
+
function buildI18nContent(rawTemplate: string, renderedText: string): object {
|
|
89
|
+
const VAR_RE = /\{\{(\w+)\}\}/g;
|
|
90
|
+
type Segment = { type: "text"; text: string } | { type: "var"; varName: string };
|
|
91
|
+
const segments: Segment[] = [];
|
|
92
|
+
|
|
93
|
+
let lastIndex = 0;
|
|
94
|
+
let m: RegExpExecArray | null;
|
|
95
|
+
while ((m = VAR_RE.exec(rawTemplate)) !== null) {
|
|
96
|
+
if (m.index > lastIndex) segments.push({ type: "text", text: rawTemplate.slice(lastIndex, m.index) });
|
|
97
|
+
segments.push({ type: "var", varName: m[1] });
|
|
98
|
+
lastIndex = m.index + m[0].length;
|
|
99
|
+
}
|
|
100
|
+
if (lastIndex < rawTemplate.length) segments.push({ type: "text", text: rawTemplate.slice(lastIndex) });
|
|
101
|
+
|
|
102
|
+
// Build a regex over the rendered text to capture variable values
|
|
103
|
+
const escRe = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
104
|
+
const regexParts = segments.map((s) => (s.type === "text" ? escRe(s.text) : "(.+?)"));
|
|
105
|
+
const fullRegex = new RegExp("^" + regexParts.join("") + "$");
|
|
106
|
+
const varSegments = segments.filter((s): s is Extract<Segment, { type: "var" }> => s.type === "var");
|
|
107
|
+
|
|
108
|
+
const varValues: Record<string, string> = {};
|
|
109
|
+
const match = renderedText.match(fullRegex);
|
|
110
|
+
if (match) {
|
|
111
|
+
varSegments.forEach((seg, i) => {
|
|
112
|
+
varValues[seg.varName] = match[i + 1] ?? `{{${seg.varName}}}`;
|
|
113
|
+
});
|
|
114
|
+
} else {
|
|
115
|
+
varSegments.forEach((seg) => {
|
|
116
|
+
varValues[seg.varName] = `{{${seg.varName}}}`;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const inlineNodes = segments
|
|
121
|
+
.map((s) =>
|
|
122
|
+
s.type === "text"
|
|
123
|
+
? { type: "text", text: s.text }
|
|
124
|
+
: { type: "templateVariable", attrs: { varName: s.varName, value: varValues[s.varName] } },
|
|
125
|
+
)
|
|
126
|
+
.filter((n) => n.type !== "text" || (n as any).text !== "");
|
|
127
|
+
|
|
128
|
+
return { type: "doc", content: [{ type: "paragraph", content: inlineNodes }] };
|
|
129
|
+
}
|
|
130
|
+
|
|
41
131
|
const activeEditors = new Map<string, EditorInstance>();
|
|
42
132
|
let i18nSyncInProgress = false;
|
|
43
133
|
const styleCache = new WeakMap<
|
|
@@ -103,18 +193,16 @@ function injectEditorStyles(): void {
|
|
|
103
193
|
* Activate editors on all editable elements. Safe to call multiple times.
|
|
104
194
|
*/
|
|
105
195
|
export function activateAllEditors(): void {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
});
|
|
117
|
-
}
|
|
196
|
+
cleanupOrphanedEditors();
|
|
197
|
+
const editables = document.querySelectorAll<HTMLElement>('[data-upstart-editable-text="true"]');
|
|
198
|
+
editables.forEach((element) => {
|
|
199
|
+
try {
|
|
200
|
+
setupEditableElement(element, resolvedOptions);
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.error("[Upstart Editor] Failed to activate element:", element.dataset.upstartHash, error);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
console.log("[Upstart Editor] Text editors activated");
|
|
118
206
|
}
|
|
119
207
|
|
|
120
208
|
/**
|
|
@@ -141,7 +229,14 @@ function activateEditor(element: HTMLElement, hash: string, options: Required<Up
|
|
|
141
229
|
const mode = getEditorMode(element, options);
|
|
142
230
|
|
|
143
231
|
let editor: Editor;
|
|
232
|
+
|
|
144
233
|
switch (mode) {
|
|
234
|
+
case "direct":
|
|
235
|
+
editor = createDirectEditor(element, hash, options);
|
|
236
|
+
break;
|
|
237
|
+
case "rich-panel":
|
|
238
|
+
editor = createRichPanelEditor(element, hash, options);
|
|
239
|
+
break;
|
|
145
240
|
case "block-rich":
|
|
146
241
|
editor = createRichTextEditor(element, hash, options);
|
|
147
242
|
break;
|
|
@@ -153,8 +248,6 @@ function activateEditor(element: HTMLElement, hash: string, options: Required<Up
|
|
|
153
248
|
break;
|
|
154
249
|
}
|
|
155
250
|
|
|
156
|
-
// Restore text rendering on ProseMirror (overrides inherited font-size: 0 etc.)
|
|
157
|
-
|
|
158
251
|
// Mark element — triggers CSS rules that hide original content
|
|
159
252
|
element.dataset.upstartEditorActive = "true";
|
|
160
253
|
|
|
@@ -167,16 +260,42 @@ function createPlainTextEditor(
|
|
|
167
260
|
hash: string,
|
|
168
261
|
options: Required<UpstartEditorOptions>,
|
|
169
262
|
): Editor {
|
|
170
|
-
const
|
|
171
|
-
|
|
263
|
+
const renderedText = element.textContent ?? "";
|
|
264
|
+
|
|
265
|
+
let content: string | object = renderedText;
|
|
266
|
+
const extraExtensions: ReturnType<typeof Node.create>[] = [];
|
|
267
|
+
|
|
268
|
+
// Mixed-text: JSXText + expression chips (e.g. © {year} Some text)
|
|
269
|
+
const mixedTemplate = element.dataset.upstartMixedTemplate;
|
|
270
|
+
if (mixedTemplate) {
|
|
271
|
+
content = buildI18nContent(mixedTemplate, renderedText);
|
|
272
|
+
extraExtensions.push(TemplateVariable);
|
|
273
|
+
} else {
|
|
274
|
+
// Check for i18n template variables (e.g. data-i18n-values="year,month")
|
|
275
|
+
const i18nValueKeys = element.dataset.i18nValues?.split(",").filter(Boolean) ?? [];
|
|
276
|
+
if (i18nValueKeys.length > 0) {
|
|
277
|
+
const i18nAttr = element.dataset.upstartI18n;
|
|
278
|
+
if (i18nAttr) {
|
|
279
|
+
const colonIdx = i18nAttr.indexOf(":");
|
|
280
|
+
const namespace = colonIdx >= 0 ? i18nAttr.slice(0, colonIdx) : "translation";
|
|
281
|
+
const key = colonIdx >= 0 ? i18nAttr.slice(colonIdx + 1) : i18nAttr;
|
|
282
|
+
const rawTemplate = options.getRawI18nTemplate(namespace, key);
|
|
283
|
+
if (rawTemplate) {
|
|
284
|
+
content = buildI18nContent(rawTemplate, renderedText);
|
|
285
|
+
extraExtensions.push(TemplateVariable);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
element.textContent = "";
|
|
172
292
|
let hasChanged = false;
|
|
173
293
|
|
|
174
294
|
const editor = new Editor({
|
|
175
295
|
element,
|
|
176
296
|
extensions: [
|
|
177
|
-
|
|
297
|
+
...extraExtensions,
|
|
178
298
|
StarterKit.configure({
|
|
179
|
-
document: false,
|
|
180
299
|
heading: false,
|
|
181
300
|
bold: false,
|
|
182
301
|
italic: false,
|
|
@@ -198,11 +317,9 @@ function createPlainTextEditor(
|
|
|
198
317
|
class: "upstart-editor-active",
|
|
199
318
|
},
|
|
200
319
|
},
|
|
201
|
-
onUpdate: (
|
|
320
|
+
onUpdate: () => {
|
|
202
321
|
if (i18nSyncInProgress) return;
|
|
203
322
|
hasChanged = true;
|
|
204
|
-
// const content = e.getText();
|
|
205
|
-
// debouncedSave(hash, content, options.autoSaveDelay);
|
|
206
323
|
syncI18nSiblings(hash);
|
|
207
324
|
},
|
|
208
325
|
onBlur: ({ editor: e }) => {
|
|
@@ -249,11 +366,9 @@ function createRichTextEditor(
|
|
|
249
366
|
class: "upstart-editor-active",
|
|
250
367
|
},
|
|
251
368
|
},
|
|
252
|
-
onUpdate: (
|
|
369
|
+
onUpdate: () => {
|
|
253
370
|
if (i18nSyncInProgress) return;
|
|
254
371
|
hasChanged = true;
|
|
255
|
-
// const content = e.getHTML();
|
|
256
|
-
// debouncedSave(hash, content, options.autoSaveDelay);
|
|
257
372
|
syncI18nSiblings(hash);
|
|
258
373
|
},
|
|
259
374
|
onBlur: ({ editor: e }) => {
|
|
@@ -315,11 +430,9 @@ function createInlineRichTextEditor(
|
|
|
315
430
|
class: "upstart-editor-active",
|
|
316
431
|
},
|
|
317
432
|
},
|
|
318
|
-
onUpdate: (
|
|
433
|
+
onUpdate: () => {
|
|
319
434
|
if (i18nSyncInProgress) return;
|
|
320
435
|
hasChanged = true;
|
|
321
|
-
// const content = e.getHTML();
|
|
322
|
-
// debouncedSave(hash, content, options.autoSaveDelay);
|
|
323
436
|
syncI18nSiblings(hash);
|
|
324
437
|
},
|
|
325
438
|
onBlur: ({ editor: e }) => {
|
|
@@ -336,9 +449,104 @@ function createInlineRichTextEditor(
|
|
|
336
449
|
return editor;
|
|
337
450
|
}
|
|
338
451
|
|
|
452
|
+
function createDirectEditor(
|
|
453
|
+
element: HTMLElement,
|
|
454
|
+
hash: string,
|
|
455
|
+
options: Required<UpstartEditorOptions>,
|
|
456
|
+
): Editor {
|
|
457
|
+
const content = element.innerHTML;
|
|
458
|
+
element.innerHTML = "";
|
|
459
|
+
let hasChanged = false;
|
|
460
|
+
|
|
461
|
+
const bubbleMenuElement = createBubbleMenuElement("inline");
|
|
462
|
+
const extensions: (Extension | ReturnType<typeof Node.create>)[] = [
|
|
463
|
+
EnterHardBreak,
|
|
464
|
+
StarterKit.configure({
|
|
465
|
+
heading: false,
|
|
466
|
+
blockquote: false,
|
|
467
|
+
bulletList: false,
|
|
468
|
+
orderedList: false,
|
|
469
|
+
listItem: false,
|
|
470
|
+
codeBlock: false,
|
|
471
|
+
horizontalRule: false,
|
|
472
|
+
}),
|
|
473
|
+
Placeholder.configure({ placeholder: "Click to edit..." }),
|
|
474
|
+
];
|
|
475
|
+
|
|
476
|
+
if (options.bubbleMenu) {
|
|
477
|
+
extensions.push(BubbleMenu.configure({ element: bubbleMenuElement }));
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const editor = new Editor({
|
|
481
|
+
element,
|
|
482
|
+
extensions,
|
|
483
|
+
content,
|
|
484
|
+
editorProps: { attributes: { class: "upstart-editor-active" } },
|
|
485
|
+
onUpdate: () => {
|
|
486
|
+
hasChanged = true;
|
|
487
|
+
},
|
|
488
|
+
onBlur: ({ editor: e }) => {
|
|
489
|
+
if (!hasChanged) return;
|
|
490
|
+
hasChanged = false;
|
|
491
|
+
// Strip the outer <p> that TipTap adds when using default document schema
|
|
492
|
+
saveText(hash, e.getHTML().replace(/^<p>([\s\S]*)<\/p>$/, "$1"));
|
|
493
|
+
},
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
if (options.bubbleMenu) {
|
|
497
|
+
wireBubbleMenu(bubbleMenuElement, editor);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return editor;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function createRichPanelEditor(
|
|
504
|
+
element: HTMLElement,
|
|
505
|
+
hash: string,
|
|
506
|
+
options: Required<UpstartEditorOptions>,
|
|
507
|
+
): Editor {
|
|
508
|
+
const content = element.innerHTML;
|
|
509
|
+
element.innerHTML = "";
|
|
510
|
+
let hasChanged = false;
|
|
511
|
+
|
|
512
|
+
const bubbleMenuElement = createBubbleMenuElement("inline");
|
|
513
|
+
const extensions: Extension[] = [StarterKit, Placeholder.configure({ placeholder: options.placeholder })];
|
|
514
|
+
|
|
515
|
+
if (options.bubbleMenu) {
|
|
516
|
+
extensions.push(BubbleMenu.configure({ element: bubbleMenuElement }));
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const editor = new Editor({
|
|
520
|
+
element,
|
|
521
|
+
extensions,
|
|
522
|
+
content,
|
|
523
|
+
editorProps: { attributes: { class: "upstart-editor-active" } },
|
|
524
|
+
onUpdate: () => {
|
|
525
|
+
hasChanged = true;
|
|
526
|
+
},
|
|
527
|
+
onBlur: ({ editor: e }) => {
|
|
528
|
+
if (!hasChanged) return;
|
|
529
|
+
hasChanged = false;
|
|
530
|
+
saveText(hash, e.getHTML());
|
|
531
|
+
},
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
if (options.bubbleMenu) {
|
|
535
|
+
wireBubbleMenu(bubbleMenuElement, editor);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
return editor;
|
|
539
|
+
}
|
|
540
|
+
|
|
339
541
|
function getEditorMode(element: HTMLElement, options: Required<UpstartEditorOptions>): TextEditorMode {
|
|
340
542
|
const modeOverride = element.dataset.upstartEditableTextMode as TextEditorMode | undefined;
|
|
341
|
-
if (
|
|
543
|
+
if (
|
|
544
|
+
modeOverride === "plain" ||
|
|
545
|
+
modeOverride === "inline-rich" ||
|
|
546
|
+
modeOverride === "block-rich" ||
|
|
547
|
+
modeOverride === "direct" ||
|
|
548
|
+
modeOverride === "rich-panel"
|
|
549
|
+
) {
|
|
342
550
|
return modeOverride;
|
|
343
551
|
}
|
|
344
552
|
|
|
@@ -408,17 +616,27 @@ function saveText(hash: string, newText: string): void {
|
|
|
408
616
|
try {
|
|
409
617
|
const instance = activeEditors.get(hash);
|
|
410
618
|
const dataset = instance?.element.dataset ?? {};
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
619
|
+
|
|
620
|
+
// direct/rich-panel always go via editTextDirect; plain goes via editTextDirect when a
|
|
621
|
+
// registry id is present (mixed-text), and via editText (i18n) otherwise.
|
|
622
|
+
if (instance?.mode === "direct" || instance?.mode === "rich-panel" || dataset.upstartId) {
|
|
623
|
+
sendToParent({
|
|
624
|
+
type: "text-edit",
|
|
625
|
+
payload: { action: "editTextDirect", id: dataset.upstartId!, content: newText },
|
|
626
|
+
});
|
|
627
|
+
} else {
|
|
628
|
+
const [namespace, key] = dataset.upstartI18n?.split(":") ?? [];
|
|
629
|
+
sendToParent({
|
|
630
|
+
type: "text-edit",
|
|
631
|
+
payload: {
|
|
632
|
+
action: "editText",
|
|
633
|
+
content: newText,
|
|
634
|
+
namespace,
|
|
635
|
+
key,
|
|
636
|
+
language: document.documentElement.lang,
|
|
637
|
+
},
|
|
638
|
+
});
|
|
639
|
+
}
|
|
422
640
|
|
|
423
641
|
console.log("[Upstart Editor] Text save message sent:", hash);
|
|
424
642
|
} catch (error) {
|
|
@@ -436,7 +654,8 @@ function destroyEditor(hash: string): void {
|
|
|
436
654
|
return;
|
|
437
655
|
}
|
|
438
656
|
|
|
439
|
-
const
|
|
657
|
+
const isPlainMode = instance.mode === "plain";
|
|
658
|
+
const finalContent = isPlainMode ? instance.editor.getText() : instance.editor.getHTML();
|
|
440
659
|
|
|
441
660
|
instance.editor.destroy();
|
|
442
661
|
|
|
@@ -448,7 +667,7 @@ function destroyEditor(hash: string): void {
|
|
|
448
667
|
delete instance.element.dataset.upstartEditorActive;
|
|
449
668
|
|
|
450
669
|
// Update element content with the final edited text
|
|
451
|
-
if (
|
|
670
|
+
if (isPlainMode) {
|
|
452
671
|
instance.element.textContent = finalContent;
|
|
453
672
|
} else {
|
|
454
673
|
instance.element.innerHTML = finalContent;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Editor } from "@tiptap/core";
|
|
2
|
-
import type { PayloadEditText } from "~/upstart-editor-api";
|
|
2
|
+
import type { PayloadEditText, PayloadEditTextDirect } from "~/upstart-editor-api";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Editor mode
|
|
@@ -8,8 +8,13 @@ export type EditorMode = "preview" | "edit";
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Text editor mode: determines schema constraints and bubble menu options.
|
|
11
|
+
* - block-rich: inline TipTap rich text (block-level elements: p, div, article…)
|
|
12
|
+
* - inline-rich: inline TipTap rich text constrained to inline nodes (spans, headings…)
|
|
13
|
+
* - plain: inline TipTap plain text, no formatting
|
|
14
|
+
* - direct: inline TipTap plain text, saved directly into the TSX source
|
|
15
|
+
* - rich-panel: inline TipTap rich text with bubble menu (block-level, like block-rich)
|
|
11
16
|
*/
|
|
12
|
-
export type TextEditorMode = "block-rich" | "inline-rich" | "plain";
|
|
17
|
+
export type TextEditorMode = "block-rich" | "inline-rich" | "plain" | "direct" | "rich-panel";
|
|
13
18
|
|
|
14
19
|
/**
|
|
15
20
|
* Element bounds used for UI positioning.
|
|
@@ -37,7 +42,7 @@ export interface EditorInstance {
|
|
|
37
42
|
* Messages sent from iframe to parent editor.
|
|
38
43
|
*/
|
|
39
44
|
export type EditorMessage =
|
|
40
|
-
| { type: "text-edit"; payload: PayloadEditText }
|
|
45
|
+
| { type: "text-edit"; payload: PayloadEditText | PayloadEditTextDirect }
|
|
41
46
|
| { type: "editor-ready" }
|
|
42
47
|
| { type: "editor-navigated" }
|
|
43
48
|
| { type: "scroll-position"; x: number; y: number }
|
|
@@ -53,6 +58,7 @@ export type EditorMessage =
|
|
|
53
58
|
recordId?: string;
|
|
54
59
|
themeColors?: Record<string, string>;
|
|
55
60
|
bounds: ElementBounds;
|
|
61
|
+
viewportWidth?: number;
|
|
56
62
|
};
|
|
57
63
|
|
|
58
64
|
/**
|
|
@@ -60,7 +66,7 @@ export type EditorMessage =
|
|
|
60
66
|
*/
|
|
61
67
|
export type ParentMessage =
|
|
62
68
|
| { type: "set-mode"; mode: EditorMode }
|
|
63
|
-
| { type: "preview-classname"; classNameId: string; className: string }
|
|
69
|
+
| { type: "preview-classname"; classNameId: string; className: string; previewCSS?: string }
|
|
64
70
|
| { type: "request-scroll-position" }
|
|
65
71
|
| { type: "restore-scroll-position"; x: number; y: number };
|
|
66
72
|
|
|
@@ -122,6 +128,13 @@ export interface UpstartEditorOptions {
|
|
|
122
128
|
* @default 1000
|
|
123
129
|
*/
|
|
124
130
|
autoSaveDelay?: number;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Callback to retrieve the raw (un-interpolated) i18n template string for a given
|
|
134
|
+
* namespace + key, e.g. "Copyright {{year}}". Used to render template variables as
|
|
135
|
+
* non-editable atoms inside i18n text editors.
|
|
136
|
+
*/
|
|
137
|
+
getRawI18nTemplate?: (namespace: string, key: string) => string | undefined;
|
|
125
138
|
}
|
|
126
139
|
|
|
127
140
|
/**
|
|
@@ -77,7 +77,10 @@ function generateFontsCSS(
|
|
|
77
77
|
const families = Array.from(allFonts.values()).map((font) => {
|
|
78
78
|
const name = font.family.replace(/ /g, "+");
|
|
79
79
|
if (font.weights && font.weights.length > 1 && font.weights[0] !== font.weights.at(-1)) {
|
|
80
|
-
|
|
80
|
+
// Take min weight and max weight
|
|
81
|
+
const minWeight = Math.min(...font.weights);
|
|
82
|
+
const maxWeight = Math.max(...font.weights);
|
|
83
|
+
return `family=${name}:wght@${minWeight}..${maxWeight}`;
|
|
81
84
|
}
|
|
82
85
|
return `family=${name}`;
|
|
83
86
|
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"state.d.ts","names":[],"sources":["../../../../src/vite-plugin-upstart-editor/runtime/state.ts"],"mappings":";;;;;AAOA;iBAAgB,cAAA,CAAA,GAAkB,UAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","names":[],"sources":["../../../../src/vite-plugin-upstart-editor/runtime/state.ts"],"sourcesContent":["import type { EditorMode } from \"./types.js\";\n\nlet currentMode: EditorMode = \"preview\";\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 (internal use only).\n */\nexport function setCurrentMode(mode: EditorMode): void {\n currentMode = mode;\n}\n"],"mappings":";AAEA,IAAI,cAA0B;;;;AAK9B,SAAgB,iBAA6B;AAC3C,QAAO;;;;;AAMT,SAAgB,eAAe,MAAwB;AACrD,eAAc"}
|
/package/dist/{src/vite-plugin-upstart-editor → vite-plugin-upstart-editor}/runtime/state.js
RENAMED
|
File without changes
|