@zuilib/text-editor 0.0.1 → 0.0.2

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/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ElementNode, NodeKey, EditorConfig, SerializedElementNode, LexicalNode } from 'lexical';
3
+ import { MultilineElementTransformer } from '@lexical/markdown';
2
4
 
3
5
  type Props = Readonly<{
4
6
  value?: string;
@@ -11,4 +13,45 @@ type Props = Readonly<{
11
13
  }>;
12
14
  declare function MarkdownEditor({ value, onChange, placeholder, readOnly, className, mode, autoFocus, }: Props): react_jsx_runtime.JSX.Element;
13
15
 
14
- export { MarkdownEditor, type Props as MarkdownEditorProps };
16
+ type SerializedFrontmatterNode = SerializedElementNode;
17
+ /**
18
+ * A block node holding a document's YAML frontmatter — the metadata section
19
+ * delimited by `---` lines at the very top of a markdown file (as used by
20
+ * Claude skills, static-site generators, etc.).
21
+ *
22
+ * Content is stored as plain TextNode children separated by LineBreakNodes, so
23
+ * `getTextContent()` yields the raw YAML with `\n` line separators — exactly
24
+ * what the FRONTMATTER transformer re-serializes on export.
25
+ */
26
+ declare class FrontmatterNode extends ElementNode {
27
+ static getType(): string;
28
+ static clone(node: FrontmatterNode): FrontmatterNode;
29
+ constructor(key?: NodeKey);
30
+ createDOM(config: EditorConfig): HTMLElement;
31
+ updateDOM(): boolean;
32
+ static importJSON(_serializedNode: SerializedFrontmatterNode): FrontmatterNode;
33
+ exportJSON(): SerializedFrontmatterNode;
34
+ isInline(): false;
35
+ canBeEmpty(): boolean;
36
+ canIndent(): false;
37
+ }
38
+ declare function $createFrontmatterNode(): FrontmatterNode;
39
+ declare function $isFrontmatterNode(node: LexicalNode | null | undefined): node is FrontmatterNode;
40
+ /**
41
+ * Markdown transformer for a leading YAML frontmatter block:
42
+ *
43
+ * ```
44
+ * ---
45
+ * name: my-skill
46
+ * description: ...
47
+ * ---
48
+ * ```
49
+ *
50
+ * Import only claims the block when it's the very first thing in the document
51
+ * (frontmatter is defined as leading metadata); a `---` fence appearing later
52
+ * falls through to normal handling. Must be ordered before other multiline
53
+ * transformers so its `export` runs first for a FrontmatterNode.
54
+ */
55
+ declare const FRONTMATTER: MultilineElementTransformer;
56
+
57
+ export { $createFrontmatterNode, $isFrontmatterNode, FRONTMATTER, FrontmatterNode, MarkdownEditor, type Props as MarkdownEditorProps, type SerializedFrontmatterNode };
package/dist/index.js CHANGED
@@ -192,6 +192,86 @@ function MarkdownSyncPlugin({
192
192
  return null;
193
193
  }
194
194
 
195
+ // src/nodes/FrontmatterNode.ts
196
+ import {
197
+ $applyNodeReplacement,
198
+ $createLineBreakNode,
199
+ $createTextNode,
200
+ ElementNode
201
+ } from "lexical";
202
+ var FrontmatterNode = class _FrontmatterNode extends ElementNode {
203
+ static getType() {
204
+ return "frontmatter";
205
+ }
206
+ static clone(node) {
207
+ return new _FrontmatterNode(node.__key);
208
+ }
209
+ constructor(key) {
210
+ super(key);
211
+ }
212
+ createDOM(config) {
213
+ const dom = document.createElement("div");
214
+ const className = config.theme.frontmatter;
215
+ dom.className = typeof className === "string" ? className : "zui-frontmatter";
216
+ return dom;
217
+ }
218
+ updateDOM() {
219
+ return false;
220
+ }
221
+ static importJSON(_serializedNode) {
222
+ return $createFrontmatterNode();
223
+ }
224
+ exportJSON() {
225
+ return {
226
+ ...super.exportJSON(),
227
+ type: "frontmatter",
228
+ version: 1
229
+ };
230
+ }
231
+ isInline() {
232
+ return false;
233
+ }
234
+ canBeEmpty() {
235
+ return false;
236
+ }
237
+ canIndent() {
238
+ return false;
239
+ }
240
+ };
241
+ function $createFrontmatterNode() {
242
+ return $applyNodeReplacement(new FrontmatterNode());
243
+ }
244
+ function $isFrontmatterNode(node) {
245
+ return node instanceof FrontmatterNode;
246
+ }
247
+ var FRONTMATTER_DELIMITER = /^---\s*$/;
248
+ var FRONTMATTER = {
249
+ dependencies: [FrontmatterNode],
250
+ export: (node) => {
251
+ if (!$isFrontmatterNode(node)) return null;
252
+ const body = node.getTextContent();
253
+ return body ? `---
254
+ ${body}
255
+ ---` : "---\n---";
256
+ },
257
+ regExpStart: FRONTMATTER_DELIMITER,
258
+ regExpEnd: FRONTMATTER_DELIMITER,
259
+ replace: (rootNode, children, _startMatch, _endMatch, linesInBetween, isImport) => {
260
+ if (!isImport || children != null || linesInBetween == null) return false;
261
+ if (rootNode.getChildrenSize() !== 0) return false;
262
+ const lines = [...linesInBetween];
263
+ while (lines.length > 0 && lines[0].length === 0) lines.shift();
264
+ while (lines.length > 0 && lines[lines.length - 1].length === 0) lines.pop();
265
+ const node = $createFrontmatterNode();
266
+ lines.forEach((line, index) => {
267
+ if (index > 0) node.append($createLineBreakNode());
268
+ node.append($createTextNode(line));
269
+ });
270
+ rootNode.append(node);
271
+ },
272
+ type: "multiline-element"
273
+ };
274
+
195
275
  // src/theme.ts
196
276
  var editorTheme = {
197
277
  ltr: "text-left",
@@ -261,12 +341,13 @@ var editorTheme = {
261
341
  url: "text-blue-500",
262
342
  variable: "text-blue-500"
263
343
  },
264
- quote: "border-l-4 border-border pl-4 italic"
344
+ quote: "border-l-4 border-border pl-4 italic",
345
+ frontmatter: "zui-frontmatter"
265
346
  };
266
347
 
267
348
  // src/MarkdownEditor.tsx
268
349
  import { jsx, jsxs } from "react/jsx-runtime";
269
- var SYNC_TRANSFORMERS = [CHECK_LIST, ...TRANSFORMERS];
350
+ var SYNC_TRANSFORMERS = [FRONTMATTER, CHECK_LIST, ...TRANSFORMERS];
270
351
  var SHORTCUT_TRANSFORMERS = TRANSFORMERS.filter((t) => t !== CODE);
271
352
  function onError(error) {
272
353
  console.error(error);
@@ -279,7 +360,8 @@ var editorNodes = [
279
360
  CodeNode,
280
361
  CodeHighlightNode,
281
362
  AutoLinkNode,
282
- LinkNode
363
+ LinkNode,
364
+ FrontmatterNode
283
365
  ];
284
366
  function ReadOnlyPlugin({ readOnly }) {
285
367
  const [editor] = useLexicalComposerContext5();
@@ -387,5 +469,9 @@ function MarkdownEditor({
387
469
  ] }) }, mountKey);
388
470
  }
389
471
  export {
472
+ $createFrontmatterNode,
473
+ $isFrontmatterNode,
474
+ FRONTMATTER,
475
+ FrontmatterNode,
390
476
  MarkdownEditor
391
477
  };
package/dist/styles.css CHANGED
@@ -64,6 +64,22 @@
64
64
  transform: rotate(45deg);
65
65
  }
66
66
 
67
+ /* Frontmatter (YAML metadata block delimited by --- at the top of a doc) */
68
+ .zui-frontmatter {
69
+ display: block;
70
+ white-space: pre-wrap;
71
+ margin: 0 0 1rem;
72
+ padding: 0.75rem 1rem;
73
+ border: 1px solid currentColor;
74
+ border-radius: 0.375rem;
75
+ border-left-width: 3px;
76
+ font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas,
77
+ 'Liberation Mono', monospace;
78
+ font-size: 0.8125rem;
79
+ line-height: 1.6;
80
+ opacity: 0.7;
81
+ }
82
+
67
83
  .zui-text-editor-textarea {
68
84
  width: 100%;
69
85
  height: 100%;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zuilib/text-editor",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "ZUI — A markdown editor component wrapping Lexical",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",