@matrajs/svelte 0.15.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 Nahim Hossain Shohan
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 DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # @matrajs/svelte
2
+
3
+ Svelte bindings for [Matra](https://matrajs.com): a `use:` action and a store
4
+ that follows the editor.
5
+
6
+ ```sh
7
+ npm i @matrajs/core @matrajs/svelte
8
+ ```
9
+
10
+ ```svelte
11
+ <script>
12
+ import { starterKit } from '@matrajs/core'
13
+ import { matra } from '@matrajs/svelte'
14
+
15
+ const { action, editor, state } = matra({
16
+ extensions: starterKit,
17
+ content: '<p>Hello</p>',
18
+ })
19
+ </script>
20
+
21
+ <button onclick={() => editor.commands.toggleBold()}
22
+ aria-pressed={$state.isActive('bold')}>
23
+ Bold
24
+ </button>
25
+
26
+ <div use:action></div>
27
+ ```
28
+
29
+ ## What it adds
30
+
31
+ Svelte already has the right shape for this — an action runs when the element
32
+ exists and is told when it goes away — so the binding is thin on purpose. What
33
+ it adds over eight lines written inline is the two things people get wrong:
34
+
35
+ - **The double-mount guard.** A component rendered twice, or an action re-run
36
+ by a hot reload, would otherwise attach a second view to one element, which
37
+ is two carets fighting over it.
38
+ - **A store.** `$state` republishes on every change and every selection move,
39
+ so a toolbar's pressed states come from the document rather than from what
40
+ was last clicked.
41
+
42
+ The editor is created immediately rather than on mount, so `commands`,
43
+ `getJSON()` and `getText()` all work before anything is on screen — which is
44
+ what a server render and a test both need.
45
+
46
+ ## Svelte 4 and 5
47
+
48
+ Stores, not runes, so the same package works in both. A rune-only build would
49
+ be a version boundary in exchange for nothing: `$state` on a store reads
50
+ identically.
51
+
52
+ ## Styling
53
+
54
+ Matra ships no appearance. See
55
+ [the styling guide](https://matrajs.com/docs/styling).
56
+
57
+ ## Licence
58
+
59
+ MIT.
package/dist/index.cjs ADDED
@@ -0,0 +1,34 @@
1
+ 'use strict';
2
+
3
+ var core = require('@matrajs/core');
4
+ var store = require('svelte/store');
5
+
6
+ // src/index.ts
7
+ function matra(options) {
8
+ const editor = core.createEditor(options);
9
+ const action = (node) => {
10
+ if (!editor.unsafe.view) editor.mount(node);
11
+ return {
12
+ destroy() {
13
+ editor.destroy();
14
+ }
15
+ };
16
+ };
17
+ return { action, editor, state: editorState(editor) };
18
+ }
19
+ function editorState(editor) {
20
+ return store.readable(editor, (set) => {
21
+ const republish = () => set(editor);
22
+ const offChange = editor.on("change", republish);
23
+ const offSelection = editor.on("selectionChange", republish);
24
+ return () => {
25
+ offChange();
26
+ offSelection();
27
+ };
28
+ });
29
+ }
30
+
31
+ exports.editorState = editorState;
32
+ exports.matra = matra;
33
+ //# sourceMappingURL=index.cjs.map
34
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":["createEditor","readable"],"mappings":";;;;;;AAkDO,SAAS,MACd,OAAA,EACgB;AAChB,EAAA,MAAM,MAAA,GAASA,kBAAa,OAAO,CAAA;AAEnC,EAAA,MAAM,MAAA,GAAS,CAAC,IAAA,KAAoC;AAGlD,IAAA,IAAI,CAAC,MAAA,CAAO,MAAA,CAAO,IAAA,EAAM,MAAA,CAAO,MAAM,IAAI,CAAA;AAC1C,IAAA,OAAO;AAAA,MACL,OAAA,GAAU;AACR,QAAA,MAAA,CAAO,OAAA,EAAQ;AAAA,MACjB;AAAA,KACF;AAAA,EACF,CAAA;AAEA,EAAA,OAAO,EAAE,MAAA,EAAQ,MAAA,EAAQ,KAAA,EAAO,WAAA,CAAY,MAAM,CAAA,EAAE;AACtD;AASO,SAAS,YACd,MAAA,EACqB;AACrB,EAAA,OAAOC,cAAA,CAAS,MAAA,EAAQ,CAAC,GAAA,KAAQ;AAC/B,IAAA,MAAM,SAAA,GAAY,MAAM,GAAA,CAAI,MAAM,CAAA;AAClC,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,EAAA,CAAG,QAAA,EAAU,SAAS,CAAA;AAC/C,IAAA,MAAM,YAAA,GAAe,MAAA,CAAO,EAAA,CAAG,iBAAA,EAAmB,SAAS,CAAA;AAC3D,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,EAAU;AACV,MAAA,YAAA,EAAa;AAAA,IACf,CAAA;AAAA,EACF,CAAC,CAAA;AACH","file":"index.cjs","sourcesContent":["import { type AnyDef, type Editor, type EditorOptions, createEditor } from '@matrajs/core'\nimport { type Readable, readable } from 'svelte/store'\n\n/**\n * Svelte bindings, which are mostly an action.\n *\n * Svelte already has the shape this needs — an action runs when the element\n * exists and is told when it goes away — so the binding is thin on purpose.\n * What it adds over eight lines written inline is the double-mount guard and a\n * store, and both of those are the parts people get wrong rather than the\n * parts they find tedious.\n *\n * Written with stores rather than runes so it works the same in Svelte 4 and\n * 5. A rune-only package would be a version boundary in exchange for nothing.\n */\n\n/** The shape Svelte's `use:` directive expects back. */\nexport interface EditorAction {\n destroy(): void\n}\n\nexport interface MatraAction<T extends readonly AnyDef[]> {\n /** Pass this to `use:` on the element the editor should mount into. */\n action: (node: HTMLElement) => EditorAction\n /** The editor itself. It exists before the element does. */\n editor: Editor<T>\n /** Bumped on every change and selection move · read it in your markup. */\n state: Readable<Editor<T>>\n}\n\n/**\n * Create an editor and the action that mounts it.\n *\n * ```svelte\n * <script>\n * import { matra } from '@matrajs/svelte'\n * import { starterKit } from '@matrajs/core'\n *\n * const { action, editor, state } = matra({ extensions: starterKit })\n * </script>\n *\n * <button onclick={() => editor.commands.toggleBold()}\n * aria-pressed={$state.isActive('bold')}>Bold</button>\n * <div use:action></div>\n * ```\n *\n * The editor is created immediately rather than on mount, so commands, content\n * and `getJSON()` all work before anything is on screen — which is what a\n * server render and a test both need.\n */\nexport function matra<const T extends readonly AnyDef[]>(\n options: EditorOptions<T>,\n): MatraAction<T> {\n const editor = createEditor(options)\n\n const action = (node: HTMLElement): EditorAction => {\n // A component rendered twice, or an action re-run by a hot reload, must not\n // attach a second view to one element.\n if (!editor.unsafe.view) editor.mount(node)\n return {\n destroy() {\n editor.destroy()\n },\n }\n }\n\n return { action, editor, state: editorState(editor) }\n}\n\n/**\n * A store that changes whenever the editor does.\n *\n * The value is the editor itself, not a copy: a toolbar wants to ask\n * `isActive` and `getText` at render time, and cloning a document to answer\n * that would be the expensive way to do nothing.\n */\nexport function editorState<T extends readonly AnyDef[]>(\n editor: Editor<T>,\n): Readable<Editor<T>> {\n return readable(editor, (set) => {\n const republish = () => set(editor)\n const offChange = editor.on('change', republish)\n const offSelection = editor.on('selectionChange', republish)\n return () => {\n offChange()\n offSelection()\n }\n })\n}\n"]}
@@ -0,0 +1,58 @@
1
+ import { AnyDef, Editor, EditorOptions } from '@matrajs/core';
2
+ import { Readable } from 'svelte/store';
3
+
4
+ /**
5
+ * Svelte bindings, which are mostly an action.
6
+ *
7
+ * Svelte already has the shape this needs — an action runs when the element
8
+ * exists and is told when it goes away — so the binding is thin on purpose.
9
+ * What it adds over eight lines written inline is the double-mount guard and a
10
+ * store, and both of those are the parts people get wrong rather than the
11
+ * parts they find tedious.
12
+ *
13
+ * Written with stores rather than runes so it works the same in Svelte 4 and
14
+ * 5. A rune-only package would be a version boundary in exchange for nothing.
15
+ */
16
+ /** The shape Svelte's `use:` directive expects back. */
17
+ interface EditorAction {
18
+ destroy(): void;
19
+ }
20
+ interface MatraAction<T extends readonly AnyDef[]> {
21
+ /** Pass this to `use:` on the element the editor should mount into. */
22
+ action: (node: HTMLElement) => EditorAction;
23
+ /** The editor itself. It exists before the element does. */
24
+ editor: Editor<T>;
25
+ /** Bumped on every change and selection move · read it in your markup. */
26
+ state: Readable<Editor<T>>;
27
+ }
28
+ /**
29
+ * Create an editor and the action that mounts it.
30
+ *
31
+ * ```svelte
32
+ * <script>
33
+ * import { matra } from '@matrajs/svelte'
34
+ * import { starterKit } from '@matrajs/core'
35
+ *
36
+ * const { action, editor, state } = matra({ extensions: starterKit })
37
+ * </script>
38
+ *
39
+ * <button onclick={() => editor.commands.toggleBold()}
40
+ * aria-pressed={$state.isActive('bold')}>Bold</button>
41
+ * <div use:action></div>
42
+ * ```
43
+ *
44
+ * The editor is created immediately rather than on mount, so commands, content
45
+ * and `getJSON()` all work before anything is on screen — which is what a
46
+ * server render and a test both need.
47
+ */
48
+ declare function matra<const T extends readonly AnyDef[]>(options: EditorOptions<T>): MatraAction<T>;
49
+ /**
50
+ * A store that changes whenever the editor does.
51
+ *
52
+ * The value is the editor itself, not a copy: a toolbar wants to ask
53
+ * `isActive` and `getText` at render time, and cloning a document to answer
54
+ * that would be the expensive way to do nothing.
55
+ */
56
+ declare function editorState<T extends readonly AnyDef[]>(editor: Editor<T>): Readable<Editor<T>>;
57
+
58
+ export { type EditorAction, type MatraAction, editorState, matra };
@@ -0,0 +1,58 @@
1
+ import { AnyDef, Editor, EditorOptions } from '@matrajs/core';
2
+ import { Readable } from 'svelte/store';
3
+
4
+ /**
5
+ * Svelte bindings, which are mostly an action.
6
+ *
7
+ * Svelte already has the shape this needs — an action runs when the element
8
+ * exists and is told when it goes away — so the binding is thin on purpose.
9
+ * What it adds over eight lines written inline is the double-mount guard and a
10
+ * store, and both of those are the parts people get wrong rather than the
11
+ * parts they find tedious.
12
+ *
13
+ * Written with stores rather than runes so it works the same in Svelte 4 and
14
+ * 5. A rune-only package would be a version boundary in exchange for nothing.
15
+ */
16
+ /** The shape Svelte's `use:` directive expects back. */
17
+ interface EditorAction {
18
+ destroy(): void;
19
+ }
20
+ interface MatraAction<T extends readonly AnyDef[]> {
21
+ /** Pass this to `use:` on the element the editor should mount into. */
22
+ action: (node: HTMLElement) => EditorAction;
23
+ /** The editor itself. It exists before the element does. */
24
+ editor: Editor<T>;
25
+ /** Bumped on every change and selection move · read it in your markup. */
26
+ state: Readable<Editor<T>>;
27
+ }
28
+ /**
29
+ * Create an editor and the action that mounts it.
30
+ *
31
+ * ```svelte
32
+ * <script>
33
+ * import { matra } from '@matrajs/svelte'
34
+ * import { starterKit } from '@matrajs/core'
35
+ *
36
+ * const { action, editor, state } = matra({ extensions: starterKit })
37
+ * </script>
38
+ *
39
+ * <button onclick={() => editor.commands.toggleBold()}
40
+ * aria-pressed={$state.isActive('bold')}>Bold</button>
41
+ * <div use:action></div>
42
+ * ```
43
+ *
44
+ * The editor is created immediately rather than on mount, so commands, content
45
+ * and `getJSON()` all work before anything is on screen — which is what a
46
+ * server render and a test both need.
47
+ */
48
+ declare function matra<const T extends readonly AnyDef[]>(options: EditorOptions<T>): MatraAction<T>;
49
+ /**
50
+ * A store that changes whenever the editor does.
51
+ *
52
+ * The value is the editor itself, not a copy: a toolbar wants to ask
53
+ * `isActive` and `getText` at render time, and cloning a document to answer
54
+ * that would be the expensive way to do nothing.
55
+ */
56
+ declare function editorState<T extends readonly AnyDef[]>(editor: Editor<T>): Readable<Editor<T>>;
57
+
58
+ export { type EditorAction, type MatraAction, editorState, matra };
package/dist/index.js ADDED
@@ -0,0 +1,31 @@
1
+ import { createEditor } from '@matrajs/core';
2
+ import { readable } from 'svelte/store';
3
+
4
+ // src/index.ts
5
+ function matra(options) {
6
+ const editor = createEditor(options);
7
+ const action = (node) => {
8
+ if (!editor.unsafe.view) editor.mount(node);
9
+ return {
10
+ destroy() {
11
+ editor.destroy();
12
+ }
13
+ };
14
+ };
15
+ return { action, editor, state: editorState(editor) };
16
+ }
17
+ function editorState(editor) {
18
+ return readable(editor, (set) => {
19
+ const republish = () => set(editor);
20
+ const offChange = editor.on("change", republish);
21
+ const offSelection = editor.on("selectionChange", republish);
22
+ return () => {
23
+ offChange();
24
+ offSelection();
25
+ };
26
+ });
27
+ }
28
+
29
+ export { editorState, matra };
30
+ //# sourceMappingURL=index.js.map
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;;AAkDO,SAAS,MACd,OAAA,EACgB;AAChB,EAAA,MAAM,MAAA,GAAS,aAAa,OAAO,CAAA;AAEnC,EAAA,MAAM,MAAA,GAAS,CAAC,IAAA,KAAoC;AAGlD,IAAA,IAAI,CAAC,MAAA,CAAO,MAAA,CAAO,IAAA,EAAM,MAAA,CAAO,MAAM,IAAI,CAAA;AAC1C,IAAA,OAAO;AAAA,MACL,OAAA,GAAU;AACR,QAAA,MAAA,CAAO,OAAA,EAAQ;AAAA,MACjB;AAAA,KACF;AAAA,EACF,CAAA;AAEA,EAAA,OAAO,EAAE,MAAA,EAAQ,MAAA,EAAQ,KAAA,EAAO,WAAA,CAAY,MAAM,CAAA,EAAE;AACtD;AASO,SAAS,YACd,MAAA,EACqB;AACrB,EAAA,OAAO,QAAA,CAAS,MAAA,EAAQ,CAAC,GAAA,KAAQ;AAC/B,IAAA,MAAM,SAAA,GAAY,MAAM,GAAA,CAAI,MAAM,CAAA;AAClC,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,EAAA,CAAG,QAAA,EAAU,SAAS,CAAA;AAC/C,IAAA,MAAM,YAAA,GAAe,MAAA,CAAO,EAAA,CAAG,iBAAA,EAAmB,SAAS,CAAA;AAC3D,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,EAAU;AACV,MAAA,YAAA,EAAa;AAAA,IACf,CAAA;AAAA,EACF,CAAC,CAAA;AACH","file":"index.js","sourcesContent":["import { type AnyDef, type Editor, type EditorOptions, createEditor } from '@matrajs/core'\nimport { type Readable, readable } from 'svelte/store'\n\n/**\n * Svelte bindings, which are mostly an action.\n *\n * Svelte already has the shape this needs — an action runs when the element\n * exists and is told when it goes away — so the binding is thin on purpose.\n * What it adds over eight lines written inline is the double-mount guard and a\n * store, and both of those are the parts people get wrong rather than the\n * parts they find tedious.\n *\n * Written with stores rather than runes so it works the same in Svelte 4 and\n * 5. A rune-only package would be a version boundary in exchange for nothing.\n */\n\n/** The shape Svelte's `use:` directive expects back. */\nexport interface EditorAction {\n destroy(): void\n}\n\nexport interface MatraAction<T extends readonly AnyDef[]> {\n /** Pass this to `use:` on the element the editor should mount into. */\n action: (node: HTMLElement) => EditorAction\n /** The editor itself. It exists before the element does. */\n editor: Editor<T>\n /** Bumped on every change and selection move · read it in your markup. */\n state: Readable<Editor<T>>\n}\n\n/**\n * Create an editor and the action that mounts it.\n *\n * ```svelte\n * <script>\n * import { matra } from '@matrajs/svelte'\n * import { starterKit } from '@matrajs/core'\n *\n * const { action, editor, state } = matra({ extensions: starterKit })\n * </script>\n *\n * <button onclick={() => editor.commands.toggleBold()}\n * aria-pressed={$state.isActive('bold')}>Bold</button>\n * <div use:action></div>\n * ```\n *\n * The editor is created immediately rather than on mount, so commands, content\n * and `getJSON()` all work before anything is on screen — which is what a\n * server render and a test both need.\n */\nexport function matra<const T extends readonly AnyDef[]>(\n options: EditorOptions<T>,\n): MatraAction<T> {\n const editor = createEditor(options)\n\n const action = (node: HTMLElement): EditorAction => {\n // A component rendered twice, or an action re-run by a hot reload, must not\n // attach a second view to one element.\n if (!editor.unsafe.view) editor.mount(node)\n return {\n destroy() {\n editor.destroy()\n },\n }\n }\n\n return { action, editor, state: editorState(editor) }\n}\n\n/**\n * A store that changes whenever the editor does.\n *\n * The value is the editor itself, not a copy: a toolbar wants to ask\n * `isActive` and `getText` at render time, and cloning a document to answer\n * that would be the expensive way to do nothing.\n */\nexport function editorState<T extends readonly AnyDef[]>(\n editor: Editor<T>,\n): Readable<Editor<T>> {\n return readable(editor, (set) => {\n const republish = () => set(editor)\n const offChange = editor.on('change', republish)\n const offSelection = editor.on('selectionChange', republish)\n return () => {\n offChange()\n offSelection()\n }\n })\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@matrajs/svelte",
3
+ "version": "0.15.0",
4
+ "description": "Svelte bindings for Matra — a use: action and a store that follows the editor.",
5
+ "license": "MIT",
6
+ "author": "Nahim Hossain Shohan",
7
+ "homepage": "https://matrajs.com",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/amrelaco/matra.git",
11
+ "directory": "packages/svelte"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/amrelaco/matra/issues"
15
+ },
16
+ "keywords": [
17
+ "editor",
18
+ "rich-text",
19
+ "svelte",
20
+ "matra"
21
+ ],
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "type": "module",
26
+ "sideEffects": false,
27
+ "main": "./dist/index.cjs",
28
+ "module": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "import": "./dist/index.js",
34
+ "require": "./dist/index.cjs"
35
+ }
36
+ },
37
+ "files": [
38
+ "dist",
39
+ "README.md"
40
+ ],
41
+ "dependencies": {
42
+ "@matrajs/core": "^0.15.0"
43
+ },
44
+ "peerDependencies": {
45
+ "svelte": ">=4"
46
+ },
47
+ "devDependencies": {
48
+ "@types/react": "^18.3.12",
49
+ "react": "^18.3.1",
50
+ "react-dom": "^18.3.1"
51
+ },
52
+ "scripts": {
53
+ "build": "tsup",
54
+ "clean": "rm -rf dist"
55
+ }
56
+ }