@haklex/rich-compose 0.1.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.
Files changed (74) hide show
  1. package/README.md +185 -0
  2. package/dist/core/compose.d.ts +4 -0
  3. package/dist/core/compose.d.ts.map +1 -0
  4. package/dist/core/dedup.d.ts +16 -0
  5. package/dist/core/dedup.d.ts.map +1 -0
  6. package/dist/core/index.d.ts +5 -0
  7. package/dist/core/index.d.ts.map +1 -0
  8. package/dist/core/lazy.d.ts +11 -0
  9. package/dist/core/lazy.d.ts.map +1 -0
  10. package/dist/core/types.d.ts +62 -0
  11. package/dist/core/types.d.ts.map +1 -0
  12. package/dist/index.d.ts +3 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.mjs +146 -0
  15. package/dist/modules/chat/index.d.ts +5 -0
  16. package/dist/modules/chat/index.d.ts.map +1 -0
  17. package/dist/modules/chat/index.mjs +11 -0
  18. package/dist/modules/chat/module.d.ts +3 -0
  19. package/dist/modules/chat/module.d.ts.map +1 -0
  20. package/dist/modules/chat/node.d.ts +3 -0
  21. package/dist/modules/chat/node.d.ts.map +1 -0
  22. package/dist/modules/chat/node.mjs +2 -0
  23. package/dist/modules/chat/renderer.d.ts +4 -0
  24. package/dist/modules/chat/renderer.d.ts.map +1 -0
  25. package/dist/modules/chat/renderer.mjs +5 -0
  26. package/dist/modules/code-block/index.d.ts +3 -0
  27. package/dist/modules/code-block/index.d.ts.map +1 -0
  28. package/dist/modules/code-block/index.mjs +39 -0
  29. package/dist/modules/code-block/module.d.ts +12 -0
  30. package/dist/modules/code-block/module.d.ts.map +1 -0
  31. package/dist/modules/code-block/renderer.d.ts +4 -0
  32. package/dist/modules/code-block/renderer.d.ts.map +1 -0
  33. package/dist/modules/code-block/renderer.mjs +5 -0
  34. package/dist/modules/code-block/ssr-fallback.d.ts +13 -0
  35. package/dist/modules/code-block/ssr-fallback.d.ts.map +1 -0
  36. package/dist/modules/code-snippet/index.d.ts +5 -0
  37. package/dist/modules/code-snippet/index.d.ts.map +1 -0
  38. package/dist/modules/code-snippet/index.mjs +11 -0
  39. package/dist/modules/code-snippet/module.d.ts +3 -0
  40. package/dist/modules/code-snippet/module.d.ts.map +1 -0
  41. package/dist/modules/code-snippet/node.d.ts +3 -0
  42. package/dist/modules/code-snippet/node.d.ts.map +1 -0
  43. package/dist/modules/code-snippet/node.mjs +2 -0
  44. package/dist/modules/code-snippet/renderer.d.ts +4 -0
  45. package/dist/modules/code-snippet/renderer.d.ts.map +1 -0
  46. package/dist/modules/code-snippet/renderer.mjs +5 -0
  47. package/dist/modules/embed/index.d.ts +4 -0
  48. package/dist/modules/embed/index.d.ts.map +1 -0
  49. package/dist/modules/embed/index.mjs +17 -0
  50. package/dist/modules/embed/module.d.ts +11 -0
  51. package/dist/modules/embed/module.d.ts.map +1 -0
  52. package/dist/modules/embed/node.d.ts +3 -0
  53. package/dist/modules/embed/node.d.ts.map +1 -0
  54. package/dist/modules/embed/node.mjs +2 -0
  55. package/dist/modules/gallery/index.d.ts +5 -0
  56. package/dist/modules/gallery/index.d.ts.map +1 -0
  57. package/dist/modules/gallery/index.mjs +11 -0
  58. package/dist/modules/gallery/module.d.ts +3 -0
  59. package/dist/modules/gallery/module.d.ts.map +1 -0
  60. package/dist/modules/gallery/node.d.ts +3 -0
  61. package/dist/modules/gallery/node.d.ts.map +1 -0
  62. package/dist/modules/gallery/node.mjs +2 -0
  63. package/dist/modules/gallery/renderer.d.ts +4 -0
  64. package/dist/modules/gallery/renderer.d.ts.map +1 -0
  65. package/dist/modules/gallery/renderer.mjs +5 -0
  66. package/dist/modules/link-card/index.d.ts +3 -0
  67. package/dist/modules/link-card/index.d.ts.map +1 -0
  68. package/dist/modules/link-card/index.mjs +15 -0
  69. package/dist/modules/link-card/module.d.ts +9 -0
  70. package/dist/modules/link-card/module.d.ts.map +1 -0
  71. package/dist/modules/link-card/renderer.d.ts +4 -0
  72. package/dist/modules/link-card/renderer.d.ts.map +1 -0
  73. package/dist/modules/link-card/renderer.mjs +5 -0
  74. package/package.json +149 -0
