@lyfie/luthor 1.1.0 → 2.0.1

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 CHANGED
@@ -1,48 +1,11 @@
1
- # Luthor
1
+ # Luthor Presets
2
2
 
3
- **Type-safe rich text editor for React developers**
3
+ Presets and plug-and-play configurations for the Luthor headless editor.
4
4
 
5
- Built on Meta's Lexical. Headless, extensible, and production-ready.
6
-
7
- [![npm version](https://badge.fury.io/js/%40luthor%2Feditor.svg)](https://badge.fury.io/js/%40luthor%2Feditor)
8
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
9
-
10
- **[🚀 Demo](https://luthor.lyfie.app/demo)** • **[📖 Documentation](https://luthor.lyfie.app/docs)** • **[⚡ Playground](https://stackblitz.com/edit/vitejs-vite-bpg2kpze?file=src%2FEditor.tsx)**
11
-
12
- ![Luthor Editor](https://github.com/user-attachments/assets/ec547406-0ab0-4e69-b9d7-ccd050adf78a)
13
-
14
- ---
15
-
16
- ## Why Luthor?
17
-
18
- Rich text editors shouldn't be a nightmare. Luthor makes building them delightful:
19
-
20
- - **🔒 Type-safe everything** - Commands and states inferred from your extensions. No runtime surprises.
21
- - **🎨 Headless & flexible** - Build any UI you want. Style it your way.
22
- - **🧩 Modular extensions** - Add only what you need, when you need it.
23
- - **⚡ Production features** - HTML/Markdown export, image handling, tables, undo/redo.
24
- - **⚛️ React-first** - Hooks, components, and patterns you already know.
25
-
26
- ```tsx
27
- // Your extensions define your API - TypeScript knows everything ✨
28
- const extensions = [boldExtension, listExtension, imageExtension] as const;
29
- const { Provider, useEditor } = createEditorSystem<typeof extensions>();
30
-
31
- function MyEditor() {
32
- const { commands, activeStates } = useEditor();
33
-
34
- // TypeScript autocompletes and validates these
35
- commands.toggleBold(); // ✅ Available
36
- commands.toggleUnorderedList(); // ✅ Available
37
- commands.insertImage(); // ✅ Available
38
- commands.nonExistent(); // ❌ TypeScript error
39
- }
40
- ```
41
-
42
- ## Quick Start
5
+ ## Install
43
6
 
44
7
  ```bash
45
- npm install @lyfie/luthor
8
+ npm install @lyfie/luthor @lyfie/luthor-headless
46
9
  ```
47
10
 
48
11
  Install the Lexical peer dependencies:
@@ -51,126 +14,35 @@ Install the Lexical peer dependencies:
51
14
  npm install lexical @lexical/react @lexical/html @lexical/markdown @lexical/list @lexical/rich-text @lexical/selection @lexical/utils
52
15
  ```
53
16
 
54
- ```tsx
55
- import {
56
- createEditorSystem,
57
- boldExtension,
58
- italicExtension,
59
- listExtension,
60
- RichText,
61
- } from "@lyfie/luthor";
62
-
63
- const extensions = [boldExtension, italicExtension, listExtension] as const;
64
- const { Provider, useEditor } = createEditorSystem<typeof extensions>();
17
+ ## Usage
65
18
 
66
- function Toolbar() {
67
- const { commands, activeStates } = useEditor();
68
- return (
69
- <div className="toolbar">
70
- <button
71
- onClick={() => commands.toggleBold()}
72
- className={activeStates.bold ? "active" : ""}
73
- >
74
- Bold
75
- </button>
76
- <button
77
- onClick={() => commands.toggleItalic()}
78
- className={activeStates.italic ? "active" : ""}
79
- >
80
- Italic
81
- </button>
82
- <button onClick={() => commands.toggleUnorderedList()}>
83
- Bullet List
84
- </button>
85
- </div>
86
- );
87
- }
19
+ ```tsx
20
+ import { presetRegistry, defaultPreset } from "@lyfie/luthor";
88
21
 
89
- function Editor() {
90
- return (
91
- <div className="editor-container">
92
- <Toolbar />
93
- <RichText placeholder="Start writing..." />
94
- </div>
95
- );
96
- }
22
+ // Get a preset by id
23
+ const preset = presetRegistry.default;
97
24
 
98
- export default function App() {
99
- return (
100
- <Provider extensions={extensions}>
101
- <Editor />
102
- </Provider>
103
- );
104
- }
25
+ // Or use a named preset directly
26
+ const explicit = defaultPreset;
105
27
  ```
106
28
 
107
- **That's it.** You now have a fully functional, type-safe rich text editor.
108
-
109
- ## Features
29
+ ## What is included
110
30
 
111
- ### 🎨 Built-in Extensions (25+)
112
- - **Text Formatting**: Bold, italic, underline, strikethrough, inline code
113
- - **Structure**: Headings, lists (with nesting), quotes, horizontal rules
114
- - **Rich Content**: Tables, images (upload/paste/alignment), links, code blocks
115
- - **Advanced**: History (undo/redo), command palette, floating toolbar, context menus
31
+ - Preset definitions (toolbar, config, optional theme)
32
+ - Registry lookup by id
33
+ - Readme files describing each preset
116
34
 
117
- ### 🎯 Smart List Handling
118
- - Toggle lists with intelligent nesting behavior
119
- - Context-aware toolbar (indent/outdent appear when needed)
120
- - Nested lists without keyboard shortcuts
121
- - Clean UX that matches modern editors
35
+ ## Customization
122
36
 
123
- ### 📤 Export & Import
124
- - **HTML** with semantic markup
125
- - **Markdown** with GitHub Flavored syntax
126
- - **JSON** for structured data
127
- - Custom transformers for specialized formats
37
+ Presets are data objects. You can clone and override any field:
128
38
 
129
- ### 🎨 Theming & Styling
130
- - CSS classes or Tailwind utilities
131
- - Custom themes for consistent styling
132
- - Dark mode support
133
- - Accessible by default
134
-
135
- ## Real World Usage
136
-
137
- Luthor powers editors in:
138
- - Content management systems
139
- - Documentation platforms
140
- - Blog editors
141
- - Note-taking applications
142
- - Comment systems
143
- - Collaborative writing tools
144
-
145
- ## Community & Support
146
-
147
- - **[💬 Discord](https://discord.gg/RAMYSDRag7)** - Get help, share ideas
148
- - **[🐛 GitHub Issues](https://github.com/lyfie-app/luthor/issues)** - Bug reports, feature requests
149
- - **[💭 Discussions](https://github.com/lyfie-app/luthor/discussions)** - Questions, showcase your projects
150
-
151
- ## Contributing
152
-
153
- We welcome contributions! Whether you:
154
- - Find and report bugs
155
- - Suggest new features
156
- - Contribute code or documentation
157
- - Share projects built with Luthor
158
- - Star the repo to show support
159
-
160
- Check our [Contributing Guide](./CONTRIBUTING.md) to get started.
161
-
162
- ## Support This Project
163
-
164
- Luthor is free and open source, built by developers for developers. If it's helping you build better editors, consider supporting its development:
165
-
166
- - **⭐ Star this repository** to show your support
167
- - **💝 [Sponsor the project](https://github.com/sponsors/rahulnsanand)** to help with maintenance and new features
168
- - **📢 Share Luthor** with other developers
169
-
170
- Your support keeps this project alive and helps us build better tools for the React community.
171
-
172
- ---
173
-
174
- **Built with ❤️ by [Rahul NS Anand](https://github.com/rahulnsanand)**
175
-
176
- MIT License - Use it however you want.</content>
39
+ ```tsx
40
+ import { defaultPreset } from "@lyfie/luthor";
41
+
42
+ const myPreset = {
43
+ ...defaultPreset,
44
+ id: "my-default",
45
+ label: "My Default",
46
+ toolbar: ["bold", "italic", "link"],
47
+ };
48
+ ```
package/dist/index.css ADDED
@@ -0,0 +1 @@
1
+ :root,.luthor-editor-wrapper[data-editor-theme=light]{--luthor-bg: #ffffff;--luthor-fg: #0f172a;--luthor-border: #e2e8f0;--luthor-border-hover: #cbd5e1;--luthor-border-active: #94a3b8;--luthor-accent: #0f172a;--luthor-accent-hover: #1e293b;--luthor-shadow: rgba(0, 0, 0, .08);--luthor-muted: #f8fafc;--luthor-muted-fg: #64748b}.luthor-editor-wrapper[data-editor-theme=dark]{--luthor-bg: #0b0b0b;--luthor-fg: #f5f5f5;--luthor-border: #2b2b2b;--luthor-border-hover: #3f3f3f;--luthor-border-active: #525252;--luthor-accent: #f5f5f5;--luthor-accent-hover: #d4d4d8;--luthor-shadow: rgba(0, 0, 0, .5);--luthor-muted: #171717;--luthor-muted-fg: #a3a3a3}.luthor-editor-wrapper{border:1px solid var(--luthor-border);border-radius:10px;background-color:var(--luthor-bg);overflow:hidden;display:flex;flex-direction:column}.luthor-editor-header{background-color:var(--luthor-muted);border-bottom:1px solid var(--luthor-border)}.luthor-mode-tabs{display:flex;border-bottom:1px solid var(--luthor-border)}.luthor-mode-tab{padding:10px 16px;background:none;border:none;cursor:pointer;color:var(--luthor-muted-fg);font-size:14px;font-weight:600;border-bottom:2px solid transparent;transition:color .2s ease,border-color .2s ease}.luthor-mode-tab:hover{color:var(--luthor-fg)}.luthor-mode-tab.active{color:var(--luthor-accent);border-bottom-color:var(--luthor-accent)}.luthor-toolbar{display:flex;align-items:center;gap:6px;padding:8px 10px;flex-wrap:wrap;min-height:52px}.luthor-toolbar-section{display:flex;align-items:center;gap:4px;padding:0 6px}.luthor-toolbar-section:not(:last-child){border-right:1px solid var(--luthor-border);margin-right:8px;padding-right:8px}.luthor-toolbar-button{display:inline-flex;align-items:center;justify-content:center;min-width:34px;height:34px;padding:0 6px;border:1px solid transparent;border-radius:6px;background-color:transparent;color:var(--luthor-fg);cursor:pointer;transition:all .2s ease}.luthor-toolbar-button:hover{background-color:var(--luthor-muted);border-color:var(--luthor-border-hover)}.luthor-toolbar-button.active{background-color:var(--luthor-accent);border-color:var(--luthor-accent);color:var(--luthor-bg)}.luthor-toolbar-button:disabled{opacity:.5;cursor:not-allowed}.luthor-select{position:relative;display:inline-block}.luthor-select-trigger{display:flex;align-items:center;gap:8px;padding:6px 10px;border:1px solid var(--luthor-border);border-radius:6px;background-color:var(--luthor-bg);color:var(--luthor-fg);cursor:pointer;font-size:13px;min-width:140px;height:34px}.luthor-select-trigger.open{border-color:var(--luthor-accent)}.luthor-select-dropdown{position:absolute;top:100%;left:0;right:0;z-index:50;background-color:var(--luthor-bg);border:1px solid var(--luthor-border);border-radius:6px;margin-top:4px;box-shadow:0 6px 20px var(--luthor-shadow)}.luthor-select-option{width:100%;text-align:left;padding:8px 10px;background:none;border:none;cursor:pointer;color:var(--luthor-fg);font-size:13px}.luthor-select-option:hover{background-color:var(--luthor-muted)}.luthor-select-option.selected{background-color:var(--luthor-accent);color:var(--luthor-bg)}.luthor-dropdown{position:relative;display:inline-flex}.luthor-dropdown-content{position:absolute;top:calc(100% + 6px);left:0;z-index:60;min-width:180px;background-color:var(--luthor-bg);border:1px solid var(--luthor-border);border-radius:8px;box-shadow:0 12px 24px var(--luthor-shadow);padding:6px}.luthor-dropdown-item{width:100%;display:flex;align-items:center;gap:8px;padding:8px 10px;border:none;background:none;color:var(--luthor-fg);cursor:pointer;border-radius:6px}.luthor-dropdown-item:hover{background-color:var(--luthor-muted)}.luthor-file-input{display:none}.luthor-editor{min-height:320px;background-color:var(--luthor-bg)}.luthor-richtext-container{position:relative}.luthor-content-editable{min-height:300px;padding:18px;font-size:15px;line-height:1.6;color:var(--luthor-fg);outline:none}.luthor-placeholder{position:absolute;top:18px;left:18px;color:var(--luthor-muted-fg);pointer-events:none}.luthor-source-panel{padding:18px}.luthor-source-view{width:100%;min-height:280px;padding:12px;border:1px solid var(--luthor-border);border-radius:8px;font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:13px;background-color:var(--luthor-bg);color:var(--luthor-fg)}.luthor-command-palette-overlay{position:fixed;inset:0;background:#0006;display:flex;align-items:flex-start;justify-content:center;padding-top:10vh;z-index:1000}.luthor-command-palette{width:min(640px,92vw);background:var(--luthor-bg);border:1px solid var(--luthor-border);border-radius:12px;overflow:hidden;box-shadow:0 20px 40px var(--luthor-shadow)}.luthor-command-palette-header{display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid var(--luthor-border)}.luthor-command-palette-icon{color:var(--luthor-muted-fg)}.luthor-command-palette-input{flex:1;border:none;outline:none;font-size:14px;background:transparent;color:var(--luthor-fg)}.luthor-command-palette-kbd{font-size:11px;padding:3px 6px;border-radius:4px;border:1px solid var(--luthor-border);color:var(--luthor-muted-fg);background:var(--luthor-muted)}.luthor-command-palette-list{max-height:320px;overflow-y:auto;padding:6px 0}.luthor-command-palette-group{padding:6px 16px}.luthor-command-palette-group-title{font-size:12px;color:var(--luthor-muted-fg);margin-bottom:4px}.luthor-command-palette-item{display:flex;align-items:center;justify-content:space-between;padding:8px 10px;border-radius:8px;cursor:pointer}.luthor-command-palette-item-content{display:flex;flex-direction:column;gap:2px}.luthor-command-palette-item:hover,.luthor-command-palette-item.selected{background:var(--luthor-muted)}.luthor-command-palette-item-title{font-size:14px;color:var(--luthor-fg)}.luthor-command-palette-item-description{font-size:12px;color:var(--luthor-muted-fg)}.luthor-command-palette-item-shortcut{font-size:11px;padding:2px 6px;border-radius:4px;border:1px solid var(--luthor-border);background:var(--luthor-muted);color:var(--luthor-muted-fg)}.luthor-command-palette-footer{border-top:1px solid var(--luthor-border);padding:10px 16px}.luthor-command-palette-hint{display:inline-flex;align-items:center;gap:8px;font-size:12px;color:var(--luthor-muted-fg)}.luthor-command-palette-empty{padding:20px;text-align:center;color:var(--luthor-muted-fg)}.luthor-dialog-overlay{position:fixed;inset:0;background-color:#00000080;display:flex;align-items:center;justify-content:center;z-index:1000}.luthor-dialog{background-color:var(--luthor-bg);border-radius:10px;box-shadow:0 20px 30px var(--luthor-shadow);min-width:360px;max-width:520px;max-height:90vh;overflow:hidden}.luthor-dialog-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--luthor-border)}.luthor-dialog-title{margin:0;font-size:16px;font-weight:600;color:var(--luthor-fg)}.luthor-dialog-close{background:none;border:none;color:var(--luthor-muted-fg);cursor:pointer;padding:4px;border-radius:6px}.luthor-dialog-close:hover{background-color:var(--luthor-muted);color:var(--luthor-fg)}.luthor-dialog-content{padding:18px 20px}.luthor-table-dialog{display:flex;flex-direction:column;gap:14px}.luthor-form-group{display:flex;flex-direction:column;gap:6px}.luthor-form-group label{font-size:13px;font-weight:600;color:var(--luthor-fg)}.luthor-input{padding:8px 12px;border:1px solid var(--luthor-border);border-radius:6px;background-color:var(--luthor-bg);color:var(--luthor-fg);font-size:13px}.luthor-input:focus{outline:none;border-color:var(--luthor-accent);box-shadow:0 0 0 3px #3b82f61f}.luthor-checkbox-label{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--luthor-fg)}.luthor-checkbox{width:16px;height:16px;accent-color:var(--luthor-accent)}.luthor-dialog-actions{display:flex;justify-content:flex-end;gap:10px;margin-top:4px}.luthor-button-primary,.luthor-button-secondary{padding:8px 14px;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;border:1px solid}.luthor-button-primary{background-color:var(--luthor-accent);border-color:var(--luthor-accent);color:var(--luthor-bg)}.luthor-button-primary:hover{background-color:var(--luthor-accent-hover);border-color:var(--luthor-accent-hover)}.luthor-button-secondary{background-color:transparent;border-color:var(--luthor-border);color:var(--luthor-fg)}.luthor-button-secondary:hover{background-color:var(--luthor-muted);border-color:var(--luthor-border-hover)}.luthor-text-bold{font-weight:700}.luthor-text-italic{font-style:italic}.luthor-text-underline{text-decoration:underline}.luthor-text-strikethrough{text-decoration:line-through}.luthor-text-code,.luthor-code-block{font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;background-color:var(--luthor-muted);border:1px solid var(--luthor-border);padding:2px 6px;border-radius:4px}.luthor-code-block{display:block;padding:12px;margin:12px 0}.luthor-paragraph{margin:8px 0}.luthor-heading-h1,.luthor-heading-h2,.luthor-heading-h3,.luthor-heading-h4,.luthor-heading-h5,.luthor-heading-h6{margin:16px 0 8px;font-weight:700}.luthor-heading-h1{font-size:28px}.luthor-heading-h2{font-size:24px}.luthor-heading-h3{font-size:20px}.luthor-heading-h4{font-size:18px}.luthor-heading-h5{font-size:16px}.luthor-heading-h6{font-size:14px}.luthor-list-ul,.luthor-list-ol{margin:10px 0 10px 20px}.luthor-list-li{margin:4px 0}.luthor-quote{border-left:4px solid var(--luthor-accent);background-color:var(--luthor-muted);padding:10px 14px;margin:12px 0;color:var(--luthor-fg)}.luthor-link{color:var(--luthor-accent);text-decoration:underline}.luthor-link:hover{color:var(--luthor-accent-hover)}.luthor-hr{margin:16px 0;border:none;border-top:1px solid var(--luthor-border);height:1px}.lexical-image{margin:1em 0;display:block;position:relative}.lexical-image img{max-width:100%;height:auto;border-radius:6px;display:block}.lexical-image.align-left{float:left;margin-right:1em;margin-bottom:1em}.lexical-image.align-right{float:right;margin-left:1em;margin-bottom:1em}.lexical-image.align-center{text-align:center;margin:1em auto}.lexical-image.align-center img{margin:0 auto}.lexical-image figcaption{margin-top:.5em;font-size:12px;color:var(--luthor-muted-fg);text-align:center;font-style:italic}.lexical-image.selected{outline:2px solid var(--luthor-accent);outline-offset:2px}.resizer{position:absolute;width:8px;height:8px;background:var(--luthor-accent);border:1px solid white;border-radius:50%}.resizer.ne{top:-4px;right:-4px;cursor:nesw-resize}.resizer.nw{top:-4px;left:-4px;cursor:nwse-resize}.resizer.se{bottom:-4px;right:-4px;cursor:nwse-resize}.resizer.sw{bottom:-4px;left:-4px;cursor:nesw-resize}.luthor-table{border-collapse:collapse;width:100%;margin:16px 0;border:1px solid var(--luthor-border)}.luthor-table-cell,.luthor-table-cell-header{border:1px solid var(--luthor-border);padding:8px 12px;text-align:left;min-width:80px;background-color:var(--luthor-bg)}.luthor-table-cell-header{background-color:var(--luthor-muted);font-weight:600}table[data-lexical-table-selection]{box-shadow:0 0 0 2px var(--luthor-accent)}table td[data-lexical-table-cell-selection]{background-color:#3b82f61a}.luthor-context-menu{position:fixed;background:var(--luthor-bg);border:1px solid var(--luthor-border);border-radius:8px;box-shadow:0 10px 30px var(--luthor-shadow);z-index:1000;min-width:160px;padding:6px 0;font-size:13px}.luthor-context-menu-item{padding:8px 14px;cursor:pointer;user-select:none}.luthor-context-menu-item:hover{background-color:var(--luthor-muted)}.luthor-context-menu-item-disabled{opacity:.5;cursor:not-allowed}.luthor-floating-toolbar{display:flex;align-items:center;gap:4px;padding:6px;background-color:var(--luthor-bg);border:1px solid var(--luthor-border);border-radius:8px;box-shadow:0 8px 24px var(--luthor-shadow)}.luthor-floating-toolbar-separator{width:1px;height:18px;background-color:var(--luthor-border);margin:0 4px}.luthor-html-embed-container{border:1px solid var(--luthor-border);border-radius:8px;padding:12px;margin:14px 0;background:var(--luthor-muted)}.luthor-html-embed-preview{padding:10px;border-radius:6px;background:var(--luthor-bg);border:1px solid var(--luthor-border)}.luthor-html-embed-editor{display:flex;flex-direction:column;gap:10px}.luthor-html-embed-textarea{width:100%;min-height:120px;padding:10px;border-radius:6px;border:1px solid var(--luthor-border);background:var(--luthor-bg);color:var(--luthor-fg);font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace}.luthor-html-embed-toggle{align-self:flex-end;background:var(--luthor-accent);color:var(--luthor-bg);border:none;border-radius:6px;padding:6px 10px;cursor:pointer;font-size:12px;font-weight:600}.luthor-draggable-handle,.luthor-draggable-up-button,.luthor-draggable-down-button{width:24px;height:24px;border-radius:6px;border:1px solid var(--luthor-border);background:var(--luthor-bg);color:var(--luthor-muted-fg);display:flex;align-items:center;justify-content:center;cursor:pointer}.luthor-draggable-button-stack{display:flex;flex-direction:column;gap:6px;align-items:center}.luthor-draggable-drop-indicator{height:3px;background:var(--luthor-accent);border-radius:999px}.luthor-draggable-block-dragging,.luthor-draggable-block-is-dragging{opacity:.6}@media(max-width:768px){.luthor-toolbar{padding:6px}.luthor-toolbar-button{min-width:30px;height:30px}.luthor-content-editable{padding:14px;font-size:16px}.luthor-placeholder{top:14px;left:14px}}