weml-monaco 0.3.2

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 ADDED
@@ -0,0 +1,97 @@
1
+ # Changelog — WEML Monaco
2
+
3
+ All notable changes to the **Monaco** contribution (`weml-monaco`) are documented
4
+ here. The format is based on [Keep a Changelog](https://keepachangelog.com/), and
5
+ this project adheres to [Semantic Versioning](https://semver.org/).
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).
10
+
11
+ ## [0.3.2] — 2026-08-13
12
+
13
+ Synced with VS Code extension `0.7.7`.
14
+
15
+ ### Fixed
16
+ - Switched canonical formatting to the safe V2 normalizer. Structurally invalid
17
+ input now produces a typed validation failure and no model edit instead of a
18
+ replacement containing an empty `<body>`.
19
+ - The HTML/pretty half of the format cycle remains usable after canonical
20
+ formatting is rejected, allowing users to repair invalid documents safely.
21
+ - Added browser/Monaco regressions for unclosed WEML containers, lists, tables,
22
+ inline elements and half-typed tags.
23
+
24
+ ## [0.3.1] — 2026-08-12
25
+
26
+ Synced with VS Code extension `0.7.6`.
27
+
28
+ ### Fixed
29
+ - **Canonical formatting of void HTML elements is more robust.** Known void
30
+ tags are normalized before parsing, preventing earlier non-self-closing tags
31
+ from corrupting later markup.
32
+ - **Inline whitespace handling now respects element boundaries and comments.**
33
+ Edge-space bleeding uses parser-compatible sibling insertion and stops at
34
+ block or void elements instead of crossing structural boundaries.
35
+
36
+ ## [0.3.0] — 2026-07-01
37
+
38
+ Ports the VS Code extension's toolbar and editor commands to Monaco.
39
+
40
+ ### Added
41
+ - **WEML toolbar** (`createWemlToolbar(monaco, editor, options)`) — a DOM top
42
+ panel with the same controls as the VS Code toolbar webview: a (stub) search
43
+ row, a "wrap selection" dropdown (`w-format` per text-formatting value, plus
44
+ `w-lang`, `w-non-egw`, `w-entity`, `a`), snippet buttons (`w-para`,
45
+ `w-heading`, `w-note`, `table`, `list`, `figure`) and "Renumber div ids (1…n)".
46
+ Mount it anywhere around the editor.
47
+ - **Editor actions / keybindings** (`registerWemlEditorActions`, also wired by
48
+ the toolbar): `Ctrl+B` / `Ctrl+I` / `Ctrl+U` wrap the selection in
49
+ `<w-format type="bold|italic|underline">`, `Alt+Shift+F` cycles the document
50
+ between HTML and canonical WEML formatting, plus a "Renumber &lt;div&gt; ids"
51
+ action. All are available in the command palette.
52
+ - Exposed the underlying operations (`wrapSelection`, `insertSnippetById`,
53
+ `renumberDivIds`, `cycleFormat`) and pure helpers (`planDivIdRenumber`,
54
+ `buildWrapSnippet`) for host/toolbar reuse and testing.
55
+
56
+ `planDivIdRenumber`/`buildWrapSnippet` are kept byte-identical to the VS Code
57
+ extension's `src/toolbar/*`; snippet insertion uses Monaco's `snippetController2`
58
+ so `$1`/`$2` tab stops behave like `editor.insertSnippet` in VS Code.
59
+
60
+ ## [0.2.1] — 2026-06-20
61
+
62
+ Synced with VS Code extension `0.7.4`.
63
+
64
+ ### Added
65
+ - **Hover tooltips for HTML character references (entities)** — named
66
+ (`&mdash;`, `&apos;`, …), decimal (`&#8212;`) and hexadecimal (`&#x2014;`)
67
+ references show name, rendered character and Unicode code point, in text and
68
+ inside attribute values.
69
+ - **Entity autocomplete** — typing `&` in text content or an attribute value
70
+ suggests named character references.
71
+
72
+ ## [0.2.0] — 2026-06-20
73
+
74
+ Synced the completion/hover providers with VS Code extension `0.7.3`.
75
+
76
+ ### Changed
77
+ - Completion is **strict and structure-aware**: only the tags allowed at the current
78
+ structural position are offered, with no generic HTML fallback noise in plain text.
79
+ - Tag completion inserts **only the opening tag** (e.g. `<w-format type="$1">`) — no
80
+ child scaffold or line breaks.
81
+ - Attribute-name completion inserts `name=""` and re-triggers the suggest widget
82
+ (`editor.action.triggerSuggest`) when the attribute has known values.
83
+
84
+ ### Fixed
85
+ - Typing a tag name after `<` (e.g. `<w`) no longer empties the suggestion list:
86
+ items carry a `<`-aware `filterText`. Same fix for closing tags after `</`.
87
+ - Half-typed `<w-` elements are no longer mistaken for their own parent, so the
88
+ structural tag list is offered correctly.
89
+ - Closing tags after `</` are suggested explicitly based on the nearest open element.
90
+ - Removed the (now unused) plain-text node hover.
91
+
92
+ ## [0.1.x] — initial port
93
+
94
+ Initial Monaco port of the WEML language support: language registration + Monarch
95
+ tokenizer, autocomplete, hover docs, structure diagnostics, document outline and
96
+ canonical formatting, plus a CDN demo and a smoke test. Parsing reuses
97
+ `vscode-html-languageservice`; Monaco is passed into `registerWeml(monaco)`.
package/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # WEML language support for Monaco
2
+
3
+ A Monaco-editor port of the WEML (White Estate Markup Language) VS Code
4
+ extension. It provides, for models with language id `weml`:
5
+
6
+ - **Syntax highlighting** — a Monarch tokenizer (HTML-flavoured, since WEML is
7
+ HTML5-compatible).
8
+ - **Context-aware autocomplete** — tags / attributes / attribute values that
9
+ respect the WEML nesting rules, with element-scaffolding snippets and
10
+ composite snippets (`table`, `w-list`, `figure`, `div + w-heading`, …).
11
+ - **Hover tooltips** — documentation generated from `docs/` (tags, attributes,
12
+ enum values, `meta` fields, `<a href>` link types).
13
+ - **Structure diagnostics** — unknown tags/attributes, required attributes,
14
+ enum values, child cardinality, `<a href>`/`id` rules, duplicate `<div id>`,
15
+ required `<meta>` in `<head>`; surfaced as editor markers.
16
+ - **Document outline** — by `<w-heading level=…>` and top-level `<div>`s.
17
+ - **Formatting** — "Format Document" runs the safe V2 canonical normalizer.
18
+ It validates matching/closed tags and text preservation before returning an
19
+ edit; invalid documents remain unchanged.
20
+ - **Toolbar & editor commands** — an optional DOM toolbar (top panel) plus
21
+ keybindings that mirror the VS Code extension: wrap selection in
22
+ `<w-format>`/other inlines, snippet buttons, "Renumber div ids", and a
23
+ HTML ↔ canonical format cycle.
24
+
25
+ This package does **not** modify the original VS Code extension — it is a
26
+ standalone re-implementation living entirely under `monaco-editor/`.
27
+
28
+ ## Install & build
29
+
30
+ ```bash
31
+ cd monaco-editor
32
+ npm install
33
+ npm run build # → dist/weml-monaco.js (IIFE global), .esm.js (ESM), *.d.ts
34
+ npm run typecheck # tsc --noEmit
35
+ npm test # build the ESM bundle + run the Node smoke test
36
+ ```
37
+
38
+ ## Usage
39
+
40
+ Monaco is **passed in** at registration time, so this works no matter how
41
+ Monaco itself is loaded (AMD/CDN, ESM bundle, or a global):
42
+
43
+ ```ts
44
+ import * as monaco from 'monaco-editor';
45
+ import { registerWeml } from 'weml-monaco'; // or the bundled dist/weml-monaco.esm.js
46
+
47
+ const disposable = registerWeml(monaco);
48
+
49
+ const model = monaco.editor.createModel(source, 'weml');
50
+ monaco.editor.create(document.getElementById('container'), { model });
51
+
52
+ // disposable.dispose(); // tears down every provider + diagnostics listener
53
+ ```
54
+
55
+ `registerWeml(monaco, options?)` accepts:
56
+
57
+ | option | default | meaning |
58
+ | ---------------------- | ----------- | -------------------------------------------------- |
59
+ | `extensions` | `['.weml']` | file extensions associated with the language |
60
+ | `diagnostics` | `true` | wire live validation → `setModelMarkers` |
61
+ | `diagnosticsDebounceMs`| `300` | debounce for re-validating after edits |
62
+
63
+ Diagnostics are attached to every `weml` model automatically (on creation,
64
+ content change, and language switch) and cleared on disposal. To validate a
65
+ model on demand instead, call `validateWemlModel(monaco, model)` and feed the
66
+ returned `IMarkerData[]` to `monaco.editor.setModelMarkers`. To run the
67
+ formatter directly, call `formatWemlText(source)`.
68
+ Structural validation failures are also available through
69
+ `isWemlStructureValidationFailure(error)` and include a code, line and column.
70
+
71
+ ## Toolbar & editor commands
72
+
73
+ Monaco has no webview panels, so the VS Code toolbar is ported as a **DOM top
74
+ panel** you mount yourself, plus per-editor actions/keybindings. Button
75
+ behaviour is identical to the VS Code toolbar.
76
+
77
+ ```ts
78
+ import { registerWeml, createWemlToolbar } from 'weml-monaco';
79
+
80
+ registerWeml(monaco);
81
+ const editor = monaco.editor.create(container, { model });
82
+
83
+ const toolbar = createWemlToolbar(monaco, editor, {
84
+ container: document.getElementById('weml-toolbar'), // where to mount the panel
85
+ onStatus: (msg) => console.log(msg), // optional status sink
86
+ // includeSearchRow: true, // stub search row (as in VS Code); default true
87
+ // onSearch: (query, lang) => { ... },
88
+ });
89
+
90
+ // toolbar.element → the toolbar DOM node
91
+ // toolbar.dispose() → removes the panel and its actions
92
+ ```
93
+
94
+ `createWemlToolbar` also registers the editor actions (so they work without the
95
+ panel too, via keybindings and the command palette):
96
+
97
+ | action / command | keybinding | effect |
98
+ | ------------------------- | ------------- | -------------------------------------------------- |
99
+ | `weml.wrapFormat.bold` | `Ctrl+B` | wrap selection in `<w-format type="bold">` |
100
+ | `weml.wrapFormat.italic` | `Ctrl+I` | wrap selection in `<w-format type="italic">` |
101
+ | `weml.wrapFormat.underline`| `Ctrl+U` | wrap selection in `<w-format type="underline">` |
102
+ | `weml.cycleFormat` | `Alt+Shift+F` | cycle document: HTML ↔ canonical WEML formatting |
103
+ | `weml.renumberDivIds` | — | renumber every `<div>` id to a sequential 1…n |
104
+
105
+ To register the actions without a toolbar, call
106
+ `registerWemlEditorActions(monaco, editor, options)`. The underlying operations
107
+ (`wrapSelection`, `insertSnippetById`, `renumberDivIds`, `cycleFormat`) and the
108
+ pure helpers (`planDivIdRenumber`, `buildWrapSnippet`) are exported too.
109
+
110
+ Snippet insertion uses Monaco's `snippetController2`, so `$1`/`$2` tab stops
111
+ behave like `editor.insertSnippet` in VS Code.
112
+
113
+ ## Demo
114
+
115
+ ```bash
116
+ npm run demo # bundles src/index.ts → demo/weml-monaco.js
117
+ # then open demo/index.html in a browser
118
+ ```
119
+
120
+ `demo/index.html` loads Monaco from a CDN (AMD loader) and the WEML bundle as a
121
+ global, registers the language and opens a sample document. Try Ctrl+Space for
122
+ completions, hover a tag, "Format Document", Ctrl+Shift+O for the outline, or
123
+ introduce an error to see diagnostics.
124
+
125
+ ## How it relates to the VS Code extension
126
+
127
+ The schema and rules are reused verbatim from the extension:
128
+
129
+ | reused as-is | adapted for Monaco |
130
+ | ------------------------------------ | ---------------------------------------- |
131
+ | `generated/schema.ts` | `schema/docs.ts` (plain `IMarkdownString`) |
132
+ | `schema/structureRules.ts` | `wemlDocument.ts` (model ↔ LSP positions) |
133
+ | `schema/snippets.ts` | `providers/*` (Monaco provider APIs) |
134
+ | `wemlContentNormalizeV2.js` | `index.ts` (registration + diagnostics) |
135
+
136
+ Parsing still uses `vscode-html-languageservice` (it is editor-agnostic and
137
+ runs in the browser); the only real work was swapping the `vscode.*` provider
138
+ APIs for `monaco.languages.*` and converting between Monaco's 1-based
139
+ line/column positions and the language service's 0-based LSP positions.
@@ -0,0 +1,13 @@
1
+ export interface WemlNormalizationFailure extends Error {
2
+ code: string;
3
+ line?: number;
4
+ column?: number;
5
+ expectedTag?: string | null;
6
+ actualTag?: string | null;
7
+ }
8
+ /** Normalises a WEML document string into its canonical, reproducible form. */
9
+ export declare function formatWemlText(input: string): string;
10
+ export declare function isWemlNormalizationFailure(error: unknown): error is WemlNormalizationFailure;
11
+ export declare function isWemlStructureValidationFailure(error: unknown): error is WemlNormalizationFailure;
12
+ /** User-facing reason shown when canonical formatting is deliberately skipped. */
13
+ export declare function canonicalFormattingFailureMessage(error: unknown): string;
@@ -0,0 +1,33 @@
1
+ export interface EnumValue {
2
+ value: string;
3
+ name: string;
4
+ description: string;
5
+ }
6
+ export interface EnumSchema {
7
+ id: string;
8
+ title: string;
9
+ values: EnumValue[];
10
+ }
11
+ export interface AttributeSchema {
12
+ name: string;
13
+ required: boolean;
14
+ default?: string;
15
+ description: string;
16
+ enumRef?: string;
17
+ }
18
+ export interface TagSchema {
19
+ tag: string;
20
+ description: string;
21
+ attributes: AttributeSchema[];
22
+ notes: string;
23
+ source: string;
24
+ }
25
+ export interface MetaFieldSchema {
26
+ name: string;
27
+ description: string;
28
+ enumRef?: string;
29
+ }
30
+ export declare const ENUMS: Record<string, EnumSchema>;
31
+ export declare const TAGS: Record<string, TagSchema>;
32
+ export declare const META_FIELDS: MetaFieldSchema[];
33
+ export declare const TEXT_NODE_DESCRIPTION: string;
@@ -0,0 +1,17 @@
1
+ import type * as monaco from 'monaco-editor';
2
+ export { WEML_LANGUAGE_ID } from './language';
3
+ export { canonicalFormattingFailureMessage, formatWemlText, isWemlNormalizationFailure, isWemlStructureValidationFailure, type WemlNormalizationFailure, } from './formatter';
4
+ export { validateWemlModel } from './providers/diagnostics';
5
+ export { createWemlToolbar, type WemlToolbar, type WemlToolbarOptions, } from './toolbar/toolbar';
6
+ export { registerWemlEditorActions, wrapSelection, insertSnippetById, renumberDivIds, cycleFormat, type WemlActionOptions, } from './toolbar/actions';
7
+ export { planDivIdRenumber, type IdEdit } from './toolbar/divRenumber';
8
+ export { buildWrapSnippet } from './toolbar/wrap';
9
+ export interface RegisterWemlOptions {
10
+ /** File extensions to associate with the language (default: [".weml"]). */
11
+ extensions?: string[];
12
+ /** Debounce for re-validating a model after edits, in ms (default: 300). */
13
+ diagnosticsDebounceMs?: number;
14
+ /** Set to false to skip live diagnostics/markers entirely (default: true). */
15
+ diagnostics?: boolean;
16
+ }
17
+ export declare function registerWeml(m: typeof monaco, options?: RegisterWemlOptions): monaco.IDisposable;
@@ -0,0 +1,4 @@
1
+ import type * as monaco from 'monaco-editor';
2
+ export declare const WEML_LANGUAGE_ID = "weml";
3
+ export declare const languageConfiguration: monaco.languages.LanguageConfiguration;
4
+ export declare const monarchLanguage: monaco.languages.IMonarchLanguage;
@@ -0,0 +1,2 @@
1
+ import type * as monaco from 'monaco-editor';
2
+ export declare function createCompletionProvider(m: typeof monaco): monaco.languages.CompletionItemProvider;
@@ -0,0 +1,3 @@
1
+ import type * as monaco from 'monaco-editor';
2
+ export declare const WEML_MARKER_OWNER = "weml";
3
+ export declare function validateWemlModel(m: typeof monaco, model: monaco.editor.ITextModel): monaco.editor.IMarkerData[];
@@ -0,0 +1,2 @@
1
+ import type * as monaco from 'monaco-editor';
2
+ export declare function createDocumentSymbolProvider(m: typeof monaco): monaco.languages.DocumentSymbolProvider;
@@ -0,0 +1,2 @@
1
+ import type * as monaco from 'monaco-editor';
2
+ export declare function createHoverProvider(_m: typeof monaco): monaco.languages.HoverProvider;
@@ -0,0 +1,13 @@
1
+ import { AttributeSchema } from '../generated/schema';
2
+ /** Mirrors `monaco.IMarkdownString` without taking a hard dependency on it. */
3
+ export interface Markdown {
4
+ value: string;
5
+ isTrusted?: boolean;
6
+ }
7
+ export declare function tagMarkdown(tag: string): Markdown;
8
+ export declare function attributeMarkdown(tag: string, attr: AttributeSchema): Markdown;
9
+ export declare function enumValueMarkdown(enumRef: string, value: string): Markdown | undefined;
10
+ export declare function enumValuesSummary(enumRef: string): string;
11
+ export declare function metaFieldMarkdown(name: string): Markdown | undefined;
12
+ export declare function entityMarkdown(reference: string): Markdown | undefined;
13
+ export declare function textNodeMarkdown(): Markdown;
@@ -0,0 +1,35 @@
1
+ export interface EntityInfo {
2
+ /** The character(s) the reference resolves to. */
3
+ char: string;
4
+ /** Human-readable name. */
5
+ name: string;
6
+ }
7
+ export declare const NAMED_ENTITIES: Record<string, EntityInfo>;
8
+ export interface ResolvedEntity {
9
+ /** The full reference text, e.g. "&mdash;". */
10
+ reference: string;
11
+ /** The resolved character, or undefined for unknown named references. */
12
+ char?: string;
13
+ /** Human-readable name, when known. */
14
+ name?: string;
15
+ /** Unicode code point, when resolvable. */
16
+ codePoint?: number;
17
+ }
18
+ /** Resolves a character reference (with surrounding `&` and `;`) to its details. */
19
+ export declare function resolveEntity(reference: string): ResolvedEntity | undefined;
20
+ export declare const ENTITY_COMPLETION_PRIORITY: string[];
21
+ /**
22
+ * If the cursor sits inside a partial entity being typed (`&`, `&md`, `&#82` …),
23
+ * returns the range to replace, including a trailing `;` if already present.
24
+ * Returns undefined when there is no `&`-initiated token before the cursor.
25
+ */
26
+ export declare function getPartialEntityRange(text: string, offset: number): {
27
+ start: number;
28
+ end: number;
29
+ } | undefined;
30
+ /** Finds the character reference whose span contains `offset` within `text`. */
31
+ export declare function findEntityAt(text: string, offset: number): {
32
+ reference: string;
33
+ start: number;
34
+ end: number;
35
+ } | undefined;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Build a `<tag ...>...</tag>` snippet body (including the opening `<`) for
3
+ * the given tag, with required attributes and required descendants filled
4
+ * in as placeholders. The `a` tag is special-cased: use
5
+ * `buildAnchorLinkSnippet`/`buildAnchorIdSnippet` instead.
6
+ */
7
+ export declare function buildElementSnippet(tag: string): string;
8
+ /**
9
+ * Opening tag only, with required attributes as placeholders and no children or
10
+ * line breaks (e.g. `<w-format type="$1">`). Used by tag completion so accepting
11
+ * a suggestion inserts just the start tag; the closing tag is offered separately
12
+ * by the `</` completion.
13
+ */
14
+ export declare function buildOpeningTag(tag: string): string;
15
+ /** `<a href="...">` variant — a link to another resource, may contain inline content. */
16
+ export declare function buildAnchorLinkSnippet(): string;
17
+ /** `<a id="...">` variant — a named anchor, must be empty. */
18
+ export declare function buildAnchorIdSnippet(): string;
19
+ export interface CompositeSnippet {
20
+ label: string;
21
+ detail: string;
22
+ /** Parent tags whose child slot this snippet is offered for, in addition to the plain tag completion. */
23
+ parents: string[];
24
+ body: string;
25
+ }
26
+ export declare const COMPOSITE_SNIPPETS: CompositeSnippet[];
@@ -0,0 +1,81 @@
1
+ export type NodeCategory = 'container' | 'block' | 'inline';
2
+ export declare const CONTAINER_TAGS: readonly ["w-para", "w-heading", "w-para-group", "w-toc"];
3
+ export declare const BLOCK_TAGS: readonly ["w-text-block", "figure", "w-list", "table", "hr"];
4
+ export declare const INLINE_TAGS: readonly ["a", "br", "w-entity", "w-format", "w-lang", "w-non-egw", "w-note", "w-page", "w-sent"];
5
+ export declare function categoryOf(tag: string): NodeCategory | undefined;
6
+ /**
7
+ * A group of sibling children that share the same allowed tags and count.
8
+ * Every element child of a node must match exactly one group of its
9
+ * StructureRule; a child that matches no group is "not allowed here".
10
+ */
11
+ export interface ChildGroup {
12
+ tags: string[];
13
+ min: number;
14
+ /** `null` means unbounded. */
15
+ max: number | null;
16
+ description: string;
17
+ }
18
+ export interface StructureRule {
19
+ children: ChildGroup[];
20
+ /** Whether non-whitespace text nodes are allowed as direct children. */
21
+ allowText?: boolean;
22
+ description: string;
23
+ }
24
+ export declare const STRUCTURE_RULES: Record<string, StructureRule>;
25
+ export interface AttributeConstraint {
26
+ tag: string;
27
+ attribute: string;
28
+ kind: 'integer-range' | 'integer-min' | 'pattern';
29
+ min?: number;
30
+ max?: number;
31
+ /** Used when `kind === 'pattern'`. Empty values are always allowed. */
32
+ pattern?: RegExp;
33
+ message: string;
34
+ }
35
+ /** Matches an arabic (`12`) or roman (`iii`, `XIV`) number. */
36
+ export declare const ARABIC_OR_ROMAN: RegExp;
37
+ export declare const ATTRIBUTE_CONSTRAINTS: AttributeConstraint[];
38
+ export interface MetaConstraint {
39
+ /** meta[name=...] this rule applies to. */
40
+ name: string;
41
+ kind: 'digits' | 'isbn';
42
+ message: string;
43
+ }
44
+ export declare const META_CONSTRAINTS: MetaConstraint[];
45
+ /** True for an empty string or a string consisting only of arabic digits. */
46
+ export declare function isDigitsOnly(value: string): boolean;
47
+ /**
48
+ * Validates an ISBN-10 or ISBN-13 by format and checksum. Hyphens and spaces
49
+ * are ignored. An empty string is considered valid (field simply unset).
50
+ */
51
+ export declare function isValidIsbn(value: string): boolean;
52
+ export interface HrefLinkType {
53
+ id: string;
54
+ label: string;
55
+ regex: RegExp;
56
+ description: string;
57
+ example: string;
58
+ }
59
+ export declare const HREF_LINK_TYPES: HrefLinkType[];
60
+ export declare function isValidHref(href: string): boolean;
61
+ export declare const DOCUMENT_RULES: {
62
+ html: {
63
+ requiredAttributes: string[];
64
+ dirEnumRef: string;
65
+ defaultDir: string;
66
+ };
67
+ head: {
68
+ requiredMeta: {
69
+ name: string;
70
+ description: string;
71
+ }[];
72
+ metaTypeName: string;
73
+ metaTypeEnumRef: string;
74
+ };
75
+ };
76
+ /**
77
+ * Verifies that every tag name referenced by the rules above exists in the
78
+ * generated TAGS map, so a docs rename that forgets to update this file is
79
+ * caught at activation time instead of silently breaking completions.
80
+ */
81
+ export declare function checkStructureRules(): string[];
@@ -0,0 +1,28 @@
1
+ import type * as monaco from 'monaco-editor';
2
+ export interface WemlActionOptions {
3
+ /** Optional sink for the short status messages the VS Code extension shows in the status bar. */
4
+ onStatus?: (message: string) => void;
5
+ }
6
+ /** Wraps the current selection in `<tag [type=...]>…</tag>` (toolbar dropdown / hotkeys). */
7
+ export declare function wrapSelection(editor: monaco.editor.ICodeEditor, tag: string, type?: string): void;
8
+ /** Inserts a snippet by its toolbar-button id (composite label or plain tag). */
9
+ export declare function insertSnippetById(editor: monaco.editor.ICodeEditor, id: string): void;
10
+ /** Renumbers every `<div>` id in document order to a sequential 1..n. */
11
+ export declare function renumberDivIds(editor: monaco.editor.ICodeEditor, options?: WemlActionOptions): void;
12
+ /**
13
+ * Cycles the whole document between the standard HTML formatting (even steps) and
14
+ * the canonical WEML normalizer (odd steps). The step counter advances on every
15
+ * invocation, even when the formatter produces no change, so the cycle stays in
16
+ * sync — matching the VS Code `weml.cycleFormat` command.
17
+ */
18
+ export declare function cycleFormat(editor: monaco.editor.ICodeEditor, options?: WemlActionOptions): void;
19
+ /**
20
+ * Registers the WEML editor actions on a specific editor instance: cycle-format
21
+ * (Alt+Shift+F), renumber div ids, and wrap-format bold/italic/underline
22
+ * (Ctrl+B / Ctrl+I / Ctrl+U). Returns an `IDisposable` that removes them.
23
+ *
24
+ * `editor.addAction` is per-editor (Monaco has no global command registry akin
25
+ * to the VS Code extension host), so call this for every WEML editor — or just
26
+ * use `createWemlToolbar`, which calls it for you.
27
+ */
28
+ export declare function registerWemlEditorActions(m: typeof monaco, editor: monaco.editor.IStandaloneCodeEditor, options?: WemlActionOptions): monaco.IDisposable;
@@ -0,0 +1,12 @@
1
+ export interface IdEdit {
2
+ /** Replacement range in the source text (start === end means a pure insertion). */
3
+ start: number;
4
+ end: number;
5
+ newText: string;
6
+ }
7
+ /**
8
+ * Computes the edits needed to renumber every `<div>` id to its 1-based position
9
+ * in document order. Divs that already carry the right id produce no edit; a div
10
+ * without an `id` attribute gets one inserted right after the tag name.
11
+ */
12
+ export declare function planDivIdRenumber(text: string): IdEdit[];
@@ -0,0 +1,27 @@
1
+ import type * as monaco from 'monaco-editor';
2
+ import { WemlActionOptions } from './actions';
3
+ export interface WemlToolbarOptions extends WemlActionOptions {
4
+ /** If given, the toolbar element is appended here. Otherwise it is only returned. */
5
+ container?: HTMLElement;
6
+ /** Include the (stub) search row, matching the VS Code toolbar. Default: true. */
7
+ includeSearchRow?: boolean;
8
+ /**
9
+ * Handler for the search row. When omitted the row is a no-op stub (as in the
10
+ * VS Code extension). Receives the query text and the language code.
11
+ */
12
+ onSearch?: (query: string, lang: string) => void;
13
+ }
14
+ export interface WemlToolbar extends monaco.IDisposable {
15
+ /** The toolbar's root DOM element. */
16
+ element: HTMLElement;
17
+ }
18
+ /**
19
+ * Builds the WEML toolbar for a Monaco editor and wires every button to the same
20
+ * operations as the VS Code toolbar (wrap selection, snippet insertion, renumber
21
+ * div ids). Also registers the editor actions/keybindings (Ctrl+B/I/U,
22
+ * Alt+Shift+F, renumber) via `registerWemlEditorActions`.
23
+ *
24
+ * Returns the root element plus a `dispose()` that removes the toolbar and its
25
+ * actions.
26
+ */
27
+ export declare function createWemlToolbar(m: typeof monaco, editor: monaco.editor.IStandaloneCodeEditor, options?: WemlToolbarOptions): WemlToolbar;
@@ -0,0 +1,11 @@
1
+ /** Escapes text so it is inserted literally inside a SnippetString. */
2
+ export declare function escapeSnippet(text: string): string;
3
+ /**
4
+ * Builds a `<tag ...>SELECTION</tag>` snippet body for wrapping `selection`.
5
+ * - `type` (e.g. for `w-format`) fills `type="..."` directly.
6
+ * - `a` gets only `href="$1"` (it lists both href and id as required, but a
7
+ * wrapped selection is a link).
8
+ * - otherwise the tag's required attributes become `name="$1"` placeholders.
9
+ * An empty selection leaves a tab stop where the content goes.
10
+ */
11
+ export declare function buildWrapSnippet(tag: string, type: string | undefined, selection: string): string;