package/README.md ADDED
@@ -0,0 +1,185 @@
1
+ # @haklex/rich-compose
2
+
3
+ Compose primitives for haklex rich content renderers — Gundam-style assembly of Lexical nodes, sync/lazy renderers, and Provider stacks.
4
+
5
+ Status: experimental (`0.1.0`). The renderer side is being migrated from `@haklex/rich-kit-shiro/renderer`. The editor side is out of scope.
6
+
7
+ ## Why
8
+
9
+ `@haklex/rich-kit-shiro/renderer` ships every default renderer eagerly: even when a consumer overrides `CodeBlock` or `LinkCard`, the upstream `shiki` and `LinkCardRenderer` chains stay in the production bundle. `rich-compose` solves three problems at once:
10
+
11
+ 1. **Subtractable** — drop a module by not importing it.
12
+ 2. **Replaceable** — swap a default renderer with no leftover bytes from the original.
13
+ 3. **Extensible** — add new renderer slots (sync or lazy) without touching the package.
14
+
15
+ Tree-shake is enforced by physical subpath isolation (`./modules/<name>/node` separates Klasses from heavy renderer code) plus ESM-only emit.
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ pnpm add @haklex/rich-compose
21
+ ```
22
+
23
+ ## Peer Dependencies
24
+
25
+ | Package | Version |
26
+ | --- | --- |
27
+ | `react` / `react-dom` | `>=19` |
28
+ | `lexical` / `@lexical/react` | `^0.44.0` |
29
+ | `@haklex/rich-editor` | workspace |
30
+ | `@haklex/rich-static-renderer` | workspace |
31
+
32
+ Per-module upstream packages (`@haklex/rich-ext-*`, `@haklex/rich-renderer-*`) are optional peers — install only those you compose into your renderer.
33
+
34
+ ## Quick start
35
+
36
+ ```tsx
37
+ import { composeRenderer } from '@haklex/rich-compose'
38
+ import { embedModule } from '@haklex/rich-compose/modules/embed'
39
+ import { codeBlockModule } from '@haklex/rich-compose/modules/code-block'
40
+ import { linkCardModule } from '@haklex/rich-compose/modules/link-card'
41
+ // import any modules you need...
42
+
43
+ const RichContent = composeRenderer({
44
+ modules: [embedModule, codeBlockModule, linkCardModule],
45
+ })
46
+
47
+ // Render
48
+ <RichContent value={editorState} theme="light" variant="article" />
49
+ ```
50
+
51
+ `composeRenderer` returns a memoized React component compatible with `<RichRenderer>`'s prop surface (theme, variant, value, className, style, …).
52
+
53
+ ## Three consumer modes
54
+
55
+ ### Mode A — defaults
56
+
57
+ Import the module sugar. Klass + renderer + (optional) lazy/SSR fallback are wired automatically.
58
+
59
+ ```tsx
60
+ import { embedModule } from '@haklex/rich-compose/modules/embed'
61
+ ```
62
+
63
+ ### Mode B — custom renderer (tree-shake the default)
64
+
65
+ Import the Klass from the `/node` subpath; supply your own renderer. The default renderer's chunk never enters the bundle.
66
+
67
+ ```tsx
68
+ import { GalleryNode } from '@haklex/rich-compose/modules/gallery/node'
69
+
70
+ const myGalleryModule: RichRendererModule = {
71
+ name: 'gallery',
72
+ nodes: [GalleryNode],
73
+ renderers: { Gallery: MyGalleryRenderer },
74
+ }
75
+ ```
76
+
77
+ For modules with no custom Klass (most of the renderer-only modules), simply construct a module with the same `name` and your renderer:
78
+
79
+ ```tsx
80
+ const myLinkCardModule: RichRendererModule = {
81
+ name: 'link-card',
82
+ renderers: { LinkCard: MyLinkCardRenderer },
83
+ }
84
+ ```
85
+
86
+ ### Mode C — wrap the default
87
+
88
+ Pull the default renderer from `/renderer` and wrap it.
89
+
90
+ ```tsx
91
+ import { LinkCardNode } from '@haklex/rich-compose/modules/link-card/node' // when applicable
92
+ import { LinkCardRenderer } from '@haklex/rich-compose/modules/link-card/renderer'
93
+
94
+ const wrappedModule: RichRendererModule = {
95
+ name: 'link-card',
96
+ renderers: {
97
+ LinkCard: (props) => (
98
+ <div className="extra-wrap">
99
+ <LinkCardRenderer {...props} />
100
+ </div>
101
+ ),
102
+ },
103
+ }
104
+ ```
105
+
106
+ ## `RichRendererModule` shape
107
+
108
+ ```ts
109
+ interface RichRendererModule {
110
+ name: string // dedup key
111
+ nodes?: Klass<LexicalNode>[] // optional — omit for builtin types
112
+ renderers?: Partial<RendererConfig> // sync renderer map
113
+ Provider?: ComponentType<{ children: ReactNode }> // optional internal provider
114
+ lazyRenderers?: Partial<{ // code-split renderers
115
+ [K in RendererKey]: () => Promise<{ default: NonNullable<RendererConfig[K]> }>
116
+ }>
117
+ ssrFallback?: Partial<Record<RendererKey, ReactNode>> // deterministic fallback
118
+ }
119
+ ```
120
+
121
+ `composeRenderer` merges modules, dedups Klasses, wraps lazy loaders in `React.lazy` (factory built once per compose), stacks Providers, and always sets `NestedContentRendererProvider` to a recursive closure for nested editor states.
122
+
123
+ ## Lazy modules
124
+
125
+ `code-block`, `excalidraw`, `katex`, and `mermaid` ship lazy by default. Each provides an `ssrFallback` that renders deterministically server-side and during hydration to avoid mismatches.
126
+
127
+ To override a lazy renderer (e.g., for a sync, pre-tokenized code block on SSR), pass an entry in `overrides`:
128
+
129
+ ```ts
130
+ composeRenderer({
131
+ modules: [/* ... */],
132
+ overrides: { CodeBlock: PreTokenizedCodeBlock },
133
+ })
134
+ ```
135
+
136
+ The lazy chunk is still emitted but never fetched at runtime.
137
+
138
+ ## Dedup rules
139
+
140
+ ```
141
+ modules:
142
+ reference seen → skip silently
143
+ same name → warn (dev), replace previous module entirely
144
+ else → append
145
+
146
+ nodes:
147
+ reference seen → skip
148
+ same getType collision → throw at compose time
149
+ (multiple Klass instances break instanceof)
150
+ ```
151
+
152
+ ## Module catalog
153
+
154
+ Implemented:
155
+
156
+ | Module | Klass | Renderer mode | Source |
157
+ | --- | --- | --- | --- |
158
+ | `embed` | `EmbedNode` | sync (via node decorate) | `rich-ext-embed` |
159
+ | `link-card` | builtin | sync | `rich-renderer-linkcard` |
160
+ | `gallery` | `GalleryNode` | sync | `rich-ext-gallery` |
161
+ | `code-snippet` | `CodeSnippetNode` | sync | `rich-ext-code-snippet` |
162
+ | `chat` | `ChatNode` | sync | `rich-ext-chat` |
163
+ | `code-block` | builtin | lazy + ssr fallback | `rich-renderer-codeblock` |
164
+
165
+ Planned (not yet shipped): `excalidraw`, `nested-doc`, `katex`, `mermaid`, `mention`, `poll`, `video`, `ruby`, `alert`, `banner`, `image`. See `docs/superpowers/specs/2026-05-08-rich-compose-design.md` for the full catalog and migration plan.
166
+
167
+ > **Note**: `@haklex/rich-editor` registers a wide set of custom Klasses in `customNodes` (LinkCard, Mention, Image, Video, KaTeX, Mermaid, Alert, Banner, Ruby, Tag, Poll, CodeBlock, Footnote, etc.). Modules for these contribute only renderer mappings — there is no Klass to register, hence no `node.ts` and no `/node` subpath.
168
+
169
+ ## Sub-path exports
170
+
171
+ | Path | Description |
172
+ | --- | --- |
173
+ | `@haklex/rich-compose` | core: `composeRenderer`, types, helpers |
174
+ | `@haklex/rich-compose/modules/<name>` | full module barrel |
175
+ | `@haklex/rich-compose/modules/<name>/node` | Klass(es) only (when applicable) |
176
+ | `@haklex/rich-compose/modules/<name>/renderer` | default renderer only |
177
+ | `@haklex/rich-compose/style.css` | core styles (skeletons, layout shell) |
178
+
179
+ ## Part of Haklex
180
+
181
+ This package is part of the [Haklex](../../README.md) rich editor ecosystem.
182
+
183
+ ## License
184
+
185
+ MIT
@@ -0,0 +1,4 @@
1
+ import { ComponentType } from 'react';
2
+ import { ComposeRendererOptions, RichRendererBaseProps } from './types';
3
+ export declare function composeRenderer(opts: ComposeRendererOptions): ComponentType<RichRendererBaseProps>;
4
+ //# sourceMappingURL=compose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../src/core/compose.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,aAAa,EAA+C,MAAM,OAAO,CAAA;AAIvF,OAAO,KAAK,EACV,sBAAsB,EACtB,qBAAqB,EAEtB,MAAM,SAAS,CAAA;AAoChB,wBAAgB,eAAe,CAC7B,IAAI,EAAE,sBAAsB,GAC3B,aAAa,CAAC,qBAAqB,CAAC,CAiDtC"}
@@ -0,0 +1,16 @@
1
+ import { Klass, LexicalNode } from 'lexical';
2
+ import { RichRendererModule } from './types';
3
+ /**
4
+ * Merge preset and modules, deduplicating by:
5
+ * 1. Same module reference → skip silently (idempotent)
6
+ * 2. Same module.name → replace previous module entirely; warn in dev
7
+ * 3. Else → append
8
+ */
9
+ export declare function mergeModules(preset: RichRendererModule[] | undefined, modules: RichRendererModule[] | undefined): RichRendererModule[];
10
+ /**
11
+ * Dedup by class reference. Throws if two distinct Klasses share the same
12
+ * `getType()` — multiple Klass instances will break `instanceof` checks
13
+ * across module boundaries.
14
+ */
15
+ export declare function dedupNodes(nodes: Klass<LexicalNode>[]): Klass<LexicalNode>[];
16
+ //# sourceMappingURL=dedup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dedup.d.ts","sourceRoot":"","sources":["../../src/core/dedup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAEjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAEjD;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,kBAAkB,EAAE,GAAG,SAAS,EACxC,OAAO,EAAE,kBAAkB,EAAE,GAAG,SAAS,GACxC,kBAAkB,EAAE,CA0BtB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,WAAW,CAAC,EAAE,GAAG,KAAK,CAAC,WAAW,CAAC,EAAE,CAyB5E"}
@@ -0,0 +1,5 @@
1
+ export { composeRenderer } from './compose';
2
+ export { dedupNodes, mergeModules } from './dedup';
3
+ export { wrapLazy } from './lazy';
4
+ export type { ComposeRendererOptions, RendererKey, RichRendererBaseProps, RichRendererModule, } from './types';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AACjC,YAAY,EACV,sBAAsB,EACtB,WAAW,EACX,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,SAAS,CAAA"}
@@ -0,0 +1,11 @@
1
+ import { RendererConfig } from '@haklex/rich-editor';
2
+ import { RichRendererModule } from './types';
3
+ /**
4
+ * Build sync wrappers around `React.lazy(loader)` factories.
5
+ *
6
+ * Each factory is created exactly **once** at compose time, not per render.
7
+ * Recreating the factory on every render would force React to refetch the
8
+ * chunk and replay the Suspense fallback, causing flicker.
9
+ */
10
+ export declare function wrapLazy(modules: RichRendererModule[]): Partial<RendererConfig>;
11
+ //# sourceMappingURL=lazy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lazy.d.ts","sourceRoot":"","sources":["../../src/core/lazy.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAIzD,OAAO,KAAK,EAAe,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAE9D;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAsB/E"}
@@ -0,0 +1,62 @@
1
+ import { RendererConfig, RichEditorVariant } from '@haklex/rich-editor';
2
+ import { BuiltinNodeRenderer } from '@haklex/rich-static-renderer';
3
+ import { Klass, LexicalNode, SerializedEditorState } from 'lexical';
4
+ import { ComponentType, CSSProperties, ReactNode } from 'react';
5
+ export type RendererKey = keyof RendererConfig;
6
+ export interface RichRendererModule {
7
+ /** Stable identifier; used for dedup, debug logs, and Provider DevTools naming. */
8
+ name: string;
9
+ /**
10
+ * Lexical node classes. Always synchronous. Optional — modules that only
11
+ * override renderers for Lexical builtin types omit this field.
12
+ * composeRenderer treats missing `nodes` as `[]`.
13
+ */
14
+ nodes?: Klass<LexicalNode>[];
15
+ /** type → Component map. Sync renderers. */
16
+ renderers?: Partial<RendererConfig>;
17
+ /**
18
+ * Optional context provider. composeRenderer stacks Providers in module
19
+ * order outside `<RichRenderer>`. Module Providers are for internal
20
+ * plumbing only — do not redeclare `NestedContentRendererProvider`,
21
+ * composeRenderer manages it.
22
+ */
23
+ Provider?: ComponentType<{
24
+ children: ReactNode;
25
+ }>;
26
+ /**
27
+ * Lazy renderers. Each loader returns the renderer component as default
28
+ * export. composeRenderer wraps each in `React.lazy` (factory created
29
+ * once at compose time) + `<Suspense fallback={ssrFallback?.[type]}>`.
30
+ */
31
+ lazyRenderers?: Partial<{
32
+ [K in RendererKey]: () => Promise<{
33
+ default: NonNullable<RendererConfig[K]>;
34
+ }>;
35
+ }>;
36
+ /**
37
+ * SSR / Suspense fallback. Must be deterministic — no Date.now, no random,
38
+ * no client-only API access. Renders identically server-side and during
39
+ * client hydration.
40
+ */
41
+ ssrFallback?: Partial<Record<RendererKey, ReactNode>>;
42
+ }
43
+ export interface ComposeRendererOptions {
44
+ /** Starting set; can be `shiroPreset`, custom array, or omitted. */
45
+ preset?: RichRendererModule[];
46
+ /** Appended to preset; later modules override earlier on `name` collision. */
47
+ modules?: RichRendererModule[];
48
+ /** Final renderer overrides; takes precedence over module-supplied renderers. */
49
+ overrides?: Partial<RendererConfig>;
50
+ /** Pass-through to `<RichRenderer>` for `paragraph` / `link` / `autolink` etc. */
51
+ builtinNodeOverrides?: Record<string, BuiltinNodeRenderer>;
52
+ }
53
+ export interface RichRendererBaseProps {
54
+ value: SerializedEditorState;
55
+ variant?: RichEditorVariant;
56
+ theme?: 'light' | 'dark';
57
+ className?: string;
58
+ style?: CSSProperties;
59
+ nested?: boolean;
60
+ as?: keyof React.JSX.IntrinsicElements;
61
+ }
62
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAA;AACvE,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAA;AACxE,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEpE,MAAM,MAAM,WAAW,GAAG,MAAM,cAAc,CAAA;AAE9C,MAAM,WAAW,kBAAkB;IACjC,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAA;IAEZ;;;;OAIG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,EAAE,CAAA;IAE5B,4CAA4C;IAC5C,SAAS,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAA;IAEnC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;QAAE,QAAQ,EAAE,SAAS,CAAA;KAAE,CAAC,CAAA;IAEjD;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;SACrB,CAAC,IAAI,WAAW,GAAG,MAAM,OAAO,CAAC;YAAE,OAAO,EAAE,WAAW,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAA;SAAE,CAAC;KAC/E,CAAC,CAAA;IAEF;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC,CAAA;CACtD;AAED,MAAM,WAAW,sBAAsB;IACrC,oEAAoE;IACpE,MAAM,CAAC,EAAE,kBAAkB,EAAE,CAAA;IAC7B,8EAA8E;IAC9E,OAAO,CAAC,EAAE,kBAAkB,EAAE,CAAA;IAC9B,iFAAiF;IACjF,SAAS,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAA;IACnC,kFAAkF;IAClF,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAA;CAC3D;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,qBAAqB,CAAA;IAC5B,OAAO,CAAC,EAAE,iBAAiB,CAAA;IAC3B,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAA;CACvC"}
@@ -0,0 +1,3 @@
1
+ export { composeRenderer, dedupNodes, mergeModules, wrapLazy, } from './core';
2
+ export type { ComposeRendererOptions, RendererKey, RichRendererBaseProps, RichRendererModule, } from './core';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,UAAU,EACV,YAAY,EACZ,QAAQ,GACT,MAAM,QAAQ,CAAA;AACf,YAAY,EACV,sBAAsB,EACtB,WAAW,EACX,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,QAAQ,CAAA"}
package/dist/index.mjs ADDED
@@ -0,0 +1,146 @@
1
+ import { NestedContentRendererProvider } from "@haklex/rich-editor";
2
+ import { RichRenderer } from "@haklex/rich-static-renderer";
3
+ import { Fragment, Suspense, lazy, memo, useCallback } from "react";
4
+ import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
5
+ //#region src/core/dedup.ts
6
+ /**
7
+ * Merge preset and modules, deduplicating by:
8
+ * 1. Same module reference → skip silently (idempotent)
9
+ * 2. Same module.name → replace previous module entirely; warn in dev
10
+ * 3. Else → append
11
+ */
12
+ function mergeModules(preset, modules) {
13
+ const all = [...preset ?? [], ...modules ?? []];
14
+ const refSeen = /* @__PURE__ */ new Set();
15
+ const byName = /* @__PURE__ */ new Map();
16
+ const result = [];
17
+ for (const m of all) {
18
+ if (refSeen.has(m)) continue;
19
+ refSeen.add(m);
20
+ const existingIdx = byName.get(m.name);
21
+ if (existingIdx !== void 0) {
22
+ console.warn(`[rich-compose] module name collision: "${m.name}" — replacing previous registration. Pass the same module reference to silence this warning.`);
23
+ result[existingIdx] = m;
24
+ continue;
25
+ }
26
+ byName.set(m.name, result.length);
27
+ result.push(m);
28
+ }
29
+ return result;
30
+ }
31
+ /**
32
+ * Dedup by class reference. Throws if two distinct Klasses share the same
33
+ * `getType()` — multiple Klass instances will break `instanceof` checks
34
+ * across module boundaries.
35
+ */
36
+ function dedupNodes(nodes) {
37
+ const seen = /* @__PURE__ */ new Set();
38
+ const byType = /* @__PURE__ */ new Map();
39
+ const result = [];
40
+ for (const Node of nodes) {
41
+ if (seen.has(Node)) continue;
42
+ seen.add(Node);
43
+ const type = typeof Node.getType === "function" ? Node.getType() : void 0;
44
+ if (type !== void 0) {
45
+ const prev = byType.get(type);
46
+ if (prev !== void 0 && prev !== Node) throw new Error(`[rich-compose] node type collision on "${type}": two distinct Klass references registered. This breaks \`instanceof\` checks. Ensure peerDeps/lexical version are pinned and a single module exports each type.`);
47
+ byType.set(type, Node);
48
+ }
49
+ result.push(Node);
50
+ }
51
+ return result;
52
+ }
53
+ //#endregion
54
+ //#region src/core/lazy.tsx
55
+ /**
56
+ * Build sync wrappers around `React.lazy(loader)` factories.
57
+ *
58
+ * Each factory is created exactly **once** at compose time, not per render.
59
+ * Recreating the factory on every render would force React to refetch the
60
+ * chunk and replay the Suspense fallback, causing flicker.
61
+ */
62
+ function wrapLazy(modules) {
63
+ const result = {};
64
+ for (const m of modules) {
65
+ if (!m.lazyRenderers) continue;
66
+ for (const key of Object.keys(m.lazyRenderers)) {
67
+ const loader = m.lazyRenderers[key];
68
+ if (!loader) continue;
69
+ const fallback = m.ssrFallback?.[key] ?? null;
70
+ const LazyInner = lazy(loader);
71
+ const Wrapped = (props) => /* @__PURE__ */ jsx(Suspense, {
72
+ fallback,
73
+ children: /* @__PURE__ */ jsx(LazyInner, { ...props })
74
+ });
75
+ Wrapped.displayName = `Lazy(${m.name}.${String(key)})`;
76
+ result[key] = Wrapped;
77
+ }
78
+ }
79
+ return result;
80
+ }
81
+ //#endregion
82
+ //#region src/core/compose.tsx
83
+ function flattenNodes(modules) {
84
+ return dedupNodes(modules.flatMap((m) => m.nodes ?? []));
85
+ }
86
+ function mergeSyncRenderers(modules) {
87
+ const out = {};
88
+ for (const m of modules) {
89
+ if (!m.renderers) continue;
90
+ Object.assign(out, m.renderers);
91
+ }
92
+ return out;
93
+ }
94
+ function composeProviders(modules) {
95
+ const providers = modules.map((m) => m.Provider).filter((P) => Boolean(P));
96
+ if (providers.length === 0) return Fragment;
97
+ const ComposedProviders = ({ children }) => {
98
+ let acc = children;
99
+ for (let i = providers.length - 1; i >= 0; i--) {
100
+ const P = providers[i];
101
+ acc = /* @__PURE__ */ jsx(P, { children: acc });
102
+ }
103
+ return /* @__PURE__ */ jsx(Fragment$1, { children: acc });
104
+ };
105
+ ComposedProviders.displayName = "ComposedProviders";
106
+ return ComposedProviders;
107
+ }
108
+ function composeRenderer(opts) {
109
+ const merged = mergeModules(opts.preset, opts.modules);
110
+ const allNodes = flattenNodes(merged);
111
+ const syncMap = mergeSyncRenderers(merged);
112
+ const lazyMap = wrapLazy(merged);
113
+ const finalConfig = {
114
+ ...syncMap,
115
+ ...lazyMap,
116
+ ...opts.overrides ?? {}
117
+ };
118
+ const ComposedProviders = composeProviders(merged);
119
+ const builtinNodeOverrides = opts.builtinNodeOverrides;
120
+ function ComposedRenderer(props) {
121
+ const { theme, variant } = props;
122
+ return /* @__PURE__ */ jsx(NestedContentRendererProvider, {
123
+ value: useCallback((value, overrideVariant) => /* @__PURE__ */ jsx(ComposedRenderer, {
124
+ nested: true,
125
+ theme,
126
+ value,
127
+ variant: overrideVariant ?? variant
128
+ }), [theme, variant]),
129
+ children: /* @__PURE__ */ jsx(ComposedProviders, { children: /* @__PURE__ */ jsx(RichRenderer, {
130
+ as: props.as,
131
+ builtinNodeOverrides,
132
+ className: props.className,
133
+ extraNodes: allNodes,
134
+ nested: props.nested,
135
+ rendererConfig: finalConfig,
136
+ style: props.style,
137
+ theme,
138
+ value: props.value,
139
+ variant
140
+ }) })
141
+ });
142
+ }
143
+ return memo(ComposedRenderer);
144
+ }
145
+ //#endregion
146
+ export { composeRenderer, dedupNodes, mergeModules, wrapLazy };
@@ -0,0 +1,5 @@
1
+ export type { ChatMessage, ChatParticipant, ChatParticipantKind, ChatRendererProps, ChatVariant, SerializedChatNode, } from '@haklex/rich-ext-chat/static';
2
+ export { $createChatNode, $isChatNode, ChatNode, chatNodes, } from './node';
3
+ export { ChatRenderer } from './renderer';
4
+ export { chatModule } from './module';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/modules/chat/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,WAAW,EACX,kBAAkB,GACnB,MAAM,8BAA8B,CAAA;AACrC,OAAO,EACL,eAAe,EACf,WAAW,EACX,QAAQ,EACR,SAAS,GACV,MAAM,QAAQ,CAAA;AACf,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA"}
@@ -0,0 +1,11 @@
1
+ import { $createChatNode, $isChatNode, ChatNode, chatNodes } from "./node.mjs";
2
+ import { ChatRenderer } from "./renderer.mjs";
3
+ import { ChatRenderer as ChatRenderer$1, chatNodes as chatNodes$1 } from "@haklex/rich-ext-chat/static";
4
+ //#region src/modules/chat/module.ts
5
+ var chatModule = {
6
+ name: "chat",
7
+ nodes: chatNodes$1,
8
+ renderers: { Chat: ChatRenderer$1 }
9
+ };
10
+ //#endregion
11
+ export { $createChatNode, $isChatNode, ChatNode, ChatRenderer, chatModule, chatNodes };
@@ -0,0 +1,3 @@
1
+ import { RichRendererModule } from '../../core/types';
2
+ export declare const chatModule: RichRendererModule;
3
+ //# sourceMappingURL=module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../src/modules/chat/module.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAE1D,eAAO,MAAM,UAAU,EAAE,kBAIxB,CAAA"}
@@ -0,0 +1,3 @@
1
+ export type { SerializedChatNode } from '@haklex/rich-ext-chat/static';
2
+ export { $createChatNode, $isChatNode, ChatNode, chatNodes, } from '@haklex/rich-ext-chat/static';
3
+ //# sourceMappingURL=node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../src/modules/chat/node.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAA;AACtE,OAAO,EACL,eAAe,EACf,WAAW,EACX,QAAQ,EACR,SAAS,GACV,MAAM,8BAA8B,CAAA"}
@@ -0,0 +1,2 @@
1
+ import { $createChatNode, $isChatNode, ChatNode, chatNodes } from "@haklex/rich-ext-chat/static";
2
+ export { $createChatNode, $isChatNode, ChatNode, chatNodes };
@@ -0,0 +1,4 @@
1
+ import { ChatRenderer } from '@haklex/rich-ext-chat/static';
2
+ export { ChatRenderer };
3
+ export default ChatRenderer;
4
+ //# sourceMappingURL=renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../src/modules/chat/renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAE3D,OAAO,EAAE,YAAY,EAAE,CAAA;AACvB,eAAe,YAAY,CAAA"}
@@ -0,0 +1,5 @@
1
+ import { ChatRenderer } from "@haklex/rich-ext-chat/static";
2
+ //#region src/modules/chat/renderer.tsx
3
+ var renderer_default = ChatRenderer;
4
+ //#endregion
5
+ export { ChatRenderer, renderer_default as default };
@@ -0,0 +1,3 @@
1
+ export { CodeBlockRenderer } from './renderer';
2
+ export { codeBlockModule } from './module';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/modules/code-block/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA"}
@@ -0,0 +1,39 @@
1
+ import { CodeBlockRenderer } from "./renderer.mjs";
2
+ import { jsx } from "react/jsx-runtime";
3
+ //#region src/modules/code-block/ssr-fallback.tsx
4
+ /**
5
+ * SSR / Suspense fallback for the lazy CodeBlock renderer.
6
+ *
7
+ * Determinism requirement: identical output server-side and client-side
8
+ * to avoid hydration mismatches. No Date, no random, no client-only API.
9
+ *
10
+ * Renders a placeholder `<pre>` shell. The lazy renderer replaces it with
11
+ * shiki-tokenized output once the chunk resolves. Even without code text
12
+ * (the fallback runs before props are routed through the lazy boundary),
13
+ * the empty pre keeps layout stable.
14
+ */
15
+ function CodeBlockSsrFallback() {
16
+ return /* @__PURE__ */ jsx("pre", {
17
+ className: "rich-codeblock-skeleton",
18
+ "data-rich-skeleton": "codeblock",
19
+ children: /* @__PURE__ */ jsx("code", {})
20
+ });
21
+ }
22
+ //#endregion
23
+ //#region src/modules/code-block/module.tsx
24
+ /**
25
+ * Code-block module — uses Lexical's builtin `code` node, so no `nodes`
26
+ * field. The default renderer (`@haklex/rich-renderer-codeblock`) is heavy
27
+ * (shiki) and ships in a separate lazy chunk.
28
+ *
29
+ * The SSR fallback emits a `<pre>` shell so server-rendered HTML keeps
30
+ * layout stable before the lazy chunk resolves. The lazy renderer replaces
31
+ * it with shiki-tokenized output after hydration.
32
+ */
33
+ var codeBlockModule = {
34
+ name: "code-block",
35
+ lazyRenderers: { CodeBlock: () => import("./renderer.mjs") },
36
+ ssrFallback: { CodeBlock: /* @__PURE__ */ jsx(CodeBlockSsrFallback, {}) }
37
+ };
38
+ //#endregion
39
+ export { CodeBlockRenderer, codeBlockModule };
@@ -0,0 +1,12 @@
1
+ import { RichRendererModule } from '../../core/types';
2
+ /**
3
+ * Code-block module — uses Lexical's builtin `code` node, so no `nodes`
4
+ * field. The default renderer (`@haklex/rich-renderer-codeblock`) is heavy
5
+ * (shiki) and ships in a separate lazy chunk.
6
+ *
7
+ * The SSR fallback emits a `<pre>` shell so server-rendered HTML keeps
8
+ * layout stable before the lazy chunk resolves. The lazy renderer replaces
9
+ * it with shiki-tokenized output after hydration.
10
+ */
11
+ export declare const codeBlockModule: RichRendererModule;
12
+ //# sourceMappingURL=module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../src/modules/code-block/module.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAG1D;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,EAAE,kBAQ7B,CAAA"}
@@ -0,0 +1,4 @@
1
+ import { CodeBlockRenderer } from '@haklex/rich-renderer-codeblock/static';
2
+ export { CodeBlockRenderer };
3
+ export default CodeBlockRenderer;
4
+ //# sourceMappingURL=renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../src/modules/code-block/renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAA;AAE1E,OAAO,EAAE,iBAAiB,EAAE,CAAA;AAC5B,eAAe,iBAAiB,CAAA"}
@@ -0,0 +1,5 @@
1
+ import { CodeBlockRenderer } from "@haklex/rich-renderer-codeblock/static";
2
+ //#region src/modules/code-block/renderer.tsx
3
+ var renderer_default = CodeBlockRenderer;
4
+ //#endregion
5
+ export { CodeBlockRenderer, renderer_default as default };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * SSR / Suspense fallback for the lazy CodeBlock renderer.
3
+ *
4
+ * Determinism requirement: identical output server-side and client-side
5
+ * to avoid hydration mismatches. No Date, no random, no client-only API.
6
+ *
7
+ * Renders a placeholder `<pre>` shell. The lazy renderer replaces it with
8
+ * shiki-tokenized output once the chunk resolves. Even without code text
9
+ * (the fallback runs before props are routed through the lazy boundary),
10
+ * the empty pre keeps layout stable.
11
+ */
12
+ export declare function CodeBlockSsrFallback(): import("react/jsx-runtime").JSX.Element;
13
+ //# sourceMappingURL=ssr-fallback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssr-fallback.d.ts","sourceRoot":"","sources":["../../../src/modules/code-block/ssr-fallback.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,4CAMnC"}
@@ -0,0 +1,5 @@
1
+ export type { SerializedCodeSnippetNode } from './node';
2
+ export { $createCodeSnippetNode, $isCodeSnippetNode, CodeSnippetNode, codeSnippetNodes, } from './node';
3
+ export { CodeSnippetRenderer } from './renderer';
4
+ export { codeSnippetModule } from './module';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/modules/code-snippet/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,yBAAyB,EAAE,MAAM,QAAQ,CAAA;AACvD,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,GACjB,MAAM,QAAQ,CAAA;AACf,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA"}
@@ -0,0 +1,11 @@
1
+ import { $createCodeSnippetNode, $isCodeSnippetNode, CodeSnippetNode, codeSnippetNodes } from "./node.mjs";
2
+ import { CodeSnippetRenderer } from "./renderer.mjs";
3
+ import { CodeSnippetRenderer as CodeSnippetRenderer$1, codeSnippetNodes as codeSnippetNodes$1 } from "@haklex/rich-ext-code-snippet/static";
4
+ //#region src/modules/code-snippet/module.ts
5
+ var codeSnippetModule = {
6
+ name: "code-snippet",
7
+ nodes: codeSnippetNodes$1,
8
+ renderers: { CodeSnippet: CodeSnippetRenderer$1 }
9
+ };
10
+ //#endregion
11
+ export { $createCodeSnippetNode, $isCodeSnippetNode, CodeSnippetNode, CodeSnippetRenderer, codeSnippetModule, codeSnippetNodes };
@@ -0,0 +1,3 @@
1
+ import { RichRendererModule } from '../../core/types';
2
+ export declare const codeSnippetModule: RichRendererModule;
3
+ //# sourceMappingURL=module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../src/modules/code-snippet/module.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAE1D,eAAO,MAAM,iBAAiB,EAAE,kBAI/B,CAAA"}
@@ -0,0 +1,3 @@
1
+ export type { SerializedCodeSnippetNode } from '@haklex/rich-ext-code-snippet/static';
2
+ export { $createCodeSnippetNode, $isCodeSnippetNode, CodeSnippetNode, codeSnippetNodes, } from '@haklex/rich-ext-code-snippet/static';
3
+ //# sourceMappingURL=node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../src/modules/code-snippet/node.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAA;AACrF,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,GACjB,MAAM,sCAAsC,CAAA"}
@@ -0,0 +1,2 @@
1
+ import { $createCodeSnippetNode, $isCodeSnippetNode, CodeSnippetNode, codeSnippetNodes } from "@haklex/rich-ext-code-snippet/static";
2
+ export { $createCodeSnippetNode, $isCodeSnippetNode, CodeSnippetNode, codeSnippetNodes };
@@ -0,0 +1,4 @@
1
+ import { CodeSnippetRenderer } from '@haklex/rich-ext-code-snippet/static';
2
+ export { CodeSnippetRenderer };
3
+ export default CodeSnippetRenderer;
4
+ //# sourceMappingURL=renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../src/modules/code-snippet/renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAA;AAE1E,OAAO,EAAE,mBAAmB,EAAE,CAAA;AAC9B,eAAe,mBAAmB,CAAA"}
@@ -0,0 +1,5 @@
1
+ import { CodeSnippetRenderer } from "@haklex/rich-ext-code-snippet/static";
2
+ //#region src/modules/code-snippet/renderer.tsx
3
+ var renderer_default = CodeSnippetRenderer;
4
+ //#endregion
5
+ export { CodeSnippetRenderer, renderer_default as default };
@@ -0,0 +1,4 @@
1
+ export type { SerializedEmbedNode } from './node';
2
+ export { $createEmbedNode, $isEmbedNode, EmbedNode, embedNodes } from './node';
3
+ export { embedModule } from './module';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/modules/embed/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAA;AACjD,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAC9E,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA"}
@@ -0,0 +1,17 @@
1
+ import { $createEmbedNode, $isEmbedNode, EmbedNode, embedNodes } from "./node.mjs";
2
+ import { embedNodes as embedNodes$1 } from "@haklex/rich-ext-embed/static";
3
+ //#region src/modules/embed/module.ts
4
+ /**
5
+ * Embed module — registers the EmbedNode Klass.
6
+ *
7
+ * The embed node's `decorate()` calls `EmbedStaticRenderer` internally; per-
8
+ * platform component injection happens via `EmbedRendererProvider` at the
9
+ * consumer level (it's data, not a renderer slot in `RendererConfig`).
10
+ * Hence this module declares only `nodes`.
11
+ */
12
+ var embedModule = {
13
+ name: "embed",
14
+ nodes: embedNodes$1
15
+ };
16
+ //#endregion
17
+ export { $createEmbedNode, $isEmbedNode, EmbedNode, embedModule, embedNodes };
@@ -0,0 +1,11 @@
1
+ import { RichRendererModule } from '../../core/types';
2
+ /**
3
+ * Embed module — registers the EmbedNode Klass.
4
+ *
5
+ * The embed node's `decorate()` calls `EmbedStaticRenderer` internally; per-
6
+ * platform component injection happens via `EmbedRendererProvider` at the
7
+ * consumer level (it's data, not a renderer slot in `RendererConfig`).
8
+ * Hence this module declares only `nodes`.
9
+ */
10
+ export declare const embedModule: RichRendererModule;
11
+ //# sourceMappingURL=module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../src/modules/embed/module.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAE1D;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,EAAE,kBAGzB,CAAA"}
@@ -0,0 +1,3 @@
1
+ export type { SerializedEmbedNode } from '@haklex/rich-ext-embed/static';
2
+ export { $createEmbedNode, $isEmbedNode, EmbedNode, embedNodes, } from '@haklex/rich-ext-embed/static';
3
+ //# sourceMappingURL=node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../src/modules/embed/node.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAA;AACxE,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,SAAS,EACT,UAAU,GACX,MAAM,+BAA+B,CAAA"}
@@ -0,0 +1,2 @@
1
+ import { $createEmbedNode, $isEmbedNode, EmbedNode, embedNodes } from "@haklex/rich-ext-embed/static";
2
+ export { $createEmbedNode, $isEmbedNode, EmbedNode, embedNodes };
@@ -0,0 +1,5 @@
1
+ export type { GalleryNodePayload, SerializedGalleryNode } from './node';
2
+ export { $createGalleryNode, $isGalleryNode, GalleryNode, galleryNodes, } from './node';
3
+ export { GalleryRenderer } from './renderer';
4
+ export { galleryModule } from './module';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/modules/gallery/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAA;AACvE,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,WAAW,EACX,YAAY,GACb,MAAM,QAAQ,CAAA;AACf,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA"}
@@ -0,0 +1,11 @@
1
+ import { $createGalleryNode, $isGalleryNode, GalleryNode, galleryNodes } from "./node.mjs";
2
+ import { GalleryRenderer } from "./renderer.mjs";
3
+ import { GalleryRenderer as GalleryRenderer$1, galleryNodes as galleryNodes$1 } from "@haklex/rich-ext-gallery/static";
4
+ //#region src/modules/gallery/module.ts
5
+ var galleryModule = {
6
+ name: "gallery",
7
+ nodes: galleryNodes$1,
8
+ renderers: { Gallery: GalleryRenderer$1 }
9
+ };
10
+ //#endregion
11
+ export { $createGalleryNode, $isGalleryNode, GalleryNode, GalleryRenderer, galleryModule, galleryNodes };
@@ -0,0 +1,3 @@
1
+ import { RichRendererModule } from '../../core/types';
2
+ export declare const galleryModule: RichRendererModule;
3
+ //# sourceMappingURL=module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../src/modules/gallery/module.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAE1D,eAAO,MAAM,aAAa,EAAE,kBAI3B,CAAA"}
@@ -0,0 +1,3 @@
1
+ export type { GalleryNodePayload, SerializedGalleryNode } from '@haklex/rich-ext-gallery/static';
2
+ export { $createGalleryNode, $isGalleryNode, GalleryNode, galleryNodes, } from '@haklex/rich-ext-gallery/static';
3
+ //# sourceMappingURL=node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../src/modules/gallery/node.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAA;AAChG,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,WAAW,EACX,YAAY,GACb,MAAM,iCAAiC,CAAA"}
@@ -0,0 +1,2 @@
1
+ import { $createGalleryNode, $isGalleryNode, GalleryNode, galleryNodes } from "@haklex/rich-ext-gallery/static";
2
+ export { $createGalleryNode, $isGalleryNode, GalleryNode, galleryNodes };
@@ -0,0 +1,4 @@
1
+ import { GalleryRenderer } from '@haklex/rich-ext-gallery/static';
2
+ export { GalleryRenderer };
3
+ export default GalleryRenderer;
4
+ //# sourceMappingURL=renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../src/modules/gallery/renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AAEjE,OAAO,EAAE,eAAe,EAAE,CAAA;AAC1B,eAAe,eAAe,CAAA"}
@@ -0,0 +1,5 @@
1
+ import { GalleryRenderer } from "@haklex/rich-ext-gallery/static";
2
+ //#region src/modules/gallery/renderer.tsx
3
+ var renderer_default = GalleryRenderer;
4
+ //#endregion
5
+ export { GalleryRenderer, renderer_default as default };
@@ -0,0 +1,3 @@
1
+ export { LinkCardRenderer } from './renderer';
2
+ export { linkCardModule } from './module';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/modules/link-card/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA"}
@@ -0,0 +1,15 @@
1
+ import { LinkCardRenderer } from "./renderer.mjs";
2
+ import { LinkCardRenderer as LinkCardRenderer$1 } from "@haklex/rich-renderer-linkcard/static";
3
+ //#region src/modules/link-card/module.ts
4
+ /**
5
+ * Link-card module — `LinkCardNode` is registered by `@haklex/rich-editor`
6
+ * (it lives in `customNodes`/`allNodes`). This module only contributes the
7
+ * default renderer; consumers wanting a custom renderer construct their own
8
+ * module with the same `name` and avoid importing `linkCardModule`.
9
+ */
10
+ var linkCardModule = {
11
+ name: "link-card",
12
+ renderers: { LinkCard: LinkCardRenderer$1 }
13
+ };
14
+ //#endregion
15
+ export { LinkCardRenderer, linkCardModule };
@@ -0,0 +1,9 @@
1
+ import { RichRendererModule } from '../../core/types';
2
+ /**
3
+ * Link-card module — `LinkCardNode` is registered by `@haklex/rich-editor`
4
+ * (it lives in `customNodes`/`allNodes`). This module only contributes the
5
+ * default renderer; consumers wanting a custom renderer construct their own
6
+ * module with the same `name` and avoid importing `linkCardModule`.
7
+ */
8
+ export declare const linkCardModule: RichRendererModule;
9
+ //# sourceMappingURL=module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../src/modules/link-card/module.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAE1D;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,kBAG5B,CAAA"}
@@ -0,0 +1,4 @@
1
+ import { LinkCardRenderer } from '@haklex/rich-renderer-linkcard/static';
2
+ export { LinkCardRenderer };
3
+ export default LinkCardRenderer;
4
+ //# sourceMappingURL=renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../src/modules/link-card/renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAA;AAExE,OAAO,EAAE,gBAAgB,EAAE,CAAA;AAC3B,eAAe,gBAAgB,CAAA"}
@@ -0,0 +1,5 @@
1
+ import { LinkCardRenderer } from "@haklex/rich-renderer-linkcard/static";
2
+ //#region src/modules/link-card/renderer.tsx
3
+ var renderer_default = LinkCardRenderer;
4
+ //#endregion
5
+ export { LinkCardRenderer, renderer_default as default };
package/package.json ADDED
@@ -0,0 +1,149 @@
1
+ {
2
+ "name": "@haklex/rich-compose",
3
+ "version": "0.1.0",
4
+ "description": "Compose primitives for haklex rich content renderers — Gundam-style assembly of Lexical nodes, sync/lazy renderers, and Provider stacks.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/Innei/haklex.git",
8
+ "directory": "packages/rich-compose"
9
+ },
10
+ "license": "MIT",
11
+ "type": "module",
12
+ "sideEffects": [
13
+ "**/*.css"
14
+ ],
15
+ "exports": {
16
+ ".": {
17
+ "import": "./dist/index.mjs",
18
+ "types": "./dist/index.d.ts"
19
+ },
20
+ "./modules/embed": {
21
+ "import": "./dist/modules/embed/index.mjs",
22
+ "types": "./dist/modules/embed/index.d.ts"
23
+ },
24
+ "./modules/embed/node": {
25
+ "import": "./dist/modules/embed/node.mjs",
26
+ "types": "./dist/modules/embed/node.d.ts"
27
+ },
28
+ "./modules/code-block": {
29
+ "import": "./dist/modules/code-block/index.mjs",
30
+ "types": "./dist/modules/code-block/index.d.ts"
31
+ },
32
+ "./modules/code-block/renderer": {
33
+ "import": "./dist/modules/code-block/renderer.mjs",
34
+ "types": "./dist/modules/code-block/renderer.d.ts"
35
+ },
36
+ "./modules/link-card": {
37
+ "import": "./dist/modules/link-card/index.mjs",
38
+ "types": "./dist/modules/link-card/index.d.ts"
39
+ },
40
+ "./modules/link-card/renderer": {
41
+ "import": "./dist/modules/link-card/renderer.mjs",
42
+ "types": "./dist/modules/link-card/renderer.d.ts"
43
+ },
44
+ "./modules/gallery": {
45
+ "import": "./dist/modules/gallery/index.mjs",
46
+ "types": "./dist/modules/gallery/index.d.ts"
47
+ },
48
+ "./modules/gallery/node": {
49
+ "import": "./dist/modules/gallery/node.mjs",
50
+ "types": "./dist/modules/gallery/node.d.ts"
51
+ },
52
+ "./modules/gallery/renderer": {
53
+ "import": "./dist/modules/gallery/renderer.mjs",
54
+ "types": "./dist/modules/gallery/renderer.d.ts"
55
+ },
56
+ "./modules/code-snippet": {
57
+ "import": "./dist/modules/code-snippet/index.mjs",
58
+ "types": "./dist/modules/code-snippet/index.d.ts"
59
+ },
60
+ "./modules/code-snippet/node": {
61
+ "import": "./dist/modules/code-snippet/node.mjs",
62
+ "types": "./dist/modules/code-snippet/node.d.ts"
63
+ },
64
+ "./modules/code-snippet/renderer": {
65
+ "import": "./dist/modules/code-snippet/renderer.mjs",
66
+ "types": "./dist/modules/code-snippet/renderer.d.ts"
67
+ },
68
+ "./modules/chat": {
69
+ "import": "./dist/modules/chat/index.mjs",
70
+ "types": "./dist/modules/chat/index.d.ts"
71
+ },
72
+ "./modules/chat/node": {
73
+ "import": "./dist/modules/chat/node.mjs",
74
+ "types": "./dist/modules/chat/node.d.ts"
75
+ },
76
+ "./modules/chat/renderer": {
77
+ "import": "./dist/modules/chat/renderer.mjs",
78
+ "types": "./dist/modules/chat/renderer.d.ts"
79
+ },
80
+ "./style.css": "./dist/rich-compose.css"
81
+ },
82
+ "main": "./dist/index.mjs",
83
+ "files": [
84
+ "dist"
85
+ ],
86
+ "devDependencies": {
87
+ "@lexical/react": "^0.44.0",
88
+ "@types/react": "^19.2.14",
89
+ "@types/react-dom": "^19.2.3",
90
+ "lexical": "^0.44.0",
91
+ "react": "19.2.5",
92
+ "react-dom": "19.2.5",
93
+ "typescript": "^5.9.3",
94
+ "vite": "^8.0.10",
95
+ "vite-plugin-dts": "^4.5.4",
96
+ "vitest": "^4.1.5",
97
+ "@haklex/rich-editor": "0.4.1",
98
+ "@haklex/rich-ext-chat": "0.4.1",
99
+ "@haklex/rich-ext-code-snippet": "0.4.1",
100
+ "@haklex/rich-ext-gallery": "0.4.1",
101
+ "@haklex/rich-ext-embed": "0.4.1",
102
+ "@haklex/rich-renderer-codeblock": "0.4.1",
103
+ "@haklex/rich-static-renderer": "0.4.1",
104
+ "@haklex/rich-renderer-linkcard": "0.4.1"
105
+ },
106
+ "peerDependencies": {
107
+ "@lexical/react": "^0.44.0",
108
+ "lexical": "^0.44.0",
109
+ "react": ">=19",
110
+ "react-dom": ">=19",
111
+ "@haklex/rich-editor": "0.4.1",
112
+ "@haklex/rich-ext-chat": "0.4.1",
113
+ "@haklex/rich-ext-embed": "0.4.1",
114
+ "@haklex/rich-ext-gallery": "0.4.1",
115
+ "@haklex/rich-ext-code-snippet": "0.4.1",
116
+ "@haklex/rich-renderer-linkcard": "0.4.1",
117
+ "@haklex/rich-static-renderer": "0.4.1",
118
+ "@haklex/rich-renderer-codeblock": "0.4.1"
119
+ },
120
+ "peerDependenciesMeta": {
121
+ "@haklex/rich-ext-chat": {
122
+ "optional": true
123
+ },
124
+ "@haklex/rich-ext-code-snippet": {
125
+ "optional": true
126
+ },
127
+ "@haklex/rich-ext-embed": {
128
+ "optional": true
129
+ },
130
+ "@haklex/rich-ext-gallery": {
131
+ "optional": true
132
+ },
133
+ "@haklex/rich-renderer-codeblock": {
134
+ "optional": true
135
+ },
136
+ "@haklex/rich-renderer-linkcard": {
137
+ "optional": true
138
+ }
139
+ },
140
+ "publishConfig": {
141
+ "access": "public"
142
+ },
143
+ "scripts": {
144
+ "build": "vite build",
145
+ "dev:build": "vite build --watch",
146
+ "test": "vitest run"
147
+ },
148
+ "types": "./dist/index.d.ts"
149
+ }