@astryxdesign/lab 0.2.0-canary.c366298 → 0.2.0-canary.c75407e

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.
@@ -1 +1 @@
1
- {"version":3,"file":"RichTextEditor.d.ts","sourceRoot":"","sources":["../../src/RichTextEditor/RichTextEditor.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAQL,KAAK,SAAS,EAEf,MAAM,OAAO,CAAC;AAgBf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAElD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAkBxD,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAC,WAAW,EAAC,MAAM,mBAAmB,CAAC;AAMnD,OAAO,KAAK,EACV,WAAW,EACX,KAAK,EACL,aAAa,EACb,WAAW,EAEZ,MAAM,SAAS,CAAC;AAoHjB,MAAM,MAAM,wBAAwB,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAEvE,MAAM,MAAM,kBAAkB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEpD,MAAM,WAAW,oBAAoB;IACnC,qCAAqC;IACrC,IAAI,EAAE,wBAAwB,CAAC;IAC/B,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB;;;OAGG;IACH,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,6EAA6E;IAC7E,cAAc,EAAE,MAAM,WAAW,CAAC;IAClC;;;;;OAKG;IACH,WAAW,EAAE,MAAM,MAAM,CAAC;IAC1B;;;;OAIG;IACH,OAAO,EAAE,MAAM,MAAM,CAAC;IACtB;;;OAGG;IACH,SAAS,EAAE,MAAM,aAAa,CAAC;CAChC;AAED,MAAM,WAAW,mBAAoB,SAAQ,IAAI,CAC/C,SAAS,EACT,UAAU,GAAG,cAAc,CAC5B;IACC,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IACrE,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,uEAAuE;IACvE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,IAAI,CAAC,EAAE,kBAAkB,CAAC;IAC1B;;;;OAIG;IACH,KAAK,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;IAC1C;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;;;;;;;;;;;;;;OAgBG;IACH,YAAY,CAAC,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IAC1C,yEAAyE;IACzE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,cAAc,mHAuLzB,CAAC"}
1
+ {"version":3,"file":"RichTextEditor.d.ts","sourceRoot":"","sources":["../../src/RichTextEditor/RichTextEditor.tsx"],"names":[],"mappings":"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"}
@@ -44,11 +44,8 @@ import { TabIndentationPlugin } from '@lexical/react/LexicalTabIndentationPlugin
44
44
  import { MarkdownShortcutPlugin } from '@lexical/react/LexicalMarkdownShortcutPlugin';
45
45
  import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin';
46
46
  import { TRANSFORMERS, $convertToMarkdownString } from '@lexical/markdown';
47
- import { ListNode, ListItemNode } from '@lexical/list';
48
- import { HeadingNode, QuoteNode } from '@lexical/rich-text';
49
- import { LinkNode, AutoLinkNode } from '@lexical/link';
50
- import { CodeNode, CodeHighlightNode } from '@lexical/code';
51
47
  import { $generateHtmlFromNodes } from '@lexical/html';
48
+ import { DEFAULT_NODES } from "./editorNodes.js";
52
49
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
53
50
  /**
54
51
  * Serialized state for an empty editor: a root containing a single empty
@@ -107,9 +104,6 @@ const sizeStyles = {
107
104
  }
108
105
  };
109
106
 
110
- /** The default OSS node set registered with the editor. */
111
- const DEFAULT_NODES = [HeadingNode, QuoteNode, ListNode, ListItemNode, LinkNode, AutoLinkNode, CodeNode, CodeHighlightNode];
112
-
113
107
  /**
114
108
  * Fraction of `maxLength` at which the character counter begins announcing
115
109
  * remaining/over-limit characters to screen readers. Matches TextArea.
@@ -0,0 +1,8 @@
1
+ import type { Klass, LexicalNode } from 'lexical';
2
+ /**
3
+ * The default OSS node set registered with the editor: headings, quotes,
4
+ * lists, links, and code. Extend via the `nodes` prop / option rather than
5
+ * editing this list.
6
+ */
7
+ export declare const DEFAULT_NODES: ReadonlyArray<Klass<LexicalNode>>;
8
+ //# sourceMappingURL=editorNodes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editorNodes.d.ts","sourceRoot":"","sources":["../../src/RichTextEditor/editorNodes.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAC,KAAK,EAAE,WAAW,EAAC,MAAM,SAAS,CAAC;AAEhD;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,aAAa,CAAC,KAAK,CAAC,WAAW,CAAC,CAS3D,CAAC"}
@@ -0,0 +1,23 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file editorNodes.ts
5
+ * @input Imports the default Lexical node classes registered by the editor.
6
+ * @output Exports DEFAULT_NODES — the base OSS node set.
7
+ * @position Shared by RichTextEditor.tsx (editor config) and
8
+ * markdownSerializers.ts (headless conversion), so both register the same
9
+ * nodes and Markdown round-trips consistently.
10
+ *
11
+ * SYNC: When the default node set changes, both consumers pick it up here.
12
+ */
13
+
14
+ import { ListNode, ListItemNode } from '@lexical/list';
15
+ import { HeadingNode, QuoteNode } from '@lexical/rich-text';
16
+ import { LinkNode, AutoLinkNode } from '@lexical/link';
17
+ import { CodeNode, CodeHighlightNode } from '@lexical/code';
18
+ /**
19
+ * The default OSS node set registered with the editor: headings, quotes,
20
+ * lists, links, and code. Extend via the `nodes` prop / option rather than
21
+ * editing this list.
22
+ */
23
+ export const DEFAULT_NODES = [HeadingNode, QuoteNode, ListNode, ListItemNode, LinkNode, AutoLinkNode, CodeNode, CodeHighlightNode];
@@ -12,4 +12,6 @@ export type { RichTextEditorProps, RichTextEditorRef, RichTextEditorStatus, Rich
12
12
  export { RichTextView } from './RichTextView';
13
13
  export type { RichTextViewProps } from './RichTextView';
14
14
  export { sharedEditorTheme } from './editorTheme';
15
+ export { markdownToEditorStateJSON, editorStateJSONToMarkdown, } from './markdownSerializers';
16
+ export type { MarkdownSerializerOptions } from './markdownSerializers';
15
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/RichTextEditor/index.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AAEH,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAC;AAChD,YAAY,EACV,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,wBAAwB,EACxB,kBAAkB,EAClB,WAAW,GACZ,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAC5C,YAAY,EAAC,iBAAiB,EAAC,MAAM,gBAAgB,CAAC;AAEtD,OAAO,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/RichTextEditor/index.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AAEH,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAC;AAChD,YAAY,EACV,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,wBAAwB,EACxB,kBAAkB,EAClB,WAAW,GACZ,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAC5C,YAAY,EAAC,iBAAiB,EAAC,MAAM,gBAAgB,CAAC;AAEtD,OAAO,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAC;AAEhD,OAAO,EACL,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAC,yBAAyB,EAAC,MAAM,uBAAuB,CAAC"}
@@ -13,4 +13,5 @@
13
13
  */
14
14
  export { RichTextEditor } from "./RichTextEditor.js";
15
15
  export { RichTextView } from "./RichTextView.js";
16
- export { sharedEditorTheme } from "./editorTheme.js";
16
+ export { sharedEditorTheme } from "./editorTheme.js";
17
+ export { markdownToEditorStateJSON, editorStateJSONToMarkdown } from "./markdownSerializers.js";
@@ -0,0 +1,40 @@
1
+ import { type Transformer } from '@lexical/markdown';
2
+ import type { Klass, LexicalNode } from 'lexical';
3
+ /** Options shared by the Markdown serializer helpers. */
4
+ export interface MarkdownSerializerOptions {
5
+ /**
6
+ * Markdown transformers to use. Defaults to the standard `@lexical/markdown`
7
+ * `TRANSFORMERS`. Pass the same array you give `RichTextEditor`'s
8
+ * `transformers` prop so content round-trips consistently.
9
+ */
10
+ transformers?: ReadonlyArray<Transformer>;
11
+ /**
12
+ * Extra Lexical nodes to register beyond the default OSS set, mirroring the
13
+ * editor's `nodes` prop. Required for custom node types to serialize.
14
+ */
15
+ nodes?: ReadonlyArray<Klass<LexicalNode>>;
16
+ }
17
+ /**
18
+ * Convert a Markdown string to a serialized Lexical `EditorState` JSON string
19
+ * — the same shape `RichTextEditor`'s `defaultValue` expects. Runs headless
20
+ * (no DOM), so it is safe in Node / SSR.
21
+ *
22
+ * @example
23
+ * ```
24
+ * const value = markdownToEditorStateJSON('# Title\n\nHello');
25
+ * <RichTextEditor label="Notes" defaultValue={value} />
26
+ * ```
27
+ */
28
+ export declare function markdownToEditorStateJSON(markdown: string, options?: MarkdownSerializerOptions): string;
29
+ /**
30
+ * Convert a serialized Lexical `EditorState` JSON string (e.g. the value
31
+ * produced by `RichTextEditor`'s `onChange` or `defaultValue`) to a Markdown
32
+ * string. Runs headless (no DOM), so it is safe in Node / SSR.
33
+ *
34
+ * @example
35
+ * ```
36
+ * const md = editorStateJSONToMarkdown(savedJson);
37
+ * ```
38
+ */
39
+ export declare function editorStateJSONToMarkdown(editorStateJSON: string, options?: MarkdownSerializerOptions): string;
40
+ //# sourceMappingURL=markdownSerializers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdownSerializers.d.ts","sourceRoot":"","sources":["../../src/RichTextEditor/markdownSerializers.ts"],"names":[],"mappings":"AA0BA,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAC,KAAK,EAAE,WAAW,EAAC,MAAM,SAAS,CAAC;AAEhD,yDAAyD;AACzD,MAAM,WAAW,yBAAyB;IACxC;;;;OAIG;IACH,YAAY,CAAC,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IAC1C;;;OAGG;IACH,KAAK,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;CAC3C;AAYD;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,yBAA8B,GACtC,MAAM,CAUR;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,eAAe,EAAE,MAAM,EACvB,OAAO,GAAE,yBAA8B,GACtC,MAAM,CAKR"}
@@ -0,0 +1,85 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file markdownSerializers.ts
5
+ * @input Uses @lexical/headless (createHeadlessEditor), @lexical/markdown
6
+ * ($convertFromMarkdownString / $convertToMarkdownString), and the shared
7
+ * DEFAULT_NODES set.
8
+ * @output Standalone Markdown <-> serialized EditorState helpers:
9
+ * markdownToEditorStateJSON, editorStateJSONToMarkdown.
10
+ * @position Re-exported from RichTextEditor/index.ts and the @astryxdesign/lab
11
+ * barrel. Complements the ref's getMarkdown() by working WITHOUT a mounted
12
+ * editor (e.g. to produce a `defaultValue` from Markdown on the server).
13
+ *
14
+ * SYNC: When modified, update:
15
+ * - /packages/lab/src/RichTextEditor/index.ts (exports)
16
+ * - /packages/lab/src/index.ts (barrel re-export)
17
+ * - /packages/lab/src/RichTextEditor/RichTextEditor.doc.mjs (usage notes)
18
+ * - /packages/lab/src/RichTextEditor/RichTextEditor.test.tsx (tests)
19
+ *
20
+ * NOTE: `@lexical/headless`, `@lexical/markdown`, and `lexical` are OPTIONAL
21
+ * peer dependencies (this is a canary lab module). Install them to use these
22
+ * helpers. `@lexical/headless` runs Lexical without a DOM, so these work in
23
+ * Node / SSR contexts.
24
+ */
25
+
26
+ import { createHeadlessEditor } from '@lexical/headless';
27
+ import { TRANSFORMERS, $convertFromMarkdownString, $convertToMarkdownString } from '@lexical/markdown';
28
+ import { DEFAULT_NODES } from "./editorNodes.js";
29
+
30
+ /** Options shared by the Markdown serializer helpers. */
31
+
32
+ function createSerializerEditor(nodes) {
33
+ return createHeadlessEditor({
34
+ namespace: 'astryx-editor-serializer',
35
+ nodes: nodes ? [...DEFAULT_NODES, ...nodes] : [...DEFAULT_NODES],
36
+ onError(error) {
37
+ throw error;
38
+ }
39
+ });
40
+ }
41
+
42
+ /**
43
+ * Convert a Markdown string to a serialized Lexical `EditorState` JSON string
44
+ * — the same shape `RichTextEditor`'s `defaultValue` expects. Runs headless
45
+ * (no DOM), so it is safe in Node / SSR.
46
+ *
47
+ * @example
48
+ * ```
49
+ * const value = markdownToEditorStateJSON('# Title\n\nHello');
50
+ * <RichTextEditor label="Notes" defaultValue={value} />
51
+ * ```
52
+ */
53
+ export function markdownToEditorStateJSON(markdown, options = {}) {
54
+ const {
55
+ transformers = TRANSFORMERS,
56
+ nodes
57
+ } = options;
58
+ const editor = createSerializerEditor(nodes);
59
+ editor.update(() => {
60
+ $convertFromMarkdownString(markdown, [...transformers]);
61
+ }, {
62
+ discrete: true
63
+ });
64
+ return JSON.stringify(editor.getEditorState().toJSON());
65
+ }
66
+
67
+ /**
68
+ * Convert a serialized Lexical `EditorState` JSON string (e.g. the value
69
+ * produced by `RichTextEditor`'s `onChange` or `defaultValue`) to a Markdown
70
+ * string. Runs headless (no DOM), so it is safe in Node / SSR.
71
+ *
72
+ * @example
73
+ * ```
74
+ * const md = editorStateJSONToMarkdown(savedJson);
75
+ * ```
76
+ */
77
+ export function editorStateJSONToMarkdown(editorStateJSON, options = {}) {
78
+ const {
79
+ transformers = TRANSFORMERS,
80
+ nodes
81
+ } = options;
82
+ const editor = createSerializerEditor(nodes);
83
+ const state = editor.parseEditorState(editorStateJSON);
84
+ return state.read(() => $convertToMarkdownString([...transformers]));
85
+ }
package/dist/index.d.ts CHANGED
@@ -28,5 +28,5 @@ export { SankeyChart, type SankeyChartProps, SankeyLink, type SankeyLinkProps, S
28
28
  export * from './Stepper';
29
29
  export * from './CircularProgress';
30
30
  export { LogStream, type LogStreamProps, type LogEntry, type LogStreamLevel, } from './LogStream';
31
- export { RichTextEditor, type RichTextEditorProps, type RichTextEditorRef, type RichTextEditorStatus, type RichTextEditorStatusType, type RichTextEditorSize, type Transformer, RichTextView, type RichTextViewProps, sharedEditorTheme, } from './RichTextEditor';
31
+ export { RichTextEditor, type RichTextEditorProps, type RichTextEditorRef, type RichTextEditorStatus, type RichTextEditorStatusType, type RichTextEditorSize, type Transformer, RichTextView, type RichTextViewProps, sharedEditorTheme, markdownToEditorStateJSON, editorStateJSONToMarkdown, type MarkdownSerializerOptions, } from './RichTextEditor';
32
32
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -64,4 +64,4 @@ export { LogStream } from "./LogStream/index.js";
64
64
 
65
65
  // RichTextEditor — experimental Lexical WYSIWYG editor (RFC facebook/astryx#3899)
66
66
  // Optional peer deps: lexical + @lexical/*. Read-only rendering via RichTextView.
67
- export { RichTextEditor, RichTextView, sharedEditorTheme } from "./RichTextEditor/index.js";
67
+ export { RichTextEditor, RichTextView, sharedEditorTheme, markdownToEditorStateJSON, editorStateJSONToMarkdown } from "./RichTextEditor/index.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/lab",
3
- "version": "0.2.0-canary.c366298",
3
+ "version": "0.2.0-canary.c75407e",
4
4
  "description": "Experimental Astryx components — published to npm only under the @canary dist-tag for early testing; never released as a stable (latest) version.",
5
5
  "author": "Meta Open Source",
6
6
  "license": "MIT",
@@ -45,12 +45,13 @@
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@stylexjs/stylex": ">=0.10.0",
48
- "@astryxdesign/core": "0.2.0-canary.c366298",
48
+ "@astryxdesign/core": "0.2.0-canary.c75407e",
49
49
  "react": ">=19.0.0",
50
50
  "react-dom": ">=19.0.0",
51
51
  "lexical": "^0.46.0",
52
52
  "@lexical/react": "^0.46.0",
53
53
  "@lexical/markdown": "^0.46.0",
54
+ "@lexical/headless": "^0.46.0",
54
55
  "@lexical/html": "^0.46.0",
55
56
  "@lexical/link": "^0.46.0",
56
57
  "@lexical/list": "^0.46.0",
@@ -65,6 +66,7 @@
65
66
  },
66
67
  "devDependencies": {
67
68
  "@lexical/code": "^0.46.0",
69
+ "@lexical/headless": "^0.46.0",
68
70
  "@lexical/html": "^0.46.0",
69
71
  "@lexical/link": "^0.46.0",
70
72
  "@lexical/list": "^0.46.0",
@@ -87,6 +89,9 @@
87
89
  "@lexical/markdown": {
88
90
  "optional": true
89
91
  },
92
+ "@lexical/headless": {
93
+ "optional": true
94
+ },
90
95
  "@lexical/html": {
91
96
  "optional": true
92
97
  },
@@ -155,6 +155,11 @@ export const docs = {
155
155
  description:
156
156
  'Use a ref (RichTextEditorRef) to imperatively focus(), clear(), read the state via getEditorState(), serialize to Markdown via getMarkdown() or HTML via getHTML(), or reach the LexicalEditor via getEditor(). The handle is available after mount. getMarkdown() uses the same transformers prop the editor is configured with. focus() and clear() are no-ops when the editor is read-only or disabled, and clear() resets to a single empty paragraph.',
157
157
  },
158
+ {
159
+ guidance: true,
160
+ description:
161
+ 'To produce a defaultValue from Markdown without mounting an editor (e.g. on the server), use markdownToEditorStateJSON(markdown). Convert the other way with editorStateJSONToMarkdown(json). Both run headless via @lexical/headless and accept the same transformers/nodes options as the editor.',
162
+ },
158
163
  {
159
164
  guidance: false,
160
165
  description:
@@ -22,6 +22,10 @@ import {
22
22
  } from '@lexical/markdown';
23
23
  import {RichTextEditor, type RichTextEditorRef} from './RichTextEditor';
24
24
  import {RichTextView} from './RichTextView';
25
+ import {
26
+ markdownToEditorStateJSON,
27
+ editorStateJSONToMarkdown,
28
+ } from './markdownSerializers';
25
29
 
26
30
  // Small plugin that captures the editor instance so tests can drive real
27
31
  // Lexical updates (jsdom does not implement contenteditable editing).
@@ -661,3 +665,42 @@ describe('RichTextView', () => {
661
665
  expect(screen.queryByRole('textbox')).not.toBeInTheDocument();
662
666
  });
663
667
  });
668
+
669
+ describe('markdown serializers', () => {
670
+ it('markdownToEditorStateJSON produces a heading node from "# "', () => {
671
+ const json = markdownToEditorStateJSON('# Title');
672
+ const parsed = JSON.parse(json);
673
+ const first = parsed.root.children[0];
674
+ expect(first.type).toBe('heading');
675
+ expect(first.tag).toBe('h1');
676
+ expect(first.children[0].text).toBe('Title');
677
+ });
678
+
679
+ it('editorStateJSONToMarkdown round-trips a heading back to "# "', () => {
680
+ const json = markdownToEditorStateJSON('# Title');
681
+ expect(editorStateJSONToMarkdown(json)).toBe('# Title');
682
+ });
683
+
684
+ it('markdown round-trips through both helpers', () => {
685
+ const md = '# Heading\n\nSome **bold** text';
686
+ const json = markdownToEditorStateJSON(md);
687
+ expect(editorStateJSONToMarkdown(json)).toBe(md);
688
+ });
689
+
690
+ it('honors a custom (empty) transformers set — no heading syntax', () => {
691
+ // With no transformers, "# Title" is not recognized as a heading; it stays
692
+ // a plain paragraph, so serializing back yields the literal text.
693
+ const json = markdownToEditorStateJSON('# Title', {transformers: []});
694
+ const parsed = JSON.parse(json);
695
+ expect(parsed.root.children[0].type).toBe('paragraph');
696
+ expect(parsed.root.children[0].children[0].text).toBe('# Title');
697
+ });
698
+
699
+ it('produces JSON consumable as RichTextEditor defaultValue', async () => {
700
+ const value = markdownToEditorStateJSON('Hello world');
701
+ render(<RichTextEditor label="Notes" defaultValue={value} />);
702
+ await waitFor(() =>
703
+ expect(screen.getByRole('textbox').textContent).toContain('Hello world'),
704
+ );
705
+ });
706
+ });
@@ -76,11 +76,8 @@ import {
76
76
  type Transformer,
77
77
  } from '@lexical/markdown';
78
78
  export type {Transformer} from '@lexical/markdown';
79
- import {ListNode, ListItemNode} from '@lexical/list';
80
- import {HeadingNode, QuoteNode} from '@lexical/rich-text';
81
- import {LinkNode, AutoLinkNode} from '@lexical/link';
82
- import {CodeNode, CodeHighlightNode} from '@lexical/code';
83
79
  import {$generateHtmlFromNodes} from '@lexical/html';
80
+ import {DEFAULT_NODES} from './editorNodes';
84
81
  import type {
85
82
  EditorState,
86
83
  Klass,
@@ -185,18 +182,6 @@ const sizeStyles = stylex.create({
185
182
  },
186
183
  });
187
184
 
188
- /** The default OSS node set registered with the editor. */
189
- const DEFAULT_NODES: ReadonlyArray<Klass<LexicalNode>> = [
190
- HeadingNode,
191
- QuoteNode,
192
- ListNode,
193
- ListItemNode,
194
- LinkNode,
195
- AutoLinkNode,
196
- CodeNode,
197
- CodeHighlightNode,
198
- ];
199
-
200
185
  /**
201
186
  * Fraction of `maxLength` at which the character counter begins announcing
202
187
  * remaining/over-limit characters to screen readers. Matches TextArea.
@@ -0,0 +1,34 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file editorNodes.ts
5
+ * @input Imports the default Lexical node classes registered by the editor.
6
+ * @output Exports DEFAULT_NODES — the base OSS node set.
7
+ * @position Shared by RichTextEditor.tsx (editor config) and
8
+ * markdownSerializers.ts (headless conversion), so both register the same
9
+ * nodes and Markdown round-trips consistently.
10
+ *
11
+ * SYNC: When the default node set changes, both consumers pick it up here.
12
+ */
13
+
14
+ import {ListNode, ListItemNode} from '@lexical/list';
15
+ import {HeadingNode, QuoteNode} from '@lexical/rich-text';
16
+ import {LinkNode, AutoLinkNode} from '@lexical/link';
17
+ import {CodeNode, CodeHighlightNode} from '@lexical/code';
18
+ import type {Klass, LexicalNode} from 'lexical';
19
+
20
+ /**
21
+ * The default OSS node set registered with the editor: headings, quotes,
22
+ * lists, links, and code. Extend via the `nodes` prop / option rather than
23
+ * editing this list.
24
+ */
25
+ export const DEFAULT_NODES: ReadonlyArray<Klass<LexicalNode>> = [
26
+ HeadingNode,
27
+ QuoteNode,
28
+ ListNode,
29
+ ListItemNode,
30
+ LinkNode,
31
+ AutoLinkNode,
32
+ CodeNode,
33
+ CodeHighlightNode,
34
+ ];
@@ -26,3 +26,9 @@ export {RichTextView} from './RichTextView';
26
26
  export type {RichTextViewProps} from './RichTextView';
27
27
 
28
28
  export {sharedEditorTheme} from './editorTheme';
29
+
30
+ export {
31
+ markdownToEditorStateJSON,
32
+ editorStateJSONToMarkdown,
33
+ } from './markdownSerializers';
34
+ export type {MarkdownSerializerOptions} from './markdownSerializers';
@@ -0,0 +1,105 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file markdownSerializers.ts
5
+ * @input Uses @lexical/headless (createHeadlessEditor), @lexical/markdown
6
+ * ($convertFromMarkdownString / $convertToMarkdownString), and the shared
7
+ * DEFAULT_NODES set.
8
+ * @output Standalone Markdown <-> serialized EditorState helpers:
9
+ * markdownToEditorStateJSON, editorStateJSONToMarkdown.
10
+ * @position Re-exported from RichTextEditor/index.ts and the @astryxdesign/lab
11
+ * barrel. Complements the ref's getMarkdown() by working WITHOUT a mounted
12
+ * editor (e.g. to produce a `defaultValue` from Markdown on the server).
13
+ *
14
+ * SYNC: When modified, update:
15
+ * - /packages/lab/src/RichTextEditor/index.ts (exports)
16
+ * - /packages/lab/src/index.ts (barrel re-export)
17
+ * - /packages/lab/src/RichTextEditor/RichTextEditor.doc.mjs (usage notes)
18
+ * - /packages/lab/src/RichTextEditor/RichTextEditor.test.tsx (tests)
19
+ *
20
+ * NOTE: `@lexical/headless`, `@lexical/markdown`, and `lexical` are OPTIONAL
21
+ * peer dependencies (this is a canary lab module). Install them to use these
22
+ * helpers. `@lexical/headless` runs Lexical without a DOM, so these work in
23
+ * Node / SSR contexts.
24
+ */
25
+
26
+ import {createHeadlessEditor} from '@lexical/headless';
27
+ import {
28
+ TRANSFORMERS,
29
+ $convertFromMarkdownString,
30
+ $convertToMarkdownString,
31
+ type Transformer,
32
+ } from '@lexical/markdown';
33
+ import {DEFAULT_NODES} from './editorNodes';
34
+ import type {Klass, LexicalNode} from 'lexical';
35
+
36
+ /** Options shared by the Markdown serializer helpers. */
37
+ export interface MarkdownSerializerOptions {
38
+ /**
39
+ * Markdown transformers to use. Defaults to the standard `@lexical/markdown`
40
+ * `TRANSFORMERS`. Pass the same array you give `RichTextEditor`'s
41
+ * `transformers` prop so content round-trips consistently.
42
+ */
43
+ transformers?: ReadonlyArray<Transformer>;
44
+ /**
45
+ * Extra Lexical nodes to register beyond the default OSS set, mirroring the
46
+ * editor's `nodes` prop. Required for custom node types to serialize.
47
+ */
48
+ nodes?: ReadonlyArray<Klass<LexicalNode>>;
49
+ }
50
+
51
+ function createSerializerEditor(nodes?: ReadonlyArray<Klass<LexicalNode>>) {
52
+ return createHeadlessEditor({
53
+ namespace: 'astryx-editor-serializer',
54
+ nodes: nodes ? [...DEFAULT_NODES, ...nodes] : [...DEFAULT_NODES],
55
+ onError(error: Error) {
56
+ throw error;
57
+ },
58
+ });
59
+ }
60
+
61
+ /**
62
+ * Convert a Markdown string to a serialized Lexical `EditorState` JSON string
63
+ * — the same shape `RichTextEditor`'s `defaultValue` expects. Runs headless
64
+ * (no DOM), so it is safe in Node / SSR.
65
+ *
66
+ * @example
67
+ * ```
68
+ * const value = markdownToEditorStateJSON('# Title\n\nHello');
69
+ * <RichTextEditor label="Notes" defaultValue={value} />
70
+ * ```
71
+ */
72
+ export function markdownToEditorStateJSON(
73
+ markdown: string,
74
+ options: MarkdownSerializerOptions = {},
75
+ ): string {
76
+ const {transformers = TRANSFORMERS, nodes} = options;
77
+ const editor = createSerializerEditor(nodes);
78
+ editor.update(
79
+ () => {
80
+ $convertFromMarkdownString(markdown, [...transformers]);
81
+ },
82
+ {discrete: true},
83
+ );
84
+ return JSON.stringify(editor.getEditorState().toJSON());
85
+ }
86
+
87
+ /**
88
+ * Convert a serialized Lexical `EditorState` JSON string (e.g. the value
89
+ * produced by `RichTextEditor`'s `onChange` or `defaultValue`) to a Markdown
90
+ * string. Runs headless (no DOM), so it is safe in Node / SSR.
91
+ *
92
+ * @example
93
+ * ```
94
+ * const md = editorStateJSONToMarkdown(savedJson);
95
+ * ```
96
+ */
97
+ export function editorStateJSONToMarkdown(
98
+ editorStateJSON: string,
99
+ options: MarkdownSerializerOptions = {},
100
+ ): string {
101
+ const {transformers = TRANSFORMERS, nodes} = options;
102
+ const editor = createSerializerEditor(nodes);
103
+ const state = editor.parseEditorState(editorStateJSON);
104
+ return state.read(() => $convertToMarkdownString([...transformers]));
105
+ }
package/src/index.ts CHANGED
@@ -246,4 +246,7 @@ export {
246
246
  RichTextView,
247
247
  type RichTextViewProps,
248
248
  sharedEditorTheme,
249
+ markdownToEditorStateJSON,
250
+ editorStateJSONToMarkdown,
251
+ type MarkdownSerializerOptions,
249
252
  } from './RichTextEditor';