weml-monaco 0.10.1 → 0.11.0
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/CHANGELOG.md +20 -0
- package/README.md +9 -1
- package/dist/editor.cjs +53 -28
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.d.mts +48 -14
- package/dist/editor.d.ts +48 -14
- package/dist/editor.mjs +53 -28
- package/dist/editor.mjs.map +1 -1
- package/dist/react.cjs +6 -4
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.mts +92 -29
- package/dist/react.d.ts +92 -29
- package/dist/react.mjs +6 -4
- package/dist/react.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,26 @@ This package shares the language logic of the VS Code extension (`weml-support`)
|
|
|
8
8
|
entries below track the port catching up with its provider changes; since the shared language
|
|
9
9
|
features, both take the same code.
|
|
10
10
|
|
|
11
|
+
## [0.11.0] — 2026-10-14
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- `fixedWrappers` (paragraph mode, `createWemlEditor` and `<WemlEditor>`): fixed tags between the
|
|
15
|
+
`<div id="1">` and the content, outermost first — a name or `{ tag, attributes }`. `['w-para']`
|
|
16
|
+
edits the content of a `<w-para>`, as a publication's description is: `<div id="1"><w-para>` …
|
|
17
|
+
`</w-para></div>`, both lines locked as the `<div>` was, `getValue()` / `onChange` the content
|
|
18
|
+
inside them, the preview rendering it inside them (without the `<div>`). A chain WEML's structure
|
|
19
|
+
rules reject throws when the editor is created; with any, an empty value starts empty (the
|
|
20
|
+
templates are a document and a `<w-para>`).
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- The options and the props are split by `mode` (`WemlEditorFileModeOptions` /
|
|
24
|
+
`WemlEditorParagraphModeOptions`, `WemlEditorFileModeProps` / `WemlEditorParagraphModeProps`):
|
|
25
|
+
`hideWrapper` and `fixedWrappers` type-check only with `mode: 'paragraph'`, `onFileLoad` and
|
|
26
|
+
`onFileSave` only in a file. A host that wraps the props in `Omit` needs a distributive one, or
|
|
27
|
+
the split is lost. No change at runtime.
|
|
28
|
+
|
|
29
|
+
VS Code has no counterpart: it edits files only; the paragraph mode is the web editor's.
|
|
30
|
+
|
|
11
31
|
## [0.10.1] — 2026-10-14
|
|
12
32
|
|
|
13
33
|
### Changed
|
package/README.md
CHANGED
|
@@ -187,7 +187,15 @@ weml.dispose();
|
|
|
187
187
|
- `mode: 'paragraph'` edits one paragraph (the content of one `<div>`): the model
|
|
188
188
|
holds it inside a `<div id="1">` wrapper the language needs, guarded against
|
|
189
189
|
edits (dimmed, or hidden with `hideWrapper`); `getValue()` is the paragraph alone,
|
|
190
|
-
and there is no Contents.
|
|
190
|
+
and there is no Contents. `fixedWrappers` puts fixed tags between the `<div>` and
|
|
191
|
+
the content, outermost first — `['w-para']` edits the content of a `<w-para>` (a
|
|
192
|
+
publication's description): the model is `<div id="1"><w-para>` … `</w-para></div>`,
|
|
193
|
+
both lines locked, `getValue()` the content inside them, the preview renders it
|
|
194
|
+
inside them. A tag is a name or `{ tag, attributes }`; one WEML does not allow
|
|
195
|
+
inside the one before it throws, and with any an empty value starts empty.
|
|
196
|
+
- The options and the component's props are split by `mode`: a paragraph's
|
|
197
|
+
(`hideWrapper`, `fixedWrappers`) do not type-check in a file, a file's (`onFileLoad`,
|
|
198
|
+
`onFileSave`) not in a paragraph.
|
|
191
199
|
- **Files on disk** (`file` mode): Open File (Ctrl+O, or a `.weml` dropped on the
|
|
192
200
|
editor) and Save to Disk (Ctrl+Shift+S: the name asked for at the top, suggested
|
|
193
201
|
`<name>-<timestamp>.weml`; Chromium's save dialog where there is one) in the
|
package/dist/editor.cjs
CHANGED
|
@@ -37666,16 +37666,36 @@ async function saveWemlFile(text, suggestedName) {
|
|
|
37666
37666
|
}
|
|
37667
37667
|
|
|
37668
37668
|
// src/editor/content.ts
|
|
37669
|
-
var
|
|
37670
|
-
var
|
|
37671
|
-
|
|
37672
|
-
|
|
37669
|
+
var DIV_OPENER = '<div id="1">';
|
|
37670
|
+
var DIV_CLOSER = "</div>";
|
|
37671
|
+
var DEFAULT_WRAPPER = { opener: DIV_OPENER, closer: DIV_CLOSER, innerOpener: "", innerCloser: "" };
|
|
37672
|
+
var escapeAttribute2 = (value) => value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
|
37673
|
+
function contentWrapper(fixedWrappers = []) {
|
|
37674
|
+
if (fixedWrappers.length === 0) return DEFAULT_WRAPPER;
|
|
37675
|
+
const tags = fixedWrappers.map((wrapper) => typeof wrapper === "string" ? { tag: wrapper } : wrapper);
|
|
37676
|
+
let parent = "div";
|
|
37677
|
+
for (const { tag } of tags) {
|
|
37678
|
+
const rule = STRUCTURE_RULES[parent];
|
|
37679
|
+
if (!rule?.children.some((group) => group.tags.includes(tag))) {
|
|
37680
|
+
throw new Error(`weml-monaco: fixedWrappers \u2014 <${tag}> is not allowed inside <${parent}>.`);
|
|
37681
|
+
}
|
|
37682
|
+
parent = tag;
|
|
37683
|
+
}
|
|
37684
|
+
const innerOpener = tags.map(({ tag, attributes }) => {
|
|
37685
|
+
const attrs = Object.entries(attributes ?? {}).map(([name, value]) => ` ${name}="${escapeAttribute2(value)}"`);
|
|
37686
|
+
return `<${tag}${attrs.join("")}>`;
|
|
37687
|
+
}).join("");
|
|
37688
|
+
const innerCloser = tags.map(({ tag }) => `</${tag}>`).reverse().join("");
|
|
37689
|
+
return { opener: DIV_OPENER + innerOpener, closer: innerCloser + DIV_CLOSER, innerOpener, innerCloser };
|
|
37690
|
+
}
|
|
37691
|
+
function initialModelText(mode, content, wrapper = DEFAULT_WRAPPER) {
|
|
37692
|
+
return mode === "file" ? content : `${wrapper.opener}
|
|
37673
37693
|
${content}
|
|
37674
|
-
${
|
|
37694
|
+
${wrapper.closer}`;
|
|
37675
37695
|
}
|
|
37676
|
-
function isWrapperIntact(model) {
|
|
37696
|
+
function isWrapperIntact(model, wrapper = DEFAULT_WRAPPER) {
|
|
37677
37697
|
const lineCount = model.getLineCount();
|
|
37678
|
-
return lineCount >= 3 && model.getLineContent(1) ===
|
|
37698
|
+
return lineCount >= 3 && model.getLineContent(1) === wrapper.opener && model.getLineContent(lineCount) === wrapper.closer;
|
|
37679
37699
|
}
|
|
37680
37700
|
function paragraphRange(model) {
|
|
37681
37701
|
const lastLine = model.getLineCount() - 1;
|
|
@@ -37686,9 +37706,9 @@ function contentRange(model, mode) {
|
|
|
37686
37706
|
const lastLine = model.getLineCount();
|
|
37687
37707
|
return { startLineNumber: 1, startColumn: 1, endLineNumber: lastLine, endColumn: model.getLineMaxColumn(lastLine) };
|
|
37688
37708
|
}
|
|
37689
|
-
function readContent(model, mode) {
|
|
37709
|
+
function readContent(model, mode, wrapper = DEFAULT_WRAPPER) {
|
|
37690
37710
|
if (mode === "file") return model.getValue();
|
|
37691
|
-
return isWrapperIntact(model) ? model.getValueInRange(paragraphRange(model)) : null;
|
|
37711
|
+
return isWrapperIntact(model, wrapper) ? model.getValueInRange(paragraphRange(model)) : null;
|
|
37692
37712
|
}
|
|
37693
37713
|
function contentLineOffset(mode, hideWrapper) {
|
|
37694
37714
|
return mode === "paragraph" && hideWrapper ? 1 : 0;
|
|
@@ -37726,17 +37746,17 @@ function findMatches(value, query, options) {
|
|
|
37726
37746
|
if (query === "") return [];
|
|
37727
37747
|
return [...value.matchAll(toRegExp(query, options))].filter((match) => match[0] !== "");
|
|
37728
37748
|
}
|
|
37729
|
-
function createContentApi(editor, mode, lineOffset, initialValue) {
|
|
37749
|
+
function createContentApi(editor, mode, lineOffset, initialValue, wrapper = DEFAULT_WRAPPER) {
|
|
37730
37750
|
let lastWhole = initialValue;
|
|
37731
37751
|
const read = () => {
|
|
37732
37752
|
const model = editor.getModel();
|
|
37733
|
-
const content = model ? readContent(model, mode) : null;
|
|
37753
|
+
const content = model ? readContent(model, mode, wrapper) : null;
|
|
37734
37754
|
if (content !== null) lastWhole = content;
|
|
37735
37755
|
return lastWhole;
|
|
37736
37756
|
};
|
|
37737
37757
|
const bounds = () => {
|
|
37738
37758
|
const model = editor.getModel();
|
|
37739
|
-
const content = model && readContent(model, mode);
|
|
37759
|
+
const content = model && readContent(model, mode, wrapper);
|
|
37740
37760
|
if (!model || content === null) return void 0;
|
|
37741
37761
|
const { startLineNumber, startColumn } = contentRange(model, mode);
|
|
37742
37762
|
return { model, start: model.getOffsetAt({ lineNumber: startLineNumber, column: startColumn }), length: content.length };
|
|
@@ -38236,13 +38256,16 @@ function injectEditorStyles(doc = document) {
|
|
|
38236
38256
|
// src/editor/wrapperGuard.ts
|
|
38237
38257
|
var HIDDEN_AREAS_SOURCE = "weml-editor.wrapper";
|
|
38238
38258
|
var WRAPPER_LINE_CLASS = "weml-editor-wrapper-line";
|
|
38239
|
-
var
|
|
38240
|
-
|
|
38259
|
+
var wrapperMessage = (wrapper) => ({
|
|
38260
|
+
value: `The \`${wrapper.opener}\` wrapper is not part of the content and cannot be edited.`
|
|
38261
|
+
});
|
|
38262
|
+
function guardWrapper(m2, editor, hideWrapper, hostOptions, wrapper = DEFAULT_WRAPPER) {
|
|
38241
38263
|
const model = editor.getModel();
|
|
38242
38264
|
if (!model) return { dispose() {
|
|
38243
38265
|
}, setHostOptions() {
|
|
38244
38266
|
} };
|
|
38245
38267
|
let host = hostOptions;
|
|
38268
|
+
const message = wrapperMessage(wrapper);
|
|
38246
38269
|
const disposables = [];
|
|
38247
38270
|
const touchesWrapper = (selection) => selection.startLineNumber < 2 || selection.endLineNumber > model.getLineCount() - 1;
|
|
38248
38271
|
const clampSelections = () => {
|
|
@@ -38263,7 +38286,7 @@ function guardWrapper(m2, editor, hideWrapper, hostOptions) {
|
|
|
38263
38286
|
const syncReadOnly = () => {
|
|
38264
38287
|
const locked = (editor.getSelections() ?? []).some(touchesWrapper);
|
|
38265
38288
|
const readOnly = host.readOnly === true || locked;
|
|
38266
|
-
const readOnlyMessage = locked && host.readOnly !== true ?
|
|
38289
|
+
const readOnlyMessage = locked && host.readOnly !== true ? message : host.readOnlyMessage;
|
|
38267
38290
|
if (applied?.readOnly === readOnly && applied.readOnlyMessage === readOnlyMessage) return;
|
|
38268
38291
|
applied = { readOnly, readOnlyMessage };
|
|
38269
38292
|
editor.updateOptions({ readOnly, readOnlyMessage });
|
|
@@ -38321,7 +38344,7 @@ function guardWrapper(m2, editor, hideWrapper, hostOptions) {
|
|
|
38321
38344
|
let repairPending = false;
|
|
38322
38345
|
disposables.push(
|
|
38323
38346
|
editor.onDidChangeModelContent(() => {
|
|
38324
|
-
if (isWrapperIntact(model)) {
|
|
38347
|
+
if (isWrapperIntact(model, wrapper)) {
|
|
38325
38348
|
showWrapper();
|
|
38326
38349
|
return;
|
|
38327
38350
|
}
|
|
@@ -38329,7 +38352,7 @@ function guardWrapper(m2, editor, hideWrapper, hostOptions) {
|
|
|
38329
38352
|
repairPending = true;
|
|
38330
38353
|
queueMicrotask(() => {
|
|
38331
38354
|
repairPending = false;
|
|
38332
|
-
if (!model.isDisposed() && !isWrapperIntact(model)) model.undo();
|
|
38355
|
+
if (!model.isDisposed() && !isWrapperIntact(model, wrapper)) model.undo();
|
|
38333
38356
|
});
|
|
38334
38357
|
})
|
|
38335
38358
|
);
|
|
@@ -38440,8 +38463,9 @@ var actionButton = (icon2, title, run) => {
|
|
|
38440
38463
|
return node;
|
|
38441
38464
|
};
|
|
38442
38465
|
function createWemlEditor(m2, container, options = {}) {
|
|
38443
|
-
if (options.injectStyles !== false) injectEditorStyles();
|
|
38444
38466
|
const mode = options.mode ?? "file";
|
|
38467
|
+
const wrapper = options.mode === "paragraph" ? contentWrapper(options.fixedWrappers) : DEFAULT_WRAPPER;
|
|
38468
|
+
if (options.injectStyles !== false) injectEditorStyles();
|
|
38445
38469
|
const parts = {
|
|
38446
38470
|
tools: options.parts?.tools ?? true,
|
|
38447
38471
|
activityBar: options.parts?.activityBar ?? true,
|
|
@@ -38479,20 +38503,20 @@ function createWemlEditor(m2, container, options = {}) {
|
|
|
38479
38503
|
const language = acquireLanguage(m2, options);
|
|
38480
38504
|
const { analysis, formatter } = language.registration;
|
|
38481
38505
|
const given = options.value ?? "";
|
|
38482
|
-
const initialValue = given.trim() || options.withDefaultContent === false ? given : mode === "file" ? NEW_WEML_DOCUMENT : NEW_WEML_PARAGRAPH;
|
|
38506
|
+
const initialValue = given.trim() || options.withDefaultContent === false || wrapper !== DEFAULT_WRAPPER ? given : mode === "file" ? NEW_WEML_DOCUMENT : NEW_WEML_PARAGRAPH;
|
|
38483
38507
|
const editorId = ++editorCount;
|
|
38484
|
-
const model = m2.editor.createModel(initialModelText(mode, initialValue), "weml", m2.Uri.parse(`inmemory://weml-editor/${editorId}/${encodeURIComponent(fileName)}`));
|
|
38508
|
+
const model = m2.editor.createModel(initialModelText(mode, initialValue, wrapper), "weml", m2.Uri.parse(`inmemory://weml-editor/${editorId}/${encodeURIComponent(fileName)}`));
|
|
38485
38509
|
const root = element("div", { className: `${EDITOR_CLASS} monaco-component` });
|
|
38486
38510
|
const editorHost = element("div", { className: "wb-editor wb-part" });
|
|
38487
38511
|
const baseOptions = { ...EDITOR_OPTIONS, ...options.editorOptions };
|
|
38488
38512
|
const editor = m2.editor.create(editorHost, { ...baseOptions, ...layout.editorOptions, model });
|
|
38489
|
-
const wrapperGuard = mode === "paragraph" ? guardWrapper(m2, editor, options.hideWrapper ?? false, { ...baseOptions, ...layout.editorOptions }) : void 0;
|
|
38513
|
+
const wrapperGuard = mode === "paragraph" ? guardWrapper(m2, editor, options.hideWrapper ?? false, { ...baseOptions, ...layout.editorOptions }, wrapper) : void 0;
|
|
38490
38514
|
if (wrapperGuard) disposables.push(wrapperGuard);
|
|
38491
38515
|
const applyEditorOptions = (next) => {
|
|
38492
38516
|
editor.updateOptions(next);
|
|
38493
38517
|
wrapperGuard?.setHostOptions(next);
|
|
38494
38518
|
};
|
|
38495
|
-
const content = createContentApi(editor, mode, lineOffset, initialValue);
|
|
38519
|
+
const content = createContentApi(editor, mode, lineOffset, initialValue, wrapper);
|
|
38496
38520
|
const valueListeners = /* @__PURE__ */ new Set();
|
|
38497
38521
|
const problemListeners = /* @__PURE__ */ new Set();
|
|
38498
38522
|
const cursorListeners = /* @__PURE__ */ new Set();
|
|
@@ -38501,7 +38525,7 @@ function createWemlEditor(m2, container, options = {}) {
|
|
|
38501
38525
|
let problems = [];
|
|
38502
38526
|
disposables.push(
|
|
38503
38527
|
model.onDidChangeContent(() => {
|
|
38504
|
-
const value = readContent(model, mode);
|
|
38528
|
+
const value = readContent(model, mode, wrapper);
|
|
38505
38529
|
if (value === null || value === lastValue) return;
|
|
38506
38530
|
lastValue = value;
|
|
38507
38531
|
for (const listener of valueListeners) listener(value);
|
|
@@ -38800,11 +38824,12 @@ function createWemlEditor(m2, container, options = {}) {
|
|
|
38800
38824
|
language: previewLanguage,
|
|
38801
38825
|
scrollSync: previewScrollSync,
|
|
38802
38826
|
container: host,
|
|
38803
|
-
// A paragraph without its
|
|
38804
|
-
// the blocks to the model's lines)
|
|
38827
|
+
// A paragraph without its `<div>`, from the model's second line (the scroll sync matches
|
|
38828
|
+
// the blocks to the model's lines), inside its `fixedWrappers` so it is styled as their
|
|
38829
|
+
// content; a broken wrapper: the whole text, as typed.
|
|
38805
38830
|
transform: mode === "paragraph" ? (text) => {
|
|
38806
|
-
const paragraph = readContent(model, mode);
|
|
38807
|
-
return paragraph === null ? text : { markup: paragraph, firstLine: 1 };
|
|
38831
|
+
const paragraph = readContent(model, mode, wrapper);
|
|
38832
|
+
return paragraph === null ? text : { markup: wrapper.innerOpener + paragraph + wrapper.innerCloser, firstLine: 1 };
|
|
38808
38833
|
} : void 0
|
|
38809
38834
|
});
|
|
38810
38835
|
return { element: host, dispose: () => preview.dispose(), preview };
|
|
@@ -39421,7 +39446,7 @@ function createWemlEditor(m2, container, options = {}) {
|
|
|
39421
39446
|
reset(value) {
|
|
39422
39447
|
baseline = value;
|
|
39423
39448
|
lastValue = value;
|
|
39424
|
-
model.setValue(initialModelText(mode, value));
|
|
39449
|
+
model.setValue(initialModelText(mode, value, wrapper));
|
|
39425
39450
|
renderTab();
|
|
39426
39451
|
renderSave();
|
|
39427
39452
|
},
|