asciidoc-editor 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alexander Thaller
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,134 @@
1
+ # AsciiDoc editor
2
+
3
+ A WYSIWYG AsciiDoc editor for the web, written in Rust and compiled to
4
+ WebAssembly. You edit the rendered document — click a heading and type in it,
5
+ press Tab in a list to indent — and the AsciiDoc source stays the thing being
6
+ edited underneath.
7
+
8
+ Parsing and rendering are [`asciidoc-parser`] and [`asciidoc-html5`]; the
9
+ interface is [Leptos].
10
+
11
+ ## Using it in a page
12
+
13
+ ```sh
14
+ npm install asciidoc-editor
15
+ ```
16
+
17
+ Or build it yourself, which is what `npm publish ./pkg` publishes:
18
+
19
+ ```sh
20
+ ./build-package.sh
21
+ ```
22
+
23
+ Either way you get an ES module, its wasm, and TypeScript types — and nothing
24
+ else to serve, because the stylesheets are inside the wasm.
25
+
26
+ ```html
27
+ <div id="editor" style="height: 34rem"></div>
28
+
29
+ <script type="module">
30
+ import init, { mount } from "./pkg/asciidoc_editor.js";
31
+
32
+ await init();
33
+ const editor = mount("#editor", { value: "= Title\n\nWords.\n" });
34
+
35
+ editor.onChange((document) => save(document));
36
+ </script>
37
+ ```
38
+
39
+ The editor fills the element it is given, so give that element a height.
40
+
41
+ Loading the module from a bundler works the same way. The wasm is fetched
42
+ relative to the module (`new URL(..., import.meta.url)`), which Vite, webpack 5
43
+ and esbuild all understand without configuration.
44
+
45
+ ### TypeScript
46
+
47
+ The types ship with the package. wasm-bindgen writes a `[Symbol.dispose]()` on
48
+ its classes, so a project compiling the package's own declarations needs either
49
+ `"skipLibCheck": true` — which most templates set already — or `"lib"` at
50
+ `ESNext`.
51
+
52
+ ### `mount(target, options?)`
53
+
54
+ `target` is a CSS selector or an element (`MountTarget`). The element keeps
55
+ whatever classes the page put on it.
56
+
57
+ | Option | Default | |
58
+ |---|---|---|
59
+ | `value` | the sample document | the document to open with |
60
+ | `autosave` | none | a `localStorage` key to keep the document under |
61
+ | `richText` | `true` | start in rich text rather than source |
62
+
63
+ With `autosave` set and no `value`, the editor opens whatever was last saved
64
+ under that key.
65
+
66
+ ### The handle
67
+
68
+ | | |
69
+ |---|---|
70
+ | `editor.value()` | the document, as AsciiDoc |
71
+ | `editor.setValue(source)` | replace the document |
72
+ | `editor.onChange(callback)` | called with the document whenever it changes |
73
+ | `editor.destroy()` | take the editor off the page |
74
+
75
+ Hold on to the handle. Dropping it unmounts the editor, which is what
76
+ `destroy()` does deliberately; after it the element is as it was found, and can
77
+ be mounted into again.
78
+
79
+ `demo/index.html` is a page doing all of this. Serve the repository root and
80
+ open `/demo/`:
81
+
82
+ ```sh
83
+ python3 -m http.server 8000
84
+ ```
85
+
86
+ ### Publishing
87
+
88
+ ```sh
89
+ ./build-package.sh
90
+ npm publish ./pkg
91
+ ```
92
+
93
+ The leading `./` matters: `npm publish pkg` reads `pkg` as the name of a
94
+ package on the registry and tries to publish that one.
95
+
96
+ ## Licence
97
+
98
+ MIT. See `LICENSE`.
99
+
100
+ ## Developing
101
+
102
+ ```sh
103
+ trunk serve # the editor on its own at http://localhost:8080
104
+ cargo test # the source-editing and parsing logic
105
+ ```
106
+
107
+ `trunk serve` runs `src/main.rs`, which is the same library mounted onto a bare
108
+ page.
109
+
110
+ ### How it works
111
+
112
+ The AsciiDoc source is the only state. It is rendered to HTML with source
113
+ locations turned on, which puts a `data-source-line` on every block, and that
114
+ HTML goes into an iframe. Blocks in the iframe are made `contenteditable`; when
115
+ one changes, its DOM is written back out as AsciiDoc and spliced over the lines
116
+ it came from.
117
+
118
+ A block is only made editable if writing its DOM back reproduces its source
119
+ exactly. Anything that fails that check is left alone and can still be edited
120
+ in source mode — better a block that refuses to be edited in place than one
121
+ that quietly rewrites itself.
122
+
123
+ The block being edited is never re-rendered, or the caret would be lost; the
124
+ line numbers of the blocks below it are shifted instead.
125
+
126
+ ### What stays source-only
127
+
128
+ Cell specifiers (`2+|`, `a|`, `^|`), explicitly numbered ordered lists, image
129
+ sizing attributes, and anything else that would not survive the round trip.
130
+ Strikethrough is not implemented.
131
+
132
+ [`asciidoc-parser`]: https://crates.io/crates/asciidoc-parser
133
+ [`asciidoc-html5`]: https://crates.io/crates/asciidoc-html5
134
+ [Leptos]: https://leptos.dev
@@ -0,0 +1,160 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ /**
4
+ * The `ReadableStreamType` enum.
5
+ *
6
+ * *This API requires the following crate features to be activated: `ReadableStreamType`*
7
+ */
8
+
9
+ export type ReadableStreamType = "bytes";
10
+
11
+ /** What the editor may be told when it is mounted. */
12
+ export interface MountOptions {
13
+ /** The document to open with. Defaults to a sample document. */
14
+ value?: string;
15
+ /** A `localStorage` key to keep the document under. */
16
+ autosave?: string;
17
+ /** Start in rich text rather than source. Defaults to `true`. */
18
+ richText?: boolean;
19
+ }
20
+
21
+ /** The element to mount into, or a selector naming it. */
22
+ export type MountTarget = Element | string;
23
+
24
+ /** Called with the document, as AsciiDoc, whenever it changes. */
25
+ export type ChangeCallback = (document: string) => void;
26
+
27
+
28
+
29
+ /**
30
+ * A mounted editor.
31
+ *
32
+ * Hold on to it: letting it go takes the editor off the page, which is what
33
+ * [`Editor::destroy`] does deliberately.
34
+ */
35
+ export class Editor {
36
+ private constructor();
37
+ free(): void;
38
+ [Symbol.dispose](): void;
39
+ /**
40
+ * Takes the editor off the page, leaving the element as it was found.
41
+ */
42
+ destroy(): void;
43
+ /**
44
+ * Calls `callback` with the document whenever it changes.
45
+ */
46
+ onChange(callback: ChangeCallback): void;
47
+ /**
48
+ * Replaces the document.
49
+ */
50
+ setValue(source: string): void;
51
+ /**
52
+ * The document, as AsciiDoc.
53
+ */
54
+ value(): string;
55
+ }
56
+
57
+ export class IntoUnderlyingByteSource {
58
+ private constructor();
59
+ free(): void;
60
+ [Symbol.dispose](): void;
61
+ cancel(): void;
62
+ pull(controller: ReadableByteStreamController): Promise<any>;
63
+ start(controller: ReadableByteStreamController): void;
64
+ readonly autoAllocateChunkSize: number;
65
+ readonly type: ReadableStreamType;
66
+ }
67
+
68
+ export class IntoUnderlyingSink {
69
+ private constructor();
70
+ free(): void;
71
+ [Symbol.dispose](): void;
72
+ abort(reason: any): Promise<any>;
73
+ close(): Promise<any>;
74
+ write(chunk: any): Promise<any>;
75
+ }
76
+
77
+ export class IntoUnderlyingSource {
78
+ private constructor();
79
+ free(): void;
80
+ [Symbol.dispose](): void;
81
+ cancel(): void;
82
+ pull(controller: ReadableStreamDefaultController): Promise<any>;
83
+ }
84
+
85
+ /**
86
+ * Puts an editor inside the element named by `target`, a CSS selector or the
87
+ * element itself.
88
+ *
89
+ * Options, all of them optional: `value` to open with, `autosave` naming a
90
+ * `localStorage` key to keep the document under, and `richText` to start in
91
+ * rich text rather than source (the default).
92
+ */
93
+ export function mount(target: MountTarget, options?: MountOptions | null): Editor;
94
+
95
+ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
96
+
97
+ export interface InitOutput {
98
+ readonly memory: WebAssembly.Memory;
99
+ readonly __wbg_editor_free: (a: number, b: number) => void;
100
+ readonly __wbg_intounderlyingbytesource_free: (a: number, b: number) => void;
101
+ readonly __wbg_intounderlyingsink_free: (a: number, b: number) => void;
102
+ readonly __wbg_intounderlyingsource_free: (a: number, b: number) => void;
103
+ readonly editor_destroy: (a: number) => void;
104
+ readonly editor_onChange: (a: number, b: any) => void;
105
+ readonly editor_setValue: (a: number, b: number, c: number) => void;
106
+ readonly editor_value: (a: number) => [number, number];
107
+ readonly intounderlyingbytesource_autoAllocateChunkSize: (a: number) => number;
108
+ readonly intounderlyingbytesource_cancel: (a: number) => void;
109
+ readonly intounderlyingbytesource_pull: (a: number, b: any) => any;
110
+ readonly intounderlyingbytesource_start: (a: number, b: any) => void;
111
+ readonly intounderlyingbytesource_type: (a: number) => number;
112
+ readonly intounderlyingsink_abort: (a: number, b: any) => any;
113
+ readonly intounderlyingsink_close: (a: number) => any;
114
+ readonly intounderlyingsink_write: (a: number, b: any) => any;
115
+ readonly intounderlyingsource_cancel: (a: number) => void;
116
+ readonly intounderlyingsource_pull: (a: number, b: any) => any;
117
+ readonly mount: (a: any, b: number) => [number, number, number];
118
+ readonly wasm_bindgen_56bfa123189e575a___convert__closures_____invoke___js_sys_b5492ab1ec297925___Function_fn_wasm_bindgen_56bfa123189e575a___JsValue_____wasm_bindgen_56bfa123189e575a___sys__Undefined___js_sys_b5492ab1ec297925___Function_fn_wasm_bindgen_56bfa123189e575a___JsValue_____wasm_bindgen_56bfa123189e575a___sys__Undefined_______true_: (a: number, b: number, c: any, d: any) => void;
119
+ readonly wasm_bindgen_56bfa123189e575a___convert__closures_____invoke___wasm_bindgen_56bfa123189e575a___JsValue__core_dcdb5b26ee14507c___result__Result_____wasm_bindgen_56bfa123189e575a___JsError___true_: (a: number, b: number, c: any) => [number, number];
120
+ readonly wasm_bindgen_56bfa123189e575a___convert__closures_____invoke___wasm_bindgen_56bfa123189e575a___JsValue______true_: (a: number, b: number, c: any) => void;
121
+ readonly wasm_bindgen_56bfa123189e575a___convert__closures_____invoke___wasm_bindgen_56bfa123189e575a___JsValue______true__20: (a: number, b: number, c: any) => void;
122
+ readonly wasm_bindgen_56bfa123189e575a___convert__closures_____invoke___web_sys_7d5907b0b8adcf95___features__gen_CloseEvent__CloseEvent______true_: (a: number, b: number, c: any) => void;
123
+ readonly wasm_bindgen_56bfa123189e575a___convert__closures_____invoke___web_sys_7d5907b0b8adcf95___features__gen_DragEvent__DragEvent______true_: (a: number, b: number, c: any) => void;
124
+ readonly wasm_bindgen_56bfa123189e575a___convert__closures_____invoke___web_sys_7d5907b0b8adcf95___features__gen_DragEvent__DragEvent______true__19: (a: number, b: number, c: any) => void;
125
+ readonly wasm_bindgen_56bfa123189e575a___convert__closures_____invoke___web_sys_7d5907b0b8adcf95___features__gen_DragEvent__DragEvent______true__22: (a: number, b: number, c: any) => void;
126
+ readonly wasm_bindgen_56bfa123189e575a___convert__closures_____invoke___web_sys_7d5907b0b8adcf95___features__gen_DragEvent__DragEvent______true__23: (a: number, b: number, c: any) => void;
127
+ readonly wasm_bindgen_56bfa123189e575a___convert__closures_____invoke_______true_: (a: number, b: number) => void;
128
+ readonly wasm_bindgen_56bfa123189e575a___convert__closures_____invoke_______true__1_: (a: number, b: number) => void;
129
+ readonly __wbindgen_malloc: (a: number, b: number) => number;
130
+ readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
131
+ readonly __externref_table_alloc: () => number;
132
+ readonly __wbindgen_externrefs: WebAssembly.Table;
133
+ readonly __wbindgen_exn_store: (a: number) => void;
134
+ readonly __wbindgen_free: (a: number, b: number, c: number) => void;
135
+ readonly __wbindgen_destroy_closure: (a: number, b: number) => void;
136
+ readonly __externref_table_dealloc: (a: number) => void;
137
+ readonly __wbindgen_start: () => void;
138
+ }
139
+
140
+ export type SyncInitInput = BufferSource | WebAssembly.Module;
141
+
142
+ /**
143
+ * Instantiates the given `module`, which can either be bytes or
144
+ * a precompiled `WebAssembly.Module`.
145
+ *
146
+ * @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
147
+ *
148
+ * @returns {InitOutput}
149
+ */
150
+ export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
151
+
152
+ /**
153
+ * If `module_or_path` is {RequestInfo} or {URL}, makes a request and
154
+ * for everything else, calls `WebAssembly.instantiate` directly.
155
+ *
156
+ * @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
157
+ *
158
+ * @returns {Promise<InitOutput>}
159
+ */
160
+ export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;