@inshapardaz/likhari-react 0.1.7-dev.75 → 0.1.7-dev.77
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/dist/EditorRoot.d.ts +0 -18
- package/dist/EditorRoot.d.ts.map +1 -1
- package/dist/EditorRoot.js +4 -55
- package/dist/components/Toolbar.d.ts.map +1 -1
- package/dist/components/Toolbar.js +4 -3
- package/dist/i18n/strings.d.ts +1 -2
- package/dist/i18n/strings.d.ts.map +1 -1
- package/dist/i18n/strings.js +3 -3
- package/package.json +1 -1
- package/dist/persistence/draftStorage.d.ts +0 -26
- package/dist/persistence/draftStorage.d.ts.map +0 -1
- package/dist/persistence/draftStorage.js +0 -71
- package/dist/plugins/AutosaveRestorePlugin.d.ts +0 -16
- package/dist/plugins/AutosaveRestorePlugin.d.ts.map +0 -1
- package/dist/plugins/AutosaveRestorePlugin.js +0 -33
package/dist/EditorRoot.d.ts
CHANGED
|
@@ -70,24 +70,6 @@ export interface EditorRootProps {
|
|
|
70
70
|
* `[...DEFAULT_FONT_OPTIONS, { name: 'Mine', family: '"Mine", serif' }]`.
|
|
71
71
|
*/
|
|
72
72
|
fontOptions?: FontOption[];
|
|
73
|
-
/**
|
|
74
|
-
* Debounced autosave of the current content to `localStorage`, namespaced
|
|
75
|
-
* by `documentId` (lexical-editor-spec.md §6.1–§6.2) — a resilience net
|
|
76
|
-
* against an accidental tab close, independent of `onSave`/the Save
|
|
77
|
-
* button. Requires `documentId` to be set; a draft newer than the initial
|
|
78
|
-
* content prompts (via `window.confirm`) to restore it on mount, and is
|
|
79
|
-
* cleared on a successful explicit save. Defaults to `true`.
|
|
80
|
-
*/
|
|
81
|
-
autosave?: boolean;
|
|
82
|
-
/** Debounce delay before writing an autosave draft, in ms. Default `750`. */
|
|
83
|
-
autosaveDelayMs?: number;
|
|
84
|
-
/**
|
|
85
|
-
* Skips (rather than throwing) an autosave write whose JSON would exceed
|
|
86
|
-
* this size — `localStorage` has a shared ~5-10MB per-origin ceiling, so a
|
|
87
|
-
* single document with embedded base64 images must not be allowed to
|
|
88
|
-
* autosave past a sane limit. Default `2_000_000` (~2MB).
|
|
89
|
-
*/
|
|
90
|
-
autosaveMaxBytes?: number;
|
|
91
73
|
}
|
|
92
74
|
export interface EditorRef {
|
|
93
75
|
getContent(format: FormatId): string;
|
package/dist/EditorRoot.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorRoot.d.ts","sourceRoot":"","sources":["../src/EditorRoot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"EditorRoot.d.ts","sourceRoot":"","sources":["../src/EditorRoot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAW1D,OAAO,KAAK,EAAe,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAyB,KAAK,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AACvF,OAAO,EAAwB,KAAK,mBAAmB,EAAE,KAAK,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAKzH,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAIjE,OAAO,EAAgC,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEnE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,OAAO,CAAC,QAAQ,EAAE,cAAc,GAAG,YAAY,CAAC,CAAC;IACzD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,aAAa,CAAC,EAAE,uBAAuB,CAAC;IACxC,KAAK,CAAC,EAAE,oBAAoB,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC/B;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAClD;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,KAAK,IAAI,CAAC;IACrD;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,SAAS;IACxB,UAAU,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;IACrC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IAClD,iBAAiB,IAAI,OAAO,CAAC;IAC7B,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,KAAK,IAAI,IAAI,CAAC;CACf;AAQD,eAAO,MAAM,UAAU,uGAmKrB,CAAC"}
|
package/dist/EditorRoot.js
CHANGED
|
@@ -7,6 +7,7 @@ import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
|
|
|
7
7
|
import { ListPlugin } from '@lexical/react/LexicalListPlugin';
|
|
8
8
|
import { CheckListPlugin } from '@lexical/react/LexicalCheckListPlugin';
|
|
9
9
|
import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin';
|
|
10
|
+
import { HorizontalRulePlugin } from '@lexical/react/LexicalHorizontalRulePlugin';
|
|
10
11
|
import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin';
|
|
11
12
|
import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary';
|
|
12
13
|
import { defaultFormatRegistry } from '@inshapardaz/likhari-converters';
|
|
@@ -17,24 +18,19 @@ import { EditorThemeProvider } from './theme/EditorThemeProvider.js';
|
|
|
17
18
|
import { Toolbar } from './components/Toolbar.js';
|
|
18
19
|
import { injectUrduWebFontsCss } from './fonts/index.js';
|
|
19
20
|
import { LinkPastePlugin } from './plugins/LinkPastePlugin.js';
|
|
20
|
-
import { AutosaveRestorePlugin } from './plugins/AutosaveRestorePlugin.js';
|
|
21
|
-
import { clearDraft, writeDraft } from './persistence/draftStorage.js';
|
|
22
21
|
import { ImageOptionsContext } from './image/ImageOptionsContext.js';
|
|
23
22
|
import { PortalTargetContext } from './PortalTargetContext.js';
|
|
24
23
|
import { UiStringsContext, getStrings } from './i18n/index.js';
|
|
25
|
-
const DEFAULT_AUTOSAVE_DELAY_MS = 750;
|
|
26
|
-
const DEFAULT_AUTOSAVE_MAX_BYTES = 2000000;
|
|
27
24
|
function initialEditorStateJson(initialContent) {
|
|
28
25
|
if (!initialContent)
|
|
29
26
|
return undefined;
|
|
30
27
|
const state = defaultFormatRegistry.parse(initialContent.format, initialContent.value);
|
|
31
28
|
return JSON.stringify(state);
|
|
32
29
|
}
|
|
33
|
-
export const EditorRoot = forwardRef(function EditorRoot({ documentId, initialContent, featureConfig, featurePreset, theme, colorScheme, accentColor, locale = 'en', placeholder, height = '480px', onChange, onSave, showSave = Boolean(onSave), onImageUpload, fetchImage, fontOptions,
|
|
30
|
+
export const EditorRoot = forwardRef(function EditorRoot({ documentId, initialContent, featureConfig, featurePreset, theme, colorScheme, accentColor, locale = 'en', placeholder, height = '480px', onChange, onSave, showSave = Boolean(onSave), onImageUpload, fetchImage, fontOptions, }, ref) {
|
|
34
31
|
const config = useMemo(() => resolveFeatureConfig(featureConfig, featurePreset), [featureConfig, featurePreset]);
|
|
35
32
|
const strings = useMemo(() => getStrings(locale), [locale]);
|
|
36
33
|
const resolvedPlaceholder = placeholder ?? strings.editor.placeholder;
|
|
37
|
-
const autosaveEnabled = autosave && Boolean(documentId);
|
|
38
34
|
// The urdu-web-fonts stylesheets are needed for the font dropdown and for
|
|
39
35
|
// RTL content, whose canvas font (editor.css) is one of those families.
|
|
40
36
|
useEffect(() => {
|
|
@@ -52,48 +48,17 @@ export const EditorRoot = forwardRef(function EditorRoot({ documentId, initialCo
|
|
|
52
48
|
const editorStateRef = useRef(null);
|
|
53
49
|
const lastSavedJsonRef = useRef(null);
|
|
54
50
|
const [isDirty, setIsDirty] = useState(false);
|
|
55
|
-
const isDirtyRef = useRef(isDirty);
|
|
56
|
-
isDirtyRef.current = isDirty;
|
|
57
|
-
const autosaveTimerRef = useRef(null);
|
|
58
|
-
// Computed once, before the first autosave write could possibly run, so
|
|
59
|
-
// the restore-prompt plugin below can tell a stored draft that's merely
|
|
60
|
-
// identical to the initial content apart (no prompt needed) from one that
|
|
61
|
-
// actually represents newer, unsaved work.
|
|
62
|
-
const initialContentJsonRef = useRef(undefined);
|
|
63
|
-
if (initialContentJsonRef.current === undefined) {
|
|
64
|
-
initialContentJsonRef.current = initialEditorStateJson(initialContent);
|
|
65
|
-
}
|
|
66
51
|
const initialConfig = useMemo(() => ({
|
|
67
52
|
namespace: `likhari-editor-${documentId ?? 'anonymous'}`,
|
|
68
53
|
nodes: EDITOR_NODES,
|
|
69
54
|
theme: editorTheme,
|
|
70
|
-
editorState:
|
|
55
|
+
editorState: initialEditorStateJson(initialContent),
|
|
71
56
|
onError(error) {
|
|
72
57
|
throw error;
|
|
73
58
|
},
|
|
74
59
|
}),
|
|
75
60
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
76
61
|
[]);
|
|
77
|
-
// beforeunload (lexical-editor-spec.md §6.4): the automatic half of the
|
|
78
|
-
// navigation guard — hasUnsavedChanges()/confirmDiscard() below are the
|
|
79
|
-
// imperative half a host's own router guard calls for in-app navigation,
|
|
80
|
-
// which the editor can't intercept on its own.
|
|
81
|
-
useEffect(() => {
|
|
82
|
-
if (typeof window === 'undefined')
|
|
83
|
-
return;
|
|
84
|
-
const handleBeforeUnload = (event) => {
|
|
85
|
-
if (!isDirtyRef.current)
|
|
86
|
-
return;
|
|
87
|
-
event.preventDefault();
|
|
88
|
-
event.returnValue = '';
|
|
89
|
-
};
|
|
90
|
-
window.addEventListener('beforeunload', handleBeforeUnload);
|
|
91
|
-
return () => window.removeEventListener('beforeunload', handleBeforeUnload);
|
|
92
|
-
}, []);
|
|
93
|
-
useEffect(() => () => {
|
|
94
|
-
if (autosaveTimerRef.current)
|
|
95
|
-
clearTimeout(autosaveTimerRef.current);
|
|
96
|
-
}, []);
|
|
97
62
|
useImperativeHandle(ref, () => ({
|
|
98
63
|
getContent(format) {
|
|
99
64
|
if (!editorStateRef.current)
|
|
@@ -130,13 +95,6 @@ export const EditorRoot = forwardRef(function EditorRoot({ documentId, initialCo
|
|
|
130
95
|
const json = JSON.stringify(state.toJSON());
|
|
131
96
|
setIsDirty(json !== lastSavedJsonRef.current);
|
|
132
97
|
onChange?.(state.toJSON());
|
|
133
|
-
if (autosaveEnabled && documentId) {
|
|
134
|
-
if (autosaveTimerRef.current)
|
|
135
|
-
clearTimeout(autosaveTimerRef.current);
|
|
136
|
-
autosaveTimerRef.current = setTimeout(() => {
|
|
137
|
-
writeDraft(documentId, json, autosaveMaxBytes);
|
|
138
|
-
}, autosaveDelayMs);
|
|
139
|
-
}
|
|
140
98
|
};
|
|
141
99
|
const handleSave = () => {
|
|
142
100
|
if (!editorStateRef.current)
|
|
@@ -146,15 +104,6 @@ export const EditorRoot = forwardRef(function EditorRoot({ documentId, initialCo
|
|
|
146
104
|
const content = defaultFormatRegistry.serialize(format, json);
|
|
147
105
|
lastSavedJsonRef.current = JSON.stringify(json);
|
|
148
106
|
setIsDirty(false);
|
|
149
|
-
// A successful explicit save supersedes any in-progress/queued autosave
|
|
150
|
-
// draft — don't let a stale one linger and falsely prompt to "restore"
|
|
151
|
-
// older content next time this document is opened (§6.2).
|
|
152
|
-
if (autosaveTimerRef.current) {
|
|
153
|
-
clearTimeout(autosaveTimerRef.current);
|
|
154
|
-
autosaveTimerRef.current = null;
|
|
155
|
-
}
|
|
156
|
-
if (documentId)
|
|
157
|
-
clearDraft(documentId);
|
|
158
107
|
onSave?.(content, format);
|
|
159
108
|
};
|
|
160
109
|
const dir = locale === 'en' ? 'ltr' : 'rtl';
|
|
@@ -163,5 +112,5 @@ export const EditorRoot = forwardRef(function EditorRoot({ documentId, initialCo
|
|
|
163
112
|
// rather than colliding on a shared one.
|
|
164
113
|
const portalTargetId = useId();
|
|
165
114
|
const portalTargetSelector = `#${CSS.escape(portalTargetId)}`;
|
|
166
|
-
return (_jsx(EditorThemeProvider, { theme: theme, colorScheme: colorScheme, accentColor: accentColor, scopeElementId: portalTargetId, children: _jsxs("div", { className: "likhari-root", dir: dir, ref: rootElementRef, "data-document-id": documentId, style: { height: typeof height === 'number' ? `${height}px` : height }, children: [_jsx("div", { id: portalTargetId, className: "likhari-portal-target" }), _jsx(PortalTargetContext.Provider, { value: portalTargetSelector, children: _jsx(UiStringsContext.Provider, { value: strings, children: _jsx(ImageOptionsContext.Provider, { value: imageOptions, children: _jsxs(LexicalComposer, { initialConfig: initialConfig, children: [_jsx(Toolbar, { config: config, onSave: handleSave, isDirty: isDirty, showSave: showSave, fontOptions: fontOptions, direction: dir, locale: locale }), _jsx("div", { className: "likhari-canvas", children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "likhari-content-editable", dir: dir, "aria-label": strings.editor.contentLabel }), placeholder: _jsx("div", { className: "likhari-placeholder", children: resolvedPlaceholder }), ErrorBoundary: LexicalErrorBoundary }) }), config.history && _jsx(HistoryPlugin, {}), (config.lists.bullet || config.lists.numbered || config.lists.check) && _jsx(ListPlugin, {}), config.lists.check && _jsx(CheckListPlugin, {}), config.links && _jsx(LinkPlugin, {}), config.links && _jsx(LinkPastePlugin, {}), _jsx(
|
|
115
|
+
return (_jsx(EditorThemeProvider, { theme: theme, colorScheme: colorScheme, accentColor: accentColor, scopeElementId: portalTargetId, children: _jsxs("div", { className: "likhari-root", dir: dir, ref: rootElementRef, "data-document-id": documentId, style: { height: typeof height === 'number' ? `${height}px` : height }, children: [_jsx("div", { id: portalTargetId, className: "likhari-portal-target" }), _jsx(PortalTargetContext.Provider, { value: portalTargetSelector, children: _jsx(UiStringsContext.Provider, { value: strings, children: _jsx(ImageOptionsContext.Provider, { value: imageOptions, children: _jsxs(LexicalComposer, { initialConfig: initialConfig, children: [_jsx(Toolbar, { config: config, onSave: handleSave, isDirty: isDirty, showSave: showSave, fontOptions: fontOptions, direction: dir, locale: locale }), _jsx("div", { className: "likhari-canvas", children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "likhari-content-editable", dir: dir, "aria-label": strings.editor.contentLabel }), placeholder: _jsx("div", { className: "likhari-placeholder", children: resolvedPlaceholder }), ErrorBoundary: LexicalErrorBoundary }) }), config.history && _jsx(HistoryPlugin, {}), (config.lists.bullet || config.lists.numbered || config.lists.check) && _jsx(ListPlugin, {}), config.lists.check && _jsx(CheckListPlugin, {}), config.links && _jsx(LinkPlugin, {}), config.links && _jsx(LinkPastePlugin, {}), config.blocks.horizontalRule && _jsx(HorizontalRulePlugin, {}), _jsx(OnChangePlugin, { onChange: handleChange })] }) }) }) })] }) }));
|
|
167
116
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"AA6CA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAA6D,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAItG,OAAO,KAAK,EAAE,MAAM,EAAW,MAAM,iBAAiB,CAAC;AAsUvD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,2BAA2B,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sEAAsE;IACtE,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,wFAAwF;IACxF,SAAS,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IAC1B,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAkC,EAAE,SAAiB,EAAE,MAAa,EAAE,EAAE,YAAY,+BAqxBhJ"}
|
|
@@ -8,6 +8,7 @@ import { $createParagraphNode } from 'lexical';
|
|
|
8
8
|
import { $createHeadingNode, $createQuoteNode, $isHeadingNode, $isQuoteNode } from '@lexical/rich-text';
|
|
9
9
|
import { $isListNode, INSERT_CHECK_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, REMOVE_LIST_COMMAND, } from '@lexical/list';
|
|
10
10
|
import { $findMatchingParent, $insertNodeToNearestRoot } from '@lexical/utils';
|
|
11
|
+
import { INSERT_HORIZONTAL_RULE_COMMAND } from '@lexical/react/LexicalHorizontalRuleNode';
|
|
11
12
|
import { ImageDialog } from '../image/ImageDialog.js';
|
|
12
13
|
import { $createImageNode } from '../image/ImageNode.js';
|
|
13
14
|
import { $createLinkNode, $isLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link';
|
|
@@ -17,7 +18,7 @@ import { CANVAS_FONT_DEFAULTS, DEFAULT_FONT_OPTIONS, FONT_SIZES_PX } from '../fo
|
|
|
17
18
|
import { setStyleProperty } from '../utils/style.js';
|
|
18
19
|
import { useStrings } from '../i18n/useStrings.js';
|
|
19
20
|
import { usePortalTarget } from '../PortalTargetContext.js';
|
|
20
|
-
import { IconAbc, IconAlignCenter, IconAlignJustified, IconAlignLeft, IconAlignRight, IconArrowBackUp, IconArrowForwardUp, IconBold, IconClearFormatting, IconDeviceFloppy, IconDots, IconExternalLink, IconFeather, IconH1, IconH2, IconH3, IconH4, IconH5, IconH6, IconIndentDecrease, IconIndentIncrease, IconItalic, IconLetterCase, IconLetterCaseLower, IconLetterCaseUpper, IconLink, IconLinkOff, IconList, IconListCheck, IconListNumbers, IconPencil, IconPhoto, IconPilcrow, IconQuote, IconSparkles, IconStrikethrough, IconSubscript, IconSuperscript, IconTextSize, IconTypography, IconUnderline, IconWand, } from '@tabler/icons-react';
|
|
21
|
+
import { IconAbc, IconAlignCenter, IconAlignJustified, IconAlignLeft, IconAlignRight, IconArrowBackUp, IconArrowForwardUp, IconBold, IconClearFormatting, IconDeviceFloppy, IconDots, IconExternalLink, IconFeather, IconH1, IconH2, IconH3, IconH4, IconH5, IconH6, IconIndentDecrease, IconIndentIncrease, IconItalic, IconLetterCase, IconLetterCaseLower, IconLetterCaseUpper, IconLink, IconLinkOff, IconList, IconListCheck, IconListNumbers, IconPencil, IconPhoto, IconPilcrow, IconQuote, IconSeparatorHorizontal, IconSparkles, IconStrikethrough, IconSubscript, IconSuperscript, IconTextSize, IconTypography, IconUnderline, IconWand, } from '@tabler/icons-react';
|
|
21
22
|
/** Sentinel for "no inline style": Lexical returns the default it is given when
|
|
22
23
|
* the selection has none, so this tells "unstyled" (show the canvas default)
|
|
23
24
|
* apart from "styled with a value the dropdown doesn't list" (show nothing). */
|
|
@@ -426,7 +427,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
|
|
|
426
427
|
const showFormattingGroup = headingLevels.length > 0 || config.lists.bullet || config.lists.numbered || config.lists.check || config.blocks.quote;
|
|
427
428
|
const showInlineGroup = fmt.bold || fmt.italic || fmt.underline;
|
|
428
429
|
const showAlignGroup = config.alignment.start || config.alignment.center || config.alignment.justify || config.alignment.left || config.alignment.right;
|
|
429
|
-
const showStubInsertGroup = config.images.linked || config.images.embedded || config.poetry.enabled;
|
|
430
|
+
const showStubInsertGroup = config.images.linked || config.images.embedded || config.blocks.horizontalRule || config.poetry.enabled;
|
|
430
431
|
const showLanguageGroup = config.language.autocorrect || config.language.textCleanup || config.language.spellCheck;
|
|
431
432
|
// The "script & cleanup" and "indent/outdent" groups (UI spec §3.3) render
|
|
432
433
|
// as normal inline toolbar groups whenever there's room, and only move
|
|
@@ -595,7 +596,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
|
|
|
595
596
|
restoreSelection();
|
|
596
597
|
openLinkDialog();
|
|
597
598
|
}, onRemove: removeLink, strings: strings }) })] })) : (_jsx(ToolbarButton, { icon: IconLink, title: strings.toolbar.insertLink, onClick: openLinkDialog })) }, "link")),
|
|
598
|
-
showStubInsertGroup && (_jsxs("div", { className: "likhari-toolbar-group likhari-toolbar-group--collapse-tablet", children: [(config.images.linked || config.images.embedded) && (_jsx(ToolbarButton, { icon: IconPhoto, title: strings.toolbar.insertImage, onClick: openImageDialog })), config.poetry.enabled && _jsx(StubButton, { icon: IconFeather, title: strings.toolbar.poetryBlocks, comingSoon: strings.toolbar.comingSoon })] }, "stubInsert")),
|
|
599
|
+
showStubInsertGroup && (_jsxs("div", { className: "likhari-toolbar-group likhari-toolbar-group--collapse-tablet", children: [(config.images.linked || config.images.embedded) && (_jsx(ToolbarButton, { icon: IconPhoto, title: strings.toolbar.insertImage, onClick: openImageDialog })), config.blocks.horizontalRule && (_jsx(ToolbarButton, { icon: IconSeparatorHorizontal, title: strings.toolbar.insertHorizontalRule, onClick: () => editor.dispatchCommand(INSERT_HORIZONTAL_RULE_COMMAND, undefined) })), config.poetry.enabled && _jsx(StubButton, { icon: IconFeather, title: strings.toolbar.poetryBlocks, comingSoon: strings.toolbar.comingSoon })] }, "stubInsert")),
|
|
599
600
|
showLanguageGroup && (_jsxs("div", { className: "likhari-toolbar-group likhari-toolbar-group--collapse-tablet", children: [config.language.autocorrect && _jsx(StubButton, { icon: IconWand, title: strings.toolbar.autocorrect, comingSoon: strings.toolbar.comingSoon }), config.language.textCleanup && _jsx(StubButton, { icon: IconSparkles, title: strings.toolbar.textCleanup, comingSoon: strings.toolbar.comingSoon }), config.language.spellCheck && _jsx(StubButton, { icon: IconAbc, title: strings.toolbar.spellChecker, comingSoon: strings.toolbar.comingSoon })] }, "language")),
|
|
600
601
|
];
|
|
601
602
|
return (_jsxs("div", { className: "likhari-toolbar", role: "toolbar", "aria-label": strings.toolbar.ariaLabel, ref: toolbarContainerRef, children: [fixedGroups, movableGroups
|
package/dist/i18n/strings.d.ts
CHANGED
|
@@ -15,8 +15,6 @@ export interface Strings {
|
|
|
15
15
|
contentLabel: string;
|
|
16
16
|
/** window.confirm() prompt shown by EditorRef.confirmDiscard() when there are unsaved changes. */
|
|
17
17
|
confirmDiscard: string;
|
|
18
|
-
/** window.confirm() prompt offering to restore a localStorage draft newer than the initial content (lexical-editor-spec.md §6.2). */
|
|
19
|
-
restoreDraftConfirm: string;
|
|
20
18
|
};
|
|
21
19
|
common: {
|
|
22
20
|
cancel: string;
|
|
@@ -42,6 +40,7 @@ export interface Strings {
|
|
|
42
40
|
linkOptions: string;
|
|
43
41
|
insertLink: string;
|
|
44
42
|
insertImage: string;
|
|
43
|
+
insertHorizontalRule: string;
|
|
45
44
|
poetryBlocks: string;
|
|
46
45
|
comingSoon: (label: string) => string;
|
|
47
46
|
autocorrect: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../../src/i18n/strings.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;+DAC+D;AAC/D,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,cAAc,CAAC;AAElD,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE;QACN,mGAAmG;QACnG,WAAW,EAAE,MAAM,CAAC;QACpB,gDAAgD;QAChD,YAAY,EAAE,MAAM,CAAC;QACrB,kGAAkG;QAClG,cAAc,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../../src/i18n/strings.ts"],"names":[],"mappings":"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"}
|
package/dist/i18n/strings.js
CHANGED
|
@@ -9,7 +9,6 @@ const en = {
|
|
|
9
9
|
placeholder: 'Start writing…',
|
|
10
10
|
contentLabel: 'Editor content',
|
|
11
11
|
confirmDiscard: 'You have unsaved changes. Discard them?',
|
|
12
|
-
restoreDraftConfirm: 'An unsaved draft of this document was found. Restore it?',
|
|
13
12
|
},
|
|
14
13
|
common: {
|
|
15
14
|
cancel: 'Cancel',
|
|
@@ -34,6 +33,7 @@ const en = {
|
|
|
34
33
|
linkOptions: 'Link options',
|
|
35
34
|
insertLink: 'Insert link (Ctrl+K)',
|
|
36
35
|
insertImage: 'Insert image',
|
|
36
|
+
insertHorizontalRule: 'Insert horizontal rule',
|
|
37
37
|
poetryBlocks: 'Poetry blocks',
|
|
38
38
|
comingSoon: (label) => `${label} (coming soon)`,
|
|
39
39
|
autocorrect: 'Auto-correct',
|
|
@@ -146,7 +146,6 @@ const ur = {
|
|
|
146
146
|
placeholder: 'لکھنا شروع کریں…',
|
|
147
147
|
contentLabel: 'ایڈیٹر کا مواد',
|
|
148
148
|
confirmDiscard: 'آپ کی غیر محفوظ شدہ تبدیلیاں ہیں۔ کیا انہیں رد کر دیا جائے؟',
|
|
149
|
-
restoreDraftConfirm: 'اس دستاویز کا ایک غیر محفوظ شدہ مسودہ ملا ہے۔ کیا اسے بحال کیا جائے؟',
|
|
150
149
|
},
|
|
151
150
|
common: {
|
|
152
151
|
cancel: 'منسوخ کریں',
|
|
@@ -171,6 +170,7 @@ const ur = {
|
|
|
171
170
|
linkOptions: 'لنک کے اختیارات',
|
|
172
171
|
insertLink: 'لنک شامل کریں (Ctrl+K)',
|
|
173
172
|
insertImage: 'تصویر شامل کریں',
|
|
173
|
+
insertHorizontalRule: 'افقی لکیر شامل کریں',
|
|
174
174
|
poetryBlocks: 'شاعری کے بلاکس',
|
|
175
175
|
comingSoon: (label) => `${label} (جلد آ رہا ہے)`,
|
|
176
176
|
autocorrect: 'خودکار تصحیح',
|
|
@@ -283,7 +283,6 @@ const paShahmukhi = {
|
|
|
283
283
|
placeholder: 'لکھنا شروع کرو…',
|
|
284
284
|
contentLabel: 'ایڈیٹر دی سامگری',
|
|
285
285
|
confirmDiscard: 'تہاڈیاں کجھ تبدیلیاں سنبھالیاں نئیں گئیاں۔ کی ایہناں نوں رد کر دیئے؟',
|
|
286
|
-
restoreDraftConfirm: 'ایس دستاویز دا اک غیر محفوظ سودھا لبھیا اے۔ کیہ ایہنوں بحال کيتا جاۓ؟',
|
|
287
286
|
},
|
|
288
287
|
common: {
|
|
289
288
|
cancel: 'رد کرو',
|
|
@@ -308,6 +307,7 @@ const paShahmukhi = {
|
|
|
308
307
|
linkOptions: 'لنک دے اختیار',
|
|
309
308
|
insertLink: 'لنک پاؤ (Ctrl+K)',
|
|
310
309
|
insertImage: 'تصویر پاؤ',
|
|
310
|
+
insertHorizontalRule: 'لیٹی لکیر پاؤ',
|
|
311
311
|
poetryBlocks: 'شاعری بلاک',
|
|
312
312
|
comingSoon: (label) => `${label} (چھیتی آ رہا اے)`,
|
|
313
313
|
autocorrect: 'خودکار درستی',
|
package/package.json
CHANGED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Autosave-to-localStorage drafts (lexical-editor-spec.md §6.2), namespaced
|
|
3
|
-
* by `documentId` (§6.1). Every function is a no-op (never throws) when
|
|
4
|
-
* `localStorage` is unavailable (SSR, privacy mode) or the stored value is
|
|
5
|
-
* corrupt — autosave is best-effort, never a hard dependency.
|
|
6
|
-
*/
|
|
7
|
-
export interface StoredDraft {
|
|
8
|
-
/** Lexical JSON (as produced by `EditorState.toJSON()`, stringified). */
|
|
9
|
-
json: string;
|
|
10
|
-
/** `Date.now()` at write time — lets a host distinguish a stale draft from a genuinely recent one. */
|
|
11
|
-
savedAt: number;
|
|
12
|
-
/** Bumped only if the stored shape itself changes; lets old drafts be recognised/discarded across an upgrade. */
|
|
13
|
-
version: 1;
|
|
14
|
-
}
|
|
15
|
-
export declare function draftKey(documentId: string): string;
|
|
16
|
-
export declare function readDraft(documentId: string): StoredDraft | null;
|
|
17
|
-
/**
|
|
18
|
-
* Best-effort debounced write. Silently skips (rather than throwing) when
|
|
19
|
-
* `json` exceeds `maxBytes` — localStorage has a shared ~5-10MB per-origin
|
|
20
|
-
* ceiling (§6.2), so a single large document (e.g. embedded base64 images)
|
|
21
|
-
* must not be allowed to autosave past a caller-configured limit — or when
|
|
22
|
-
* the write itself fails (quota exceeded, privacy mode).
|
|
23
|
-
*/
|
|
24
|
-
export declare function writeDraft(documentId: string, json: string, maxBytes: number): void;
|
|
25
|
-
export declare function clearDraft(documentId: string): void;
|
|
26
|
-
//# sourceMappingURL=draftStorage.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"draftStorage.d.ts","sourceRoot":"","sources":["../../src/persistence/draftStorage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,WAAW,WAAW;IAC1B,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,sGAAsG;IACtG,OAAO,EAAE,MAAM,CAAC;IAChB,iHAAiH;IACjH,OAAO,EAAE,CAAC,CAAC;CACZ;AAED,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEnD;AAWD,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAYhE;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAanF;AAED,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAQnD"}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Autosave-to-localStorage drafts (lexical-editor-spec.md §6.2), namespaced
|
|
3
|
-
* by `documentId` (§6.1). Every function is a no-op (never throws) when
|
|
4
|
-
* `localStorage` is unavailable (SSR, privacy mode) or the stored value is
|
|
5
|
-
* corrupt — autosave is best-effort, never a hard dependency.
|
|
6
|
-
*/
|
|
7
|
-
const KEY_PREFIX = 'likhari:draft:';
|
|
8
|
-
export function draftKey(documentId) {
|
|
9
|
-
return `${KEY_PREFIX}${documentId}`;
|
|
10
|
-
}
|
|
11
|
-
function getLocalStorage() {
|
|
12
|
-
try {
|
|
13
|
-
return typeof localStorage === 'undefined' ? null : localStorage;
|
|
14
|
-
}
|
|
15
|
-
catch {
|
|
16
|
-
// Some browsers throw on accessing localStorage in certain privacy modes.
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
export function readDraft(documentId) {
|
|
21
|
-
const storage = getLocalStorage();
|
|
22
|
-
if (!storage)
|
|
23
|
-
return null;
|
|
24
|
-
try {
|
|
25
|
-
const raw = storage.getItem(draftKey(documentId));
|
|
26
|
-
if (!raw)
|
|
27
|
-
return null;
|
|
28
|
-
const parsed = JSON.parse(raw);
|
|
29
|
-
if (typeof parsed.json !== 'string' || typeof parsed.savedAt !== 'number' || parsed.version !== 1)
|
|
30
|
-
return null;
|
|
31
|
-
return parsed;
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Best-effort debounced write. Silently skips (rather than throwing) when
|
|
39
|
-
* `json` exceeds `maxBytes` — localStorage has a shared ~5-10MB per-origin
|
|
40
|
-
* ceiling (§6.2), so a single large document (e.g. embedded base64 images)
|
|
41
|
-
* must not be allowed to autosave past a caller-configured limit — or when
|
|
42
|
-
* the write itself fails (quota exceeded, privacy mode).
|
|
43
|
-
*/
|
|
44
|
-
export function writeDraft(documentId, json, maxBytes) {
|
|
45
|
-
const storage = getLocalStorage();
|
|
46
|
-
if (!storage)
|
|
47
|
-
return;
|
|
48
|
-
// .length (UTF-16 code units) over-estimates UTF-8 byte size for any
|
|
49
|
-
// non-Latin text, which only makes this check more conservative — fine
|
|
50
|
-
// for a soft cap that doesn't need to be exact.
|
|
51
|
-
if (json.length > maxBytes)
|
|
52
|
-
return;
|
|
53
|
-
const draft = { json, savedAt: Date.now(), version: 1 };
|
|
54
|
-
try {
|
|
55
|
-
storage.setItem(draftKey(documentId), JSON.stringify(draft));
|
|
56
|
-
}
|
|
57
|
-
catch {
|
|
58
|
-
// Quota exceeded or similar — autosave is best-effort.
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
export function clearDraft(documentId) {
|
|
62
|
-
const storage = getLocalStorage();
|
|
63
|
-
if (!storage)
|
|
64
|
-
return;
|
|
65
|
-
try {
|
|
66
|
-
storage.removeItem(draftKey(documentId));
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
// ignore
|
|
70
|
-
}
|
|
71
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export interface AutosaveRestorePluginProps {
|
|
2
|
-
documentId?: string;
|
|
3
|
-
enabled: boolean;
|
|
4
|
-
/** The Lexical JSON the editor was constructed with (stringified), or
|
|
5
|
-
* undefined for a blank document — compared against a stored draft so an
|
|
6
|
-
* identical draft doesn't trigger a pointless prompt. */
|
|
7
|
-
initialJson?: string;
|
|
8
|
-
confirmMessage: string;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* On mount, offers to restore a newer localStorage draft over the initial
|
|
12
|
-
* content passed in (lexical-editor-spec.md §6.2) — data-loss prevention is
|
|
13
|
-
* the explicit goal, so this never silently picks one over the other.
|
|
14
|
-
*/
|
|
15
|
-
export declare function AutosaveRestorePlugin({ documentId, enabled, initialJson, confirmMessage }: AutosaveRestorePluginProps): null;
|
|
16
|
-
//# sourceMappingURL=AutosaveRestorePlugin.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AutosaveRestorePlugin.d.ts","sourceRoot":"","sources":["../../src/plugins/AutosaveRestorePlugin.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,0BAA0B;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB;;6DAEyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,EAAE,0BAA0B,QAwBrH"}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef } from 'react';
|
|
2
|
-
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
3
|
-
import { readDraft } from '../persistence/draftStorage.js';
|
|
4
|
-
/**
|
|
5
|
-
* On mount, offers to restore a newer localStorage draft over the initial
|
|
6
|
-
* content passed in (lexical-editor-spec.md §6.2) — data-loss prevention is
|
|
7
|
-
* the explicit goal, so this never silently picks one over the other.
|
|
8
|
-
*/
|
|
9
|
-
export function AutosaveRestorePlugin({ documentId, enabled, initialJson, confirmMessage }) {
|
|
10
|
-
const [editor] = useLexicalComposerContext();
|
|
11
|
-
const checked = useRef(false);
|
|
12
|
-
useEffect(() => {
|
|
13
|
-
if (checked.current || !enabled || !documentId)
|
|
14
|
-
return;
|
|
15
|
-
checked.current = true;
|
|
16
|
-
const draft = readDraft(documentId);
|
|
17
|
-
if (!draft || draft.json === initialJson)
|
|
18
|
-
return;
|
|
19
|
-
if (typeof window === 'undefined' || !window.confirm(confirmMessage))
|
|
20
|
-
return;
|
|
21
|
-
try {
|
|
22
|
-
const parsed = editor.parseEditorState(draft.json);
|
|
23
|
-
editor.setEditorState(parsed);
|
|
24
|
-
}
|
|
25
|
-
catch {
|
|
26
|
-
// Corrupt or incompatible draft (e.g. from a much older node schema) —
|
|
27
|
-
// ignore it and keep the initial content rather than crashing.
|
|
28
|
-
}
|
|
29
|
-
// Deliberately runs once on mount only.
|
|
30
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
31
|
-
}, []);
|
|
32
|
-
return null;
|
|
33
|
-
}
|