@localess/richtext 3.4.1-dev.20260901200805 → 4.0.0-dev.20260905071322

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/SKILL.md CHANGED
@@ -1,13 +1,19 @@
1
1
  ---
2
2
  name: localess-richtext
3
- description: Framework-neutral rich text model and renderer for Localess TipTap JSON content. Zero dependencies. Use when rendering Localess RICH_TEXT fields to HTML or building a framework-specific rich text walker.
3
+ description: Framework-neutral rich text model and renderer for Localess TipTap JSON content. Zero dependencies beyond the shared @localess/model types package. Use when rendering Localess RICH_TEXT fields to HTML or building a framework-specific rich text walker.
4
4
  ---
5
5
 
6
6
  # @localess/richtext
7
7
 
8
8
  Renders Localess rich text field values (TipTap/ProseMirror JSON produced by
9
- the Localess Studio editor) without TipTap at runtime. Zero production
10
- dependencies; safe in browsers, SSR, and edge runtimes.
9
+ the Localess Studio editor) without TipTap at runtime. Zero external
10
+ dependencies its only dependency is the in-monorepo, itself-zero-dependency
11
+ `@localess/model` types package; safe in browsers, SSR, and edge runtimes.
12
+ Requires Node.js >= 24.0.0 when used server-side.
13
+
14
+ ```bash
15
+ npm install @localess/richtext
16
+ ```
11
17
 
12
18
  Framework packages (`@localess/react`, `@localess/vue`, `@localess/svelte`,
13
19
  `@localess/astro`, `@localess/angular`) ship idiomatic wrappers — prefer those
@@ -23,7 +29,7 @@ const html = renderRichTextToHtml(data.body);
23
29
  ```
24
30
 
25
31
  - Input: `LocalessRichTextInput` — a `doc`, a node, a node array, `null`, or
26
- `@localess/client`'s `ContentRichText` (structurally compatible, no cast).
32
+ `ContentRichText` (re-exported from `@localess/model`, no cast).
27
33
  - Output is byte-identical to TipTap's `generateHTML` for the Studio's
28
34
  extension set, except link `href`s pass a protocol allowlist
29
35
  (`http:`/`https:`/`mailto:`/`tel:`/scheme-less); `javascript:`/`data:`
@@ -32,13 +38,22 @@ const html = renderRichTextToHtml(data.body);
32
38
 
33
39
  ## Supported node set
34
40
 
35
- Nodes: `doc`, `paragraph`, `heading` (1–6), `bulletList`, `orderedList`
36
- (`start`), `listItem`, `codeBlock` (`language` → `class="language-x"` on
37
- `<code>`), `text`. Marks: `bold` `<strong>`, `italic` → `<em>`,
38
- `strike` → `<s>`, `underline` → `<u>`, `code` `<code>`, `link` → `<a>`.
41
+ Nodes: `doc`, `paragraph` → `<p>`, `heading` (`level` 1–6 `<h1>`…`<h6>`;
42
+ any other level falls back to `<h1>`), `bulletList` → `<ul>`, `orderedList`
43
+ `<ol>` (`start` emitted only when present and not `1`), `listItem` → `<li>`,
44
+ `codeBlock` → `<pre><code>` (`language` → `class="language-x"` on `<code>`),
45
+ `text`. Marks: `bold` → `<strong>`, `italic` → `<em>`, `strike` → `<s>`,
46
+ `underline` → `<u>`, `code` → `<code>`, `link` → `<a>` (`target`, `rel`,
47
+ sanitized `href`, `class`, in that order; `null`/empty attrs are dropped).
48
+
49
+ Adjacent `text` nodes sharing outer marks are merged into one wrapper
50
+ (`<strong>a<em>b</em></strong>`, one `<a>` per link span), matching
51
+ ProseMirror's serializer. Text is escaped `& < >`; attribute values `& " < >`.
39
52
 
40
- Unknown types are skipped with a dev-only warning unless a custom renderer is
41
- provided for that type string.
53
+ Unknown node/mark types are skipped (marks: their children are still emitted)
54
+ with a `console.warn` once per type per render — suppressed when
55
+ `process.env.NODE_ENV === 'production'` — unless a custom renderer is provided
56
+ for that type string.
42
57
 
