@openeditor/native 0.0.1

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/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # `@openeditor/native`
2
+
3
+ Component-first React Native SDK for OpenEditor.
4
+
5
+ ## Public surface
6
+
7
+ - `OpenEditorNative` as the primary editing surface
8
+ - `OpenEditorNativeViewer` for read-only rendering
9
+ - native document bridge helpers where renderer adaptation is required
10
+
11
+ ## Required peers
12
+
13
+ - `@openeditor/react-native-prose-editor`
14
+ - `react`
15
+ - `react-native`
16
+ - `react-native-keyboard-controller` (optional but recommended for keyboard toolbar placement)
17
+
18
+ ## Quick start
19
+
20
+ ```tsx
21
+ import { OpenEditorNative } from "@openeditor/native";
22
+
23
+ export function Example() {
24
+ return (
25
+ <OpenEditorNative
26
+ onChange={(document) => {
27
+ console.log(document);
28
+ }}
29
+ />
30
+ );
31
+ }
32
+ ```
33
+
34
+ ## Internal notes
35
+
36
+ - The native prose editor engine remains an implementation detail behind this package.
37
+ - Shared contracts come from `@openeditor/core`; content definitions come from `@openeditor/schema`.
@@ -0,0 +1,7 @@
1
+ import { type OpenEditorDocument, type OpenEditorDocumentMeta, type ProseMirrorDocument, type ProseMirrorNode } from "@openeditor/core";
2
+ export declare const sanitizeNativeEditorDocument: (document: OpenEditorDocument, meta?: OpenEditorDocumentMeta | undefined) => OpenEditorDocument;
3
+ export declare const toNativeEditorNode: (node: ProseMirrorNode) => ProseMirrorNode;
4
+ export declare const fromNativeEditorNode: (node: ProseMirrorNode) => ProseMirrorNode;
5
+ export declare const toNativeEditorDocument: (document: OpenEditorDocument) => ProseMirrorDocument;
6
+ export declare const fromNativeEditorDocument: (document: ProseMirrorDocument, meta?: OpenEditorDocumentMeta) => OpenEditorDocument;
7
+ export declare const createNativeEditorDocument: (content: ProseMirrorNode[], meta?: OpenEditorDocumentMeta) => ProseMirrorDocument;
@@ -0,0 +1,69 @@
1
+ import { cloneNode, createDocument, findBlockSpecForNode, fromProseMirrorDocument, normalizeDocument, toProseMirrorDocument, } from "@openeditor/core";
2
+ import { defaultBlockRegistry } from "@openeditor/schema";
3
+ const NATIVE_HEADING_PATTERN = /^h([1-6])$/;
4
+ const clampHeadingLevel = (value) => typeof value === "number" ? Math.min(Math.max(value, 1), 6) : 2;
5
+ const cloneAttrsWithoutHeadingLevel = (attrs) => {
6
+ if (!attrs) {
7
+ return undefined;
8
+ }
9
+ const { level: _level, ...nextAttrs } = attrs;
10
+ return Object.keys(nextAttrs).length ? nextAttrs : undefined;
11
+ };
12
+ const withMappedContent = (node, mapNode) => {
13
+ const cloned = cloneNode(node);
14
+ return {
15
+ ...cloned,
16
+ ...(cloned.content ? { content: cloned.content.map(mapNode) } : {}),
17
+ };
18
+ };
19
+ const sanitizeNativeNode = (node) => {
20
+ const spec = findBlockSpecForNode(defaultBlockRegistry, node);
21
+ if (spec?.support?.native === "unsupported") {
22
+ return null;
23
+ }
24
+ const cloned = cloneNode(node);
25
+ const nextContent = cloned.content
26
+ ?.map(sanitizeNativeNode)
27
+ .filter((child) => child !== null);
28
+ return {
29
+ ...cloned,
30
+ ...(nextContent ? { content: nextContent } : {}),
31
+ };
32
+ };
33
+ export const sanitizeNativeEditorDocument = (document, meta = document.meta) => createDocument(normalizeDocument(document).content
34
+ .map(sanitizeNativeNode)
35
+ .filter((node) => node !== null), meta);
36
+ export const toNativeEditorNode = (node) => {
37
+ if (node.type === "heading") {
38
+ const cloned = withMappedContent(node, toNativeEditorNode);
39
+ const attrs = cloneAttrsWithoutHeadingLevel(node.attrs);
40
+ const { attrs: _attrs, ...headingNode } = cloned;
41
+ return {
42
+ ...headingNode,
43
+ type: `h${clampHeadingLevel(node.attrs?.level)}`,
44
+ ...(attrs ? { attrs } : {}),
45
+ };
46
+ }
47
+ return withMappedContent(node, toNativeEditorNode);
48
+ };
49
+ export const fromNativeEditorNode = (node) => {
50
+ const headingMatch = NATIVE_HEADING_PATTERN.exec(node.type);
51
+ if (headingMatch) {
52
+ const cloned = withMappedContent(node, fromNativeEditorNode);
53
+ return {
54
+ ...cloned,
55
+ type: "heading",
56
+ attrs: { ...node.attrs, level: Number(headingMatch[1]) },
57
+ };
58
+ }
59
+ return withMappedContent(node, fromNativeEditorNode);
60
+ };
61
+ export const toNativeEditorDocument = (document) => ({
62
+ type: "doc",
63
+ content: toProseMirrorDocument(sanitizeNativeEditorDocument(document)).content.map(toNativeEditorNode),
64
+ });
65
+ export const fromNativeEditorDocument = (document, meta) => normalizeDocument(fromProseMirrorDocument({
66
+ type: "doc",
67
+ content: document.content.map(fromNativeEditorNode),
68
+ }, meta));
69
+ export const createNativeEditorDocument = (content, meta) => toNativeEditorDocument(createDocument(content, meta));
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/document.ts"],"names":[],"mappings":"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","file":"document.js","sourcesContent":["import {\n cloneNode,\n createDocument,\n findBlockSpecForNode,\n fromProseMirrorDocument,\n normalizeDocument,\n toProseMirrorDocument,\n type OpenEditorDocument,\n type OpenEditorDocumentMeta,\n type ProseMirrorAttrs,\n type ProseMirrorDocument,\n type ProseMirrorNode,\n} from \"@openeditor/core\";\nimport { defaultBlockRegistry } from \"@openeditor/schema\";\n\nconst NATIVE_HEADING_PATTERN = /^h([1-6])$/;\n\nconst clampHeadingLevel = (value: unknown) =>\n typeof value === \"number\" ? Math.min(Math.max(value, 1), 6) : 2;\n\nconst cloneAttrsWithoutHeadingLevel = (attrs?: ProseMirrorAttrs): ProseMirrorAttrs | undefined => {\n if (!attrs) {\n return undefined;\n }\n\n const { level: _level, ...nextAttrs } = attrs;\n return Object.keys(nextAttrs).length ? nextAttrs : undefined;\n};\n\nconst withMappedContent = (\n node: ProseMirrorNode,\n mapNode: (node: ProseMirrorNode) => ProseMirrorNode,\n): ProseMirrorNode => {\n const cloned = cloneNode(node);\n return {\n ...cloned,\n ...(cloned.content ? { content: cloned.content.map(mapNode) } : {}),\n };\n};\n\nconst sanitizeNativeNode = (node: ProseMirrorNode): ProseMirrorNode | null => {\n const spec = findBlockSpecForNode(defaultBlockRegistry, node);\n if (spec?.support?.native === \"unsupported\") {\n return null;\n }\n\n const cloned = cloneNode(node);\n const nextContent = cloned.content\n ?.map(sanitizeNativeNode)\n .filter((child): child is ProseMirrorNode => child !== null);\n\n return {\n ...cloned,\n ...(nextContent ? { content: nextContent } : {}),\n };\n};\n\nexport const sanitizeNativeEditorDocument = (\n document: OpenEditorDocument,\n meta: OpenEditorDocumentMeta | undefined = document.meta,\n): OpenEditorDocument =>\n createDocument(\n normalizeDocument(document).content\n .map(sanitizeNativeNode)\n .filter((node): node is ProseMirrorNode => node !== null),\n meta,\n );\n\nexport const toNativeEditorNode = (node: ProseMirrorNode): ProseMirrorNode => {\n if (node.type === \"heading\") {\n const cloned = withMappedContent(node, toNativeEditorNode);\n const attrs = cloneAttrsWithoutHeadingLevel(node.attrs);\n const { attrs: _attrs, ...headingNode } = cloned;\n\n return {\n ...headingNode,\n type: `h${clampHeadingLevel(node.attrs?.level)}`,\n ...(attrs ? { attrs } : {}),\n };\n }\n\n return withMappedContent(node, toNativeEditorNode);\n};\n\nexport const fromNativeEditorNode = (node: ProseMirrorNode): ProseMirrorNode => {\n const headingMatch = NATIVE_HEADING_PATTERN.exec(node.type);\n\n if (headingMatch) {\n const cloned = withMappedContent(node, fromNativeEditorNode);\n return {\n ...cloned,\n type: \"heading\",\n attrs: { ...node.attrs, level: Number(headingMatch[1]) },\n };\n }\n\n return withMappedContent(node, fromNativeEditorNode);\n};\n\nexport const toNativeEditorDocument = (document: OpenEditorDocument): ProseMirrorDocument =>\n ({\n type: \"doc\",\n content: toProseMirrorDocument(sanitizeNativeEditorDocument(document)).content.map(toNativeEditorNode),\n });\n\nexport const fromNativeEditorDocument = (\n document: ProseMirrorDocument,\n meta?: OpenEditorDocumentMeta,\n): OpenEditorDocument =>\n normalizeDocument(\n fromProseMirrorDocument(\n {\n type: \"doc\",\n content: document.content.map(fromNativeEditorNode),\n },\n meta,\n ),\n );\n\nexport const createNativeEditorDocument = (\n content: ProseMirrorNode[],\n meta?: OpenEditorDocumentMeta,\n): ProseMirrorDocument => toNativeEditorDocument(createDocument(content, meta));\n"]}
@@ -0,0 +1,66 @@
1
+ import { type OpenEditorDocument, type ProseMirrorNode } from "@openeditor/core";
2
+ import { type ReactNode } from "react";
3
+ import type { NativeRichTextEditorToolbarPlacement } from "@openeditor/react-native-prose-editor";
4
+ export type OpenEditorNativeProps = {
5
+ document?: OpenEditorDocument;
6
+ editable?: boolean;
7
+ showToolbar?: boolean;
8
+ theme?: Partial<OpenEditorNativeTheme>;
9
+ toolbarPlacement?: NativeRichTextEditorToolbarPlacement;
10
+ onChange?: (document: OpenEditorDocument) => void;
11
+ };
12
+ export type OpenEditorNativeViewerProps = Omit<OpenEditorNativeProps, "editable" | "showToolbar" | "onChange"> & {
13
+ showToolbar?: false;
14
+ renderers?: Partial<Record<string, OpenEditorNativeViewerRenderer>>;
15
+ };
16
+ export type OpenEditorNativeToolbarAction = {
17
+ key: string;
18
+ label: string;
19
+ onPress?: (key: string) => void;
20
+ };
21
+ export type OpenEditorNativeToolbarProps = {
22
+ actions?: readonly OpenEditorNativeToolbarAction[];
23
+ onActionPress?: (key: string) => void;
24
+ theme?: Partial<OpenEditorNativeTheme>;
25
+ };
26
+ export type OpenEditorNativeViewerRenderer = (context: {
27
+ node: ProseMirrorNode;
28
+ children: ReactNode;
29
+ theme: Required<OpenEditorNativeTheme>;
30
+ }) => ReactNode;
31
+ export type OpenEditorNativeToolbarTheme = {
32
+ height?: number;
33
+ };
34
+ export type OpenEditorNativeTheme = {
35
+ background: string;
36
+ surface: string;
37
+ surfaceMuted: string;
38
+ text: string;
39
+ textSoft?: string;
40
+ muted: string;
41
+ placeholder?: string;
42
+ border: string;
43
+ borderStrong?: string;
44
+ accent: string;
45
+ accentText: string;
46
+ accentStrong?: string;
47
+ primary: string;
48
+ primaryText: string;
49
+ codeBackground?: string;
50
+ codeText?: string;
51
+ debugBackground?: string;
52
+ debugText?: string;
53
+ debugHeading?: string;
54
+ debugBorder?: string;
55
+ debugButtonBackground?: string;
56
+ success?: string;
57
+ shadow?: string;
58
+ backdrop: string;
59
+ toolbar?: OpenEditorNativeToolbarTheme;
60
+ };
61
+ export declare const OpenEditorNativeToolbar: ({ actions, onActionPress, theme, }: OpenEditorNativeToolbarProps) => import("react").JSX.Element;
62
+ export declare const OpenEditorNative: ({ document, editable, showToolbar, theme, toolbarPlacement, onChange, }: OpenEditorNativeProps) => import("react").JSX.Element;
63
+ export declare const OpenEditorNativeViewer: ({ document, theme, renderers, }: OpenEditorNativeViewerProps) => import("react").JSX.Element;
64
+ export { createNativeEditorDocument, fromNativeEditorDocument, sanitizeNativeEditorDocument, toNativeEditorDocument, } from "./document.js";
65
+ export { createOpenEditorNativeToolbarItems, defaultDocumentForToolbarAction, nativeToolbarParityCoverage, openEditorNativeToolbarActionKeys, openEditorNativeToolbarItems, } from "./toolbar.js";
66
+ export type { OpenEditorNativeToolbarActionKey } from "./toolbar.js";