weml-monaco 0.6.0 → 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 CHANGED
@@ -4,9 +4,52 @@ 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 mirrors the language logic of the VS Code extension; entries below
8
- track the port catching up with the VS Code provider changes (see the root
9
- `CHANGELOG.md` for the extension).
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).
10
53
 
11
54
  ## [0.6.0] — 2026-10-06
12
55
 
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 under `monaco-editor/`.
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` and
35
- `vscode-html-languageservice` are bundled in) and `monaco-editor` is a peer
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
- cd monaco-editor
48
- npm install
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
@@ -141,31 +142,104 @@ hovers, outline and markers are the same; only where they are computed changes.
141
142
  terminated on `dispose()`). The global `<script>` build (`index.global.js`) has
142
143
  no worker variant.
143
144
 
144
- ## Toolbar & editor commands
145
+ ## The WEML panel
145
146
 
146
- Monaco has no webview panels, so the VS Code toolbar is ported as a **DOM top
147
- panel** you mount yourself, plus per-editor actions/keybindings. Button
148
- behaviour is identical to the VS Code toolbar.
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`.
149
153
 
150
154
  ```ts
151
- import { registerWeml, createWemlToolbar } from 'weml-monaco';
155
+ import { createMonacoPanelHost, registerWeml, registerWemlEditorActions } from 'weml-monaco';
156
+ import { createWemlPanel } from 'weml-monaco/panel';
152
157
 
153
158
  registerWeml(monaco);
154
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
+ ```
155
175
 
156
- const toolbar = createWemlToolbar(monaco, editor, {
157
- container: document.getElementById('weml-toolbar'), // where to mount the panel
158
- onStatus: (msg) => console.log(msg), // optional status sink
159
- // includeSearchRow: true, // stub search row (as in VS Code); default true
160
- // onSearch: (query, lang) => { ... },
161
- });
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';
162
213
 
163
- // toolbar.element → the toolbar DOM node
164
- // toolbar.dispose() → removes the panel and its actions
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();
165
220
  ```
166
221
 
167
- `createWemlToolbar` also registers the editor actions (so they work without the
168
- panel too, via keybindings and the command palette):
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):
169
243
 
170
244
  | action / command | keybinding | effect |
171
245
  | ------------------------- | ------------- | -------------------------------------------------- |
@@ -175,38 +249,36 @@ panel too, via keybindings and the command palette):
175
249
  | `weml.cycleFormat` | `Alt+Shift+F` | cycle document: HTML ↔ canonical WEML formatting |
176
250
  | `weml.renumberDivIds` | — | renumber every `<div>` id to a sequential 1…n |
177
251
 
178
- To register the actions without a toolbar, call
179
- `registerWemlEditorActions(monaco, editor, options)`. The underlying operations
180
- (`wrapSelection`, `insertSnippetById`, `renumberDivIds`, `cycleFormat`) and the
181
- pure helpers (`planDivIdRenumber`, `buildWrapSnippet`) are exported too.
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.
182
257
 
183
- Snippet insertion uses Monaco's `snippetController2`, so `$1`/`$2` tab stops
184
- behave like `editor.insertSnippet` in VS Code.
258
+ ### `createWemlToolbar` (deprecated)
185
259
 
186
- ## Demo
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.
187
263
 
188
- ```bash
189
- npm run demo # builds dist/ and copies the IIFE bundle → demo/weml-monaco.js
190
- # then open demo/index.html in a browser
191
- ```
264
+ ## Trying it out
192
265
 
193
- `demo/index.html` loads Monaco from a CDN (AMD loader) and the WEML bundle as a
194
- global, registers the language and opens a sample document. Try Ctrl+Space for
195
- completions, hover a tag, "Format Document", Ctrl+Shift+O for the outline, or
196
- introduce an error to see diagnostics.
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.
197
270
 
198
271
  ## How it relates to the VS Code extension
199
272
 
200
- The schema and rules are reused verbatim from the extension:
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):
201
275
 
202
- | reused as-is | adapted for Monaco |
276
+ | shared (workspace package) | adapted for Monaco |
203
277
  | ------------------------------------ | ---------------------------------------- |
204
- | `generated/schema.ts` | `schema/docs.ts` (plain `IMarkdownString`) |
205
- | `schema/structureRules.ts` | `wemlDocument.ts` (model ↔ LSP positions) |
206
- | `schema/snippets.ts` | `providers/*` (Monaco provider APIs) |
207
- | `wemlContentNormalizeV2.js` | `index.ts` (registration + diagnostics) |
208
-
209
- Parsing still uses `vscode-html-languageservice` (it is editor-agnostic and
210
- runs in the browser); the only real work was swapping the `vscode.*` provider
211
- APIs for `monaco.languages.*` and converting between Monaco's 1-based
212
- 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).