43
58
  ## Custom renderers
44
59
 
@@ -54,13 +69,25 @@ renderRichTextToHtml(data.body, {
54
69
  type unset — pass it to a nested `renderRichTextToHtml` call to re-render your
55
70
  own node without infinite recursion.
56
71
 
72
+ Renderer props (`LocalessRichTextRendererProps<TOut>`): `type`, `attrs?`,
73
+ `text?`, `marks?`, `content?`, `children`, `context: { renderers? }`, `_key?`.
74
+ Options type: `LocalessRichTextHtmlOptions` (`{ renderers?:
75
+ LocalessRichTextRenderers<string> }`). A custom `text` renderer receives the
76
+ HTML-escaped text as `children` and disables adjacent-mark merging for that
77
+ render; a custom mark renderer receives `context.renderers` unchanged (marks
78
+ don't nest into themselves).
79
+
57
80
  ## Building a native walker
58
81
 
59
82
  The helpers encode the algorithms once so walkers are mechanical translations:
60
- `normalizeInput(input, { withKeys: true })` (keyed node list),
61
- `buildMarkTree(textRun)` (adjacent-mark merging), `processAttrs(type, attrs,
83
+ `normalizeInput(input, { withKeys: true })` (keyed node list, `_key` =
84
+ `paragraph-1`, `text-3`, …; never throws, malformed input → `[]`),
85
+ `buildMarkTree(textRun)` → `MarkTreeSegment[]` (adjacent-mark merging;
86
+ `marksEqual(a, b)` is the comparison it uses), `processAttrs(type, attrs,
62
87
  { attrMap })` (attribute normalization; React passes `{ class: 'className' }`),
63
- `NODE_RENDER_MAP` / `MARK_RENDER_MAP` / `resolveHeadingTag` (default table).
88
+ `NODE_RENDER_MAP` / `MARK_RENDER_MAP` / `resolveHeadingTag` (default table;
89
+ `null` entry = transparent, missing key = unknown), `escapeHtml` /
90
+ `escapeAttr` / `sanitizeUrl` (escaping and URL allowlist).
64
91
  See `@localess/react`'s `src/core/richtext.ts` for the reference walker.
65
92
 
66
93
  ## Test fixtures
@@ -69,7 +96,29 @@ See `@localess/react`'s `src/core/richtext.ts` for the reference walker.
69
96
  import { richTextFixtures } from '@localess/richtext/test-utils';
70
97
  ```
71
98
 
72
- `{ title, input, expected, parity }` corpus asserted by every Localess
73
- renderer. `parity: true` fixtures are additionally byte-compared to TipTap's
74
- `generateHTML` — parity is normative; never weaken an assertion to
75
- `toContain`.
99
+ `richTextFixtures: RichTextFixture[]` — a `{ title, input, expected, parity }`
100
+ corpus asserted by every Localess renderer. `parity: true` fixtures are
101
+ additionally byte-compared to TipTap's `generateHTML` — parity is normative;
102
+ never weaken an assertion to `toContain`.
103
+
104
+ ## Exports Reference
105
+
106
+ ```typescript
107
+ // @localess/richtext
108
+ export { renderRichTextToHtml } // HTML string renderer
109
+ export { normalizeInput, buildMarkTree, marksEqual, processAttrs } // walker helpers
110
+ export { escapeHtml, escapeAttr, sanitizeUrl } // escaping / URL policy
111
+ export { NODE_RENDER_MAP, MARK_RENDER_MAP, resolveHeadingTag } // default render table
112
+ export type {
113
+ LocalessRichTextDocument, LocalessRichTextNode, LocalessRichTextNodeWithKey,
114
+ LocalessRichTextMark, LocalessRichTextLinkAttrs, LocalessRichTextElement,
115
+ LocalessRichTextInput, ContentRichText /* re-exported from @localess/model */,
116
+ LocalessRichTextRenderer, LocalessRichTextRenderers, LocalessRichTextRendererProps,
117
+ LocalessRichTextHtmlOptions, NormalizeInputOptions, ProcessAttrsOptions,
118
+ RichTextRenderSpec, MarkTreeSegment, MarkTreeText, MarkTreeMark,
119
+ }
120
+
121
+ // @localess/richtext/test-utils
122
+ export { richTextFixtures }
123
+ export type { RichTextFixture }
124
+ ```
package/dist/index.js CHANGED
@@ -106,7 +106,7 @@ function buildMarkTree(nodes) {
106
106
  //#region src/normalize.ts
107
107
  /**
108
108
  * Flattens any accepted rich text input (document, node, node array, or the
109
- * loose `ContentRichText` shape from `@localess/client`) into a node list.
109
+ * loose `ContentRichText` shape from `@localess/model`) into a node list.
110
110
  * Never throws; malformed input yields `[]`.
111
111
  */
112
112
  function normalizeInput(input, options = {}) {
package/dist/index.mjs CHANGED
@@ -105,7 +105,7 @@ function buildMarkTree(nodes) {
105
105
  //#region src/normalize.ts
106
106
  /**
107
107
  * Flattens any accepted rich text input (document, node, node array, or the
108
- * loose `ContentRichText` shape from `@localess/client`) into a node list.
108
+ * loose `ContentRichText` shape from `@localess/model`) into a node list.
109
109
  * Never throws; malformed input yields `[]`.
110
110
  */
111
111
  function normalizeInput(input, options = {}) {
package/dist/model.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { ContentRichText } from '@localess/model';
1
2
  /**
2
3
  * Attributes of a `link` mark as stored by the Localess Studio editor
3
4
  * (TipTap Link extension JSON).
@@ -71,16 +72,12 @@ export interface LocalessRichTextDocument {
71
72
  content?: LocalessRichTextNode[];
72
73
  }
73
74
  /**
74
- * Structural stand-in for `@localess/client`'s `ContentRichText` so client
75
- * values pass without casting. Deliberately not imported — this package has
76
- * zero dependencies.
75
+ * Re-exported from `@localess/model` so `LocalessRichTextInput` accepts
76
+ * `ContentRichText` values without casting.
77
77
  */
78
- export interface ContentRichTextLike {
79
- type?: string;
80
- content?: ContentRichTextLike[];
81
- }
78
+ export type { ContentRichText };
82
79
  /** Anything a render function accepts. */
83
- export type LocalessRichTextInput = LocalessRichTextDocument | LocalessRichTextNode | LocalessRichTextNode[] | ContentRichTextLike | null | undefined;
80
+ export type LocalessRichTextInput = LocalessRichTextDocument | LocalessRichTextNode | LocalessRichTextNode[] | ContentRichText | null | undefined;
84
81
  /** Union of every known node and mark type name. */
85
82
  export type LocalessRichTextElement = LocalessRichTextNode['type'] | LocalessRichTextMark['type'] | 'doc';
86
83
  /**
@@ -5,7 +5,7 @@ export interface NormalizeInputOptions {
5
5
  }
6
6
  /**
7
7
  * Flattens any accepted rich text input (document, node, node array, or the
8
- * loose `ContentRichText` shape from `@localess/client`) into a node list.
8
+ * loose `ContentRichText` shape from `@localess/model`) into a node list.
9
9
  * Never throws; malformed input yields `[]`.
10
10
  */
11
11
  export declare function normalizeInput(input: LocalessRichTextInput, options?: NormalizeInputOptions): LocalessRichTextNodeWithKey[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@localess/richtext",
3
- "version": "3.4.1-dev.20260901200805",
3
+ "version": "4.0.0-dev.20260905071322",
4
4
  "description": "Framework-neutral rich text model and renderer for Localess's TipTap JSON content.",
5
5
  "keywords": [
6
6
  "localess",
@@ -47,6 +47,9 @@
47
47
  "test:coverage": "vitest run --coverage"
48
48
  },
49
49
  "license": "MIT",
50
+ "dependencies": {
51
+ "@localess/model": "4.0.0-dev.20260905071322"
52
+ },
50
53
  "devDependencies": {
51
54
  "@tiptap/extension-bold": "^3.22.5",
52
55
  "@tiptap/extension-bullet-list": "^3.22.5",