@lyfie/luthor 2.1.0 → 2.3.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.
- package/README.md +228 -463
- package/dist/ExtensiveEditor-Cn84lsVT.d.ts +448 -0
- package/dist/index-BtdZVw3X.d.ts +17 -0
- package/dist/index-DFnca6tP.d.ts +30 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +206 -52
- package/dist/index.js +3 -1
- package/dist/presets/chat-window/index.css +1 -0
- package/dist/presets/chat-window/index.d.ts +22 -0
- package/dist/presets/chat-window/index.js +3 -0
- package/dist/presets/email-compose/index.css +1 -0
- package/dist/presets/email-compose/index.d.ts +17 -0
- package/dist/presets/email-compose/index.js +3 -0
- package/dist/presets/extensive/index.css +1 -0
- package/dist/presets/extensive/index.d.ts +5 -0
- package/dist/presets/extensive/index.js +3 -0
- package/dist/presets/headless-editor/index.css +1 -0
- package/dist/presets/headless-editor/index.d.ts +13 -0
- package/dist/presets/headless-editor/index.js +1 -0
- package/dist/presets/md-text/index.css +1 -0
- package/dist/presets/md-text/index.d.ts +15 -0
- package/dist/presets/md-text/index.js +3 -0
- package/dist/presets/notes/index.css +1 -0
- package/dist/presets/notes/index.d.ts +21 -0
- package/dist/presets/notes/index.js +3 -0
- package/dist/presets/notion-like/index.css +1 -0
- package/dist/presets/notion-like/index.d.ts +17 -0
- package/dist/presets/notion-like/index.js +3 -0
- package/dist/presets/rich-text-box/index.css +1 -0
- package/dist/presets/rich-text-box/index.d.ts +17 -0
- package/dist/presets/rich-text-box/index.js +3 -0
- package/dist/presets/simple-text/index.css +1 -0
- package/dist/presets/simple-text/index.d.ts +15 -0
- package/dist/presets/simple-text/index.js +3 -0
- package/package.json +76 -18
package/README.md
CHANGED
|
@@ -1,555 +1,320 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @lyfie/luthor
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Plug-and-play React editor preset package built on top of `@lyfie/luthor-headless`.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Package responsibility
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## Why Use This Package?
|
|
13
|
-
|
|
14
|
-
### ✅ Zero Configuration Hassle
|
|
15
|
-
- All Lexical packages bundled as dependencies
|
|
16
|
-
- No peer dependency warnings
|
|
17
|
-
- No version conflicts to resolve
|
|
18
|
-
|
|
19
|
-
### 🎮 Ready-to-Use Editors
|
|
20
|
-
- Multiple presets for different use cases
|
|
21
|
-
- Pre-built toolbar components
|
|
22
|
-
- Styled and themed out of the box
|
|
23
|
-
|
|
24
|
-
### 🔧 Still Fully Customizable
|
|
25
|
-
- Extend or modify any preset
|
|
26
|
-
- Access all luthor-headless features
|
|
27
|
-
- Build on top of presets with your own extensions
|
|
28
|
-
|
|
29
|
-
---
|
|
7
|
+
- Owns preset UX/composition and plug-and-play defaults.
|
|
8
|
+
- Reuses headless behavior APIs instead of re-implementing Lexical semantics.
|
|
9
|
+
- Exposes headless namespace for advanced users who need lower-level control.
|
|
30
10
|
|
|
31
11
|
## Installation
|
|
32
12
|
|
|
33
|
-
Install both the headless package and this preset package:
|
|
34
|
-
|
|
35
13
|
```bash
|
|
36
|
-
|
|
37
|
-
npm install @lyfie/luthor
|
|
14
|
+
pnpm add @lyfie/luthor react react-dom
|
|
38
15
|
```
|
|
39
16
|
|
|
40
|
-
**That's it!** All required Lexical packages are automatically installed as dependencies of `@lyfie/luthor`.
|
|
41
|
-
|
|
42
|
-
### What Gets Installed
|
|
43
|
-
|
|
44
|
-
When you install `@lyfie/luthor`, npm automatically installs:
|
|
45
|
-
- `@lyfie/luthor-headless` (the core editor)
|
|
46
|
-
- `lexical` (the Lexical framework)
|
|
47
|
-
- All `@lexical/*` packages (code, html, link, list, markdown, react, rich-text, selection, table, utils)
|
|
48
|
-
|
|
49
|
-
These satisfy the peer dependencies of luthor-headless, so you don't need to install anything else.
|
|
50
|
-
|
|
51
|
-
### Peer Dependencies
|
|
52
|
-
|
|
53
|
-
Only React remains as a peer dependency (which you already have in your project):
|
|
54
|
-
- `react` (^18.0.0 or ^19.0.0)
|
|
55
|
-
- `react-dom` (^18.0.0 or ^19.0.0)
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
17
|
## Quick Start
|
|
60
18
|
|
|
61
|
-
### Using the Extensive Editor (Recommended)
|
|
62
|
-
|
|
63
|
-
The fastest way to get a full-featured editor:
|
|
64
|
-
|
|
65
19
|
```tsx
|
|
66
20
|
import { ExtensiveEditor } from "@lyfie/luthor";
|
|
67
|
-
import
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const editorRef = useRef<ExtensiveEditorRef>(null);
|
|
72
|
-
|
|
73
|
-
const handleSave = () => {
|
|
74
|
-
const html = editorRef.current?.getHTML();
|
|
75
|
-
const markdown = editorRef.current?.getMarkdown();
|
|
76
|
-
console.log({ html, markdown });
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
return (
|
|
80
|
-
<div>
|
|
81
|
-
<ExtensiveEditor
|
|
82
|
-
ref={editorRef}
|
|
83
|
-
placeholder="Start writing..."
|
|
84
|
-
onReady={(methods) => {
|
|
85
|
-
console.log("Editor ready!", methods);
|
|
86
|
-
}}
|
|
87
|
-
/>
|
|
88
|
-
<button onClick={handleSave}>Save Content</button>
|
|
89
|
-
</div>
|
|
90
|
-
);
|
|
21
|
+
import "@lyfie/luthor/styles.css";
|
|
22
|
+
|
|
23
|
+
export function App() {
|
|
24
|
+
return <ExtensiveEditor placeholder="Start writing..." />;
|
|
91
25
|
}
|
|
92
26
|
```
|
|
93
27
|
|
|
94
|
-
This
|
|
95
|
-
- ✅ Full-featured toolbar
|
|
96
|
-
- ✅ All formatting options (bold, italic, underline, etc.)
|
|
97
|
-
- ✅ Lists, tables, images, links
|
|
98
|
-
- ✅ Code blocks with syntax highlighting
|
|
99
|
-
- ✅ HTML/Markdown export
|
|
100
|
-
- ✅ Dark mode support
|
|
101
|
-
- ✅ Command palette (Cmd+K / Ctrl+K)
|
|
102
|
-
|
|
103
|
-
### Using Preset Definitions
|
|
28
|
+
## What This Package Exposes
|
|
104
29
|
|
|
105
|
-
|
|
30
|
+
- Preset editor component: `ExtensiveEditor`
|
|
31
|
+
- Additional preset editors: `SimpleTextEditor`, `RichTextBoxEditor`, `ChatWindowEditor`, `EmailComposeEditor`, `MDTextEditor`, `NotionLikeEditor`, `HeadlessEditorPreset`, `NotesEditor`
|
|
32
|
+
- Preset builder helpers: `extensivePreset`, `createExtensivePreset`, `presetRegistry`
|
|
33
|
+
- Extension composition helpers: `createExtensiveExtensions`, `extensiveExtensions`
|
|
34
|
+
- Feature flag helpers: `resolveFeatureFlags`, `isFeatureEnabled`, `DEFAULT_FEATURE_FLAGS`
|
|
35
|
+
- Core UI + command utilities: `Toolbar`, `FloatingToolbar`, `generateCommands`, `registerKeyboardShortcuts`, and more
|
|
36
|
+
- Full headless passthrough namespace: `headless`
|
|
106
37
|
|
|
107
38
|
```tsx
|
|
108
|
-
import {
|
|
109
|
-
import { extensiveExtensions } from "@lyfie/luthor";
|
|
110
|
-
|
|
111
|
-
const { Provider, useEditor } = createEditorSystem<typeof extensiveExtensions>();
|
|
112
|
-
|
|
113
|
-
function MyToolbar() {
|
|
114
|
-
const { commands, activeStates } = useEditor();
|
|
115
|
-
|
|
116
|
-
return (
|
|
117
|
-
<div className="my-custom-toolbar">
|
|
118
|
-
<button onClick={() => commands.toggleBold()}>
|
|
119
|
-
Bold
|
|
120
|
-
</button>
|
|
121
|
-
{/* Add your custom UI */}
|
|
122
|
-
</div>
|
|
123
|
-
);
|
|
124
|
-
}
|
|
39
|
+
import { headless } from "@lyfie/luthor";
|
|
125
40
|
|
|
126
|
-
|
|
127
|
-
return (
|
|
128
|
-
<Provider extensions={extensiveExtensions}>
|
|
129
|
-
<MyToolbar />
|
|
130
|
-
<RichText placeholder="Start writing..." />
|
|
131
|
-
</Provider>
|
|
132
|
-
);
|
|
133
|
-
}
|
|
41
|
+
const { createEditorSystem, richTextExtension, boldExtension } = headless;
|
|
134
42
|
```
|
|
135
43
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
-
|
|
145
|
-
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
-
|
|
178
|
-
-
|
|
44
|
+
## Preset Catalog
|
|
45
|
+
|
|
46
|
+
- `ExtensiveEditor`: full-feature visual/jsonb preset.
|
|
47
|
+
- `SimpleTextEditor`: plain-text locked preset.
|
|
48
|
+
- `RichTextBoxEditor`: inline formatting + list preset with optional compact toolbar.
|
|
49
|
+
- `ChatWindowEditor`: chat composer with send action row.
|
|
50
|
+
- `EmailComposeEditor`: compose shell (To/CC/BCC/Subject) + rich body.
|
|
51
|
+
- `MDTextEditor`: visual/markdown mode switching.
|
|
52
|
+
- `NotionLikeEditor`: slash-first + draggable defaults.
|
|
53
|
+
- `HeadlessEditorPreset`: minimal plain-control reference.
|
|
54
|
+
- `NotesEditor`: note-taking shell with title/actions callbacks.
|
|
55
|
+
|
|
56
|
+
## ExtensiveEditor API
|
|
57
|
+
|
|
58
|
+
### Props
|
|
59
|
+
|
|
60
|
+
| Prop | Type | Default | Notes |
|
|
61
|
+
|---|---|---|---|
|
|
62
|
+
| `className` | `string` | `undefined` | Appended to wrapper root. |
|
|
63
|
+
| `variantClassName` | `string` | `undefined` | Extra preset variant class on wrapper. |
|
|
64
|
+
| `onReady` | `(methods: ExtensiveEditorRef) => void` | `undefined` | Called when editor methods are ready. |
|
|
65
|
+
| `initialTheme` | `"light" \| "dark"` | `"light"` | Initial visual theme mode. |
|
|
66
|
+
| `theme` | `Partial<LuthorTheme>` | `undefined` | Theme class/style overrides merged with default theme. |
|
|
67
|
+
| `defaultContent` | `string` | `undefined` | If provided, injected as initial content (JSON parsed or plain text converted to JSONB). |
|
|
68
|
+
| `showDefaultContent` | `boolean` | `true` | If `true` and `defaultContent` is not provided, loads built-in welcome content. |
|
|
69
|
+
| `placeholder` | `string \| { visual?: string; jsonb?: string }` | `"Write anything..."` | String applies to visual mode; object lets you set visual and JSONB placeholders separately. |
|
|
70
|
+
| `initialMode` | `"visual" \| "jsonb"` | `"visual"` | Initial open mode; clamped to `availableModes`. |
|
|
71
|
+
| `availableModes` | `readonly ("visual" \| "jsonb")[]` | `["visual", "jsonb"]` | Visible mode tabs and allowed switching targets. |
|
|
72
|
+
| `toolbarLayout` | `ToolbarLayout` | `TRADITIONAL_TOOLBAR_LAYOUT` | Custom toolbar sections/items order. |
|
|
73
|
+
| `toolbarVisibility` | `Partial<Record<ToolbarItemType, boolean>>` | `undefined` | Per-item hide/show map. Unsupported items are auto-hidden. |
|
|
74
|
+
| `toolbarPosition` | `"top" \| "bottom"` | `"top"` | Renders toolbar above or below visual editor. |
|
|
75
|
+
| `toolbarAlignment` | `"left" \| "center" \| "right"` | `"left"` | Horizontal toolbar alignment class. |
|
|
76
|
+
| `toolbarClassName` | `string` | `undefined` | Extra class for toolbar root (`.luthor-toolbar`). |
|
|
77
|
+
| `toolbarStyleVars` | `ToolbarStyleVars` | `undefined` | Inline `--luthor-toolbar-*` CSS variable overrides. |
|
|
78
|
+
| `isToolbarEnabled` | `boolean` | `true` | Hides toolbar UI only; keyboard/commands still exist unless feature-flagged off. |
|
|
79
|
+
| `quoteClassName` | `string` | `undefined` | Appended to quote node class. |
|
|
80
|
+
| `quoteStyleVars` | `QuoteStyleVars` | `undefined` | Inline `--luthor-quote-*` CSS variable overrides. |
|
|
81
|
+
| `defaultSettings` | `DefaultSettings` | `undefined` | High-level style token API for common color settings. |
|
|
82
|
+
| `editorThemeOverrides` | `LuthorEditorThemeOverrides` | `undefined` | Inline editor-wide `--luthor-*` token overrides. |
|
|
83
|
+
| `fontFamilyOptions` | `readonly FontFamilyOption[]` | preset defaults | Sanitized: duplicates removed, invalid tokens removed, `default` auto-added if missing. |
|
|
84
|
+
| `fontSizeOptions` | `readonly FontSizeOption[]` | preset defaults | Sanitized similarly to font family options. |
|
|
85
|
+
| `minimumDefaultLineHeight` | `string \| number` | `1.5` | Sets the editor default line height and the minimum selectable/accepted line-height floor for the line-height feature; minimum accepted value is `1.0`. |
|
|
86
|
+
| `lineHeightOptions` | `readonly LineHeightOption[]` | preset defaults | Uses unitless ratios (`"1.5"`) for non-default values; minimum allowed is `1.0`. |
|
|
87
|
+
| `scaleByRatio` | `boolean` | `false` | Image resize behavior (`true` = keep ratio by default, Shift unlocks). |
|
|
88
|
+
| `headingOptions` | `readonly ("h1"\|"h2"\|"h3"\|"h4"\|"h5"\|"h6")[]` | all headings | Invalid/duplicate entries are removed. |
|
|
89
|
+
| `paragraphLabel` | `string` | `"Paragraph"` behavior | Label for paragraph entry in block format menu/commands. |
|
|
90
|
+
| `syncHeadingOptionsWithCommands` | `boolean` | `true` | Syncs heading command generation with `headingOptions`. |
|
|
91
|
+
| `slashCommandVisibility` | `SlashCommandVisibility` | `undefined` | Slash command filter using allowlist/denylist or enabled-ID selection array form. |
|
|
92
|
+
| `shortcutConfig` | `ShortcutConfig` | `undefined` | Per-instance command shortcut config (disable/remap, collision and native conflict prevention). |
|
|
93
|
+
| `commandPaletteShortcutOnly` | `boolean` | `false` | When `true`, command palette only shows commands that have a visible keyboard shortcut. |
|
|
94
|
+
| `isDraggableBoxEnabled` | `boolean` | `undefined` | Shortcut for enabling/disabling draggable block UI (maps into `featureFlags.draggableBlock`). |
|
|
95
|
+
| `featureFlags` | `Partial<Record<FeatureFlag, boolean>>` | all `true` | Central capability switchboard; affects extensions, toolbar, commands, shortcuts. |
|
|
96
|
+
| `syntaxHighlighting` | `"auto" \| "disabled"` | extension default (`"auto"`) | Code block syntax highlighting strategy. |
|
|
97
|
+
| `codeHighlightProvider` | `CodeHighlightProvider \| null` | `undefined` | Injected highlight provider instance. |
|
|
98
|
+
| `loadCodeHighlightProvider` | `() => Promise<CodeHighlightProvider \| null>` | `undefined` | Lazy async provider loader. |
|
|
99
|
+
| `maxAutoDetectCodeLength` | `number` | `12000` in code intelligence extension | Guard for auto language detection input size. |
|
|
100
|
+
| `isCopyAllowed` | `boolean` | `true` | Enables/disables code block copy button and command path. |
|
|
101
|
+
| `languageOptions` | `readonly string[] \| { mode?: "append"\|"replace"; values: readonly string[] }` | default language catalog | Controls visible code language options. |
|
|
102
|
+
|
|
103
|
+
### Ref Methods
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
type ExtensiveEditorRef = {
|
|
107
|
+
injectJSONB: (content: string) => void;
|
|
108
|
+
getJSONB: () => string;
|
|
109
|
+
};
|
|
110
|
+
```
|
|
179
111
|
|
|
180
|
-
|
|
181
|
-
Markdown-first editing.
|
|
182
|
-
- Markdown shortcuts
|
|
183
|
-
- Preview mode
|
|
184
|
-
- Clean export
|
|
112
|
+
## Valid Argument Sets
|
|
185
113
|
|
|
186
|
-
###
|
|
187
|
-
Code snippet editor.
|
|
188
|
-
- Syntax highlighting
|
|
189
|
-
- Multiple language support
|
|
190
|
-
- Line numbers
|
|
114
|
+
### `ToolbarItemType`
|
|
191
115
|
|
|
192
|
-
|
|
193
|
-
Balanced general-purpose editor.
|
|
194
|
-
- Good starting point for customization
|
|
116
|
+
`"fontFamily"`, `"fontSize"`, `"lineHeight"`, `"textColor"`, `"textHighlight"`, `"bold"`, `"italic"`, `"underline"`, `"strikethrough"`, `"subscript"`, `"superscript"`, `"code"`, `"link"`, `"blockFormat"`, `"quote"`, `"alignLeft"`, `"alignCenter"`, `"alignRight"`, `"alignJustify"`, `"codeBlock"`, `"unorderedList"`, `"orderedList"`, `"checkList"`, `"indentList"`, `"outdentList"`, `"horizontalRule"`, `"table"`, `"image"`, `"emoji"`, `"embed"`, `"undo"`, `"redo"`, `"commandPalette"`, `"themeToggle"`.
|
|
195
117
|
|
|
196
|
-
###
|
|
197
|
-
Full-featured editor with everything.
|
|
198
|
-
- All extensions included
|
|
199
|
-
- Complete toolbar
|
|
200
|
-
- Pre-built component
|
|
118
|
+
### `FeatureFlag`
|
|
201
119
|
|
|
202
|
-
|
|
120
|
+
`"bold"`, `"italic"`, `"underline"`, `"strikethrough"`, `"fontFamily"`, `"fontSize"`, `"lineHeight"`, `"textColor"`, `"textHighlight"`, `"subscript"`, `"superscript"`, `"link"`, `"horizontalRule"`, `"table"`, `"list"`, `"history"`, `"image"`, `"blockFormat"`, `"code"`, `"codeIntelligence"`, `"codeFormat"`, `"tabIndent"`, `"enterKeyBehavior"`, `"iframeEmbed"`, `"youTubeEmbed"`, `"floatingToolbar"`, `"contextMenu"`, `"commandPalette"`, `"slashCommand"`, `"emoji"`, `"draggableBlock"`, `"customNode"`, `"themeToggle"`.
|
|
203
121
|
|
|
204
|
-
## Usage
|
|
122
|
+
## Usage Recipes
|
|
205
123
|
|
|
206
|
-
###
|
|
124
|
+
### 1) Minimal with defaults
|
|
207
125
|
|
|
208
126
|
```tsx
|
|
209
|
-
import {
|
|
210
|
-
|
|
211
|
-
// Get a preset by ID
|
|
212
|
-
const blogPreset = presetRegistry.blog;
|
|
213
|
-
const minimalPreset = presetRegistry.minimal;
|
|
127
|
+
import { ExtensiveEditor } from "@lyfie/luthor";
|
|
128
|
+
import "@lyfie/luthor/styles.css";
|
|
214
129
|
|
|
215
|
-
|
|
216
|
-
|
|
130
|
+
export function App() {
|
|
131
|
+
return <ExtensiveEditor />;
|
|
132
|
+
}
|
|
217
133
|
```
|
|
218
134
|
|
|
219
|
-
###
|
|
135
|
+
### 2) Placeholder per mode + restricted modes
|
|
220
136
|
|
|
221
137
|
```tsx
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
toolbar: ["bold", "italic", "link"], // Override toolbar
|
|
231
|
-
config: {
|
|
232
|
-
...defaultPreset.config,
|
|
233
|
-
placeholder: "Write your story...",
|
|
234
|
-
},
|
|
235
|
-
};
|
|
138
|
+
<ExtensiveEditor
|
|
139
|
+
initialMode="visual"
|
|
140
|
+
availableModes={["visual", "jsonb"]}
|
|
141
|
+
placeholder={{
|
|
142
|
+
visual: "Write release notes...",
|
|
143
|
+
jsonb: "Paste JSONB...",
|
|
144
|
+
}}
|
|
145
|
+
/>
|
|
236
146
|
```
|
|
237
147
|
|
|
238
|
-
###
|
|
148
|
+
### 3) Feature-gated editor (product tiers)
|
|
239
149
|
|
|
240
150
|
```tsx
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
<RichText />
|
|
256
|
-
</div>
|
|
257
|
-
);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function App() {
|
|
261
|
-
return (
|
|
262
|
-
<Provider extensions={extensiveExtensions}>
|
|
263
|
-
<Editor />
|
|
264
|
-
</Provider>
|
|
265
|
-
);
|
|
266
|
-
}
|
|
151
|
+
<ExtensiveEditor
|
|
152
|
+
featureFlags={{
|
|
153
|
+
image: false,
|
|
154
|
+
iframeEmbed: false,
|
|
155
|
+
youTubeEmbed: false,
|
|
156
|
+
emoji: false,
|
|
157
|
+
commandPalette: false,
|
|
158
|
+
}}
|
|
159
|
+
toolbarVisibility={{
|
|
160
|
+
embed: false,
|
|
161
|
+
image: false,
|
|
162
|
+
emoji: false,
|
|
163
|
+
}}
|
|
164
|
+
/>
|
|
267
165
|
```
|
|
268
166
|
|
|
269
|
-
###
|
|
167
|
+
### 4) Slash command allowlist
|
|
270
168
|
|
|
271
169
|
```tsx
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
setSavedContent(html || "");
|
|
284
|
-
console.log({ html, markdown });
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
const handleImport = () => {
|
|
288
|
-
editorRef.current?.injectHTML(savedContent);
|
|
289
|
-
// or
|
|
290
|
-
editorRef.current?.injectMarkdown("# Hello\n\nMarkdown content");
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
return (
|
|
294
|
-
<div>
|
|
295
|
-
<ExtensiveEditor ref={editorRef} />
|
|
296
|
-
<button onClick={handleExport}>Export</button>
|
|
297
|
-
<button onClick={handleImport}>Import</button>
|
|
298
|
-
</div>
|
|
299
|
-
);
|
|
300
|
-
}
|
|
170
|
+
<ExtensiveEditor
|
|
171
|
+
slashCommandVisibility={{
|
|
172
|
+
allowlist: [
|
|
173
|
+
"block.paragraph",
|
|
174
|
+
"block.heading1",
|
|
175
|
+
"block.heading2",
|
|
176
|
+
"insert.table",
|
|
177
|
+
],
|
|
178
|
+
denylist: ["insert.image"],
|
|
179
|
+
}}
|
|
180
|
+
/>
|
|
301
181
|
```
|
|
302
182
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
## API Reference
|
|
306
|
-
|
|
307
|
-
### ExtensiveEditor Component
|
|
183
|
+
### 5) Code language list replacement
|
|
308
184
|
|
|
309
185
|
```tsx
|
|
310
|
-
|
|
311
|
-
|
|
186
|
+
<ExtensiveEditor
|
|
187
|
+
languageOptions={{
|
|
188
|
+
mode: "replace",
|
|
189
|
+
values: ["plaintext", "typescript", "javascript", "markdown", "sql"],
|
|
190
|
+
}}
|
|
191
|
+
syntaxHighlighting="auto"
|
|
192
|
+
maxAutoDetectCodeLength={10000}
|
|
193
|
+
/>
|
|
312
194
|
```
|
|
313
195
|
|
|
314
|
-
|
|
315
|
-
```typescript
|
|
316
|
-
interface ExtensiveEditorProps {
|
|
317
|
-
placeholder?: string; // Placeholder text
|
|
318
|
-
className?: string; // CSS class for container
|
|
319
|
-
onReady?: (ref: ExtensiveEditorRef) => void; // Called when editor is ready
|
|
320
|
-
}
|
|
321
|
-
```
|
|
196
|
+
Optional: use highlight.js stylesheet for richer code colors
|
|
322
197
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
interface ExtensiveEditorRef {
|
|
326
|
-
injectMarkdown: (content: string) => void; // Import markdown
|
|
327
|
-
injectHTML: (content: string) => void; // Import HTML
|
|
328
|
-
getMarkdown: () => string; // Export as markdown
|
|
329
|
-
getHTML: () => string; // Export as HTML
|
|
330
|
-
}
|
|
198
|
+
```bash
|
|
199
|
+
pnpm add highlight.js
|
|
331
200
|
```
|
|
332
201
|
|
|
333
|
-
### Preset Registry
|
|
334
|
-
|
|
335
202
|
```tsx
|
|
336
|
-
import {
|
|
337
|
-
import
|
|
338
|
-
|
|
203
|
+
import { ExtensiveEditor } from "@lyfie/luthor";
|
|
204
|
+
import "@lyfie/luthor/styles.css";
|
|
205
|
+
import "highlight.js/styles/github.css"; // any highlight.js theme
|
|
339
206
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
interface EditorPreset {
|
|
343
|
-
id: string; // Unique preset ID
|
|
344
|
-
label: string; // Display name
|
|
345
|
-
description?: string; // Preset description
|
|
346
|
-
extensions?: Extension[]; // Included extensions
|
|
347
|
-
config?: EditorConfig; // Editor configuration
|
|
348
|
-
theme?: LuthorTheme; // Custom theme
|
|
349
|
-
toolbar?: string[]; // Toolbar items
|
|
350
|
-
components?: Record<string, unknown>; // Custom components
|
|
351
|
-
css?: string; // CSS file path
|
|
207
|
+
export function App() {
|
|
208
|
+
return <ExtensiveEditor syntaxHighlighting="auto" />;
|
|
352
209
|
}
|
|
353
210
|
```
|
|
354
211
|
|
|
355
|
-
|
|
356
|
-
- `presetRegistry.minimal`
|
|
357
|
-
- `presetRegistry.classic`
|
|
358
|
-
- `presetRegistry.blog`
|
|
359
|
-
- `presetRegistry.cms`
|
|
360
|
-
- `presetRegistry.docs`
|
|
361
|
-
- `presetRegistry.chat`
|
|
362
|
-
- `presetRegistry.email`
|
|
363
|
-
- `presetRegistry.markdown`
|
|
364
|
-
- `presetRegistry.code`
|
|
365
|
-
- `presetRegistry.default`
|
|
366
|
-
- `presetRegistry.extensive`
|
|
367
|
-
|
|
368
|
-
### Extension Sets
|
|
369
|
-
|
|
370
|
-
```tsx
|
|
371
|
-
import { extensiveExtensions } from "@lyfie/luthor";
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
Pre-configured extension arrays that can be used with luthor-headless:
|
|
375
|
-
|
|
376
|
-
```typescript
|
|
377
|
-
const extensions = extensiveExtensions as const;
|
|
378
|
-
const { Provider } = createEditorSystem<typeof extensions>();
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
---
|
|
382
|
-
|
|
383
|
-
## Comparison: Headless vs Luthor
|
|
384
|
-
|
|
385
|
-
| Feature | @lyfie/luthor-headless | @lyfie/luthor |
|
|
386
|
-
|---------|----------------------|---------------|
|
|
387
|
-
| **Installation** | Manual Lexical deps | Zero additional deps |
|
|
388
|
-
| **Bundle Size** | Minimal | Includes all Lexical |
|
|
389
|
-
| **Setup Time** | More configuration | Instant |
|
|
390
|
-
| **Flexibility** | Maximum control | Pre-configured |
|
|
391
|
-
| **Use Case** | Custom editors | Quick implementation |
|
|
392
|
-
| **UI Components** | Build your own | ExtensiveEditor included |
|
|
393
|
-
| **Presets** | None | 11 ready-to-use |
|
|
394
|
-
| **Dependencies** | Peer deps | Bundled deps |
|
|
395
|
-
|
|
396
|
-
**Choose luthor-headless when:**
|
|
397
|
-
- Building completely custom UI
|
|
398
|
-
- Need minimal bundle size
|
|
399
|
-
- Want control over Lexical versions
|
|
400
|
-
- Have specific dependency requirements
|
|
401
|
-
|
|
402
|
-
**Choose @lyfie/luthor when:**
|
|
403
|
-
- Want to start quickly
|
|
404
|
-
- Need a working editor ASAP
|
|
405
|
-
- Don't want to manage dependencies
|
|
406
|
-
- Want ready-to-use components
|
|
407
|
-
|
|
408
|
-
[📖 Learn more about luthor-headless](../headless/README.md)
|
|
409
|
-
|
|
410
|
-
---
|
|
212
|
+
What happens:
|
|
411
213
|
|
|
412
|
-
|
|
214
|
+
- Luthor does not auto-detect code language anymore. Language comes from the selected code language option.
|
|
215
|
+
- Language options are normalized through Lexical/Prism aliases (`md` -> `markdown`, `js` -> `javascript` family, `ts` -> `typescript`).
|
|
216
|
+
- Only Prism-supported loaded languages are accepted. Unsupported values (for example `yaml` without Prism YAML grammar loaded) fall back to plain text.
|
|
217
|
+
- `plaintext` uses plain fallback syntax styling.
|
|
218
|
+
- Non-plaintext languages emit `hljs-*` token classes.
|
|
219
|
+
- If a highlight.js stylesheet is loaded, those `hljs-*` tokens use highlight.js colors.
|
|
220
|
+
- Without highlight.js stylesheet, code uses the built-in muted/plain fallback.
|
|
413
221
|
|
|
414
|
-
###
|
|
222
|
+
### 6) Per-instance shortcut remap/disable
|
|
415
223
|
|
|
416
224
|
```tsx
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
extensions: [boldExtension, italicExtension, linkExtension],
|
|
429
|
-
config: {
|
|
430
|
-
placeholder: "Start typing...",
|
|
431
|
-
editable: true,
|
|
432
|
-
},
|
|
433
|
-
toolbar: ["bold", "italic", "link"],
|
|
434
|
-
};
|
|
225
|
+
<ExtensiveEditor
|
|
226
|
+
shortcutConfig={{
|
|
227
|
+
disabledCommandIds: ["format.italic"],
|
|
228
|
+
bindings: {
|
|
229
|
+
"format.bold": { key: "m", ctrlKey: true },
|
|
230
|
+
"palette.show": [
|
|
231
|
+
{ key: "k", ctrlKey: true, shiftKey: true },
|
|
232
|
+
],
|
|
233
|
+
},
|
|
234
|
+
}}
|
|
235
|
+
/>
|
|
435
236
|
```
|
|
436
237
|
|
|
437
|
-
###
|
|
238
|
+
### 7) Command palette shortcut-only mode
|
|
438
239
|
|
|
439
240
|
```tsx
|
|
440
|
-
|
|
441
|
-
import { myCustomExtension } from "./my-extension";
|
|
442
|
-
|
|
443
|
-
const enhancedPreset: EditorPreset = {
|
|
444
|
-
...defaultPreset,
|
|
445
|
-
id: "enhanced-default",
|
|
446
|
-
extensions: [
|
|
447
|
-
...(defaultPreset.extensions || []),
|
|
448
|
-
myCustomExtension,
|
|
449
|
-
],
|
|
450
|
-
toolbar: [
|
|
451
|
-
...(defaultPreset.toolbar || []),
|
|
452
|
-
"myCustomCommand",
|
|
453
|
-
],
|
|
454
|
-
};
|
|
241
|
+
<ExtensiveEditor commandPaletteShortcutOnly />
|
|
455
242
|
```
|
|
456
243
|
|
|
457
|
-
###
|
|
458
|
-
|
|
459
|
-
Since `@lyfie/luthor` depends on `@lyfie/luthor-headless`, you have access to all headless features:
|
|
244
|
+
### 8) Style token overrides
|
|
460
245
|
|
|
461
246
|
```tsx
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
247
|
+
<ExtensiveEditor
|
|
248
|
+
editorThemeOverrides={{
|
|
249
|
+
"--luthor-bg": "#fffaf2",
|
|
250
|
+
"--luthor-fg": "#431407",
|
|
251
|
+
"--luthor-accent": "#c2410c",
|
|
252
|
+
}}
|
|
253
|
+
toolbarStyleVars={{
|
|
254
|
+
"--luthor-toolbar-button-active-bg": "#ea580c",
|
|
255
|
+
"--luthor-toolbar-button-active-fg": "#ffffff",
|
|
256
|
+
}}
|
|
257
|
+
quoteStyleVars={{
|
|
258
|
+
"--luthor-quote-bg": "#fff7ed",
|
|
259
|
+
"--luthor-quote-fg": "#7c2d12",
|
|
260
|
+
"--luthor-quote-border": "#ea580c",
|
|
261
|
+
}}
|
|
262
|
+
/>
|
|
478
263
|
```
|
|
479
264
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
## TypeScript Support
|
|
265
|
+
### 9) Emoji library auto-detection (works in `apps/demo`)
|
|
483
266
|
|
|
484
|
-
|
|
267
|
+
Install emoji-mart data in the app:
|
|
485
268
|
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
EditorPreset,
|
|
489
|
-
ExtensiveEditorRef,
|
|
490
|
-
ExtensiveEditorProps,
|
|
491
|
-
ExtensiveEditorMode,
|
|
492
|
-
} from "@lyfie/luthor";
|
|
269
|
+
```bash
|
|
270
|
+
pnpm add -F demo @emoji-mart/data
|
|
493
271
|
```
|
|
494
272
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
## Styling
|
|
498
|
-
|
|
499
|
-
The `ExtensiveEditor` component includes default styles. To customize:
|
|
273
|
+
Then use `ExtensiveEditor` normally:
|
|
500
274
|
|
|
501
275
|
```tsx
|
|
502
276
|
import { ExtensiveEditor } from "@lyfie/luthor";
|
|
277
|
+
import "@lyfie/luthor/styles.css";
|
|
503
278
|
|
|
504
|
-
|
|
505
|
-
<ExtensiveEditor
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
```css
|
|
509
|
-
/* Override default styles */
|
|
510
|
-
.my-editor {
|
|
511
|
-
--luthor-bg: #ffffff;
|
|
512
|
-
--luthor-text: #000000;
|
|
513
|
-
--luthor-border: #e5e5e5;
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
/* Dark mode */
|
|
517
|
-
.my-editor.dark {
|
|
518
|
-
--luthor-bg: #1a1a1a;
|
|
519
|
-
--luthor-text: #ffffff;
|
|
520
|
-
--luthor-border: #333333;
|
|
279
|
+
export function App() {
|
|
280
|
+
return <ExtensiveEditor />;
|
|
521
281
|
}
|
|
522
282
|
```
|
|
523
283
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
## Migration from Headless
|
|
284
|
+
What happens:
|
|
527
285
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
```bash
|
|
532
|
-
npm install @lyfie/luthor-headless
|
|
533
|
-
npm install lexical @lexical/react @lexical/html # ... many packages
|
|
534
|
-
```
|
|
286
|
+
- `:shortcode` suggestions and resolution use the detected emoji-mart dataset.
|
|
287
|
+
- Emoji toolbar dropdown uses the detected emoji-mart catalog.
|
|
288
|
+
- If the library is not installed/available, it automatically falls back to the built-in emoji list.
|
|
535
289
|
|
|
536
|
-
|
|
537
|
-
```bash
|
|
538
|
-
npm install @lyfie/luthor-headless
|
|
539
|
-
npm install @lyfie/luthor
|
|
540
|
-
# Remove individual @lexical/* packages if desired
|
|
541
|
-
```
|
|
290
|
+
## Notes and Nuances
|
|
542
291
|
|
|
543
|
-
|
|
292
|
+
- `featureFlags` are authoritative. If a feature is disabled, related toolbar items and commands are removed/no-op even if you attempt to show them.
|
|
293
|
+
- `slashCommandVisibility` keeps original command ordering; it only filters visibility.
|
|
294
|
+
- `shortcutConfig.disabledCommandIds` removes commands from keyboard handling and command UIs for that editor instance.
|
|
295
|
+
- `shortcutConfig` drops duplicate bindings by default and blocks native editable conflicts by default.
|
|
296
|
+
- `commandPaletteShortcutOnly` is optional; by default command palette can include command items without shortcuts.
|
|
297
|
+
- `languageOptions` normalizes aliases (for example `js` becomes `javascript`) and rejects duplicates after normalization.
|
|
298
|
+
- Emoji suggestions/tooling auto-detect external emoji-mart data when available, and otherwise use the built-in default emoji catalog.
|
|
299
|
+
- `defaultSettings` is style-only; behavior is controlled by explicit props (for example `featureFlags`, `availableModes`).
|
|
300
|
+
- `isToolbarEnabled={false}` hides toolbar UI, but editor shortcuts/features still work unless disabled via `featureFlags`.
|
|
544
301
|
|
|
545
|
-
|
|
302
|
+
## Which Package Should I Use?
|
|
546
303
|
|
|
547
|
-
|
|
304
|
+
- Use `@lyfie/luthor` for fast onboarding and polished defaults.
|
|
305
|
+
- Use `@lyfie/luthor-headless` for full extension/UI control.
|
|
548
306
|
|
|
549
|
-
|
|
307
|
+
## Documentation
|
|
550
308
|
|
|
551
|
-
|
|
309
|
+
- Monorepo docs index: [../../documentation/index.md](../../documentation/index.md)
|
|
310
|
+
- Headless package README: [../headless/README.md](../headless/README.md)
|
|
311
|
+
- User docs: [../../documentation/user/luthor/getting-started.md](../../documentation/user/luthor/getting-started.md)
|
|
312
|
+
- Developer docs: [../../documentation/developer/luthor/architecture.md](../../documentation/developer/luthor/architecture.md)
|
|
552
313
|
|
|
553
|
-
|
|
314
|
+
## Workspace Development
|
|
554
315
|
|
|
555
|
-
|
|
316
|
+
```bash
|
|
317
|
+
pnpm --filter @lyfie/luthor dev
|
|
318
|
+
pnpm --filter @lyfie/luthor build
|
|
319
|
+
pnpm --filter @lyfie/luthor lint
|
|
320
|
+
```
|