authscape 1.0.738 → 1.0.742

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,103 +1,103 @@
1
- # AuthScape NPM Package
2
-
3
- Complete authentication and user management solution for Next.js applications.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install authscape
9
- ```
10
-
11
- ## Quick Start
12
-
13
- See the main [AuthScape Documentation](https://authscape.com/docs) for complete setup instructions.
14
-
15
- ## Features
16
-
17
- ### Core Features
18
- - OAuth2/PKCE Authentication
19
- - Multi-tenant support
20
- - User management
21
- - Role-based permissions
22
- - Analytics integration (GA4, Microsoft Clarity)
23
- - Material-UI components
24
-
25
- ### Components
26
- - Document Manager
27
- - File Uploader
28
- - Rich Text Editor
29
- - Data Tables
30
- - Stripe Payment Integration
31
- - Google Maps Integration
32
- - And more...
33
-
34
- ## Additional Features
35
-
36
- ### Sitemap Generation (NEW!)
37
-
38
- Automatically generate SEO-friendly sitemaps for your Next.js application.
39
-
40
- **Automatic setup on install:**
41
-
42
- When you run `npm install authscape`, a sitemap is automatically configured at `/sitemap.xml` that syncs with your AuthScape content.
43
-
44
- - Supports both Pages Router and App Router
45
- - Automatically detects your Next.js project structure
46
- - Works with both `pages/` and `src/pages/` layouts
47
- - Works with both `app/` and `src/app/` layouts
48
-
49
- **To disable:** Simply delete the auto-generated file:
50
- - Pages Router: `pages/sitemap.xml.js`
51
- - App Router: `app/sitemap.xml/route.js`
52
-
53
- ## Environment Variables
54
-
55
- Required environment variables in your `.env.local`:
56
-
57
- ```env
58
- apiUri=https://your-authscape-api.com
59
- authorityUri=https://your-auth-server.com
60
- client_id=your-client-id
61
- client_secret=your-client-secret
62
- ```
63
-
64
- Optional analytics:
65
-
66
- ```env
67
- googleAnalytics4=G-XXXXXXXXXX
68
- microsoftClarityTrackingCode=xxxxxxxxxx
69
- enableDatabaseAnalytics=true
70
- ```
71
-
72
- ## Usage Example
73
-
74
- ```javascript
75
- // pages/_app.js
76
- import { AuthScapeApp } from 'authscape';
77
- import 'react-toastify/dist/ReactToastify.css';
78
-
79
- function MyApp({ Component, pageProps }) {
80
- return (
81
- <AuthScapeApp
82
- Component={Component}
83
- pageProps={pageProps}
84
- enforceLoggedIn={false}
85
- enableAuth={true}
86
- />
87
- );
88
- }
89
-
90
- export default MyApp;
91
- ```
92
-
93
- ## Documentation
94
-
95
- - [AuthScape Docs](https://authscape.com/docs) - Complete documentation
96
-
97
- ## Support
98
-
99
- For issues or questions, contact AuthScape support or visit [authscape.com](https://authscape.com).
100
-
101
- ## License
102
-
103
- ISC
1
+ # AuthScape NPM Package
2
+
3
+ Complete authentication and user management solution for Next.js applications.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install authscape
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ See the main [AuthScape Documentation](https://authscape.com/docs) for complete setup instructions.
14
+
15
+ ## Features
16
+
17
+ ### Core Features
18
+ - OAuth2/PKCE Authentication
19
+ - Multi-tenant support
20
+ - User management
21
+ - Role-based permissions
22
+ - Analytics integration (GA4, Microsoft Clarity)
23
+ - Material-UI components
24
+
25
+ ### Components
26
+ - Document Manager
27
+ - File Uploader
28
+ - Rich Text Editor
29
+ - Data Tables
30
+ - Stripe Payment Integration
31
+ - Google Maps Integration
32
+ - And more...
33
+
34
+ ## Additional Features
35
+
36
+ ### Sitemap Generation (NEW!)
37
+
38
+ Automatically generate SEO-friendly sitemaps for your Next.js application.
39
+
40
+ **Automatic setup on install:**
41
+
42
+ When you run `npm install authscape`, a sitemap is automatically configured at `/sitemap.xml` that syncs with your AuthScape content.
43
+
44
+ - Supports both Pages Router and App Router
45
+ - Automatically detects your Next.js project structure
46
+ - Works with both `pages/` and `src/pages/` layouts
47
+ - Works with both `app/` and `src/app/` layouts
48
+
49
+ **To disable:** Simply delete the auto-generated file:
50
+ - Pages Router: `pages/sitemap.xml.js`
51
+ - App Router: `app/sitemap.xml/route.js`
52
+
53
+ ## Environment Variables
54
+
55
+ Required environment variables in your `.env.local`:
56
+
57
+ ```env
58
+ apiUri=https://your-authscape-api.com
59
+ authorityUri=https://your-auth-server.com
60
+ client_id=your-client-id
61
+ client_secret=your-client-secret
62
+ ```
63
+
64
+ Optional analytics:
65
+
66
+ ```env
67
+ googleAnalytics4=G-XXXXXXXXXX
68
+ microsoftClarityTrackingCode=xxxxxxxxxx
69
+ enableDatabaseAnalytics=true
70
+ ```
71
+
72
+ ## Usage Example
73
+
74
+ ```javascript
75
+ // pages/_app.js
76
+ import { AuthScapeApp } from 'authscape';
77
+ import 'react-toastify/dist/ReactToastify.css';
78
+
79
+ function MyApp({ Component, pageProps }) {
80
+ return (
81
+ <AuthScapeApp
82
+ Component={Component}
83
+ pageProps={pageProps}
84
+ enforceLoggedIn={false}
85
+ enableAuth={true}
86
+ />
87
+ );
88
+ }
89
+
90
+ export default MyApp;
91
+ ```
92
+
93
+ ## Documentation
94
+
95
+ - [AuthScape Docs](https://authscape.com/docs) - Complete documentation
96
+
97
+ ## Support
98
+
99
+ For issues or questions, contact AuthScape support or visit [authscape.com](https://authscape.com).
100
+
101
+ ## License
102
+
103
+ ISC
@@ -9,7 +9,6 @@ import Router from "next/router";
9
9
  import GA4React from "ga-4-react";
10
10
  import { create } from "zustand";
11
11
  import { clarity } from "react-microsoft-clarity";
12
- import { authService } from "authscape";
13
12
 
14
13
  // ---- optional: import your cookie util if not global ----
15
14
  // import { setCookie } from "cookies-next";
@@ -0,0 +1,470 @@
1
+ import React, { useEffect, useCallback } from 'react';
2
+ import { LexicalComposer } from '@lexical/react/LexicalComposer';
3
+ import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
4
+ import { ContentEditable } from '@lexical/react/LexicalContentEditable';
5
+ import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
6
+ import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin';
7
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
8
+ import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary';
9
+ import { ListPlugin } from '@lexical/react/LexicalListPlugin';
10
+ import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin';
11
+ import { $generateHtmlFromNodes, $generateNodesFromDOM } from '@lexical/html';
12
+ import { $getRoot, $getSelection, $isRangeSelection, FORMAT_TEXT_COMMAND, $createParagraphNode } from 'lexical';
13
+ import { $setBlocksType } from '@lexical/selection';
14
+ import { $createHeadingNode, $createQuoteNode, HeadingNode, QuoteNode } from '@lexical/rich-text';
15
+ import { ListItemNode, ListNode, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, REMOVE_LIST_COMMAND } from '@lexical/list';
16
+ import { LinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link';
17
+ import { CodeNode } from '@lexical/code';
18
+ import Box from '@mui/material/Box';
19
+ import IconButton from '@mui/material/IconButton';
20
+ import Divider from '@mui/material/Divider';
21
+ import Select from '@mui/material/Select';
22
+ import MenuItem from '@mui/material/MenuItem';
23
+ import Tooltip from '@mui/material/Tooltip';
24
+ import FormatBoldIcon from '@mui/icons-material/FormatBold';
25
+ import FormatItalicIcon from '@mui/icons-material/FormatItalic';
26
+ import FormatUnderlinedIcon from '@mui/icons-material/FormatUnderlined';
27
+ import StrikethroughSIcon from '@mui/icons-material/StrikethroughS';
28
+ import FormatListBulletedIcon from '@mui/icons-material/FormatListBulleted';
29
+ import FormatListNumberedIcon from '@mui/icons-material/FormatListNumbered';
30
+ import FormatQuoteIcon from '@mui/icons-material/FormatQuote';
31
+ import CodeIcon from '@mui/icons-material/Code';
32
+ import LinkIcon from '@mui/icons-material/Link';
33
+ import LinkOffIcon from '@mui/icons-material/LinkOff';
34
+ import UndoIcon from '@mui/icons-material/Undo';
35
+ import RedoIcon from '@mui/icons-material/Redo';
36
+
37
+ const theme = {
38
+ paragraph: 'lexical-paragraph',
39
+ quote: 'lexical-quote',
40
+ heading: {
41
+ h1: 'lexical-h1',
42
+ h2: 'lexical-h2',
43
+ h3: 'lexical-h3',
44
+ h4: 'lexical-h4',
45
+ h5: 'lexical-h5',
46
+ h6: 'lexical-h6',
47
+ },
48
+ list: {
49
+ nested: {
50
+ listitem: 'lexical-nested-listitem',
51
+ },
52
+ ol: 'lexical-ol',
53
+ ul: 'lexical-ul',
54
+ listitem: 'lexical-listitem',
55
+ },
56
+ text: {
57
+ bold: 'lexical-bold',
58
+ italic: 'lexical-italic',
59
+ underline: 'lexical-underline',
60
+ strikethrough: 'lexical-strikethrough',
61
+ code: 'lexical-code',
62
+ },
63
+ link: 'lexical-link',
64
+ code: 'lexical-code-block',
65
+ };
66
+
67
+ const editorStyles = `
68
+ .lexical-editor-container {
69
+ border: 1px solid #ccc;
70
+ border-radius: 4px;
71
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
72
+ }
73
+ .lexical-editor-inner {
74
+ position: relative;
75
+ }
76
+ .lexical-editor-input {
77
+ padding: 12px;
78
+ outline: none;
79
+ overflow-y: auto;
80
+ }
81
+ .lexical-editor-input:focus {
82
+ outline: none;
83
+ }
84
+ .lexical-placeholder {
85
+ position: absolute;
86
+ top: 12px;
87
+ left: 12px;
88
+ color: #999;
89
+ pointer-events: none;
90
+ user-select: none;
91
+ }
92
+ .lexical-paragraph {
93
+ margin: 0 0 8px 0;
94
+ }
95
+ .lexical-h1 { font-size: 2em; font-weight: bold; margin: 16px 0 8px 0; }
96
+ .lexical-h2 { font-size: 1.5em; font-weight: bold; margin: 14px 0 8px 0; }
97
+ .lexical-h3 { font-size: 1.17em; font-weight: bold; margin: 12px 0 8px 0; }
98
+ .lexical-h4 { font-size: 1em; font-weight: bold; margin: 10px 0 8px 0; }
99
+ .lexical-h5 { font-size: 0.83em; font-weight: bold; margin: 8px 0 8px 0; }
100
+ .lexical-h6 { font-size: 0.67em; font-weight: bold; margin: 6px 0 8px 0; }
101
+ .lexical-quote {
102
+ margin: 8px 0;
103
+ padding: 8px 16px;
104
+ border-left: 4px solid #ccc;
105
+ background: #f9f9f9;
106
+ font-style: italic;
107
+ }
108
+ .lexical-ul, .lexical-ol {
109
+ margin: 8px 0;
110
+ padding-left: 24px;
111
+ }
112
+ .lexical-listitem {
113
+ margin: 4px 0;
114
+ }
115
+ .lexical-bold { font-weight: bold; }
116
+ .lexical-italic { font-style: italic; }
117
+ .lexical-underline { text-decoration: underline; }
118
+ .lexical-strikethrough { text-decoration: line-through; }
119
+ .lexical-code {
120
+ background: #f0f0f0;
121
+ padding: 2px 4px;
122
+ border-radius: 3px;
123
+ font-family: monospace;
124
+ }
125
+ .lexical-link {
126
+ color: #0066cc;
127
+ text-decoration: underline;
128
+ }
129
+ .lexical-code-block {
130
+ background: #f5f5f5;
131
+ padding: 12px;
132
+ border-radius: 4px;
133
+ font-family: monospace;
134
+ overflow-x: auto;
135
+ }
136
+ `;
137
+
138
+ // Toolbar button style to match react-draft-wysiwyg
139
+ const toolbarButtonStyle = {
140
+ minWidth: 32,
141
+ width: 32,
142
+ height: 32,
143
+ padding: '4px',
144
+ margin: '2px',
145
+ borderRadius: '2px',
146
+ border: '1px solid transparent',
147
+ '&:hover': {
148
+ backgroundColor: '#f0f0f0',
149
+ border: '1px solid #ccc',
150
+ },
151
+ };
152
+
153
+ const activeButtonStyle = {
154
+ ...toolbarButtonStyle,
155
+ backgroundColor: '#e0e0e0',
156
+ border: '1px solid #ccc',
157
+ };
158
+
159
+ // HTML Import Plugin
160
+ function HtmlImportPlugin({ initialHtml }) {
161
+ const [editor] = useLexicalComposerContext();
162
+ const [initialized, setInitialized] = React.useState(false);
163
+
164
+ useEffect(() => {
165
+ if (initialHtml && !initialized) {
166
+ editor.update(() => {
167
+ const parser = new DOMParser();
168
+ const dom = parser.parseFromString(initialHtml, 'text/html');
169
+ const nodes = $generateNodesFromDOM(editor, dom);
170
+ const root = $getRoot();
171
+ root.clear();
172
+ nodes.forEach(node => root.append(node));
173
+ });
174
+ setInitialized(true);
175
+ }
176
+ }, [editor, initialHtml, initialized]);
177
+
178
+ return null;
179
+ }
180
+
181
+ // HTML Export Plugin
182
+ function HtmlExportPlugin({ onChange }) {
183
+ const [editor] = useLexicalComposerContext();
184
+
185
+ const handleChange = useCallback(() => {
186
+ editor.update(() => {
187
+ const html = $generateHtmlFromNodes(editor, null);
188
+ onChange(html);
189
+ });
190
+ }, [editor, onChange]);
191
+
192
+ return <OnChangePlugin onChange={handleChange} />;
193
+ }
194
+
195
+ // Toolbar Component
196
+ function Toolbar({ isDisabled }) {
197
+ const [editor] = useLexicalComposerContext();
198
+ const [isBold, setIsBold] = React.useState(false);
199
+ const [isItalic, setIsItalic] = React.useState(false);
200
+ const [isUnderline, setIsUnderline] = React.useState(false);
201
+ const [isStrikethrough, setIsStrikethrough] = React.useState(false);
202
+ const [isCode, setIsCode] = React.useState(false);
203
+ const [isLink, setIsLink] = React.useState(false);
204
+ const [blockType, setBlockType] = React.useState('paragraph');
205
+
206
+ const updateToolbar = useCallback(() => {
207
+ const selection = $getSelection();
208
+ if ($isRangeSelection(selection)) {
209
+ setIsBold(selection.hasFormat('bold'));
210
+ setIsItalic(selection.hasFormat('italic'));
211
+ setIsUnderline(selection.hasFormat('underline'));
212
+ setIsStrikethrough(selection.hasFormat('strikethrough'));
213
+ setIsCode(selection.hasFormat('code'));
214
+ }
215
+ }, []);
216
+
217
+ useEffect(() => {
218
+ return editor.registerUpdateListener(({ editorState }) => {
219
+ editorState.read(() => {
220
+ updateToolbar();
221
+ });
222
+ });
223
+ }, [editor, updateToolbar]);
224
+
225
+ const formatText = (format) => {
226
+ editor.dispatchCommand(FORMAT_TEXT_COMMAND, format);
227
+ };
228
+
229
+ const formatBlock = (type) => {
230
+ editor.update(() => {
231
+ const selection = $getSelection();
232
+ if ($isRangeSelection(selection)) {
233
+ if (type === 'paragraph') {
234
+ $setBlocksType(selection, () => $createParagraphNode());
235
+ } else if (type === 'quote') {
236
+ $setBlocksType(selection, () => $createQuoteNode());
237
+ } else if (type.startsWith('h')) {
238
+ $setBlocksType(selection, () => $createHeadingNode(type));
239
+ }
240
+ setBlockType(type);
241
+ }
242
+ });
243
+ };
244
+
245
+ const formatList = (listType) => {
246
+ if (listType === 'bullet') {
247
+ editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, undefined);
248
+ } else if (listType === 'number') {
249
+ editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, undefined);
250
+ }
251
+ };
252
+
253
+ const insertLink = () => {
254
+ const url = prompt('Enter URL:');
255
+ if (url) {
256
+ editor.dispatchCommand(TOGGLE_LINK_COMMAND, url);
257
+ }
258
+ };
259
+
260
+ const removeLink = () => {
261
+ editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);
262
+ };
263
+
264
+ const undo = () => {
265
+ editor.dispatchCommand('UNDO', undefined);
266
+ };
267
+
268
+ const redo = () => {
269
+ editor.dispatchCommand('REDO', undefined);
270
+ };
271
+
272
+ if (isDisabled) {
273
+ return null;
274
+ }
275
+
276
+ return (
277
+ <Box
278
+ sx={{
279
+ display: 'flex',
280
+ flexWrap: 'wrap',
281
+ alignItems: 'center',
282
+ padding: '8px',
283
+ borderBottom: '1px solid #ccc',
284
+ backgroundColor: '#f9f9f9',
285
+ gap: '4px',
286
+ }}
287
+ >
288
+ {/* Block Type Dropdown */}
289
+ <Select
290
+ size="small"
291
+ value={blockType}
292
+ onChange={(e) => formatBlock(e.target.value)}
293
+ sx={{ minWidth: 120, height: 32, mr: 1 }}
294
+ >
295
+ <MenuItem value="paragraph">Normal</MenuItem>
296
+ <MenuItem value="h1">Heading 1</MenuItem>
297
+ <MenuItem value="h2">Heading 2</MenuItem>
298
+ <MenuItem value="h3">Heading 3</MenuItem>
299
+ <MenuItem value="h4">Heading 4</MenuItem>
300
+ <MenuItem value="h5">Heading 5</MenuItem>
301
+ <MenuItem value="h6">Heading 6</MenuItem>
302
+ <MenuItem value="quote">Blockquote</MenuItem>
303
+ </Select>
304
+
305
+ <Divider orientation="vertical" flexItem sx={{ mx: 1 }} />
306
+
307
+ {/* Text Formatting */}
308
+ <Tooltip title="Bold (Ctrl+B)">
309
+ <IconButton
310
+ size="small"
311
+ onClick={() => formatText('bold')}
312
+ sx={isBold ? activeButtonStyle : toolbarButtonStyle}
313
+ >
314
+ <FormatBoldIcon fontSize="small" />
315
+ </IconButton>
316
+ </Tooltip>
317
+
318
+ <Tooltip title="Italic (Ctrl+I)">
319
+ <IconButton
320
+ size="small"
321
+ onClick={() => formatText('italic')}
322
+ sx={isItalic ? activeButtonStyle : toolbarButtonStyle}
323
+ >
324
+ <FormatItalicIcon fontSize="small" />
325
+ </IconButton>
326
+ </Tooltip>
327
+
328
+ <Tooltip title="Underline (Ctrl+U)">
329
+ <IconButton
330
+ size="small"
331
+ onClick={() => formatText('underline')}
332
+ sx={isUnderline ? activeButtonStyle : toolbarButtonStyle}
333
+ >
334
+ <FormatUnderlinedIcon fontSize="small" />
335
+ </IconButton>
336
+ </Tooltip>
337
+
338
+ <Tooltip title="Strikethrough">
339
+ <IconButton
340
+ size="small"
341
+ onClick={() => formatText('strikethrough')}
342
+ sx={isStrikethrough ? activeButtonStyle : toolbarButtonStyle}
343
+ >
344
+ <StrikethroughSIcon fontSize="small" />
345
+ </IconButton>
346
+ </Tooltip>
347
+
348
+ <Tooltip title="Code">
349
+ <IconButton
350
+ size="small"
351
+ onClick={() => formatText('code')}
352
+ sx={isCode ? activeButtonStyle : toolbarButtonStyle}
353
+ >
354
+ <CodeIcon fontSize="small" />
355
+ </IconButton>
356
+ </Tooltip>
357
+
358
+ <Divider orientation="vertical" flexItem sx={{ mx: 1 }} />
359
+
360
+ {/* Lists */}
361
+ <Tooltip title="Bullet List">
362
+ <IconButton
363
+ size="small"
364
+ onClick={() => formatList('bullet')}
365
+ sx={toolbarButtonStyle}
366
+ >
367
+ <FormatListBulletedIcon fontSize="small" />
368
+ </IconButton>
369
+ </Tooltip>
370
+
371
+ <Tooltip title="Numbered List">
372
+ <IconButton
373
+ size="small"
374
+ onClick={() => formatList('number')}
375
+ sx={toolbarButtonStyle}
376
+ >
377
+ <FormatListNumberedIcon fontSize="small" />
378
+ </IconButton>
379
+ </Tooltip>
380
+
381
+ <Divider orientation="vertical" flexItem sx={{ mx: 1 }} />
382
+
383
+ {/* Links */}
384
+ <Tooltip title="Insert Link">
385
+ <IconButton
386
+ size="small"
387
+ onClick={insertLink}
388
+ sx={toolbarButtonStyle}
389
+ >
390
+ <LinkIcon fontSize="small" />
391
+ </IconButton>
392
+ </Tooltip>
393
+
394
+ <Tooltip title="Remove Link">
395
+ <IconButton
396
+ size="small"
397
+ onClick={removeLink}
398
+ sx={toolbarButtonStyle}
399
+ >
400
+ <LinkOffIcon fontSize="small" />
401
+ </IconButton>
402
+ </Tooltip>
403
+
404
+ <Divider orientation="vertical" flexItem sx={{ mx: 1 }} />
405
+
406
+ {/* Undo/Redo */}
407
+ <Tooltip title="Undo (Ctrl+Z)">
408
+ <IconButton
409
+ size="small"
410
+ onClick={undo}
411
+ sx={toolbarButtonStyle}
412
+ >
413
+ <UndoIcon fontSize="small" />
414
+ </IconButton>
415
+ </Tooltip>
416
+
417
+ <Tooltip title="Redo (Ctrl+Y)">
418
+ <IconButton
419
+ size="small"
420
+ onClick={redo}
421
+ sx={toolbarButtonStyle}
422
+ >
423
+ <RedoIcon fontSize="small" />
424
+ </IconButton>
425
+ </Tooltip>
426
+ </Box>
427
+ );
428
+ }
429
+
430
+ function onError(error) {
431
+ console.error('Lexical error:', error);
432
+ }
433
+
434
+ export function LexicalEditor({ initialHtml, onChange, height = 400, isDisabled = false }) {
435
+ const initialConfig = {
436
+ namespace: 'RichTextEditor',
437
+ theme,
438
+ onError,
439
+ nodes: [HeadingNode, QuoteNode, ListNode, ListItemNode, LinkNode, CodeNode],
440
+ editable: !isDisabled,
441
+ };
442
+
443
+ return (
444
+ <>
445
+ <style>{editorStyles}</style>
446
+ <LexicalComposer initialConfig={initialConfig}>
447
+ <div className="lexical-editor-container">
448
+ <Toolbar isDisabled={isDisabled} />
449
+ <div className="lexical-editor-inner">
450
+ <RichTextPlugin
451
+ contentEditable={
452
+ <ContentEditable
453
+ className="lexical-editor-input"
454
+ style={{ minHeight: height, maxHeight: height }}
455
+ />
456
+ }
457
+ placeholder={<div className="lexical-placeholder">Enter text...</div>}
458
+ ErrorBoundary={LexicalErrorBoundary}
459
+ />
460
+ <HistoryPlugin />
461
+ <ListPlugin />
462
+ <LinkPlugin />
463
+ <HtmlImportPlugin initialHtml={initialHtml} />
464
+ <HtmlExportPlugin onChange={onChange} />
465
+ </div>
466
+ </div>
467
+ </LexicalComposer>
468
+ </>
469
+ );
470
+ }