mechanica 2.0.0-alpha.4 → 2.0.0-alpha.6
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/bin/mechanica.js +0 -0
- package/dist/chunks/{ImagePickerDialog-BfjNtRoR.js → ImagePickerDialog-CY-mn2UP.js} +8 -8
- package/dist/chunks/{editor-BYMvxQTM.js → editor-9tquvDcS.js} +182 -22
- package/dist/cli.js +38 -0
- package/dist/composer.js +2 -2
- package/dist/editor.css +90 -43
- package/dist/editor.js +7 -3
- package/dist/plugin.js +93 -3
- package/dist/types/cli/export.d.ts +4 -1
- package/dist/types/editor/lib/typography.d.ts +39 -0
- package/dist/types/vite/dev/middleware.d.ts +10 -1
- package/dist/types/vite/dev/page-state.d.ts +24 -1
- package/dist/types/vite/entries.d.ts +3 -0
- package/dist/types/vite/generate-pages.d.ts +35 -0
- package/dist/types/vite/index.d.ts +1 -0
- package/dist/types/vite/plugin.d.ts +9 -0
- package/dist/widgets.js +1 -1
- package/package.json +2 -2
- package/src/cli/export.ts +53 -0
- package/src/editor/components/TypographyInput.vue +131 -0
- package/src/editor/editor.ts +7 -1
- package/src/editor/fields/editors/StringField.vue +4 -6
- package/src/editor/fields/editors/TextField.vue +6 -6
- package/src/editor/fields/richtext/RichTextEditor.vue +38 -0
- package/src/editor/fields/richtext/config.ts +6 -0
- package/src/editor/lib/typography.ts +80 -0
- package/src/vite/dev/middleware.ts +15 -2
- package/src/vite/dev/page-state.ts +40 -1
- package/src/vite/entries.ts +4 -0
- package/src/vite/generate-pages.ts +51 -0
- package/src/vite/index.ts +1 -0
- package/src/vite/plugin.ts +55 -3
package/bin/mechanica.js
CHANGED
|
File without changes
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { Fragment, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, inject, mergeProps, normalizeClass, onMounted, openBlock, ref, renderList, renderSlot, shallowReactive, toDisplayString, unref, withCtx, withModifiers } from "vue";
|
|
2
|
+
//#region \0plugin-vue:export-helper
|
|
3
|
+
var _plugin_vue_export_helper_default = (sfc, props) => {
|
|
4
|
+
const target = sfc.__vccOpts || sfc;
|
|
5
|
+
for (const [key, val] of props) target[key] = val;
|
|
6
|
+
return target;
|
|
7
|
+
};
|
|
8
|
+
//#endregion
|
|
2
9
|
//#region \0svg-glob:C:\Users\Den\Desktop\CURVES\mechanica\packages\mechanica\src\editor\icons
|
|
3
10
|
var contents = {
|
|
4
11
|
"align-center": `<path d="M4 6h16M7 12h10M6 18h12"/>`,
|
|
@@ -543,13 +550,6 @@ var VIcon_default = /* @__PURE__ */ defineComponent({
|
|
|
543
550
|
}
|
|
544
551
|
});
|
|
545
552
|
//#endregion
|
|
546
|
-
//#region \0plugin-vue:export-helper
|
|
547
|
-
var _plugin_vue_export_helper_default = (sfc, props) => {
|
|
548
|
-
const target = sfc.__vccOpts || sfc;
|
|
549
|
-
for (const [key, val] of props) target[key] = val;
|
|
550
|
-
return target;
|
|
551
|
-
};
|
|
552
|
-
//#endregion
|
|
553
553
|
//#region src/editor/ui/dialog.ts
|
|
554
554
|
var dialogKey = Symbol("mech-dialog");
|
|
555
555
|
/** Create the reactive dialog store (provided once by the editor shell). */
|
|
@@ -869,4 +869,4 @@ var ImagePickerDialog_default = /*#__PURE__*/ _plugin_vue_export_helper_default(
|
|
|
869
869
|
}
|
|
870
870
|
}), [["__scopeId", "data-v-4ef9dd0d"]]);
|
|
871
871
|
//#endregion
|
|
872
|
-
export { renderCrop as a, useDialog as c,
|
|
872
|
+
export { renderCrop as a, useDialog as c, _plugin_vue_export_helper_default as d, readImageSize as i, VIcon_default as l, VDialog_default as n, createDialogStore as o, analyzeImageFile as r, dialogKey as s, ImagePickerDialog_default as t, contents as u };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as renderCrop, c as useDialog, i as readImageSize, l as
|
|
1
|
+
import { a as renderCrop, c as useDialog, d as _plugin_vue_export_helper_default, i as readImageSize, l as VIcon_default, n as VDialog_default, r as analyzeImageFile, s as dialogKey, t as ImagePickerDialog_default } from "./ImagePickerDialog-CY-mn2UP.js";
|
|
2
2
|
import { Fragment, Teleport, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, inject, mergeProps, nextTick, normalizeClass, normalizeStyle, onBeforeUnmount, onErrorCaptured, onMounted, openBlock, provide, reactive, ref, renderList, renderSlot, resolveDynamicComponent, shallowRef, toDisplayString, unref, vShow, watch, withCtx, withDirectives, withKeys, withModifiers } from "vue";
|
|
3
3
|
import { deepEqual, getDefaultValue } from "mechanica-shared";
|
|
4
4
|
import { unfoldSchema } from "compact-json-schema";
|
|
@@ -19,9 +19,146 @@ function resolveFieldEditor(schema) {
|
|
|
19
19
|
if (schema.type && editors.has(schema.type)) return editors.get(schema.type);
|
|
20
20
|
return editors.get("string");
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* The non-breaking character a keyboard event asks for, or `null`. Matches Word's
|
|
24
|
+
* muscle memory: Ctrl/Cmd+Shift+Space → nbsp, Ctrl/Cmd+Shift+Minus → nb-hyphen.
|
|
25
|
+
* Keyed off `event.code` (physical key) so it fires on any keyboard layout, the
|
|
26
|
+
* same way the editor's other shortcuts stay layout-independent.
|
|
27
|
+
*/
|
|
28
|
+
function typographyCharForEvent(event) {
|
|
29
|
+
if (!(event.ctrlKey || event.metaKey) || !event.shiftKey || event.altKey) return null;
|
|
30
|
+
if (event.code === "Space") return "\xA0";
|
|
31
|
+
if (event.code === "Minus") return "‑";
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Insert `char` at the caret of an `<input>`/`<textarea>`, replacing any
|
|
36
|
+
* selection, and leave the caret after it. Returns the field's new value so the
|
|
37
|
+
* caller can emit it. `setRangeText` keeps native undo working.
|
|
38
|
+
*/
|
|
39
|
+
function insertAtCursor(el, char) {
|
|
40
|
+
const start = el.selectionStart ?? el.value.length;
|
|
41
|
+
const end = el.selectionEnd ?? start;
|
|
42
|
+
el.setRangeText(char, start, end, "end");
|
|
43
|
+
return el.value;
|
|
44
|
+
}
|
|
45
|
+
var HTML_ESCAPES = {
|
|
46
|
+
"&": "&",
|
|
47
|
+
"<": "<",
|
|
48
|
+
">": ">"
|
|
49
|
+
};
|
|
50
|
+
function escapeHtml(text) {
|
|
51
|
+
return text.replace(/[&<>]/g, (ch) => HTML_ESCAPES[ch] ?? ch);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* HTML that mirrors `text` for the plain-field marker overlay: every character
|
|
55
|
+
* keeps its exact glyph advance (so the layer lines up with the real field), but
|
|
56
|
+
* the two non-breaking characters are wrapped in marker spans the CSS lights up.
|
|
57
|
+
* The characters themselves stay in the spans — that's what preserves the width.
|
|
58
|
+
*/
|
|
59
|
+
function highlightTypographyHtml(text) {
|
|
60
|
+
return escapeHtml(text).replaceAll("\xA0", `<span class="mech-typo__nbsp"> </span>`).replaceAll("‑", `<span class="mech-typo__nbhyphen">‑</span>`);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* vuewrite `parser`: scans a block's text and emits a one-character style span
|
|
64
|
+
* over every non-breaking character. vuewrite merges these at *render* time only
|
|
65
|
+
* — they never enter the stored model, so the Markdown on disk stays clean while
|
|
66
|
+
* the editor still shows where the glue is.
|
|
67
|
+
*/
|
|
68
|
+
function typographyParser(text) {
|
|
69
|
+
const spans = [];
|
|
70
|
+
for (let i = 0; i < text.length; i++) if (text[i] === "\xA0") spans.push({
|
|
71
|
+
start: i,
|
|
72
|
+
end: i + 1,
|
|
73
|
+
style: "nbsp"
|
|
74
|
+
});
|
|
75
|
+
else if (text[i] === "‑") spans.push({
|
|
76
|
+
start: i,
|
|
77
|
+
end: i + 1,
|
|
78
|
+
style: "nbHyphen"
|
|
79
|
+
});
|
|
80
|
+
return spans;
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/editor/components/TypographyInput.vue?vue&type=script&setup=true&lang.ts
|
|
84
|
+
var _hoisted_1$19 = { class: "mech-typo" };
|
|
85
|
+
var _hoisted_2$15 = [
|
|
86
|
+
"value",
|
|
87
|
+
"placeholder",
|
|
88
|
+
"rows"
|
|
89
|
+
];
|
|
90
|
+
var _hoisted_3$12 = ["value", "placeholder"];
|
|
91
|
+
var _hoisted_4$9 = ["innerHTML"];
|
|
22
92
|
//#endregion
|
|
23
|
-
//#region src/editor/
|
|
24
|
-
var
|
|
93
|
+
//#region src/editor/components/TypographyInput.vue
|
|
94
|
+
var TypographyInput_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
95
|
+
__name: "TypographyInput",
|
|
96
|
+
props: {
|
|
97
|
+
modelValue: {},
|
|
98
|
+
multiline: { type: Boolean },
|
|
99
|
+
placeholder: {},
|
|
100
|
+
rows: { default: 3 }
|
|
101
|
+
},
|
|
102
|
+
emits: ["update:modelValue"],
|
|
103
|
+
setup(__props, { emit: __emit }) {
|
|
104
|
+
const props = __props;
|
|
105
|
+
const emit = __emit;
|
|
106
|
+
const fieldRef = ref();
|
|
107
|
+
const overlayRef = ref();
|
|
108
|
+
const highlighted = computed(() => highlightTypographyHtml(props.modelValue ?? ""));
|
|
109
|
+
function onInput(event) {
|
|
110
|
+
emit("update:modelValue", event.target.value);
|
|
111
|
+
nextTick(syncScroll);
|
|
112
|
+
}
|
|
113
|
+
function onKeyDown(event) {
|
|
114
|
+
const char = typographyCharForEvent(event);
|
|
115
|
+
if (!char) return;
|
|
116
|
+
event.preventDefault();
|
|
117
|
+
const el = fieldRef.value;
|
|
118
|
+
if (!el) return;
|
|
119
|
+
emit("update:modelValue", insertAtCursor(el, char));
|
|
120
|
+
nextTick(syncScroll);
|
|
121
|
+
}
|
|
122
|
+
function syncScroll() {
|
|
123
|
+
const el = fieldRef.value;
|
|
124
|
+
const overlay = overlayRef.value;
|
|
125
|
+
if (!el || !overlay) return;
|
|
126
|
+
overlay.scrollTop = el.scrollTop;
|
|
127
|
+
overlay.scrollLeft = el.scrollLeft;
|
|
128
|
+
}
|
|
129
|
+
return (_ctx, _cache) => {
|
|
130
|
+
return openBlock(), createElementBlock("div", _hoisted_1$19, [__props.multiline ? (openBlock(), createElementBlock("textarea", {
|
|
131
|
+
key: 0,
|
|
132
|
+
ref_key: "fieldRef",
|
|
133
|
+
ref: fieldRef,
|
|
134
|
+
class: "mech-input mech-textarea",
|
|
135
|
+
value: __props.modelValue,
|
|
136
|
+
placeholder: __props.placeholder,
|
|
137
|
+
rows: __props.rows,
|
|
138
|
+
onInput,
|
|
139
|
+
onKeydown: onKeyDown,
|
|
140
|
+
onScroll: syncScroll
|
|
141
|
+
}, null, 40, _hoisted_2$15)) : (openBlock(), createElementBlock("input", {
|
|
142
|
+
key: 1,
|
|
143
|
+
ref_key: "fieldRef",
|
|
144
|
+
ref: fieldRef,
|
|
145
|
+
class: "mech-input",
|
|
146
|
+
type: "text",
|
|
147
|
+
value: __props.modelValue,
|
|
148
|
+
placeholder: __props.placeholder,
|
|
149
|
+
onInput,
|
|
150
|
+
onKeydown: onKeyDown,
|
|
151
|
+
onScroll: syncScroll
|
|
152
|
+
}, null, 40, _hoisted_3$12)), createElementVNode("div", {
|
|
153
|
+
ref_key: "overlayRef",
|
|
154
|
+
ref: overlayRef,
|
|
155
|
+
class: normalizeClass(["mech-typo__overlay", __props.multiline ? "is-multiline" : "is-single"]),
|
|
156
|
+
"aria-hidden": "true",
|
|
157
|
+
innerHTML: highlighted.value
|
|
158
|
+
}, null, 10, _hoisted_4$9)]);
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}), [["__scopeId", "data-v-0086c808"]]);
|
|
25
162
|
//#endregion
|
|
26
163
|
//#region src/editor/fields/editors/StringField.vue
|
|
27
164
|
var StringField_default = /* @__PURE__ */ defineComponent({
|
|
@@ -33,22 +170,16 @@ var StringField_default = /* @__PURE__ */ defineComponent({
|
|
|
33
170
|
emits: ["update:modelValue"],
|
|
34
171
|
setup(__props, { emit: __emit }) {
|
|
35
172
|
const emit = __emit;
|
|
36
|
-
const onInput = (event) => emit("update:modelValue", event.target.value);
|
|
37
173
|
return (_ctx, _cache) => {
|
|
38
|
-
return openBlock(),
|
|
39
|
-
|
|
40
|
-
type: "text",
|
|
41
|
-
value: __props.modelValue,
|
|
174
|
+
return openBlock(), createBlock(TypographyInput_default, {
|
|
175
|
+
"model-value": __props.modelValue,
|
|
42
176
|
placeholder: __props.schema.placeholder,
|
|
43
|
-
|
|
44
|
-
}, null,
|
|
177
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => emit("update:modelValue", $event))
|
|
178
|
+
}, null, 8, ["model-value", "placeholder"]);
|
|
45
179
|
};
|
|
46
180
|
}
|
|
47
181
|
});
|
|
48
182
|
//#endregion
|
|
49
|
-
//#region src/editor/fields/editors/TextField.vue?vue&type=script&setup=true&lang.ts
|
|
50
|
-
var _hoisted_1$19 = ["value", "placeholder"];
|
|
51
|
-
//#endregion
|
|
52
183
|
//#region src/editor/fields/editors/TextField.vue
|
|
53
184
|
var TextField_default = /* @__PURE__ */ defineComponent({
|
|
54
185
|
__name: "TextField",
|
|
@@ -59,15 +190,14 @@ var TextField_default = /* @__PURE__ */ defineComponent({
|
|
|
59
190
|
emits: ["update:modelValue"],
|
|
60
191
|
setup(__props, { emit: __emit }) {
|
|
61
192
|
const emit = __emit;
|
|
62
|
-
const onInput = (event) => emit("update:modelValue", event.target.value);
|
|
63
193
|
return (_ctx, _cache) => {
|
|
64
|
-
return openBlock(),
|
|
65
|
-
|
|
66
|
-
value: __props.modelValue,
|
|
194
|
+
return openBlock(), createBlock(TypographyInput_default, {
|
|
195
|
+
multiline: "",
|
|
196
|
+
"model-value": __props.modelValue,
|
|
67
197
|
placeholder: __props.schema.placeholder,
|
|
68
|
-
rows:
|
|
69
|
-
|
|
70
|
-
}, null,
|
|
198
|
+
rows: 3,
|
|
199
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => emit("update:modelValue", $event))
|
|
200
|
+
}, null, 8, ["model-value", "placeholder"]);
|
|
71
201
|
};
|
|
72
202
|
}
|
|
73
203
|
});
|
|
@@ -1477,6 +1607,14 @@ var decorator = (style) => {
|
|
|
1477
1607
|
}
|
|
1478
1608
|
};
|
|
1479
1609
|
case "color": return { style: `color: ${style.meta?.color};` };
|
|
1610
|
+
case "nbsp": return {
|
|
1611
|
+
tag: "span",
|
|
1612
|
+
class: "rt-nbsp"
|
|
1613
|
+
};
|
|
1614
|
+
case "nbHyphen": return {
|
|
1615
|
+
tag: "span",
|
|
1616
|
+
class: "rt-nbhyphen"
|
|
1617
|
+
};
|
|
1480
1618
|
default: return;
|
|
1481
1619
|
}
|
|
1482
1620
|
};
|
|
@@ -2065,9 +2203,28 @@ var RichTextEditor_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
2065
2203
|
autosizeMarkdown();
|
|
2066
2204
|
nextTick(() => fromMarkdown = false);
|
|
2067
2205
|
}
|
|
2206
|
+
/** The nbsp / non-breaking-hyphen shortcuts in the raw Markdown view too. */
|
|
2207
|
+
function onMarkdownKeyDown(event) {
|
|
2208
|
+
const char = typographyCharForEvent(event);
|
|
2209
|
+
if (!char || !markdownRef.value) return;
|
|
2210
|
+
event.preventDefault();
|
|
2211
|
+
const md = insertAtCursor(markdownRef.value, char);
|
|
2212
|
+
markdown.value = md;
|
|
2213
|
+
fromMarkdown = true;
|
|
2214
|
+
model.value = markdownToBlocks(md, model.value, { softBreaks: true });
|
|
2215
|
+
autosizeMarkdown();
|
|
2216
|
+
nextTick(() => fromMarkdown = false);
|
|
2217
|
+
}
|
|
2068
2218
|
function onKeyDown(event) {
|
|
2069
2219
|
const editor = editorRef.value;
|
|
2070
2220
|
if (!editor) return;
|
|
2221
|
+
const nb = typographyCharForEvent(event);
|
|
2222
|
+
if (nb) {
|
|
2223
|
+
event.preventDefault();
|
|
2224
|
+
editor.insertText(nb);
|
|
2225
|
+
editor.pushHistory("insertText");
|
|
2226
|
+
return;
|
|
2227
|
+
}
|
|
2071
2228
|
if ((event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey) {
|
|
2072
2229
|
const style = {
|
|
2073
2230
|
KeyB: "bold",
|
|
@@ -2192,6 +2349,7 @@ var RichTextEditor_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
2192
2349
|
class: "mech-rte__surface",
|
|
2193
2350
|
renderer: unref(renderer),
|
|
2194
2351
|
decorator: unref(decorator),
|
|
2352
|
+
parser: unref(typographyParser),
|
|
2195
2353
|
"html-parser": unref(htmlParser),
|
|
2196
2354
|
onKeydown: onKeyDown,
|
|
2197
2355
|
onContextmenu: onContextMenu
|
|
@@ -2213,6 +2371,7 @@ var RichTextEditor_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
2213
2371
|
"modelValue",
|
|
2214
2372
|
"renderer",
|
|
2215
2373
|
"decorator",
|
|
2374
|
+
"parser",
|
|
2216
2375
|
"html-parser"
|
|
2217
2376
|
])) : (openBlock(), createElementBlock("textarea", {
|
|
2218
2377
|
key: 2,
|
|
@@ -2222,11 +2381,12 @@ var RichTextEditor_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
2222
2381
|
value: markdown.value,
|
|
2223
2382
|
spellcheck: "false",
|
|
2224
2383
|
placeholder: __props.placeholder,
|
|
2225
|
-
onInput: onMarkdownInput
|
|
2384
|
+
onInput: onMarkdownInput,
|
|
2385
|
+
onKeydown: onMarkdownKeyDown
|
|
2226
2386
|
}, null, 40, _hoisted_8))])], 2);
|
|
2227
2387
|
};
|
|
2228
2388
|
}
|
|
2229
|
-
}), [["__scopeId", "data-v-
|
|
2389
|
+
}), [["__scopeId", "data-v-6729e5a2"]]);
|
|
2230
2390
|
//#endregion
|
|
2231
2391
|
//#region src/editor/dialogs/RichTextDialog.vue
|
|
2232
2392
|
var RichTextDialog_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
package/dist/cli.js
CHANGED
|
@@ -204,6 +204,34 @@ function breadcrumbsFor(path, names) {
|
|
|
204
204
|
}));
|
|
205
205
|
}
|
|
206
206
|
/**
|
|
207
|
+
* Map a baked {@link VirtualPage} to an {@link ExportPage}. The provider emits
|
|
208
|
+
* one per (logical page × locale) and self-declares `locale`/`locales`, so the
|
|
209
|
+
* mapping just localizes the served `path` and carries the alternates — it never
|
|
210
|
+
* goes through the file-based `readTranslation`.
|
|
211
|
+
*/
|
|
212
|
+
function virtualToExportPage(v, config) {
|
|
213
|
+
const isDefault = !config || !v.locale || v.locale === config.default;
|
|
214
|
+
const locale = config ? v.locale ?? config.default : void 0;
|
|
215
|
+
const path = isDefault ? v.path : localePath(v.path, v.locale, config);
|
|
216
|
+
const name = typeof v.meta?.title === "string" ? v.meta.title : void 0;
|
|
217
|
+
return {
|
|
218
|
+
path,
|
|
219
|
+
logicalPath: v.path,
|
|
220
|
+
locale,
|
|
221
|
+
translations: v.locales,
|
|
222
|
+
content: v.content ?? [],
|
|
223
|
+
data: v.data ?? {},
|
|
224
|
+
name,
|
|
225
|
+
lastmod: v.lastmod,
|
|
226
|
+
page: {
|
|
227
|
+
meta: v.meta ?? {},
|
|
228
|
+
locale,
|
|
229
|
+
locales: v.locales,
|
|
230
|
+
path: v.path
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
207
235
|
* Load the pieces needed to preload per-page block chunks: the client build's
|
|
208
236
|
* Vite manifest and the plugin's block map. Older builds (or builds outside
|
|
209
237
|
* `mechanica build`) may not have them — links are simply skipped then.
|
|
@@ -328,6 +356,15 @@ async function exportProject(cwd, ssr, options = {}) {
|
|
|
328
356
|
}
|
|
329
357
|
for (const [locale, paths] of Object.entries(missing)) if (paths.length) warn(`[mechanica] locale "${locale}" is missing ${paths.length} translation(s) (rendered only in ${config.default}): ${paths.join(", ")}`);
|
|
330
358
|
}
|
|
359
|
+
if (ssr.generatedPages?.length) {
|
|
360
|
+
const taken = new Set(pages.map((page) => page.path));
|
|
361
|
+
for (const vp of ssr.generatedPages) {
|
|
362
|
+
const generated = virtualToExportPage(vp, config);
|
|
363
|
+
if (taken.has(generated.path)) throw new Error(`[mechanica] generatePages produced "${generated.path}", but a page already exists there — change the generated path or remove the conflicting page.`);
|
|
364
|
+
taken.add(generated.path);
|
|
365
|
+
pages.push(generated);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
331
368
|
setPageCodec(richText);
|
|
332
369
|
const querySource = {
|
|
333
370
|
listPages: (opts) => listPages(join(cwd, ".mech"), opts),
|
|
@@ -421,6 +458,7 @@ async function exportProject(cwd, ssr, options = {}) {
|
|
|
421
458
|
};
|
|
422
459
|
const templateHandlesPagination = index.includes("page.pagination");
|
|
423
460
|
const pageNames = new Map(defaultPages.map((page) => [page.logicalPath, page.name ?? (page.logicalPath === "/" ? "Home" : page.logicalPath.split("/").pop())]));
|
|
461
|
+
for (const vp of ssr.generatedPages ?? []) pageNames.set(vp.path, typeof vp.meta?.title === "string" ? vp.meta.title : vp.path.split("/").pop());
|
|
424
462
|
/** hreflang alternates for a translated page (each locale + x-default), at page N. */
|
|
425
463
|
const alternatesFor = (source, pageNum = 1) => {
|
|
426
464
|
if (!config || !source.translations || source.translations.length < 2) return void 0;
|
package/dist/composer.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import './editor.css';
|
|
2
2
|
import { c as ANCHOR_H, i as renderBlocks, l as ANCHOR_V, t as createMechanica } from "./chunks/create-mechanica-CnBFiyfJ.js";
|
|
3
|
-
import { C as registerBuiltinFieldEditors, N as VSelect_default, P as VPopover_default, S as uid, _ as findParentSlot, a as FieldControl_default, b as relocateBlock, d as humanize, f as cloneBlock, g as findParentList, h as findBlock, i as SchemaField_default, m as ensureSlotList, n as createSaveQueue, o as shortcutChar, p as duplicateBlock, t as SaveConflictError, u as toBlockMeta, v as moveBlock, x as removeBlock, y as placeBlock } from "./chunks/editor-
|
|
4
|
-
import { d as
|
|
3
|
+
import { C as registerBuiltinFieldEditors, N as VSelect_default, P as VPopover_default, S as uid, _ as findParentSlot, a as FieldControl_default, b as relocateBlock, d as humanize, f as cloneBlock, g as findParentList, h as findBlock, i as SchemaField_default, m as ensureSlotList, n as createSaveQueue, o as shortcutChar, p as duplicateBlock, t as SaveConflictError, u as toBlockMeta, v as moveBlock, x as removeBlock, y as placeBlock } from "./chunks/editor-9tquvDcS.js";
|
|
4
|
+
import { d as _plugin_vue_export_helper_default, l as VIcon_default, u as contents } from "./chunks/ImagePickerDialog-CY-mn2UP.js";
|
|
5
5
|
import { Fragment, Teleport, computed, createApp, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, inject, nextTick, normalizeClass, normalizeStyle, onBeforeUnmount, onMounted, onScopeDispose, openBlock, provide, reactive, ref, renderList, renderSlot, resolveComponent, toDisplayString, unref, vModelText, watch, withCtx, withDirectives, withKeys, withModifiers } from "vue";
|
|
6
6
|
import { buildPreviewData, getDefaultValue, isBinding, registerFieldSchemas, resolveBindings, walkTree } from "mechanica-shared";
|
|
7
7
|
import { unfoldSchema } from "compact-json-schema";
|
package/dist/editor.css
CHANGED
|
@@ -467,6 +467,43 @@
|
|
|
467
467
|
padding: 8px 0;
|
|
468
468
|
color: var(--mech-muted);
|
|
469
469
|
font-size: 13px;
|
|
470
|
+
}.mech-typo[data-v-0086c808] {
|
|
471
|
+
position: relative;
|
|
472
|
+
display: block;
|
|
473
|
+
width: 100%;
|
|
474
|
+
}
|
|
475
|
+
.mech-typo__overlay[data-v-0086c808] {
|
|
476
|
+
position: absolute;
|
|
477
|
+
inset: 0;
|
|
478
|
+
box-sizing: border-box;
|
|
479
|
+
border: 1px solid transparent;
|
|
480
|
+
z-index: 1;
|
|
481
|
+
overflow: hidden;
|
|
482
|
+
pointer-events: none;
|
|
483
|
+
color: transparent;
|
|
484
|
+
font: inherit;
|
|
485
|
+
font-size: 13.5px;
|
|
486
|
+
}
|
|
487
|
+
.mech-typo__overlay.is-single[data-v-0086c808] {
|
|
488
|
+
padding: 0 12px;
|
|
489
|
+
line-height: 36px;
|
|
490
|
+
white-space: pre;
|
|
491
|
+
}
|
|
492
|
+
.mech-typo__overlay.is-multiline[data-v-0086c808] {
|
|
493
|
+
padding: 9px 12px;
|
|
494
|
+
line-height: 1.45;
|
|
495
|
+
white-space: pre-wrap;
|
|
496
|
+
overflow-wrap: break-word;
|
|
497
|
+
}
|
|
498
|
+
.mech-typo__overlay[data-v-0086c808] .mech-typo__nbsp,
|
|
499
|
+
.mech-typo__overlay[data-v-0086c808] .mech-typo__nbhyphen {
|
|
500
|
+
border-radius: 3px;
|
|
501
|
+
background-color: color-mix(in srgb, var(--mech-accent) 15%, transparent);
|
|
502
|
+
}
|
|
503
|
+
.mech-typo__overlay[data-v-0086c808] .mech-typo__nbsp {
|
|
504
|
+
background-image: radial-gradient(circle at center, var(--mech-accent) 0 1px, transparent 1.4px);
|
|
505
|
+
background-repeat: no-repeat;
|
|
506
|
+
background-position: center;
|
|
470
507
|
}.mech-popover[data-v-4884cec4] {
|
|
471
508
|
position: fixed;
|
|
472
509
|
z-index: 2147483680;
|
|
@@ -1124,60 +1161,60 @@ from {
|
|
|
1124
1161
|
}
|
|
1125
1162
|
.mech-rt-table[data-v-0b304924] .vw-table-cell {
|
|
1126
1163
|
font-size: 13.5px;
|
|
1127
|
-
}.mech-rte[data-v-
|
|
1164
|
+
}.mech-rte[data-v-6729e5a2] {
|
|
1128
1165
|
display: flex;
|
|
1129
1166
|
flex-direction: column;
|
|
1130
1167
|
gap: 6px;
|
|
1131
1168
|
}
|
|
1132
|
-
.mech-rte__switchbar[data-v-
|
|
1169
|
+
.mech-rte__switchbar[data-v-6729e5a2] {
|
|
1133
1170
|
display: flex;
|
|
1134
1171
|
justify-content: flex-end;
|
|
1135
1172
|
margin-top: -25px;
|
|
1136
1173
|
}
|
|
1137
|
-
.mech-rte__box[data-v-
|
|
1174
|
+
.mech-rte__box[data-v-6729e5a2] {
|
|
1138
1175
|
display: flex;
|
|
1139
1176
|
flex-direction: column;
|
|
1140
1177
|
border-radius: var(--mech-radius);
|
|
1141
1178
|
transition: background 0.12s, border-color 0.12s, box-shadow 0.12s;
|
|
1142
1179
|
}
|
|
1143
|
-
.mech-rte--full .mech-rte__box[data-v-
|
|
1180
|
+
.mech-rte--full .mech-rte__box[data-v-6729e5a2] {
|
|
1144
1181
|
border: 1px solid var(--mech-input-border);
|
|
1145
1182
|
background: var(--mech-bg);
|
|
1146
1183
|
overflow: hidden;
|
|
1147
1184
|
}
|
|
1148
|
-
.mech-rte--full .mech-rte__box[data-v-
|
|
1185
|
+
.mech-rte--full .mech-rte__box[data-v-6729e5a2]:focus-within {
|
|
1149
1186
|
border-color: var(--mech-accent);
|
|
1150
1187
|
box-shadow: 0 0 0 3px var(--mech-ring);
|
|
1151
1188
|
}
|
|
1152
|
-
.mech-rte--minimal .mech-rte__box[data-v-
|
|
1189
|
+
.mech-rte--minimal .mech-rte__box[data-v-6729e5a2] {
|
|
1153
1190
|
border: 1px solid transparent;
|
|
1154
1191
|
background: var(--mech-field-bg);
|
|
1155
1192
|
}
|
|
1156
|
-
.mech-rte--minimal .mech-rte__box[data-v-
|
|
1193
|
+
.mech-rte--minimal .mech-rte__box[data-v-6729e5a2]:hover {
|
|
1157
1194
|
background: var(--mech-field-bg-hover);
|
|
1158
1195
|
}
|
|
1159
|
-
.mech-rte--minimal .mech-rte__box[data-v-
|
|
1196
|
+
.mech-rte--minimal .mech-rte__box[data-v-6729e5a2]:focus-within {
|
|
1160
1197
|
background: var(--mech-bg);
|
|
1161
1198
|
border-color: var(--mech-accent);
|
|
1162
1199
|
box-shadow: 0 0 0 3px var(--mech-ring);
|
|
1163
1200
|
}
|
|
1164
|
-
.mech-rte__bar[data-v-
|
|
1201
|
+
.mech-rte__bar[data-v-6729e5a2] {
|
|
1165
1202
|
display: flex;
|
|
1166
1203
|
align-items: center;
|
|
1167
1204
|
gap: 2px;
|
|
1168
1205
|
padding: 5px 6px;
|
|
1169
1206
|
border-bottom: 1px solid var(--mech-border);
|
|
1170
1207
|
}
|
|
1171
|
-
.mech-rte__toggle[data-v-
|
|
1208
|
+
.mech-rte__toggle[data-v-6729e5a2] {
|
|
1172
1209
|
margin-left: auto;
|
|
1173
1210
|
}
|
|
1174
|
-
.mech-rte__divider[data-v-
|
|
1211
|
+
.mech-rte__divider[data-v-6729e5a2] {
|
|
1175
1212
|
width: 1px;
|
|
1176
1213
|
align-self: stretch;
|
|
1177
1214
|
margin: 4px 4px;
|
|
1178
1215
|
background: var(--mech-border);
|
|
1179
1216
|
}
|
|
1180
|
-
.mech-rte__insert-item[data-v-
|
|
1217
|
+
.mech-rte__insert-item[data-v-6729e5a2] {
|
|
1181
1218
|
display: flex;
|
|
1182
1219
|
align-items: center;
|
|
1183
1220
|
gap: 8px;
|
|
@@ -1194,26 +1231,26 @@ from {
|
|
|
1194
1231
|
text-align: left;
|
|
1195
1232
|
cursor: pointer;
|
|
1196
1233
|
}
|
|
1197
|
-
.mech-rte__insert-item[data-v-
|
|
1234
|
+
.mech-rte__insert-item[data-v-6729e5a2]:hover {
|
|
1198
1235
|
background: var(--mech-hover);
|
|
1199
1236
|
}
|
|
1200
|
-
.mech-rte__insert-icon[data-v-
|
|
1237
|
+
.mech-rte__insert-icon[data-v-6729e5a2] {
|
|
1201
1238
|
width: 16px;
|
|
1202
1239
|
height: 16px;
|
|
1203
1240
|
flex: none;
|
|
1204
1241
|
color: var(--mech-muted);
|
|
1205
1242
|
}
|
|
1206
|
-
.mech-rte__insert-icon[data-v-
|
|
1243
|
+
.mech-rte__insert-icon[data-v-6729e5a2] svg {
|
|
1207
1244
|
display: block;
|
|
1208
1245
|
width: 100%;
|
|
1209
1246
|
height: 100%;
|
|
1210
1247
|
}
|
|
1211
|
-
.mech-rte__surface[data-v-
|
|
1248
|
+
.mech-rte__surface[data-v-6729e5a2] .mech-rte__menu-target {
|
|
1212
1249
|
border-radius: var(--mech-radius-sm);
|
|
1213
1250
|
background: var(--mech-accent-soft);
|
|
1214
1251
|
box-shadow: 0 0 0 3px var(--mech-accent-soft);
|
|
1215
1252
|
}
|
|
1216
|
-
.mech-rte__surface[data-v-
|
|
1253
|
+
.mech-rte__surface[data-v-6729e5a2] {
|
|
1217
1254
|
padding: 11px 13px;
|
|
1218
1255
|
min-height: 64px;
|
|
1219
1256
|
line-height: 1.6;
|
|
@@ -1223,56 +1260,56 @@ from {
|
|
|
1223
1260
|
white-space: pre-wrap;
|
|
1224
1261
|
overflow-y: auto;
|
|
1225
1262
|
}
|
|
1226
|
-
.mech-rte__surface[data-v-
|
|
1263
|
+
.mech-rte__surface[data-v-6729e5a2] p {
|
|
1227
1264
|
margin: 0 0 0.6em;
|
|
1228
1265
|
}
|
|
1229
|
-
.mech-rte__surface[data-v-
|
|
1230
|
-
.mech-rte__surface[data-v-
|
|
1231
|
-
.mech-rte__surface[data-v-
|
|
1266
|
+
.mech-rte__surface[data-v-6729e5a2] h1,
|
|
1267
|
+
.mech-rte__surface[data-v-6729e5a2] h2,
|
|
1268
|
+
.mech-rte__surface[data-v-6729e5a2] h3 {
|
|
1232
1269
|
font-weight: 700;
|
|
1233
1270
|
margin: 0.85em 0 0.35em;
|
|
1234
1271
|
}
|
|
1235
|
-
.mech-rte__surface[data-v-
|
|
1272
|
+
.mech-rte__surface[data-v-6729e5a2] h1 {
|
|
1236
1273
|
font-size: 1.5em;
|
|
1237
1274
|
}
|
|
1238
|
-
.mech-rte__surface[data-v-
|
|
1275
|
+
.mech-rte__surface[data-v-6729e5a2] h2 {
|
|
1239
1276
|
font-size: 1.3em;
|
|
1240
1277
|
}
|
|
1241
|
-
.mech-rte__surface[data-v-
|
|
1278
|
+
.mech-rte__surface[data-v-6729e5a2] h3 {
|
|
1242
1279
|
font-size: 1.1em;
|
|
1243
1280
|
}
|
|
1244
|
-
.mech-rte__surface[data-v-
|
|
1245
|
-
.mech-rte__surface[data-v-
|
|
1281
|
+
.mech-rte__surface[data-v-6729e5a2] ul,
|
|
1282
|
+
.mech-rte__surface[data-v-6729e5a2] ol {
|
|
1246
1283
|
margin: 0 0 0.6em;
|
|
1247
1284
|
padding-left: 0;
|
|
1248
1285
|
}
|
|
1249
|
-
.mech-rte__surface[data-v-
|
|
1286
|
+
.mech-rte__surface[data-v-6729e5a2] li {
|
|
1250
1287
|
margin: 0.15em 0 0.15em 1.7em;
|
|
1251
1288
|
list-style: disc;
|
|
1252
1289
|
}
|
|
1253
|
-
.mech-rte__surface[data-v-
|
|
1290
|
+
.mech-rte__surface[data-v-6729e5a2] li.ol {
|
|
1254
1291
|
list-style: decimal;
|
|
1255
1292
|
}
|
|
1256
|
-
.mech-rte__surface[data-v-
|
|
1293
|
+
.mech-rte__surface[data-v-6729e5a2] :last-child {
|
|
1257
1294
|
margin-bottom: 0;
|
|
1258
1295
|
}
|
|
1259
|
-
.mech-rte__surface[data-v-
|
|
1296
|
+
.mech-rte__surface[data-v-6729e5a2] b {
|
|
1260
1297
|
font-weight: 700;
|
|
1261
1298
|
}
|
|
1262
|
-
.mech-rte__surface[data-v-
|
|
1299
|
+
.mech-rte__surface[data-v-6729e5a2] i {
|
|
1263
1300
|
font-style: italic;
|
|
1264
1301
|
}
|
|
1265
|
-
.mech-rte__surface[data-v-
|
|
1302
|
+
.mech-rte__surface[data-v-6729e5a2] u {
|
|
1266
1303
|
text-decoration: underline;
|
|
1267
1304
|
}
|
|
1268
|
-
.mech-rte__surface[data-v-
|
|
1305
|
+
.mech-rte__surface[data-v-6729e5a2] s {
|
|
1269
1306
|
text-decoration: line-through;
|
|
1270
1307
|
}
|
|
1271
|
-
.mech-rte__surface[data-v-
|
|
1308
|
+
.mech-rte__surface[data-v-6729e5a2] a {
|
|
1272
1309
|
color: var(--mech-accent);
|
|
1273
1310
|
text-decoration: underline;
|
|
1274
1311
|
}
|
|
1275
|
-
.mech-rte__surface[data-v-
|
|
1312
|
+
.mech-rte__surface[data-v-6729e5a2] code {
|
|
1276
1313
|
font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
|
|
1277
1314
|
font-size: 0.88em;
|
|
1278
1315
|
background: var(--mech-field-bg);
|
|
@@ -1280,7 +1317,17 @@ from {
|
|
|
1280
1317
|
border-radius: 4px;
|
|
1281
1318
|
border: 1px solid var(--mech-border);
|
|
1282
1319
|
}
|
|
1283
|
-
.mech-rte__surface[data-v-
|
|
1320
|
+
.mech-rte__surface[data-v-6729e5a2] .rt-nbsp,
|
|
1321
|
+
.mech-rte__surface[data-v-6729e5a2] .rt-nbhyphen {
|
|
1322
|
+
border-radius: 3px;
|
|
1323
|
+
background-color: color-mix(in srgb, var(--mech-accent) 15%, transparent);
|
|
1324
|
+
}
|
|
1325
|
+
.mech-rte__surface[data-v-6729e5a2] .rt-nbsp {
|
|
1326
|
+
background-image: radial-gradient(circle at center, var(--mech-accent) 0 1px, transparent 1.4px);
|
|
1327
|
+
background-repeat: no-repeat;
|
|
1328
|
+
background-position: center;
|
|
1329
|
+
}
|
|
1330
|
+
.mech-rte__surface[data-v-6729e5a2] .rt-callout {
|
|
1284
1331
|
margin: 0.6em 0;
|
|
1285
1332
|
padding: 9px 13px;
|
|
1286
1333
|
border: 1px solid var(--c-border);
|
|
@@ -1290,24 +1337,24 @@ from {
|
|
|
1290
1337
|
--c-border: #b9c8f5;
|
|
1291
1338
|
--c-soft: #eef2fe;
|
|
1292
1339
|
}
|
|
1293
|
-
.mech-rte__surface[data-v-
|
|
1340
|
+
.mech-rte__surface[data-v-6729e5a2] .rt-callout--tip {
|
|
1294
1341
|
--c-border: #aee0c4;
|
|
1295
1342
|
--c-soft: #eaf8f0;
|
|
1296
1343
|
}
|
|
1297
|
-
.mech-rte__surface[data-v-
|
|
1344
|
+
.mech-rte__surface[data-v-6729e5a2] .rt-callout--warning {
|
|
1298
1345
|
--c-border: #f4d39a;
|
|
1299
1346
|
--c-soft: #fdf4e3;
|
|
1300
1347
|
}
|
|
1301
|
-
.mech-rte--minimal .mech-rte__surface[data-v-
|
|
1348
|
+
.mech-rte--minimal .mech-rte__surface[data-v-6729e5a2] {
|
|
1302
1349
|
min-height: 52px;
|
|
1303
1350
|
max-height: 220px;
|
|
1304
1351
|
}
|
|
1305
|
-
.mech-rte--minimal .mech-rte__markdown[data-v-
|
|
1352
|
+
.mech-rte--minimal .mech-rte__markdown[data-v-6729e5a2] {
|
|
1306
1353
|
min-height: 52px;
|
|
1307
1354
|
max-height: 220px;
|
|
1308
1355
|
resize: none;
|
|
1309
1356
|
}
|
|
1310
|
-
.mech-rte__markdown[data-v-
|
|
1357
|
+
.mech-rte__markdown[data-v-6729e5a2] {
|
|
1311
1358
|
border: none;
|
|
1312
1359
|
resize: none;
|
|
1313
1360
|
font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
|
|
@@ -1318,7 +1365,7 @@ from {
|
|
|
1318
1365
|
width: 100%;
|
|
1319
1366
|
box-sizing: border-box;
|
|
1320
1367
|
}
|
|
1321
|
-
.mech-rte__code[data-v-
|
|
1368
|
+
.mech-rte__code[data-v-6729e5a2] {
|
|
1322
1369
|
margin: 0.4em 0;
|
|
1323
1370
|
padding: 10px 12px;
|
|
1324
1371
|
background: var(--mech-field-bg);
|
|
@@ -1328,7 +1375,7 @@ from {
|
|
|
1328
1375
|
font-size: 12.5px;
|
|
1329
1376
|
overflow-x: auto;
|
|
1330
1377
|
}
|
|
1331
|
-
.mech-rte__placeholder[data-v-
|
|
1378
|
+
.mech-rte__placeholder[data-v-6729e5a2] {
|
|
1332
1379
|
position: absolute;
|
|
1333
1380
|
color: var(--mech-placeholder);
|
|
1334
1381
|
pointer-events: none;
|