@octane-xplat/tiptap 0.0.1 → 0.10.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.
@@ -0,0 +1,3 @@
1
+ import { ensureJSONBridge, jsonBridgeReady } from "./index2.js";
2
+ import { TiptapEditor, supported } from "./index3.js";
3
+ export { TiptapEditor, ensureJSONBridge, jsonBridgeReady, supported };
@@ -0,0 +1,82 @@
1
+ //#region src/json-bridge.ts
2
+ var bridge;
3
+ /** zeed-dom's classList is a plain {contains,add,remove} object; tiptap's
4
+ * codeBlock parseHTML spreads it. Wrap the VElement getter so it hands back
5
+ * an iterable array that keeps the mutation methods. */
6
+ function patchClassList(VElement) {
7
+ const proto = VElement?.prototype;
8
+ if (!proto) throw new Error("VElement.prototype unavailable");
9
+ const desc = Object.getOwnPropertyDescriptor(proto, "classList");
10
+ if (!desc?.get) throw new Error("classList is not a prototype getter");
11
+ if (desc.get.__xplatPatched) return;
12
+ const patched = function() {
13
+ const cl = desc.get.call(this);
14
+ if (cl && typeof cl[Symbol.iterator] !== "function") {
15
+ const tokens = String(this.getAttribute("class") ?? "").split(/\s+/).filter(Boolean);
16
+ return Object.assign(tokens, cl);
17
+ }
18
+ return cl;
19
+ };
20
+ patched.__xplatPatched = true;
21
+ Object.defineProperty(proto, "classList", {
22
+ configurable: true,
23
+ get: patched,
24
+ set: desc.set
25
+ });
26
+ }
27
+ /** Install the minimal parse globals generateJSON's elementFromString
28
+ * reads. Only installs when the host lacks them, so a real DOM (web
29
+ * target) is never clobbered. */
30
+ function installDomShim(zeed) {
31
+ const g = globalThis;
32
+ if (g.window?.DOMParser) return;
33
+ class ZeedDOMParser {
34
+ parseFromString(html) {
35
+ const doc = zeed.createHTMLDocument();
36
+ doc.body.innerHTML = html;
37
+ return doc;
38
+ }
39
+ }
40
+ g.window = { DOMParser: ZeedDOMParser };
41
+ g.document = zeed.createHTMLDocument();
42
+ }
43
+ async function ensureJSONBridge() {
44
+ if (bridge !== void 0) return bridge !== null;
45
+ try {
46
+ const [core, sk, sr, zeed] = await Promise.all([
47
+ import("@tiptap/core"),
48
+ import("@tiptap/starter-kit"),
49
+ import("@tiptap/static-renderer"),
50
+ import("zeed-dom")
51
+ ]);
52
+ patchClassList(zeed.VElement);
53
+ installDomShim(zeed);
54
+ const StarterKit = sk.default ?? sk.StarterKit;
55
+ bridge = {
56
+ generateJSON: core.generateJSON,
57
+ renderToHTMLString: sr.renderToHTMLString,
58
+ extensions: [StarterKit]
59
+ };
60
+ return true;
61
+ } catch {
62
+ bridge = null;
63
+ return false;
64
+ }
65
+ }
66
+ /** HTML → doc JSON through tiptap's own parseHTML rules. Null when the
67
+ * bridge hasn't loaded (call ensureJSONBridge first / check `ready`). */
68
+ function htmlToJSON(html) {
69
+ return bridge?.generateJSON(html, bridge.extensions) ?? null;
70
+ }
71
+ /** Doc JSON → HTML via tiptap's own renderHTML rules (no DOM). */
72
+ function jsonToHTML(doc) {
73
+ return bridge?.renderToHTMLString({
74
+ extensions: bridge.extensions,
75
+ content: doc
76
+ }) ?? null;
77
+ }
78
+ function jsonBridgeReady() {
79
+ return bridge != null;
80
+ }
81
+ //#endregion
82
+ export { ensureJSONBridge, htmlToJSON, jsonBridgeReady, jsonToHTML };
@@ -0,0 +1,211 @@
1
+ import { ensureJSONBridge, htmlToJSON, jsonBridgeReady, jsonToHTML } from "./index2.js";
2
+ import { defineUniversalComponent, hookSlots, universalComponent, universalPlan, universalProps, universalValue, useCallback, useEffect, useImperativeHandle, useRef, useState } from "@nativescript-community/octane";
3
+ import { RichTextEditor, supported } from "@octane-xplat/richtext";
4
+ //#region src/TiptapEditor.tsrx
5
+ var _hs$ = /* @__PURE__ */ hookSlots(8);
6
+ var _h$0 = /* @__PURE__ */ Symbol(_hs$);
7
+ var _h$1 = /* @__PURE__ */ Symbol(_hs$ + 1);
8
+ var _h$2 = /* @__PURE__ */ Symbol(_hs$ + 2);
9
+ var _h$3 = /* @__PURE__ */ Symbol(_hs$ + 3);
10
+ var _h$4 = /* @__PURE__ */ Symbol(_hs$ + 4);
11
+ var _h$5 = /* @__PURE__ */ Symbol(_hs$ + 5);
12
+ var _h$6 = /* @__PURE__ */ Symbol(_hs$ + 6);
13
+ var _h$7 = /* @__PURE__ */ Symbol(_hs$ + 7);
14
+ var __octaneUniversalPlan0 = universalPlan("nativescript", {
15
+ "kind": "slot",
16
+ "slot": 0
17
+ });
18
+ var supported$1 = supported;
19
+ var TiptapEditor = defineUniversalComponent("nativescript", function TiptapEditor(props) {
20
+ const inner = useRef(null, _h$0);
21
+ const [exposedHandle, setHandle] = useState(null, _h$1);
22
+ useImperativeHandle(props.ref, () => exposedHandle, [exposedHandle], _h$2);
23
+ const jsonReady = useRef(false, _h$3);
24
+ const lastJSON = useRef(void 0, _h$4);
25
+ const bindHost = useCallback((h) => {
26
+ inner.current = h;
27
+ setHandle((previous) => previous?.native === h?.native ? previous : h ? {
28
+ getHTML: () => h.getHTML(),
29
+ setHTML: (html) => h.setHTML(html),
30
+ getJSON: () => jsonBridgeReady() ? htmlToJSON(h.getHTML()) : null,
31
+ setJSON: (doc) => {
32
+ const html = jsonBridgeReady() ? jsonToHTML(doc) : null;
33
+ if (html != null) {
34
+ lastJSON.current = JSON.stringify(doc);
35
+ h.setHTML(html);
36
+ }
37
+ },
38
+ apply: (format) => h.apply(format),
39
+ linkTo: (url, anchor) => h.linkTo(url, anchor),
40
+ removeLink: () => h.removeLink(),
41
+ isActive: (format) => h.isActive(format),
42
+ undo: () => h.undo(),
43
+ redo: () => h.redo(),
44
+ focus: () => h.focus(),
45
+ blur: () => h.blur(),
46
+ isFocused: () => h.isFocused(),
47
+ native: h.native
48
+ } : null);
49
+ }, [], _h$5);
50
+ useEffect(() => {
51
+ ensureJSONBridge().then((ok) => {
52
+ jsonReady.current = ok;
53
+ props.onJSONReady?.(ok);
54
+ });
55
+ }, [], _h$6);
56
+ useEffect(() => {
57
+ if (!props.json || !jsonBridgeReady() || !inner.current) return;
58
+ const stamp = JSON.stringify(props.json);
59
+ if (stamp === lastJSON.current) return;
60
+ const html = jsonToHTML(props.json);
61
+ if (html != null) {
62
+ lastJSON.current = stamp;
63
+ inner.current.setHTML(html);
64
+ }
65
+ }, [props.json], _h$7);
66
+ const emitJSON = () => {
67
+ if (!inner.current || !jsonBridgeReady()) return;
68
+ const doc = htmlToJSON(inner.current.getHTML());
69
+ if (doc) {
70
+ lastJSON.current = JSON.stringify(doc);
71
+ props.onJSONChange?.(doc);
72
+ }
73
+ };
74
+ return universalValue(__octaneUniversalPlan0, [universalComponent("nativescript", RichTextEditor, universalProps([
75
+ [
76
+ "set",
77
+ "id",
78
+ props.id
79
+ ],
80
+ [
81
+ "set",
82
+ "className",
83
+ props.className
84
+ ],
85
+ [
86
+ "set",
87
+ "style",
88
+ props.style
89
+ ],
90
+ [
91
+ "set",
92
+ "value",
93
+ props.value
94
+ ],
95
+ [
96
+ "set",
97
+ "placeholder",
98
+ props.placeholder
99
+ ],
100
+ [
101
+ "set",
102
+ "editable",
103
+ props.editable
104
+ ],
105
+ [
106
+ "set",
107
+ "autofocus",
108
+ props.autofocus
109
+ ],
110
+ [
111
+ "set",
112
+ "onReady",
113
+ props.onReady
114
+ ],
115
+ [
116
+ "set",
117
+ "onChange",
118
+ (html) => {
119
+ props.onChange?.(html);
120
+ emitJSON();
121
+ }
122
+ ],
123
+ [
124
+ "set",
125
+ "onSelectionChange",
126
+ props.onSelectionChange
127
+ ],
128
+ [
129
+ "set",
130
+ "onFocus",
131
+ props.onFocus
132
+ ],
133
+ [
134
+ "set",
135
+ "onBlur",
136
+ props.onBlur
137
+ ],
138
+ [
139
+ "set",
140
+ "ref",
141
+ bindHost
142
+ ],
143
+ [
144
+ "set",
145
+ "android",
146
+ props.android
147
+ ],
148
+ [
149
+ "set",
150
+ "ios",
151
+ props.ios
152
+ ],
153
+ [
154
+ "set",
155
+ "row",
156
+ props.row
157
+ ],
158
+ [
159
+ "set",
160
+ "col",
161
+ props.col
162
+ ],
163
+ [
164
+ "set",
165
+ "rowSpan",
166
+ props.rowSpan
167
+ ],
168
+ [
169
+ "set",
170
+ "colSpan",
171
+ props.colSpan
172
+ ],
173
+ [
174
+ "set",
175
+ "dock",
176
+ props.dock
177
+ ],
178
+ [
179
+ "set",
180
+ "left",
181
+ props.left
182
+ ],
183
+ [
184
+ "set",
185
+ "top",
186
+ props.top
187
+ ],
188
+ [
189
+ "set",
190
+ "flexGrow",
191
+ props.flexGrow
192
+ ],
193
+ [
194
+ "set",
195
+ "flexShrink",
196
+ props.flexShrink
197
+ ],
198
+ [
199
+ "set",
200
+ "alignSelf",
201
+ props.alignSelf
202
+ ],
203
+ [
204
+ "set",
205
+ "order",
206
+ props.order
207
+ ]
208
+ ]))]);
209
+ }, { "module": "@nativescript-community/octane" });
210
+ //#endregion
211
+ export { TiptapEditor, supported$1 as supported };
@@ -0,0 +1,8 @@
1
+ {
2
+ "version": 1,
3
+ "buildId": "3880e698-2b9a-436f-af5a-a7ec180643bf",
4
+ "mode": "production",
5
+ "capabilities": {
6
+ "independentHydration": false
7
+ }
8
+ }
@@ -0,0 +1,12 @@
1
+ import { TiptapEditor, supported } from "./index2.js";
2
+ //#region src/index.web.ts
3
+ /** Web needs no lazy bridge — tiptap is in the bundle already. Kept for
4
+ * call-site parity with the native entry. */
5
+ function ensureJSONBridge() {
6
+ return Promise.resolve(true);
7
+ }
8
+ function jsonBridgeReady() {
9
+ return true;
10
+ }
11
+ //#endregion
12
+ export { TiptapEditor, ensureJSONBridge, jsonBridgeReady, supported };
@@ -0,0 +1,183 @@
1
+ import { __s, bag3, clone, componentSlot, hookSlots, setStyle, template, useEffect, useImperativeHandle, useRef, withSlot } from "octane";
2
+ import { updateFreshClassName } from "octane/internal/client";
3
+ import { EditorContent, useEditor } from "@octanejs/tiptap";
4
+ import StarterKit from "@tiptap/starter-kit";
5
+ //#region src/TiptapEditor.web.tsrx
6
+ var _t$0 = /* @__PURE__ */ template("<div></div>");
7
+ var _hs$ = /* @__PURE__ */ hookSlots(10);
8
+ var _h$5 = /* @__PURE__ */ Symbol(_hs$ + 5);
9
+ var supported = true;
10
+ var COMMAND = {
11
+ bold: (c) => c.toggleBold(),
12
+ italic: (c) => c.toggleItalic(),
13
+ underline: (c) => c.toggleUnderline(),
14
+ strikethrough: (c) => c.toggleStrike(),
15
+ code: (c) => c.toggleCode(),
16
+ blockquote: (c) => c.toggleBlockquote(),
17
+ bulletList: (c) => c.toggleBulletList(),
18
+ orderedList: (c) => c.toggleOrderedList(),
19
+ codeBlock: (c) => c.toggleCodeBlock(),
20
+ horizontalRule: (c) => c.setHorizontalRule(),
21
+ paragraph: (c) => c.setParagraph(),
22
+ heading1: (c) => c.toggleHeading({ level: 1 }),
23
+ heading2: (c) => c.toggleHeading({ level: 2 }),
24
+ heading3: (c) => c.toggleHeading({ level: 3 }),
25
+ heading4: (c) => c.toggleHeading({ level: 4 }),
26
+ heading5: (c) => c.toggleHeading({ level: 5 }),
27
+ heading6: (c) => c.toggleHeading({ level: 6 })
28
+ };
29
+ var ACTIVE_OF = [
30
+ ["bold", "bold"],
31
+ ["italic", "italic"],
32
+ ["underline", "underline"],
33
+ ["strikethrough", "strike"],
34
+ ["code", "code"],
35
+ ["blockquote", "blockquote"],
36
+ ["bulletList", "bulletList"],
37
+ ["orderedList", "orderedList"],
38
+ ["codeBlock", "codeBlock"],
39
+ ["link", "link"],
40
+ [
41
+ "heading1",
42
+ "heading",
43
+ { level: 1 }
44
+ ],
45
+ [
46
+ "heading2",
47
+ "heading",
48
+ { level: 2 }
49
+ ],
50
+ [
51
+ "heading3",
52
+ "heading",
53
+ { level: 3 }
54
+ ],
55
+ [
56
+ "heading4",
57
+ "heading",
58
+ { level: 4 }
59
+ ],
60
+ [
61
+ "heading5",
62
+ "heading",
63
+ { level: 5 }
64
+ ],
65
+ [
66
+ "heading6",
67
+ "heading",
68
+ { level: 6 }
69
+ ],
70
+ ["paragraph", "paragraph"]
71
+ ];
72
+ function activeFormats(editor) {
73
+ const out = [];
74
+ for (const [name, kind, attrs] of ACTIVE_OF) if (editor.isActive(kind, attrs)) out.push(name);
75
+ return out;
76
+ }
77
+ var TiptapEditor = /* @__PURE__ */ __s(function TiptapEditor(props, __s, __extra) {
78
+ const lastEmitted = useRef(void 0, _hs$ + 0);
79
+ const lastJSON = useRef(void 0, _hs$ + 1);
80
+ const lastHTMLInput = useRef(JSON.stringify([props.json !== void 0, props.value]), _hs$ + 2);
81
+ const lastJSONInput = useRef(JSON.stringify(props.json), _hs$ + 3);
82
+ const bound = useRef(void 0, _hs$ + 4);
83
+ const editor = withSlot(_h$5, useEditor, {
84
+ extensions: [StarterKit],
85
+ content: props.json ?? props.value ?? "",
86
+ editable: props.editable !== false,
87
+ autofocus: props.autofocus ?? false,
88
+ onCreate: () => props.onReady?.(),
89
+ onUpdate: ({ editor: e }) => {
90
+ const html = e.getHTML();
91
+ lastEmitted.current = html;
92
+ props.onChange?.(html);
93
+ const doc = e.getJSON();
94
+ lastJSON.current = JSON.stringify(doc);
95
+ props.onJSONChange?.(doc);
96
+ },
97
+ onFocus: () => props.onFocus?.(),
98
+ onBlur: () => props.onBlur?.(),
99
+ onSelectionUpdate: ({ editor: e }) => props.onSelectionChange?.({
100
+ start: e.state.selection.from,
101
+ end: e.state.selection.to,
102
+ active: activeFormats(e)
103
+ })
104
+ });
105
+ useEffect(() => {
106
+ if (!editor) return;
107
+ const stamp = JSON.stringify([props.json !== void 0, props.value]);
108
+ if (stamp === lastHTMLInput.current) return;
109
+ lastHTMLInput.current = stamp;
110
+ if (props.value === void 0 || props.json !== void 0) return;
111
+ if (props.value === lastEmitted.current) return;
112
+ editor.commands.setContent(props.value);
113
+ }, [
114
+ editor,
115
+ props.value,
116
+ props.json
117
+ ], _hs$ + 6);
118
+ useEffect(() => {
119
+ if (!editor) return;
120
+ const stamp = JSON.stringify(props.json);
121
+ if (stamp === lastJSONInput.current) return;
122
+ lastJSONInput.current = stamp;
123
+ if (props.json === void 0 || stamp === lastJSON.current) return;
124
+ editor.commands.setContent(props.json);
125
+ }, [editor, props.json], _hs$ + 7);
126
+ useEffect(() => {
127
+ if (!editor) return;
128
+ editor.setEditable(props.editable !== false);
129
+ }, [editor, props.editable], _hs$ + 8);
130
+ useImperativeHandle(props.ref, () => {
131
+ if (!editor) return null;
132
+ const e = editor;
133
+ bound.current = {
134
+ getHTML: () => e.getHTML(),
135
+ setHTML: (html) => e.commands.setContent(html),
136
+ getJSON: () => e.getJSON(),
137
+ setJSON: (doc) => e.commands.setContent(doc),
138
+ apply: (format) => {
139
+ const run = COMMAND[format];
140
+ if (run) run(e.chain().focus()).run();
141
+ },
142
+ linkTo: (url, anchor) => {
143
+ if (anchor != null && anchor !== "") e.chain().focus().insertContent({
144
+ type: "text",
145
+ text: anchor,
146
+ marks: [{
147
+ type: "link",
148
+ attrs: { href: url }
149
+ }]
150
+ }).run();
151
+ else e.chain().focus().extendMarkRange("link").setLink({ href: url }).run();
152
+ },
153
+ removeLink: () => e.chain().focus().unsetLink().run(),
154
+ isActive: (format) => activeFormats(e).includes(format),
155
+ undo: () => e.commands.undo(),
156
+ redo: () => e.commands.redo(),
157
+ focus: () => e.commands.focus(),
158
+ blur: () => e.commands.blur(),
159
+ isFocused: () => e.isFocused,
160
+ native: e
161
+ };
162
+ return bound.current;
163
+ }, [editor], _hs$ + 9);
164
+ if (__s.block.pending && !__s.block.crossRenderUpdate) return;
165
+ let _b = __s.slots[0];
166
+ if (_b === void 0) {
167
+ let _m1;
168
+ const _root = clone(_t$0);
169
+ _m1 = _root;
170
+ _b = bag3(__s, _root, void 0, _m1, void 0);
171
+ }
172
+ _b.a = updateFreshClassName(["vx-richtext", props.className], _b.a, _b.b);
173
+ {
174
+ const _v = props.style;
175
+ if (_b.c !== _v) {
176
+ setStyle(_b.b, _v, _b.c);
177
+ _b.c = _v;
178
+ }
179
+ }
180
+ componentSlot(__s, 1, _b.b, EditorContent, { "editor": editor }, void 0, void 0, 2, void 0, void 0, "c:e9be3bbb");
181
+ });
182
+ //#endregion
183
+ export { TiptapEditor, supported };
@@ -0,0 +1,8 @@
1
+ {
2
+ "version": 1,
3
+ "buildId": "e01adeb4-b2cd-4b21-9fdd-57831df35c5b",
4
+ "mode": "production",
5
+ "capabilities": {
6
+ "independentHydration": false
7
+ }
8
+ }
package/package.json CHANGED
@@ -1,19 +1,93 @@
1
1
  {
2
2
  "name": "@octane-xplat/tiptap",
3
- "version": "0.0.1",
4
- "description": "Unified rich-text editor facade for Octane xplat — @octanejs/tiptap on web, @octane-xplat/richtext (Aztec) + tiptap doc model on native",
3
+ "version": "0.10.0",
4
+ "description": "Unified rich-text editor facade for Octane xplat — live tiptap on web/AppKit, Aztec + tiptap document model on Android",
5
+ "keywords": [
6
+ "cross-platform",
7
+ "editor",
8
+ "octane",
9
+ "prosemirror",
10
+ "rich-text",
11
+ "richtext",
12
+ "tiptap",
13
+ "xplat"
14
+ ],
15
+ "homepage": "https://github.com/octane-xplat/octane-xplat/tree/main/packages/tiptap",
5
16
  "license": "MIT",
6
17
  "repository": {
7
18
  "type": "git",
8
- "url": "https://github.com/octane-xplat/octane-xplat.git",
19
+ "url": "https://github.com/octane-xplat/octane-xplat",
9
20
  "directory": "packages/tiptap"
10
21
  },
11
- "homepage": "https://github.com/octane-xplat/octane-xplat/tree/main/packages/tiptap",
12
- "main": "./index.js",
13
- "types": "./index.d.ts",
14
22
  "files": [
15
- "README.md",
16
- "index.js",
17
- "index.d.ts"
18
- ]
19
- }
23
+ "dist",
24
+ "types",
25
+ "src",
26
+ "platforms"
27
+ ],
28
+ "type": "module",
29
+ "exports": {
30
+ ".": {
31
+ "macos": {
32
+ "types": "./types/index.macos.d.ts",
33
+ "default": "./src/index.macos.ts"
34
+ },
35
+ "web": {
36
+ "types": "./types/index.d.ts",
37
+ "default": "./dist/web/index.js"
38
+ },
39
+ "native": {
40
+ "types": "./types/index.d.ts",
41
+ "default": "./dist/native/index.js"
42
+ },
43
+ "types": "./types/index.d.ts",
44
+ "default": "./dist/native/index.js"
45
+ }
46
+ },
47
+ "publishConfig": {
48
+ "access": "public"
49
+ },
50
+ "dependencies": {
51
+ "@octane-xplat/richtext": "0.10.0",
52
+ "@octanejs/tiptap": "0.0.51",
53
+ "@tiptap/core": "3.28.0",
54
+ "@tiptap/pm": "3.28.0",
55
+ "@tiptap/starter-kit": "3.28.0",
56
+ "@tiptap/static-renderer": "3.28.0",
57
+ "zeed-dom": "^0.15.1"
58
+ },
59
+ "devDependencies": {
60
+ "@nativescript-community/octane": "0.2.4",
61
+ "@nativescript/core": "9.1.2",
62
+ "@octane-xplat/macos-renderer": "0.10.0",
63
+ "@octanejs/vite-plugin": "0.1.61",
64
+ "octane": "0.6.3",
65
+ "typescript": "5.9.3",
66
+ "vite": "8.3.0"
67
+ },
68
+ "peerDependencies": {
69
+ "@nativescript-community/octane": ">=0.2.1 <1",
70
+ "@nativescript/core": ">=9.1.0 <10",
71
+ "octane": ">=0.6.3 <1"
72
+ },
73
+ "peerDependenciesMeta": {
74
+ "@nativescript-community/octane": {
75
+ "optional": true
76
+ },
77
+ "@nativescript/core": {
78
+ "optional": true
79
+ },
80
+ "octane": {
81
+ "optional": true
82
+ }
83
+ },
84
+ "nativescript": {
85
+ "platforms": {
86
+ "android": "9.1.0",
87
+ "ios": "9.1.0"
88
+ }
89
+ },
90
+ "scripts": {
91
+ "build": "vite build && vite build --mode native && tsc -p tsconfig.types.json && node ../../scripts/check-native-dist.mjs && node ../richtext/build-browser.mjs"
92
+ }
93
+ }
@@ -0,0 +1,9 @@
1
+ /** @jsxImportSource @octane-xplat/macos-renderer */
2
+ import type { TiptapEditorProps } from './types'
3
+ import browser from '../dist/macos/browser.js'
4
+ import { NativeEditor } from '@octane-xplat/richtext/macos-host'
5
+
6
+ /** AppKit mounts the local editor engine in WKWebView. */
7
+ export function TiptapEditor(props: TiptapEditorProps) {
8
+ return <NativeEditor {...props} browser={browser} />
9
+ }