weml-monaco 0.5.2 → 0.7.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 +56 -3
- package/README.md +161 -47
- package/dist/index.cjs +25587 -24926
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +204 -11
- package/dist/index.d.ts +204 -11
- package/dist/index.global.js +142 -132
- package/dist/index.mjs +25591 -24930
- package/dist/index.mjs.map +1 -1
- package/dist/panel.cjs +1314 -0
- package/dist/panel.cjs.map +1 -0
- package/dist/panel.css +261 -0
- package/dist/panel.d.mts +106 -0
- package/dist/panel.d.ts +106 -0
- package/dist/panel.mjs +1291 -0
- package/dist/panel.mjs.map +1 -0
- package/dist/preview.cjs +324 -0
- package/dist/preview.cjs.map +1 -0
- package/dist/preview.d.mts +63 -0
- package/dist/preview.d.ts +63 -0
- package/dist/preview.mjs +301 -0
- package/dist/preview.mjs.map +1 -0
- package/dist/weml.worker.d.mts +2 -0
- package/dist/weml.worker.mjs +25164 -0
- package/dist/weml.worker.mjs.map +1 -0
- package/dist/worker.cjs +8596 -0
- package/dist/worker.cjs.map +1 -0
- package/dist/worker.d.mts +21 -0
- package/dist/worker.d.ts +21 -0
- package/dist/worker.mjs +8597 -0
- package/dist/worker.mjs.map +1 -0
- package/package.json +57 -12
package/CHANGELOG.md
CHANGED
|
@@ -4,9 +4,62 @@ All notable changes to the **Monaco** contribution (`weml-monaco`) are documente
|
|
|
4
4
|
here. The format is based on [Keep a Changelog](https://keepachangelog.com/), and
|
|
5
5
|
this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
-
This package
|
|
8
|
-
track the port catching up with
|
|
9
|
-
|
|
7
|
+
This package shares the language logic of the VS Code extension (`weml-support`): older
|
|
8
|
+
entries below track the port catching up with its provider changes; since the shared language
|
|
9
|
+
features, both take the same code.
|
|
10
|
+
|
|
11
|
+
## [0.7.0] — 2026-10-08
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- The WEML panel, `weml-monaco/panel` (+ `weml-monaco/panel.css`): the Tools and Search
|
|
15
|
+
Suggestions of the WEML Tools VS Code extension as a framework-free DOM component
|
|
16
|
+
(`createWemlPanel`), the same look and behaviour, theme from Monaco's `--vscode-*` variables.
|
|
17
|
+
- `createMonacoPanelHost(editor, options)`: the panel's host for a Monaco editor — tools on the
|
|
18
|
+
editor, EGW search with the host application's access token, layout in `localStorage`.
|
|
19
|
+
- `createEgwWebAuth(options)`: the EGW sign-in for a web page without one of its own — OIDC
|
|
20
|
+
code flow + PKCE in a popup, tokens in `sessionStorage` (or `localStorage`), refreshed; spread
|
|
21
|
+
into `createMonacoPanelHost`'s `search`. The panel shows why a sign-in failed under its button
|
|
22
|
+
(`signIn` may return a promise).
|
|
23
|
+
- The WEML preview, `weml-monaco/preview` (`createWemlPreview`): a live preview of the editor's
|
|
24
|
+
document rendered with weml-stylesheet — now an optional peer dependency (`^4.0.0`), needed
|
|
25
|
+
only by this entry.
|
|
26
|
+
- The preview's scroll sync with the editor, both ways (`scrollSync` option, `setScrollSync`),
|
|
27
|
+
sharing its line mapping with the VS Code preview; a `transform` may return
|
|
28
|
+
`{ markup, firstLine }`; double-click on a block goes to its line (`doubleClickToEditor`).
|
|
29
|
+
It looks like the open file next to it: the editor's background, text and scrollbars (10px,
|
|
30
|
+
the theme's slider) through `monaco-component` on the pane (`monacoTheme: false` to leave it
|
|
31
|
+
off), not the page's background and the browser's scrollbars.
|
|
32
|
+
- `TOOL_ITEMS` / `TOOL_GROUPS` / `filterToolItems` / `toolItemsFor(mode)`: everything the Tools
|
|
33
|
+
offer as one list, grouped Containers / Blocks / Inlines, for a host that builds its own picker.
|
|
34
|
+
- Completion in `<body>` offers a new section with any container — *div + w-para*,
|
|
35
|
+
*div + w-toc* and *div + w-page* join *div + w-heading* and *div + w-para-group*.
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
- The WEML panel's Tools: the wrap `<select>` and the Insert buttons are one searchable tag picker
|
|
39
|
+
drawn as VS Code's quick pick (Enter or a click chooses, the button in the field wraps or
|
|
40
|
+
inserts; the choice is remembered). It offers every tag of the docs' containers, blocks and
|
|
41
|
+
inlines — new: `w-para-group`, `w-toc`, a new `div` section (with `w-para`, `w-heading`,
|
|
42
|
+
`w-para-group`, `w-toc`, or a `w-page` page break), `w-text-block`, `hr`, `w-page`, `br`,
|
|
43
|
+
the `a id` anchor, and `w-sent` to wrap in. Inputs and buttons are 26px, as in VS Code,
|
|
44
|
+
whatever the host page's line height; the panes' borders show under Monaco themes too.
|
|
45
|
+
- `createWemlPanel`'s `renumber` option is `mode: 'file' | 'paragraph'`: a paragraph has no
|
|
46
|
+
Renumber and no `div` sections to insert (`toolItemsFor(mode)`).
|
|
47
|
+
- Diagnostics, hover, completion and the outline come from the language logic shared with the
|
|
48
|
+
VS Code extension instead of a copy of it — the same problems, tooltips and suggestions in
|
|
49
|
+
both editors; no change in behaviour or API.
|
|
50
|
+
|
|
51
|
+
### Deprecated
|
|
52
|
+
- `createWemlToolbar`: use the WEML panel (and `registerWemlEditorActions` for the hotkeys alone).
|
|
53
|
+
|
|
54
|
+
## [0.6.0] — 2026-10-06
|
|
55
|
+
|
|
56
|
+
### Added
|
|
57
|
+
- `registerWemlWorker` (`weml-monaco/worker`) and the worker module
|
|
58
|
+
(`weml-monaco/weml.worker`): the same language with completion, hover, outline
|
|
59
|
+
and diagnostics computed in a Web Worker, so typing in a large document no
|
|
60
|
+
longer waits for a full parse per keystroke. The worker mirrors models from
|
|
61
|
+
their edits and answers only the current version of a model. `registerWeml`
|
|
62
|
+
and the global build are unchanged.
|
|
10
63
|
|
|
11
64
|
## [0.5.2] — 2026-09-26
|
|
12
65
|
|
package/README.md
CHANGED
|
@@ -23,7 +23,8 @@ extension. It provides, for models with language id `weml`:
|
|
|
23
23
|
HTML ↔ canonical format cycle.
|
|
24
24
|
|
|
25
25
|
This package does **not** modify the original VS Code extension — it is a
|
|
26
|
-
standalone re-implementation living entirely
|
|
26
|
+
standalone re-implementation living entirely in this package (`packages/weml-monaco` of the
|
|
27
|
+
monorepo).
|
|
27
28
|
|
|
28
29
|
## Install & build
|
|
29
30
|
|
|
@@ -31,9 +32,9 @@ standalone re-implementation living entirely under `monaco-editor/`.
|
|
|
31
32
|
npm install weml-monaco
|
|
32
33
|
```
|
|
33
34
|
|
|
34
|
-
The package has no runtime dependencies (`node-html-parser
|
|
35
|
-
`vscode-html-languageservice` are bundled in) and
|
|
36
|
-
dependency you already have.
|
|
35
|
+
The package has no runtime dependencies (`node-html-parser`,
|
|
36
|
+
`vscode-html-languageservice` and the shared normalizer are bundled in) and
|
|
37
|
+
`monaco-editor` is a peer dependency you already have.
|
|
37
38
|
|
|
38
39
|
| entry point | file | for |
|
|
39
40
|
| ---------------------- | ----------------------- | -------------------------------------- |
|
|
@@ -44,8 +45,8 @@ dependency you already have.
|
|
|
44
45
|
Working on the package itself:
|
|
45
46
|
|
|
46
47
|
```bash
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
npm ci # at the repository root (npm workspaces)
|
|
49
|
+
cd packages/weml-monaco
|
|
49
50
|
npm run build # tsup → dist/ (ESM + CJS + IIFE + declarations)
|
|
50
51
|
npm run typecheck # tsc --noEmit
|
|
51
52
|
npm test # build + Node smoke test
|
|
@@ -99,31 +100,146 @@ formatter directly, call `formatWemlText(source)`.
|
|
|
99
100
|
Structural validation failures are also available through
|
|
100
101
|
`isWemlStructureValidationFailure(error)` and include a code, line and column.
|
|
101
102
|
|
|
102
|
-
##
|
|
103
|
+
## Analysis in a Web Worker
|
|
103
104
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
`registerWeml` runs completion, hover, outline and diagnostics on the main
|
|
106
|
+
thread, and each of them parses the whole document for every new model version.
|
|
107
|
+
On a large file — a whole book, a megabyte and more — that is tens of
|
|
108
|
+
milliseconds per keystroke on the thread that handles the keystrokes, and typing
|
|
109
|
+
falls behind. `registerWemlWorker` registers the same language with that work in
|
|
110
|
+
a Web Worker:
|
|
107
111
|
|
|
108
112
|
```ts
|
|
109
|
-
import
|
|
113
|
+
import * as monaco from 'monaco-editor/editor/editor.api.js';
|
|
114
|
+
import { registerWemlWorker } from 'weml-monaco/worker';
|
|
115
|
+
import WemlWorker from 'weml-monaco/weml.worker?worker'; // Vite
|
|
116
|
+
|
|
117
|
+
const disposable = registerWemlWorker(monaco, { worker: () => new WemlWorker() });
|
|
118
|
+
// Without Vite's `?worker`:
|
|
119
|
+
// registerWemlWorker(monaco, {
|
|
120
|
+
// worker: () => new Worker(new URL('weml-monaco/weml.worker', import.meta.url), { type: 'module' }),
|
|
121
|
+
// });
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
It is an addition, not a replacement: `registerWeml` is unchanged, and an
|
|
125
|
+
application chooses one of the two (registering both doubles every provider).
|
|
126
|
+
The worker runs the very providers `registerWeml` registers, so suggestions,
|
|
127
|
+
hovers, outline and markers are the same; only where they are computed changes.
|
|
128
|
+
|
|
129
|
+
- The worker mirrors each `weml` model's text and keeps it current from the
|
|
130
|
+
edits Monaco reports — the main thread never sends the whole text again.
|
|
131
|
+
- A request is answered only for the model's current version: one overtaken by
|
|
132
|
+
further typing is answered empty without parsing, so holding a key down does
|
|
133
|
+
not build up a queue of parses.
|
|
134
|
+
- Markers computed for a version the model has since left are dropped.
|
|
135
|
+
- The tokenizer, language configuration and formatting stay on the main thread,
|
|
136
|
+
as with `registerWeml` — they are cheap or run on demand.
|
|
137
|
+
- `weml-monaco/weml.worker` imports nothing of Monaco at runtime; it needs a
|
|
138
|
+
bundler (or a module worker) to load, and is ESM only.
|
|
139
|
+
|
|
140
|
+
`registerWemlWorker(monaco, options)` takes the options of `registerWeml` plus
|
|
141
|
+
`worker`, a function that starts the worker (called once; the worker is
|
|
142
|
+
terminated on `dispose()`). The global `<script>` build (`index.global.js`) has
|
|
143
|
+
no worker variant.
|
|
144
|
+
|
|
145
|
+
## The WEML panel
|
|
146
|
+
|
|
147
|
+
The side panel of the WEML Tools VS Code extension — **Tools** (wrap the
|
|
148
|
+
selection, insert snippets, renumber `<div>` ids) and **Search Suggestions**
|
|
149
|
+
(EGW search with ParaId / BookCode and `egw://` links to copy or insert) — as a
|
|
150
|
+
framework-free DOM component: the same panel, look and behaviour as in VS Code
|
|
151
|
+
(the extension's webview bundles this very code). It follows Monaco's theme
|
|
152
|
+
through the `--vscode-*` variables Monaco defines on `.monaco-component`.
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
import { createMonacoPanelHost, registerWeml, registerWemlEditorActions } from 'weml-monaco';
|
|
156
|
+
import { createWemlPanel } from 'weml-monaco/panel';
|
|
110
157
|
|
|
111
158
|
registerWeml(monaco);
|
|
112
159
|
const editor = monaco.editor.create(container, { model });
|
|
160
|
+
registerWemlEditorActions(monaco, editor); // the hotkeys (below)
|
|
161
|
+
|
|
162
|
+
const panel = createWemlPanel(
|
|
163
|
+
createMonacoPanelHost(editor, {
|
|
164
|
+
search: {
|
|
165
|
+
apiUrl: 'https://…', // the EGW API
|
|
166
|
+
getAccessToken: () => auth.token, // your app's sign-in — the only part you supply
|
|
167
|
+
signIn: () => auth.login(),
|
|
168
|
+
onDidChangeSession: (listener) => auth.subscribe(listener),
|
|
169
|
+
},
|
|
170
|
+
}),
|
|
171
|
+
{ container: document.getElementById('weml-panel') },
|
|
172
|
+
);
|
|
173
|
+
// panel.dispose() removes it.
|
|
174
|
+
```
|
|
113
175
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
176
|
+
- `createWemlPanel` (`weml-monaco/panel`, light: UI only) takes any
|
|
177
|
+
`WemlPanelHost` — tools, an optional search backend, optional storage.
|
|
178
|
+
`createMonacoPanelHost` (main entry) is that host for a Monaco editor: the
|
|
179
|
+
tools edit the editor, the search sends your token as a bearer (nothing is
|
|
180
|
+
sent while signed out), and the layout is remembered in `localStorage`
|
|
181
|
+
(`storage: false` to opt out). Without `search` the pane says the search is
|
|
182
|
+
not configured; `search: false` leaves the pane out.
|
|
183
|
+
- An app without a sign-in of its own takes `createEgwWebAuth` — the EGW
|
|
184
|
+
sign-in (OIDC code flow + PKCE) in a popup, tokens in `sessionStorage`,
|
|
185
|
+
refreshed before they expire:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
import { createEgwWebAuth } from 'weml-monaco';
|
|
189
|
+
|
|
190
|
+
const auth = createEgwWebAuth({ authority, clientId, scopes: 'openid offline_access search' });
|
|
191
|
+
createMonacoPanelHost(editor, { search: { apiUrl, ...auth } });
|
|
192
|
+
auth.signOut(); // from the app's own account menu
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
The popup comes back to `<origin>/auth-callback` (`redirectUri` to change it),
|
|
196
|
+
which the client must allow; a blank page there is enough. `storage: 'local'`
|
|
197
|
+
shares the session between tabs. A failed sign-in (popup blocked or closed,
|
|
198
|
+
refused) shows its message under the Sign in button.
|
|
199
|
+
- The stylesheet is injected into the page by default; it is also published as
|
|
200
|
+
`weml-monaco/panel.css` (`injectStyles: false` to link it yourself).
|
|
201
|
+
- `mode: 'paragraph'` for an editor of one paragraph (the content of one `<div>`): no Renumber
|
|
202
|
+
div ids, and no `div + …` sections among the tags to insert. Default: `'file'`.
|
|
203
|
+
|
|
204
|
+
## The WEML preview
|
|
205
|
+
|
|
206
|
+
A live preview of the editor's document, rendered with
|
|
207
|
+
[`weml-stylesheet`](../weml-stylesheet) — an **optional peer dependency**:
|
|
208
|
+
install it (and load its CSS) to use this entry; nothing else needs it.
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
import { createWemlPreview } from 'weml-monaco/preview';
|
|
212
|
+
import 'weml-stylesheet/css/weml.scoped.css';
|
|
120
213
|
|
|
121
|
-
|
|
122
|
-
|
|
214
|
+
const preview = createWemlPreview(editor, {
|
|
215
|
+
container: document.getElementById('weml-preview'),
|
|
216
|
+
theme: 'dark', // data-weml-theme; omit to follow prefers-color-scheme
|
|
217
|
+
});
|
|
218
|
+
preview.setTheme('light');
|
|
219
|
+
preview.dispose();
|
|
123
220
|
```
|
|
124
221
|
|
|
125
|
-
|
|
126
|
-
|
|
222
|
+
It renders at once, then after a pause in typing (`delay`, 300 ms), and at once
|
|
223
|
+
again when the editor gets another model; an unchanged text is not rendered
|
|
224
|
+
twice (a whole book is not cheap). A whole document keeps its `dir` / `lang`
|
|
225
|
+
(right-to-left books render right to left). Blank text shows `emptyText`;
|
|
226
|
+
`transform(text)` picks what to render (e.g. without a wrapper your editor adds).
|
|
227
|
+
|
|
228
|
+
**Scroll sync**, both ways, as in the VS Code preview (and with the same code,
|
|
229
|
+
weml-language-core's): the rendered blocks carry their source line, so the
|
|
230
|
+
preview follows the editor's top line and the editor follows the preview. Turn
|
|
231
|
+
it off, or one way only, with `scrollSync` — `true` (default), `false`, or
|
|
232
|
+
`{ previewWithEditor, editorWithPreview }` — and later with
|
|
233
|
+
`preview.setScrollSync(...)`; there is no button of its own, the host decides.
|
|
234
|
+
A `transform` that drops lines from the top returns `{ markup, firstLine }` (the
|
|
235
|
+
model line its markup starts on) so the lines still match. A double-click on a
|
|
236
|
+
block puts the editor's cursor on its line (`doubleClickToEditor: false` to
|
|
237
|
+
turn it off).
|
|
238
|
+
|
|
239
|
+
## Editor commands
|
|
240
|
+
|
|
241
|
+
`registerWemlEditorActions(monaco, editor, options)` registers the editor
|
|
242
|
+
actions (keybindings and the command palette):
|
|
127
243
|
|
|
128
244
|
| action / command | keybinding | effect |
|
|
129
245
|
| ------------------------- | ------------- | -------------------------------------------------- |
|
|
@@ -133,38 +249,36 @@ panel too, via keybindings and the command palette):
|
|
|
133
249
|
| `weml.cycleFormat` | `Alt+Shift+F` | cycle document: HTML ↔ canonical WEML formatting |
|
|
134
250
|
| `weml.renumberDivIds` | — | renumber every `<div>` id to a sequential 1…n |
|
|
135
251
|
|
|
136
|
-
|
|
137
|
-
`
|
|
138
|
-
|
|
139
|
-
|
|
252
|
+
The underlying operations (`wrapSelection`, `insertSnippetById`,
|
|
253
|
+
`renumberDivIds`, `cycleFormat`) and the pure helpers (`planDivIdRenumber`,
|
|
254
|
+
`buildWrapSnippet`) are exported too. Snippet insertion uses Monaco's
|
|
255
|
+
`snippetController2`, so `$1`/`$2` tab stops behave like `editor.insertSnippet`
|
|
256
|
+
in VS Code.
|
|
140
257
|
|
|
141
|
-
|
|
142
|
-
behave like `editor.insertSnippet` in VS Code.
|
|
258
|
+
### `createWemlToolbar` (deprecated)
|
|
143
259
|
|
|
144
|
-
|
|
260
|
+
The earlier flat DOM toolbar (`createWemlToolbar(monaco, editor, { container,
|
|
261
|
+
onStatus })`) still works and still registers the editor actions, but it is
|
|
262
|
+
deprecated in favour of the WEML panel and gets no new features.
|
|
145
263
|
|
|
146
|
-
|
|
147
|
-
npm run demo # builds dist/ and copies the IIFE bundle → demo/weml-monaco.js
|
|
148
|
-
# then open demo/index.html in a browser
|
|
149
|
-
```
|
|
264
|
+
## Trying it out
|
|
150
265
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
266
|
+
The monorepo's dev app, [`weml-storybook`](../weml-storybook)
|
|
267
|
+
(`npm start -w weml-storybook` from the repository root), runs this package from
|
|
268
|
+
its sources in a full editor: completions (Ctrl+Space), hover, "Format
|
|
269
|
+
Document", the outline, diagnostics and the toolbar.
|
|
155
270
|
|
|
156
271
|
## How it relates to the VS Code extension
|
|
157
272
|
|
|
158
|
-
The
|
|
273
|
+
The language logic is shared with the VS Code extensions, not copied — two workspace packages,
|
|
274
|
+
bundled into every build (neither is a runtime dependency):
|
|
159
275
|
|
|
160
|
-
|
|
|
276
|
+
| shared (workspace package) | adapted for Monaco |
|
|
161
277
|
| ------------------------------------ | ---------------------------------------- |
|
|
162
|
-
| `
|
|
163
|
-
| `
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
runs in the browser
|
|
169
|
-
APIs for `monaco.languages.*` and converting between Monaco's 1-based
|
|
170
|
-
line/column positions and the language service's 0-based LSP positions.
|
|
278
|
+
| `weml-language-core`: schema, structure rules, snippets, entities, markdown text, the language features (diagnostics, hover, completion, outline), tool model, wrap / renumber | `schema/docs.ts` (plain `IMarkdownString`) |
|
|
279
|
+
| `weml-normalizer`: the canonical normalizer | `wemlDocument.ts` (parse cache, offsets ↔ Monaco positions), `providers/*` (the features' data → Monaco provider types), `index.ts` (registration + diagnostics) |
|
|
280
|
+
|
|
281
|
+
The diagnostics, tooltips, suggestions and outline are computed once, in the shared package, as
|
|
282
|
+
plain data with offsets — the VS Code extension and this package only map them to their editor's
|
|
283
|
+
types, so both editors report the same problems and offer the same items. Parsing uses
|
|
284
|
+
`vscode-html-languageservice` (editor-agnostic, runs in the browser and in the worker).
|