@inshapardaz/likhari-react 0.1.21-dev.213 → 0.1.21-dev.215
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 -3
- package/dist/EditorRoot.d.ts.map +1 -1
- package/dist/EditorRoot.js +3 -15
- package/dist/autocorrect/AutoCorrectPlugin.d.ts.map +1 -1
- package/dist/autocorrect/AutoCorrectPlugin.js +2 -18
- package/dist/autocorrect/autoCorrectActions.d.ts +0 -6
- package/dist/autocorrect/autoCorrectActions.d.ts.map +1 -1
- package/dist/autocorrect/autoCorrectActions.js +0 -30
- package/dist/components/AutoCorrectPanel.d.ts +1 -5
- package/dist/components/AutoCorrectPanel.d.ts.map +1 -1
- package/dist/components/AutoCorrectPanel.js +3 -3
- package/dist/components/SpellcheckPanel.d.ts +1 -5
- package/dist/components/SpellcheckPanel.d.ts.map +1 -1
- package/dist/components/SpellcheckPanel.js +2 -12
- package/dist/components/Toolbar.d.ts +1 -7
- package/dist/components/Toolbar.d.ts.map +1 -1
- package/dist/components/Toolbar.js +108 -126
- package/dist/i18n/strings.d.ts +0 -11
- package/dist/i18n/strings.d.ts.map +1 -1
- package/dist/i18n/strings.js +0 -33
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/spellcheck/SpellHighlightPlugin.d.ts.map +1 -1
- package/dist/spellcheck/SpellHighlightPlugin.js +0 -3
- package/dist/spellcheck/spellDictionaries.d.ts.map +1 -1
- package/dist/spellcheck/spellDictionaries.js +1 -8
- package/package.json +1 -1
- package/dist/spellcheck/spellingMenu.d.ts +0 -15
- package/dist/spellcheck/spellingMenu.d.ts.map +0 -1
- package/dist/spellcheck/spellingMenu.js +0 -27
- package/dist/spellcheck/userWords.d.ts +0 -39
- package/dist/spellcheck/userWords.d.ts.map +0 -1
- package/dist/spellcheck/userWords.js +0 -132
package/dist/EditorRoot.d.ts
CHANGED
|
@@ -7,7 +7,6 @@ import { type DraftRestoreMode } from './components/DraftRestore.js';
|
|
|
7
7
|
import { type AutoCorrectStore } from './autocorrect/autoCorrectStores.js';
|
|
8
8
|
import type { UrduNormalizationOptions } from './normalization/urduNormalize.js';
|
|
9
9
|
import type { PunctuationOptions } from './autocorrect/punctuationRules.js';
|
|
10
|
-
import { type UserWordStore } from './spellcheck/userWords.js';
|
|
11
10
|
import { type Locale } from './i18n/index.js';
|
|
12
11
|
/** What happens when the user is about to leave with unsaved changes. */
|
|
13
12
|
export type NavigationGuardMode = 'confirm' | 'save-draft' | 'off';
|
|
@@ -19,8 +18,6 @@ export interface EditorRootProps {
|
|
|
19
18
|
documentId?: string;
|
|
20
19
|
/** Where auto-corrections are loaded from and saved to, in priority order. Pass a stable array. */
|
|
21
20
|
autoCorrectStores?: AutoCorrectStore[];
|
|
22
|
-
/** Where words added to the spelling dictionary are saved, in priority order. Pass a stable array. */
|
|
23
|
-
dictionaryStores?: UserWordStore[];
|
|
24
21
|
/** Urdu normalisation applied as part of auto-correct. Diacritics are kept unless `removeDiacritics` is set. */
|
|
25
22
|
urduNormalization?: UrduNormalizationOptions;
|
|
26
23
|
/** Common punctuation fixes, and whether a straight " becomes ”. Both on by default. */
|
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;AAY1D,OAAO,KAAK,EAA8B,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAEjF,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;AAOjE,OAAO,EAAgB,KAAK,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAIhF,OAAO,EAAgC,KAAK,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACtG,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"EditorRoot.d.ts","sourceRoot":"","sources":["../src/EditorRoot.tsx"],"names":[],"mappings":"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"}
|
package/dist/EditorRoot.js
CHANGED
|
@@ -32,7 +32,6 @@ import { AutoCorrectPlugin } from './autocorrect/AutoCorrectPlugin.js';
|
|
|
32
32
|
import { localStorageAutoCorrectStore } from './autocorrect/autoCorrectStores.js';
|
|
33
33
|
import { SpellcheckPanel } from './components/SpellcheckPanel.js';
|
|
34
34
|
import { SpellHighlightPlugin } from './spellcheck/SpellHighlightPlugin.js';
|
|
35
|
-
import { loadUserWords, localStorageUserWordStore } from './spellcheck/userWords.js';
|
|
36
35
|
import { LeaveDialog } from './components/LeaveDialog.js';
|
|
37
36
|
import { clearDraft, createDraftId, hasDraftContent, pruneDrafts, writeDraft, } from './persistence/draftStorage.js';
|
|
38
37
|
import { ImageOptionsContext } from './image/ImageOptionsContext.js';
|
|
@@ -43,15 +42,13 @@ const DEFAULT_AUTOSAVE_MAX_BYTES = 2000000;
|
|
|
43
42
|
const DEFAULT_AUTOSAVE_MAX_DRAFTS = 20;
|
|
44
43
|
/** Used when the host passes no stores: corrections are kept in this browser. */
|
|
45
44
|
const DEFAULT_AUTOCORRECT_STORES = [localStorageAutoCorrectStore()];
|
|
46
|
-
/** Used when the host passes no dictionary stores: added words are kept in this browser. */
|
|
47
|
-
const DEFAULT_DICTIONARY_STORES = [localStorageUserWordStore()];
|
|
48
45
|
function initialEditorStateJson(initialContent) {
|
|
49
46
|
if (!initialContent)
|
|
50
47
|
return undefined;
|
|
51
48
|
const state = defaultFormatRegistry.parse(initialContent.format, initialContent.value);
|
|
52
49
|
return JSON.stringify(state);
|
|
53
50
|
}
|
|
54
|
-
export const EditorRoot = forwardRef(function EditorRoot({ documentId, autoCorrectStores,
|
|
51
|
+
export const EditorRoot = forwardRef(function EditorRoot({ documentId, autoCorrectStores, urduNormalization, punctuation, initialContent, featureConfig, featurePreset, theme, colorScheme, accentColor, locale = 'en', placeholder, height = '480px', onChange, onSave, showSave = Boolean(onSave), onImageUpload, fetchImage, fontOptions, autosave = true, restoreDraft = 'prompt', navigationGuard = 'confirm', onDraftRestored, autosaveDelayMs = DEFAULT_AUTOSAVE_DELAY_MS, autosaveMaxBytes = DEFAULT_AUTOSAVE_MAX_BYTES, autosaveMaxDrafts = DEFAULT_AUTOSAVE_MAX_DRAFTS, }, ref) {
|
|
55
52
|
const config = useMemo(() => resolveFeatureConfig(featureConfig, featurePreset), [featureConfig, featurePreset]);
|
|
56
53
|
const strings = useMemo(() => getStrings(locale), [locale]);
|
|
57
54
|
const resolvedPlaceholder = placeholder ?? strings.editor.placeholder;
|
|
@@ -79,17 +76,8 @@ export const EditorRoot = forwardRef(function EditorRoot({ documentId, autoCorre
|
|
|
79
76
|
const lastSavedJsonRef = useRef(null);
|
|
80
77
|
const [isDirty, setIsDirty] = useState(false);
|
|
81
78
|
const [openPanel, setOpenPanel] = useState(null);
|
|
82
|
-
const [autoCorrectPrefill, setAutoCorrectPrefill] = useState(null);
|
|
83
|
-
const openAutoCorrectFor = (from, language) => {
|
|
84
|
-
setAutoCorrectPrefill({ id: Date.now(), from, language });
|
|
85
|
-
setOpenPanel('autocorrect');
|
|
86
|
-
};
|
|
87
79
|
const [autoCorrectVersion, setAutoCorrectVersion] = useState(0);
|
|
88
80
|
const stores = autoCorrectStores ?? DEFAULT_AUTOCORRECT_STORES;
|
|
89
|
-
const wordStores = dictionaryStores ?? DEFAULT_DICTIONARY_STORES;
|
|
90
|
-
useEffect(() => {
|
|
91
|
-
void loadUserWords(wordStores);
|
|
92
|
-
}, [wordStores]);
|
|
93
81
|
const togglePanel = (panel) => setOpenPanel((current) => (current === panel ? null : panel));
|
|
94
82
|
const isDirtyRef = useRef(isDirty);
|
|
95
83
|
isDirtyRef.current = isDirty;
|
|
@@ -303,9 +291,9 @@ export const EditorRoot = forwardRef(function EditorRoot({ documentId, autoCorre
|
|
|
303
291
|
// rather than colliding on a shared one.
|
|
304
292
|
const portalTargetId = useId();
|
|
305
293
|
const portalTargetSelector = `#${CSS.escape(portalTargetId)}`;
|
|
306
|
-
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: _jsxs(UiStringsContext.Provider, { value: strings, children: [_jsx(ImageOptionsContext.Provider, { value: imageOptions, children: _jsxs(LexicalComposer, { initialConfig: initialConfig, children: [_jsx(Toolbar, { config: config,
|
|
294
|
+
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: _jsxs(UiStringsContext.Provider, { value: strings, children: [_jsx(ImageOptionsContext.Provider, { value: imageOptions, children: _jsxs(LexicalComposer, { initialConfig: initialConfig, children: [_jsx(Toolbar, { config: config, onSave: handleSave, findOpen: openPanel === 'find', onToggleFind: () => togglePanel('find'), spellOpen: openPanel === 'spell', onToggleSpell: () => togglePanel('spell'), autoCorrectOpen: openPanel === 'autocorrect', onToggleAutoCorrect: () => togglePanel('autocorrect'), isDirty: isDirty, showSave: showSave, fontOptions: fontOptions, direction: dir, locale: locale, drafts: autosave
|
|
307
295
|
? { currentId: draftId, maxBytes: autosaveMaxBytes, adoptOnRestore: !documentId, onRestored: handleDraftRestored }
|
|
308
|
-
: undefined }), documentId && (_jsx(DraftRestore, { draftId: documentId, mode: restoreDraft, initialJson: initialContentJsonRef.current, locale: locale, onRestored: handleDraftRestored })), _jsxs("div", { className: "likhari-canvas-frame", children: [config.findReplace && openPanel === 'find' && _jsx(FindReplaceBar, { strings: strings, dir: dir, onClose: () => setOpenPanel(null) }), config.language.spellCheck && openPanel === 'spell' && (_jsx(SpellcheckPanel, { strings: strings, dir: dir,
|
|
296
|
+
: undefined }), documentId && (_jsx(DraftRestore, { draftId: documentId, mode: restoreDraft, initialJson: initialContentJsonRef.current, locale: locale, onRestored: handleDraftRestored })), _jsxs("div", { className: "likhari-canvas-frame", children: [config.findReplace && openPanel === 'find' && _jsx(FindReplaceBar, { strings: strings, dir: dir, onClose: () => setOpenPanel(null) }), config.language.spellCheck && openPanel === 'spell' && (_jsx(SpellcheckPanel, { strings: strings, dir: dir, onClose: () => setOpenPanel(null) })), config.language.autocorrect && openPanel === 'autocorrect' && (_jsx(AutoCorrectPanel, { strings: strings, dir: dir, stores: stores, onSaved: () => setAutoCorrectVersion((v) => v + 1), onClose: () => setOpenPanel(null) })), _jsx("div", { className: "likhari-canvas", children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "likhari-content-editable", dir: dir, spellCheck: false, "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.pageBreak && _jsx(PageBreakPlugin, {}), config.language.spellCheck && _jsx(SpellHighlightPlugin, {}), (config.language.autocorrect || config.language.textCleanup) && _jsx(AutoCorrectPlugin, { stores: stores, version: autoCorrectVersion, enabled: true, typing: config.language.autocorrect, urduNormalization: urduNormalization, punctuation: punctuation }), config.columns && _jsx(LayoutPlugin, {}), config.footnotes && _jsx(FootnotePlugin, {}), config.poetry.enabled && _jsx(PoetryPlugin, {}), config.poetry.enabled && _jsx(PoetryResizer, {}), config.tables && _jsx(TablePlugin, { hasCellMerge: true, hasTabHandler: true }), config.blocks.horizontalRule && _jsx(HorizontalRulePlugin, {}), _jsx(EditorInstancePlugin, { instanceRef: editorInstanceRef }), _jsx(OnChangePlugin, { onChange: handleChange })] }) }), _jsx(LeaveDialog, { opened: leavePrompt !== null, canSave: Boolean(onSave), canSaveDraft: autosave || navigationGuard === 'save-draft', draftFailed: leavePrompt?.draftFailed ?? false, onSave: () => {
|
|
309
297
|
handleSave();
|
|
310
298
|
settleLeave(true);
|
|
311
299
|
}, onSaveDraft: () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AutoCorrectPlugin.d.ts","sourceRoot":"","sources":["../../src/autocorrect/AutoCorrectPlugin.tsx"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"AutoCorrectPlugin.d.ts","sourceRoot":"","sources":["../../src/autocorrect/AutoCorrectPlugin.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA4E,KAAK,cAAc,EAAE,MAAM,SAAS,CAAC;AAUxH,OAAO,EAA0B,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACrF,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAiB,KAAK,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAG9F,sFAAsF;AACtF,kFAAkF;AAClF,eAAO,MAAM,wBAAwB,EAAE,cAAc,CAAC,IAAI,CAA6C,CAAC;AAMxG;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,OAAO,EACP,OAAO,EACP,MAAM,EACN,iBAAiB,EACjB,WAAW,GACZ,EAAE;IACD,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,OAAO,EAAE,OAAO,CAAC;IACjB,oFAAoF;IACpF,MAAM,EAAE,OAAO,CAAC;IAChB,mFAAmF;IACnF,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;IAC7C,kEAAkE;IAClE,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC,QA4FA"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useEffect, useRef } from 'react';
|
|
2
2
|
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
3
|
-
import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_EDITOR,
|
|
4
|
-
import { $correctDocument, $correctPunctuationBeforeCaret, $correctPunctuationDocument, $correctWordBeforeCaret, $
|
|
3
|
+
import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_EDITOR, createCommand } from 'lexical';
|
|
4
|
+
import { $correctDocument, $correctPunctuationBeforeCaret, $correctPunctuationDocument, $correctWordBeforeCaret, $preservingSelection, $spacePunctuationDocument, } from './autoCorrectActions.js';
|
|
5
5
|
import { $cleanUpText } from '../cleanup/textCleanupActions.js';
|
|
6
6
|
import { activePunctuationRules } from './punctuationRules.js';
|
|
7
7
|
import { loadAutoCorrections } from './autoCorrectStores.js';
|
|
@@ -99,24 +99,8 @@ export function AutoCorrectPlugin({ stores, version, enabled, typing, urduNormal
|
|
|
99
99
|
root?.addEventListener('keydown', onKeyDown);
|
|
100
100
|
};
|
|
101
101
|
const unregister = editor.registerRootListener((next) => attach(next));
|
|
102
|
-
// A word with nothing after it (end of line or document) is corrected when the
|
|
103
|
-
// caret leaves it: Enter splits the line, and blur means the user has moved on.
|
|
104
|
-
const correctEndingWord = () => {
|
|
105
|
-
const selection = $getSelection();
|
|
106
|
-
if (!$isRangeSelection(selection))
|
|
107
|
-
return false;
|
|
108
|
-
const rtl = selection.anchor.getNode().getTopLevelElement()?.getDirection() === 'rtl';
|
|
109
|
-
const table = tableFor(rtl);
|
|
110
|
-
if (table)
|
|
111
|
-
$correctWordEndingAtCaret(table, rtl ? normalizeRtl : undefined);
|
|
112
|
-
return false;
|
|
113
|
-
};
|
|
114
|
-
const unregisterEnter = editor.registerCommand(KEY_ENTER_COMMAND, correctEndingWord, COMMAND_PRIORITY_HIGH);
|
|
115
|
-
const unregisterBlur = editor.registerCommand(BLUR_COMMAND, correctEndingWord, COMMAND_PRIORITY_EDITOR);
|
|
116
102
|
return () => {
|
|
117
103
|
unregister();
|
|
118
|
-
unregisterEnter();
|
|
119
|
-
unregisterBlur();
|
|
120
104
|
attach(null);
|
|
121
105
|
};
|
|
122
106
|
}, [editor, enabled, typing]);
|
|
@@ -32,10 +32,4 @@ export declare function $correctPunctuationDocument(rules: PunctuationRule[]): n
|
|
|
32
32
|
export declare function $preservingSelection(change: () => void): void;
|
|
33
33
|
/** Applies urduhack-style punctuation spacing to every text node. Returns how many changed. */
|
|
34
34
|
export declare function $spacePunctuationDocument(): number;
|
|
35
|
-
/**
|
|
36
|
-
* Corrects the word that ends at the caret when nothing follows it, which is the
|
|
37
|
-
* end of a line: the caret is about to leave the line (Enter). The caret ends
|
|
38
|
-
* after the replacement.
|
|
39
|
-
*/
|
|
40
|
-
export declare function $correctWordEndingAtCaret(table: Map<string, string>, normalize?: (word: string) => string): boolean;
|
|
41
35
|
//# sourceMappingURL=autoCorrectActions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autoCorrectActions.d.ts","sourceRoot":"","sources":["../../src/autocorrect/autoCorrectActions.ts"],"names":[],"mappings":"AAWA,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAMhF;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAiCjH;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACxB,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACxB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GACtC,MAAM,CA8BR;AAQD;;;;GAIG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,OAAO,CAqBhF;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,MAAM,CA2B5E;AAID;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,IAAI,GAAG,IAAI,CAoB7D;AAED,+FAA+F;AAC/F,wBAAgB,yBAAyB,IAAI,MAAM,CAWlD
|
|
1
|
+
{"version":3,"file":"autoCorrectActions.d.ts","sourceRoot":"","sources":["../../src/autocorrect/autoCorrectActions.ts"],"names":[],"mappings":"AAWA,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAMhF;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAiCjH;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACxB,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACxB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GACtC,MAAM,CA8BR;AAQD;;;;GAIG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,OAAO,CAqBhF;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,MAAM,CA2B5E;AAID;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,IAAI,GAAG,IAAI,CAoB7D;AAED,+FAA+F;AAC/F,wBAAgB,yBAAyB,IAAI,MAAM,CAWlD"}
|
|
@@ -180,33 +180,3 @@ export function $spacePunctuationDocument() {
|
|
|
180
180
|
}
|
|
181
181
|
return changed;
|
|
182
182
|
}
|
|
183
|
-
/**
|
|
184
|
-
* Corrects the word that ends at the caret when nothing follows it, which is the
|
|
185
|
-
* end of a line: the caret is about to leave the line (Enter). The caret ends
|
|
186
|
-
* after the replacement.
|
|
187
|
-
*/
|
|
188
|
-
export function $correctWordEndingAtCaret(table, normalize) {
|
|
189
|
-
const selection = $getSelection();
|
|
190
|
-
if (!$isRangeSelection(selection) || !selection.isCollapsed())
|
|
191
|
-
return false;
|
|
192
|
-
const anchor = selection.anchor;
|
|
193
|
-
const node = anchor.getNode();
|
|
194
|
-
if (!$isTextNode(node))
|
|
195
|
-
return false;
|
|
196
|
-
const before = node.getTextContent().slice(0, anchor.offset);
|
|
197
|
-
const word = WORD_END.exec(before)?.[0];
|
|
198
|
-
if (!word)
|
|
199
|
-
return false;
|
|
200
|
-
const replacement = correctionFor(word, table, normalize);
|
|
201
|
-
if (replacement === undefined || replacement === word)
|
|
202
|
-
return false;
|
|
203
|
-
const start = before.length - word.length;
|
|
204
|
-
$replaceMatch({ anchorKey: node.getKey(), anchorOffset: start, focusKey: node.getKey(), focusOffset: before.length }, replacement);
|
|
205
|
-
const after = $getSelection();
|
|
206
|
-
if ($isRangeSelection(after)) {
|
|
207
|
-
const caret = start + replacement.length;
|
|
208
|
-
after.anchor.set(node.getKey(), caret, 'text');
|
|
209
|
-
after.focus.set(node.getKey(), caret, 'text');
|
|
210
|
-
}
|
|
211
|
-
return true;
|
|
212
|
-
}
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
import type { Strings } from '../i18n/strings.js';
|
|
2
2
|
import { type AutoCorrectStore } from '../autocorrect/autoCorrectStores.js';
|
|
3
|
-
import type { SpellLanguage } from '../spellcheck/spellDictionaries.js';
|
|
4
3
|
/**
|
|
5
4
|
* Adds one correction (a typed word and its replacement) to the first store
|
|
6
5
|
* that accepts writes. Saving bumps the editor's correction version, so the new
|
|
7
6
|
* entry applies to the next word the user types.
|
|
8
7
|
*/
|
|
9
|
-
export declare function AutoCorrectPanel({ strings, dir, stores,
|
|
8
|
+
export declare function AutoCorrectPanel({ strings, dir, stores, onSaved, onClose, }: {
|
|
10
9
|
strings: Strings;
|
|
11
10
|
dir: 'ltr' | 'rtl';
|
|
12
11
|
stores: AutoCorrectStore[];
|
|
13
|
-
/** The typed word to start with, e.g. a misspelled word the user chose to correct. */
|
|
14
|
-
initialFrom?: string;
|
|
15
|
-
initialLanguage?: SpellLanguage;
|
|
16
12
|
onSaved: () => void;
|
|
17
13
|
onClose: () => void;
|
|
18
14
|
}): import("react").JSX.Element;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AutoCorrectPanel.d.ts","sourceRoot":"","sources":["../../src/components/AutoCorrectPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAwB,KAAK,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"AutoCorrectPanel.d.ts","sourceRoot":"","sources":["../../src/components/AutoCorrectPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAwB,KAAK,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAS/F;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,GAAG,EACH,MAAM,EACN,OAAO,EACP,OAAO,GACR,EAAE;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,KAAK,GAAG,KAAK,CAAC;IACnB,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,+BAqEA"}
|
|
@@ -12,10 +12,10 @@ const LANGUAGES = [
|
|
|
12
12
|
* that accepts writes. Saving bumps the editor's correction version, so the new
|
|
13
13
|
* entry applies to the next word the user types.
|
|
14
14
|
*/
|
|
15
|
-
export function AutoCorrectPanel({ strings, dir, stores,
|
|
15
|
+
export function AutoCorrectPanel({ strings, dir, stores, onSaved, onClose, }) {
|
|
16
16
|
const t = strings.autoCorrect;
|
|
17
|
-
const [language, setLanguage] = useState(
|
|
18
|
-
const [from, setFrom] = useState(
|
|
17
|
+
const [language, setLanguage] = useState('en');
|
|
18
|
+
const [from, setFrom] = useState('');
|
|
19
19
|
const [to, setTo] = useState('');
|
|
20
20
|
const [status, setStatus] = useState('idle');
|
|
21
21
|
const save = async () => {
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
import type { Strings } from '../i18n/strings.js';
|
|
2
|
-
import { type SpellLanguage } from '../spellcheck/spellDictionaries.js';
|
|
3
|
-
import { type UserWordStore } from '../spellcheck/userWords.js';
|
|
4
2
|
/**
|
|
5
3
|
* Steps through the misspelled words one at a time, like the find widget.
|
|
6
4
|
* Each word is selected in the editor as it comes up, and its suggestions
|
|
7
5
|
* can replace it. Checking reads the document on demand, so it never flags
|
|
8
6
|
* words while the user is still typing.
|
|
9
7
|
*/
|
|
10
|
-
export declare function SpellcheckPanel({ strings, dir,
|
|
8
|
+
export declare function SpellcheckPanel({ strings, dir, onClose }: {
|
|
11
9
|
strings: Strings;
|
|
12
10
|
dir: 'ltr' | 'rtl';
|
|
13
|
-
dictionaryStores: UserWordStore[];
|
|
14
|
-
onAddAutoCorrect?: (word: string, language: SpellLanguage) => void;
|
|
15
11
|
onClose: () => void;
|
|
16
12
|
}): import("react").JSX.Element;
|
|
17
13
|
//# sourceMappingURL=SpellcheckPanel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpellcheckPanel.d.ts","sourceRoot":"","sources":["../../src/components/SpellcheckPanel.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"SpellcheckPanel.d.ts","sourceRoot":"","sources":["../../src/components/SpellcheckPanel.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAmB/C;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,KAAK,GAAG,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,+BAwIvH"}
|
|
@@ -5,7 +5,6 @@ import { IconChevronDown, IconX } from '@tabler/icons-react';
|
|
|
5
5
|
import { $replaceMatch, $selectMatch } from '../find/findReplaceActions.js';
|
|
6
6
|
import { $collectMisspellings } from '../spellcheck/spellcheckActions.js';
|
|
7
7
|
import { getSpeller, hasSpellDictionary } from '../spellcheck/spellDictionaries.js';
|
|
8
|
-
import { addUserWord, ignoreWord } from '../spellcheck/userWords.js';
|
|
9
8
|
const LANGUAGES = [
|
|
10
9
|
{ value: 'en', label: 'English' },
|
|
11
10
|
{ value: 'ur', label: 'اردو' },
|
|
@@ -18,7 +17,7 @@ const SUGGESTIONS_PER_WORD = 3;
|
|
|
18
17
|
* can replace it. Checking reads the document on demand, so it never flags
|
|
19
18
|
* words while the user is still typing.
|
|
20
19
|
*/
|
|
21
|
-
export function SpellcheckPanel({ strings, dir,
|
|
20
|
+
export function SpellcheckPanel({ strings, dir, onClose }) {
|
|
22
21
|
const [editor] = useLexicalComposerContext();
|
|
23
22
|
const t = strings.spellcheck;
|
|
24
23
|
const [language, setLanguage] = useState('en');
|
|
@@ -78,14 +77,5 @@ export function SpellcheckPanel({ strings, dir, dictionaryStores, onAddAutoCorre
|
|
|
78
77
|
return (_jsxs("div", { className: "likhari-spell-panel", role: "region", "aria-label": t.title, dir: dir, children: [_jsxs("div", { className: "likhari-spell-header", children: [_jsx("strong", { children: t.title }), _jsx("button", { type: "button", className: "likhari-spell-icon", onClick: onClose, "aria-label": t.close, title: t.close, children: _jsx(IconX, { size: 14, stroke: 1.75 }) })] }), _jsxs("div", { className: "likhari-spell-controls", children: [_jsx("select", { "aria-label": t.language, value: language, onChange: (e) => {
|
|
79
78
|
setLanguage(e.target.value);
|
|
80
79
|
setItems(null);
|
|
81
|
-
}, children: LANGUAGES.filter((l) => hasSpellDictionary(l.value)).map((l) => (_jsx("option", { value: l.value, children: l.label }, l.value))) }), _jsx("button", { type: "button", onClick: check, disabled: checking, children: t.check })] }), items !== null && (_jsxs("div", { className: "likhari-spell-nav", children: [_jsx("span", { className: "likhari-spell-status", "aria-live": "polite", children: status }), _jsx("button", { type: "button", className: "likhari-spell-icon", onClick: () => step(-1), disabled: items.length === 0, "aria-label": t.previous, title: t.previous, children: _jsx(IconChevronDown, { size: 14, stroke: 1.75, style: { transform: 'rotate(180deg)' } }) }), _jsx("button", { type: "button", className: "likhari-spell-icon", onClick: () => step(1), disabled: items.length === 0, "aria-label": t.next, title: t.next, children: _jsx(IconChevronDown, { size: 14, stroke: 1.75 }) })] })), current && (_jsxs("div", { className: "likhari-spell-item", children: [_jsx("span", { className: "likhari-spell-word", children: current.word }),
|
|
82
|
-
ignoreWord(language, current.word);
|
|
83
|
-
void check();
|
|
84
|
-
}, children: strings.contextMenu.ignore }), _jsx("button", { type: "button", className: "likhari-spell-suggestion", onClick: () => {
|
|
85
|
-
void addUserWord(dictionaryStores, language, current.word).then((added) => {
|
|
86
|
-
if (!added)
|
|
87
|
-
ignoreWord(language, current.word);
|
|
88
|
-
void check();
|
|
89
|
-
});
|
|
90
|
-
}, children: strings.contextMenu.addToDictionary }), onAddAutoCorrect && (_jsx("button", { type: "button", className: "likhari-spell-suggestion", onClick: () => onAddAutoCorrect(current.word, language), children: strings.contextMenu.addToAutoCorrect }))] }), current.suggestions.length === 0 ? (_jsx("span", { className: "likhari-spell-note", children: t.noSuggestions })) : (_jsx("div", { className: "likhari-spell-suggestions", children: current.suggestions.map((s) => (_jsx("button", { type: "button", className: "likhari-spell-suggestion", onClick: () => replace(current, s), children: s }, s))) }))] }))] }));
|
|
80
|
+
}, children: LANGUAGES.filter((l) => hasSpellDictionary(l.value)).map((l) => (_jsx("option", { value: l.value, children: l.label }, l.value))) }), _jsx("button", { type: "button", onClick: check, disabled: checking, children: t.check })] }), items !== null && (_jsxs("div", { className: "likhari-spell-nav", children: [_jsx("span", { className: "likhari-spell-status", "aria-live": "polite", children: status }), _jsx("button", { type: "button", className: "likhari-spell-icon", onClick: () => step(-1), disabled: items.length === 0, "aria-label": t.previous, title: t.previous, children: _jsx(IconChevronDown, { size: 14, stroke: 1.75, style: { transform: 'rotate(180deg)' } }) }), _jsx("button", { type: "button", className: "likhari-spell-icon", onClick: () => step(1), disabled: items.length === 0, "aria-label": t.next, title: t.next, children: _jsx(IconChevronDown, { size: 14, stroke: 1.75 }) })] })), current && (_jsxs("div", { className: "likhari-spell-item", children: [_jsx("span", { className: "likhari-spell-word", children: current.word }), current.suggestions.length === 0 ? (_jsx("span", { className: "likhari-spell-note", children: t.noSuggestions })) : (_jsx("div", { className: "likhari-spell-suggestions", children: current.suggestions.map((s) => (_jsx("button", { type: "button", className: "likhari-spell-suggestion", onClick: () => replace(current, s), children: s }, s))) }))] }))] }));
|
|
91
81
|
}
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import { type SpellLanguage } from '../spellcheck/spellDictionaries.js';
|
|
2
|
-
import { type UserWordStore } from '../spellcheck/userWords.js';
|
|
3
1
|
import { type DraftsToolbarOptions } from './DraftsDialog.js';
|
|
4
2
|
import type { ResolvedEditorFeatureConfig } from '@inshapardaz/likhari-core';
|
|
5
3
|
import { type FontOption } from '../fonts/index.js';
|
|
6
4
|
import type { Locale } from '../i18n/strings.js';
|
|
7
5
|
export interface ToolbarProps {
|
|
8
6
|
config: ResolvedEditorFeatureConfig;
|
|
9
|
-
/** Where words added to the dictionary are saved. */
|
|
10
|
-
dictionaryStores?: UserWordStore[];
|
|
11
|
-
/** Opens the auto-correct panel with the word filled in. */
|
|
12
|
-
onAddAutoCorrect?: (word: string, language: SpellLanguage) => void;
|
|
13
7
|
/** Whether the find-and-replace widget is open, and how to toggle it (owned by EditorRoot). */
|
|
14
8
|
findOpen?: boolean;
|
|
15
9
|
onToggleFind?: () => void;
|
|
@@ -31,5 +25,5 @@ export interface ToolbarProps {
|
|
|
31
25
|
/** Autosave drafts: when set, the toolbar offers a Drafts button that lists and restores them. */
|
|
32
26
|
drafts?: DraftsToolbarOptions;
|
|
33
27
|
}
|
|
34
|
-
export declare function Toolbar({ config,
|
|
28
|
+
export declare function Toolbar({ config, onSave, isDirty, showSave, findOpen, onToggleFind, spellOpen, onToggleSpell, autoCorrectOpen, onToggleAutoCorrect, fontOptions, direction, locale, drafts }: ToolbarProps): import("react").JSX.Element;
|
|
35
29
|
//# sourceMappingURL=Toolbar.d.ts.map
|
|
@@ -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":"AAsFA,OAAO,EAAgB,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEzE,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;AA+gBvD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,2BAA2B,CAAC;IACpC,+FAA+F;IAC/F,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,wFAAwF;IACxF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,iGAAiG;IACjG,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;IACjC,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;IAChB,kGAAkG;IAClG,MAAM,CAAC,EAAE,oBAAoB,CAAC;CAC/B;AAED,wBAAgB,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAgB,EAAE,YAAY,EAAE,SAAiB,EAAE,aAAa,EAAE,eAAuB,EAAE,mBAAmB,EAAE,WAAkC,EAAE,SAAiB,EAAE,MAAa,EAAE,MAAM,EAAE,EAAE,YAAY,+BAglCxQ"}
|
|
@@ -2,17 +2,13 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { Menu, Select } from '@mantine/core';
|
|
4
4
|
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
5
|
-
import { $getSelection, $isRangeSelection, $isElementNode, $setSelection, $getRoot, $
|
|
5
|
+
import { $getSelection, $isRangeSelection, $isElementNode, $setSelection, $getRoot, $getNearestNodeFromDOMNode, $createTextNode, KEY_MODIFIER_COMMAND, COMMAND_PRIORITY_NORMAL, FORMAT_TEXT_COMMAND, FORMAT_ELEMENT_COMMAND, INDENT_CONTENT_COMMAND, OUTDENT_CONTENT_COMMAND, UNDO_COMMAND, REDO_COMMAND, SELECTION_CHANGE_COMMAND, CAN_UNDO_COMMAND, CAN_REDO_COMMAND, COMMAND_PRIORITY_LOW, } from 'lexical';
|
|
6
6
|
import { $getSelectionStyleValueForProperty, $patchStyleText, $setBlocksType } from '@lexical/selection';
|
|
7
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
11
|
import { CORRECT_DOCUMENT_COMMAND } from '../autocorrect/AutoCorrectPlugin.js';
|
|
12
|
-
import { $replaceMatch } from '../find/findReplaceActions.js';
|
|
13
|
-
import { getSpeller } from '../spellcheck/spellDictionaries.js';
|
|
14
|
-
import { addUserWord, ignoreWord } from '../spellcheck/userWords.js';
|
|
15
|
-
import { $wordAtDomPoint, textPointAt } from '../spellcheck/spellingMenu.js';
|
|
16
12
|
import { INSERT_HORIZONTAL_RULE_COMMAND } from '@lexical/react/LexicalHorizontalRuleNode';
|
|
17
13
|
import { ImageDialog } from '../image/ImageDialog.js';
|
|
18
14
|
import { $createImageNode } from '../image/ImageNode.js';
|
|
@@ -34,7 +30,7 @@ import { CANVAS_FONT_DEFAULTS, DEFAULT_FONT_OPTIONS, FONT_SIZES_PX } from '../fo
|
|
|
34
30
|
import { setStyleProperty } from '../utils/style.js';
|
|
35
31
|
import { useStrings } from '../i18n/useStrings.js';
|
|
36
32
|
import { usePortalTarget } from '../PortalTargetContext.js';
|
|
37
|
-
import { IconAbc, IconAlignCenter, IconAlignJustified, IconAlignLeft, IconAlignRight, IconArrowBackUp, IconArrowForwardUp, IconBold, IconClearFormatting, IconDeviceFloppy, IconDots, IconHistory, IconExternalLink, IconFeather, IconH1, IconH2, IconH3, IconH4, IconH5, IconH6, IconIndentDecrease, IconIndentIncrease, IconItalic, IconLetterCase, IconLetterCaseLower, IconLetterCaseUpper, IconLink, IconLinkOff, IconList, IconListCheck, IconListNumbers, IconPageBreak, IconPencil, IconPhoto, IconPilcrow, IconQuote, IconSeparatorHorizontal, IconSparkles, IconStrikethrough, IconSubscript, IconSuperscript, IconArrowMergeBoth, IconArrowsSplit2, IconColumnInsertLeft, IconColumnInsertRight, IconColumnRemove, IconColumns,
|
|
33
|
+
import { IconAbc, IconAlignCenter, IconAlignJustified, IconAlignLeft, IconAlignRight, IconArrowBackUp, IconArrowForwardUp, IconBold, IconClearFormatting, IconDeviceFloppy, IconDots, IconHistory, IconExternalLink, IconFeather, IconH1, IconH2, IconH3, IconH4, IconH5, IconH6, IconIndentDecrease, IconIndentIncrease, IconItalic, IconLetterCase, IconLetterCaseLower, IconLetterCaseUpper, IconLink, IconLinkOff, IconList, IconListCheck, IconListNumbers, IconPageBreak, IconPencil, IconPhoto, IconPilcrow, IconQuote, IconSeparatorHorizontal, IconSparkles, IconStrikethrough, IconSubscript, IconSuperscript, IconArrowMergeBoth, IconArrowsSplit2, IconColumnInsertLeft, IconColumnInsertRight, IconColumnRemove, IconColumns, IconSearch, IconColumns1, IconColumns2, IconNumber1Small, IconRowInsertBottom, IconRowInsertTop, IconRowRemove, IconArrowsMaximize, IconArrowsMinimize, IconArrowsShuffle, IconTable, IconTableMinus, IconTrash, IconTableOptions, IconTextSize, IconTypography, IconUnderline, IconWand, } from '@tabler/icons-react';
|
|
38
34
|
/** Sentinel for "no inline style": Lexical returns the default it is given when
|
|
39
35
|
* the selection has none, so this tells "unstyled" (show the canvas default)
|
|
40
36
|
* apart from "styled with a value the dropdown doesn't list" (show nothing). */
|
|
@@ -144,6 +140,8 @@ function OverflowItem({ icon: Icon, label, active, onClick, }) {
|
|
|
144
140
|
// Keep the editor's selection: a mousedown here would blur the canvas.
|
|
145
141
|
onMouseDown: (e) => e.preventDefault(), onClick: onClick, children: label }));
|
|
146
142
|
}
|
|
143
|
+
/** The link menu's contents — shared by the toolbar button and the right-click
|
|
144
|
+
* menu: the URL (opens in a new tab), Edit link, Remove link. */
|
|
147
145
|
function LinkMenuItems({ url, onEdit, onRemove, strings }) {
|
|
148
146
|
// Only offer the link as clickable if it's a URL the editor would have accepted.
|
|
149
147
|
const safeUrl = normalizeLinkUrl(url);
|
|
@@ -185,7 +183,7 @@ function PoetryMenuItems({ strings, layout, scale, centered, onAction, }) {
|
|
|
185
183
|
};
|
|
186
184
|
return (_jsxs(_Fragment, { children: [_jsx(Menu.Label, { children: t.menuLabel }), item(IconColumns1, t.singleColumn, setLayout('single'), layout === 'single'), item(IconColumns2, t.twoColumn, setLayout('two-column'), layout === 'two-column'), item(IconArrowsShuffle, t.staggered, setLayout('staggered'), layout === 'staggered'), _jsx(Menu.Divider, {}), _jsx(Menu.Divider, {}), item(IconRowInsertTop, t.insertCoupletBefore, () => $insertCoupletRelativeToSelection('before')), item(IconRowInsertBottom, t.insertCoupletAfter, () => $insertCoupletRelativeToSelection('after')), item(IconArrowsMinimize, t.tighterSpacing, () => $adjustPoetrySpacing(-1), !canStep(POETRY_SPACINGS, scale.spacing, -1)), item(IconArrowsMaximize, t.looserSpacing, () => $adjustPoetrySpacing(1), !canStep(POETRY_SPACINGS, scale.spacing, 1)), layout === 'staggered' && (_jsxs(_Fragment, { children: [item(IconArrowsMinimize, t.narrowerCouplets, () => $adjustPoetryStagger(-1), !canStep(POETRY_STAGGERS, scale.stagger, -1)), item(IconArrowsMaximize, t.widerCouplets, () => $adjustPoetryStagger(1), !canStep(POETRY_STAGGERS, scale.stagger, 1))] })), layout === 'two-column' && (_jsxs(_Fragment, { children: [item(IconAlignCenter, centered ? t.uncenterCouplet : t.centerCouplet, () => $setCoupletCentered(!centered)), item(IconColumns2, t.narrowerGutter, () => $adjustPoetryGutter(-1), !canStep(POETRY_GUTTERS, scale.gutter, -1)), item(IconColumns2, t.widerGutter, () => $adjustPoetryGutter(1), !canStep(POETRY_GUTTERS, scale.gutter, 1))] })), item(IconTrash, t.deleteCouplet, $deletePoetryCouplet, false, 'red'), item(IconTrash, t.deletePoetry, $deletePoetryBlock, false, 'red')] }));
|
|
187
185
|
}
|
|
188
|
-
export function Toolbar({ config,
|
|
186
|
+
export function Toolbar({ config, onSave, isDirty, showSave, findOpen = false, onToggleFind, spellOpen = false, onToggleSpell, autoCorrectOpen = false, onToggleAutoCorrect, fontOptions = DEFAULT_FONT_OPTIONS, direction = 'ltr', locale = 'en', drafts }) {
|
|
189
187
|
const [editor] = useLexicalComposerContext();
|
|
190
188
|
const [state, setState] = useState(INITIAL_STATE);
|
|
191
189
|
const strings = useStrings(locale);
|
|
@@ -466,116 +464,112 @@ export function Toolbar({ config, dictionaryStores = [], onAddAutoCorrect, onSav
|
|
|
466
464
|
editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);
|
|
467
465
|
closeLinkDialog();
|
|
468
466
|
};
|
|
469
|
-
//
|
|
470
|
-
|
|
471
|
-
|
|
467
|
+
// Right-clicking a link in the text opens the same menu, at the pointer.
|
|
468
|
+
const [linkContext, setLinkContext] = useState(null);
|
|
469
|
+
useEffect(() => {
|
|
470
|
+
if (!config.links)
|
|
471
|
+
return;
|
|
472
|
+
const onContextMenu = (event) => {
|
|
473
|
+
const anchor = event.target?.closest?.('a');
|
|
474
|
+
if (!anchor)
|
|
475
|
+
return;
|
|
476
|
+
let url = null;
|
|
477
|
+
// Select the whole link first, so Edit / Remove act on it wherever the caret was.
|
|
478
|
+
editor.update(() => {
|
|
479
|
+
const node = $getNearestNodeFromDOMNode(anchor);
|
|
480
|
+
const link = node ? $findMatchingParent(node, $isLinkNode) : null;
|
|
481
|
+
if (!link || !$isLinkNode(link))
|
|
482
|
+
return;
|
|
483
|
+
url = link.getURL();
|
|
484
|
+
link.select(0, link.getChildrenSize());
|
|
485
|
+
}, { discrete: true });
|
|
486
|
+
if (url === null)
|
|
487
|
+
return;
|
|
488
|
+
event.preventDefault();
|
|
489
|
+
snapshotSelectionRef.current();
|
|
490
|
+
setLinkContext({ x: event.clientX, y: event.clientY, url });
|
|
491
|
+
};
|
|
492
|
+
return editor.registerRootListener((root, previous) => {
|
|
493
|
+
previous?.removeEventListener('contextmenu', onContextMenu);
|
|
494
|
+
root?.addEventListener('contextmenu', onContextMenu);
|
|
495
|
+
});
|
|
496
|
+
}, [editor, config.links]);
|
|
497
|
+
// Table/poetry contextual-menu actions. The menu takes focus, so the
|
|
498
|
+
// selection is snapshotted when it opens and restored before an action runs.
|
|
472
499
|
const runMenuAction = (action) => () => {
|
|
473
500
|
restoreSelection();
|
|
474
501
|
editor.update(action);
|
|
475
502
|
editor.focus();
|
|
476
503
|
};
|
|
477
|
-
|
|
504
|
+
// Right-clicking a table cell opens the same actions at the pointer.
|
|
505
|
+
const [tableContext, setTableContext] = useState(null);
|
|
478
506
|
useEffect(() => {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
let word = null;
|
|
507
|
+
if (!config.tables)
|
|
508
|
+
return;
|
|
509
|
+
const onContextMenu = (event) => {
|
|
510
|
+
if (event.defaultPrevented)
|
|
511
|
+
return; // a link inside the cell already took it
|
|
512
|
+
const cellElement = event.target?.closest?.('td, th');
|
|
513
|
+
if (!cellElement)
|
|
514
|
+
return;
|
|
515
|
+
let handled = false;
|
|
489
516
|
editor.update(() => {
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
if (
|
|
500
|
-
|
|
501
|
-
const tableCell = node ? $getTableCellNodeFromLexicalNode(node) : null;
|
|
502
|
-
if (tableCell) {
|
|
503
|
-
const selection = $getSelection();
|
|
504
|
-
// Keep a multi-cell selection that includes this cell.
|
|
505
|
-
const insideSelection = $isTableSelection(selection) && selection.getNodes().some((n) => n.getKey() === tableCell.getKey());
|
|
506
|
-
if (!insideSelection)
|
|
507
|
-
tableCell.selectEnd();
|
|
508
|
-
const size = $getTableSelectionSize();
|
|
509
|
-
menu.table = { rows: size.rows, columns: size.columns };
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
if (couplet) {
|
|
513
|
-
const node = $getNearestNodeFromDOMNode(couplet);
|
|
514
|
-
const block = node ? $findMatchingParent(node, $isPoetryBlockNode) : null;
|
|
515
|
-
if (block) {
|
|
516
|
-
menu.poetry = true;
|
|
517
|
-
if (!$getPoetryBlockFromSelection())
|
|
518
|
-
block.selectStart();
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
if (point)
|
|
522
|
-
word = $wordAtDomPoint(point.node, point.offset);
|
|
517
|
+
const node = $getNearestNodeFromDOMNode(cellElement);
|
|
518
|
+
const cell = node ? $getTableCellNodeFromLexicalNode(node) : null;
|
|
519
|
+
if (!cell)
|
|
520
|
+
return;
|
|
521
|
+
handled = true;
|
|
522
|
+
const selection = $getSelection();
|
|
523
|
+
// Keep a multi-cell selection that includes this cell, so "insert
|
|
524
|
+
// rows" / "delete columns" act on everything selected.
|
|
525
|
+
const insideSelection = $isTableSelection(selection) && selection.getNodes().some((n) => n.getKey() === cell.getKey());
|
|
526
|
+
if (!insideSelection)
|
|
527
|
+
cell.selectEnd();
|
|
523
528
|
}, { discrete: true });
|
|
524
|
-
if (
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
menu.spelling = { word: word.word, language, suggestions: checked.suggest(word.word).slice(0, 5), match: word.match };
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
setContextMenu(menu);
|
|
529
|
+
if (!handled)
|
|
530
|
+
return;
|
|
531
|
+
event.preventDefault();
|
|
532
|
+
snapshotSelectionRef.current();
|
|
533
|
+
const size = editor.getEditorState().read($getTableSelectionSize);
|
|
534
|
+
setTableContext({ x: event.clientX, y: event.clientY, rows: size.rows, columns: size.columns });
|
|
535
535
|
};
|
|
536
536
|
return editor.registerRootListener((root, previous) => {
|
|
537
537
|
previous?.removeEventListener('contextmenu', onContextMenu);
|
|
538
538
|
root?.addEventListener('contextmenu', onContextMenu);
|
|
539
539
|
});
|
|
540
|
-
}, [editor, config.
|
|
541
|
-
//
|
|
542
|
-
const
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
editor.update(() => {
|
|
546
|
-
const selection = $getSelection();
|
|
547
|
-
if (!$isRangeSelection(selection))
|
|
548
|
-
return;
|
|
549
|
-
text = selection.getTextContent();
|
|
550
|
-
if (cut)
|
|
551
|
-
selection.removeText();
|
|
552
|
-
}, { discrete: true });
|
|
553
|
-
if (text)
|
|
554
|
-
await navigator.clipboard?.writeText(text).catch(() => undefined);
|
|
555
|
-
editor.focus();
|
|
556
|
-
};
|
|
557
|
-
// Paste reads plain text from the clipboard (the browser may ask for permission).
|
|
558
|
-
const paste = async () => {
|
|
559
|
-
restoreSelection();
|
|
560
|
-
let text;
|
|
561
|
-
try {
|
|
562
|
-
text = await navigator.clipboard.readText();
|
|
563
|
-
}
|
|
564
|
-
catch {
|
|
540
|
+
}, [editor, config.tables]);
|
|
541
|
+
// Right-clicking a poetry couplet opens the same options menu at the pointer.
|
|
542
|
+
const [poetryContext, setPoetryContext] = useState(null);
|
|
543
|
+
useEffect(() => {
|
|
544
|
+
if (!config.poetry.enabled)
|
|
565
545
|
return;
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
546
|
+
const onContextMenu = (event) => {
|
|
547
|
+
if (event.defaultPrevented)
|
|
548
|
+
return; // a link inside the couplet already took it
|
|
549
|
+
const coupletElement = event.target?.closest?.('.likhari-poetry');
|
|
550
|
+
if (!coupletElement)
|
|
570
551
|
return;
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
552
|
+
let handled = false;
|
|
553
|
+
editor.update(() => {
|
|
554
|
+
const node = $getNearestNodeFromDOMNode(coupletElement);
|
|
555
|
+
const couplet = node ? $findMatchingParent(node, $isPoetryBlockNode) : null;
|
|
556
|
+
if (!couplet)
|
|
557
|
+
return;
|
|
558
|
+
handled = true;
|
|
559
|
+
if (!$getPoetryBlockFromSelection())
|
|
560
|
+
couplet.selectStart();
|
|
561
|
+
}, { discrete: true });
|
|
562
|
+
if (!handled)
|
|
563
|
+
return;
|
|
564
|
+
event.preventDefault();
|
|
565
|
+
snapshotSelectionRef.current();
|
|
566
|
+
setPoetryContext({ x: event.clientX, y: event.clientY });
|
|
567
|
+
};
|
|
568
|
+
return editor.registerRootListener((root, previous) => {
|
|
569
|
+
previous?.removeEventListener('contextmenu', onContextMenu);
|
|
570
|
+
root?.addEventListener('contextmenu', onContextMenu);
|
|
571
|
+
});
|
|
572
|
+
}, [editor, config.poetry.enabled]);
|
|
579
573
|
// Ctrl/Cmd+K opens the link dialog.
|
|
580
574
|
useEffect(() => {
|
|
581
575
|
if (!config.links)
|
|
@@ -824,31 +818,19 @@ export function Toolbar({ config, dictionaryStores = [], onAddAutoCorrect, onSav
|
|
|
824
818
|
.filter((group) => !overflowGroupKeys.has(group.key))
|
|
825
819
|
.map((group) => (_jsx(MovableGroupButtons, { group: group }, group.key))), overflowGroupKeys.size > 0 && (_jsxs(Menu, { position: "bottom-end", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 210, closeOnItemClick: true, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx("button", { type: "button", className: "likhari-toolbar-button", "aria-label": strings.toolbar.moreFormatting, title: strings.toolbar.moreFormatting, onMouseDown: (e) => e.preventDefault(), children: _jsx(IconDots, { size: ICON_SIZE, stroke: ICON_STROKE }) }) }), _jsx(Menu.Dropdown, { children: movableGroups
|
|
826
820
|
.filter((group) => overflowGroupKeys.has(group.key))
|
|
827
|
-
.flatMap((group) => group.items.map((item) => (_jsx(OverflowItem, { icon: item.icon, label: item.label, active: item.active, onClick: runOverflowAction(item.onClick) }, item.key)))) })] })), _jsxs(Menu, { opened:
|
|
821
|
+
.flatMap((group) => group.items.map((item) => (_jsx(OverflowItem, { icon: item.icon, label: item.label, active: item.active, onClick: runOverflowAction(item.onClick) }, item.key)))) })] })), config.links && (_jsxs(Menu, { opened: linkContext !== null, onChange: (opened) => {
|
|
822
|
+
if (!opened)
|
|
823
|
+
setLinkContext(null);
|
|
824
|
+
}, position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 240, children: [_jsx(Menu.Target, { children: _jsx("span", { "aria-hidden": "true", style: { position: 'fixed', left: linkContext?.x ?? -9999, top: linkContext?.y ?? -9999, width: 1, height: 1, pointerEvents: 'none' } }) }), _jsx(Menu.Dropdown, { children: _jsx(LinkMenuItems, { url: linkContext?.url ?? '', onEdit: () => {
|
|
825
|
+
restoreSelection();
|
|
826
|
+
openLinkDialog();
|
|
827
|
+
}, onRemove: removeLink, strings: strings }) })] })), config.tables && (_jsxs(Menu, { opened: tableContext !== null, onChange: (opened) => {
|
|
828
|
+
if (!opened)
|
|
829
|
+
setTableContext(null);
|
|
830
|
+
}, position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 240, children: [_jsx(Menu.Target, { children: _jsx("span", { "aria-hidden": "true", style: { position: 'fixed', left: tableContext?.x ?? -9999, top: tableContext?.y ?? -9999, width: 1, height: 1, pointerEvents: 'none' } }) }), _jsx(Menu.Dropdown, { children: _jsx(TableMenuItems, { strings: strings, direction: direction, rows: tableContext?.rows ?? 1, columns: tableContext?.columns ?? 1, canMergeCells: state.canMergeCells, canUnmergeCell: state.canUnmergeCell, onAction: runMenuAction }) })] })), config.poetry.enabled && (_jsxs(Menu, { opened: poetryContext !== null, onChange: (opened) => {
|
|
828
831
|
if (!opened)
|
|
829
|
-
|
|
830
|
-
}, position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width:
|
|
831
|
-
const match = contextMenu.spelling.match;
|
|
832
|
-
editor.update(() => $replaceMatch(match, suggestion), { discrete: true });
|
|
833
|
-
editor.focus();
|
|
834
|
-
}, children: suggestion }, suggestion))), _jsx(Menu.Item, { onClick: () => {
|
|
835
|
-
ignoreWord(contextMenu.spelling.language, contextMenu.spelling.word);
|
|
836
|
-
setContextMenu(null);
|
|
837
|
-
}, children: strings.contextMenu.ignore }), _jsx(Menu.Item, { onClick: () => {
|
|
838
|
-
const { language, word } = contextMenu.spelling;
|
|
839
|
-
// Without a writable store, the word is ignored for this session instead.
|
|
840
|
-
void addUserWord(dictionaryStores, language, word).then((added) => {
|
|
841
|
-
if (!added)
|
|
842
|
-
ignoreWord(language, word);
|
|
843
|
-
});
|
|
844
|
-
setContextMenu(null);
|
|
845
|
-
}, children: strings.contextMenu.addToDictionary }), onAddAutoCorrect && config.language.autocorrect && (_jsx(Menu.Item, { onClick: () => {
|
|
846
|
-
onAddAutoCorrect(contextMenu.spelling.word, contextMenu.spelling.language);
|
|
847
|
-
setContextMenu(null);
|
|
848
|
-
}, children: strings.contextMenu.addToAutoCorrect })), _jsx(Menu.Divider, {})] })), contextMenu?.link !== undefined && (_jsxs(_Fragment, { children: [_jsx(LinkMenuItems, { url: contextMenu.link, onEdit: () => {
|
|
849
|
-
restoreSelection();
|
|
850
|
-
openLinkDialog();
|
|
851
|
-
}, onRemove: removeLink, strings: strings }), _jsx(Menu.Divider, {})] })), contextMenu?.table && (_jsxs(_Fragment, { children: [_jsx(TableMenuItems, { strings: strings, direction: direction, rows: contextMenu.table.rows, columns: contextMenu.table.columns, canMergeCells: state.canMergeCells, canUnmergeCell: state.canUnmergeCell, onAction: runMenuAction }), _jsx(Menu.Divider, {})] })), contextMenu?.poetry && (_jsxs(_Fragment, { children: [_jsx(PoetryMenuItems, { strings: strings, layout: state.poetryLayout, scale: state.poetryScale, centered: state.poetryCentered, onAction: runMenuAction }), _jsx(Menu.Divider, {})] })), _jsx(Menu.Item, { leftSection: _jsx(IconCut, { size: ICON_SIZE, stroke: ICON_STROKE }), onClick: () => void copyOrCut(true)(), children: strings.contextMenu.cut }), _jsx(Menu.Item, { leftSection: _jsx(IconCopy, { size: ICON_SIZE, stroke: ICON_STROKE }), onClick: () => void copyOrCut(false)(), children: strings.contextMenu.copy }), _jsx(Menu.Item, { leftSection: _jsx(IconClipboard, { size: ICON_SIZE, stroke: ICON_STROKE }), onClick: () => void paste(), children: strings.contextMenu.paste }), _jsx(Menu.Item, { leftSection: _jsx(IconSelectAll, { size: ICON_SIZE, stroke: ICON_STROKE }), onClick: runMenuAction(() => $selectAll()), children: strings.contextMenu.selectAll })] })] }), _jsxs("div", { "aria-hidden": "true", ref: toolbarMeasureRef, style: {
|
|
832
|
+
setPoetryContext(null);
|
|
833
|
+
}, position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 200, children: [_jsx(Menu.Target, { children: _jsx("span", { "aria-hidden": "true", style: { position: 'fixed', left: poetryContext?.x ?? -9999, top: poetryContext?.y ?? -9999, width: 1, height: 1, pointerEvents: 'none' } }) }), _jsx(Menu.Dropdown, { children: _jsx(PoetryMenuItems, { strings: strings, layout: state.poetryLayout, scale: state.poetryScale, centered: state.poetryCentered, onAction: runMenuAction }) })] })), _jsxs("div", { "aria-hidden": "true", ref: toolbarMeasureRef, style: {
|
|
852
834
|
position: 'absolute',
|
|
853
835
|
visibility: 'hidden',
|
|
854
836
|
pointerEvents: 'none',
|
package/dist/i18n/strings.d.ts
CHANGED
|
@@ -193,17 +193,6 @@ export interface Strings {
|
|
|
193
193
|
close: string;
|
|
194
194
|
correctDocument: string;
|
|
195
195
|
};
|
|
196
|
-
contextMenu: {
|
|
197
|
-
cut: string;
|
|
198
|
-
copy: string;
|
|
199
|
-
paste: string;
|
|
200
|
-
selectAll: string;
|
|
201
|
-
ignore: string;
|
|
202
|
-
addToDictionary: string;
|
|
203
|
-
addToAutoCorrect: string;
|
|
204
|
-
spelling: string;
|
|
205
|
-
noSuggestions: string;
|
|
206
|
-
};
|
|
207
196
|
link: {
|
|
208
197
|
menuLabel: string;
|
|
209
198
|
noUrl: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../../src/i18n/strings.ts"],"names":[],"mappings":"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
|
|
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
|
@@ -176,17 +176,6 @@ const en = {
|
|
|
176
176
|
close: 'Close',
|
|
177
177
|
correctDocument: 'Correct whole document',
|
|
178
178
|
},
|
|
179
|
-
contextMenu: {
|
|
180
|
-
cut: 'Cut',
|
|
181
|
-
copy: 'Copy',
|
|
182
|
-
paste: 'Paste',
|
|
183
|
-
selectAll: 'Select all',
|
|
184
|
-
ignore: 'Ignore',
|
|
185
|
-
addToDictionary: 'Add to dictionary',
|
|
186
|
-
addToAutoCorrect: 'Add to auto-correct',
|
|
187
|
-
spelling: 'Spelling suggestions',
|
|
188
|
-
noSuggestions: 'No suggestions',
|
|
189
|
-
},
|
|
190
179
|
link: {
|
|
191
180
|
menuLabel: 'Link',
|
|
192
181
|
noUrl: '(no URL)',
|
|
@@ -444,17 +433,6 @@ const ur = {
|
|
|
444
433
|
close: 'بند کریں',
|
|
445
434
|
correctDocument: 'تمام متن کی تصحیح کریں',
|
|
446
435
|
},
|
|
447
|
-
contextMenu: {
|
|
448
|
-
cut: 'کاٹیں',
|
|
449
|
-
copy: 'کاپی کریں',
|
|
450
|
-
paste: 'چسپاں کریں',
|
|
451
|
-
selectAll: 'سب منتخب کریں',
|
|
452
|
-
ignore: 'نظرانداز کریں',
|
|
453
|
-
addToDictionary: 'لغت میں شامل کریں',
|
|
454
|
-
addToAutoCorrect: 'خودکار تصحیح میں شامل کریں',
|
|
455
|
-
spelling: 'ہجے کی تجاویز',
|
|
456
|
-
noSuggestions: 'کوئی تجویز نہیں',
|
|
457
|
-
},
|
|
458
436
|
link: {
|
|
459
437
|
menuLabel: 'لنک',
|
|
460
438
|
noUrl: '(کوئی یو آر ایل نہیں)',
|
|
@@ -712,17 +690,6 @@ const paShahmukhi = {
|
|
|
712
690
|
close: 'بند کرو',
|
|
713
691
|
correctDocument: 'پورے متن دی درستی کرو',
|
|
714
692
|
},
|
|
715
|
-
contextMenu: {
|
|
716
|
-
cut: 'کٹو',
|
|
717
|
-
copy: 'کاپی کرو',
|
|
718
|
-
paste: 'چسپاں کرو',
|
|
719
|
-
selectAll: 'سارا چنو',
|
|
720
|
-
ignore: 'نظر انداز کرو',
|
|
721
|
-
addToDictionary: 'شبدکوش وچ پاؤ',
|
|
722
|
-
addToAutoCorrect: 'خودکار درستی وچ پاؤ',
|
|
723
|
-
spelling: 'ہجے دیاں تجویزاں',
|
|
724
|
-
noSuggestions: 'کوئی تجویز نئیں',
|
|
725
|
-
},
|
|
726
693
|
link: {
|
|
727
694
|
menuLabel: 'لنک',
|
|
728
695
|
noUrl: '(کوئی یو آر ایل نئیں)',
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,4 @@ export type { AutoCorrectStore, AutoCorrectEntry } from './autocorrect/autoCorre
|
|
|
14
14
|
export { normalizeUrdu, normalizeUrduCharacters, removeUrduDiacritics, replaceUrduDigits } from './normalization/urduNormalize.js';
|
|
15
15
|
export type { UrduNormalizationOptions } from './normalization/urduNormalize.js';
|
|
16
16
|
export type { PunctuationOptions, PunctuationRule } from './autocorrect/punctuationRules.js';
|
|
17
|
-
export { localStorageUserWordStore, apiUserWordStore, ignoreWord, addUserWord, onSpellWordsChange } from './spellcheck/userWords.js';
|
|
18
|
-
export type { UserWordStore } from './spellcheck/userWords.js';
|
|
19
17
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAC1G,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9E,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC1F,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACrF,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AACzF,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACrI,OAAO,EAAE,4BAA4B,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAC1H,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAC1F,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAChI,YAAY,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAC9E,YAAY,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAC1G,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9E,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC1F,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACrF,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AACzF,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACrI,OAAO,EAAE,4BAA4B,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAC1H,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAC1F,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAChI,YAAY,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAC9E,YAAY,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -6,4 +6,3 @@ export { STRINGS, getStrings, useStrings, useUiStrings, UiStringsContext } from
|
|
|
6
6
|
export { registerSpellDictionary, hasSpellDictionary } from './spellcheck/spellDictionaries.js';
|
|
7
7
|
export { localStorageAutoCorrectStore, apiAutoCorrectStore, fileAutoCorrectStore } from './autocorrect/autoCorrectStores.js';
|
|
8
8
|
export { normalizeUrdu, normalizeUrduCharacters, removeUrduDiacritics, replaceUrduDigits } from './normalization/urduNormalize.js';
|
|
9
|
-
export { localStorageUserWordStore, apiUserWordStore, ignoreWord, addUserWord, onSpellWordsChange } from './spellcheck/userWords.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpellHighlightPlugin.d.ts","sourceRoot":"","sources":["../../src/spellcheck/SpellHighlightPlugin.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SpellHighlightPlugin.d.ts","sourceRoot":"","sources":["../../src/spellcheck/SpellHighlightPlugin.tsx"],"names":[],"mappings":"AASA;;;;;GAKG;AACH,wBAAgB,oBAAoB,SAgDnC"}
|
|
@@ -2,7 +2,6 @@ import { useEffect } from 'react';
|
|
|
2
2
|
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
3
3
|
import { $collectMisspellings } from './spellcheckActions.js';
|
|
4
4
|
import { getSpeller } from './spellDictionaries.js';
|
|
5
|
-
import { onSpellWordsChange } from './userWords.js';
|
|
6
5
|
/** The CSS Custom Highlight name; editor.css styles it as a red wavy underline. */
|
|
7
6
|
const HIGHLIGHT = 'likhari-spelling';
|
|
8
7
|
const DEBOUNCE_MS = 300;
|
|
@@ -43,7 +42,6 @@ export function SpellHighlightPlugin() {
|
|
|
43
42
|
timer = setTimeout(paint, DEBOUNCE_MS);
|
|
44
43
|
};
|
|
45
44
|
const unregister = editor.registerUpdateListener(schedule);
|
|
46
|
-
const unsubscribe = onSpellWordsChange(schedule);
|
|
47
45
|
Promise.all([getSpeller('en'), getSpeller('ur')]).then(async ([en, ur]) => {
|
|
48
46
|
checkers = { ltr: en ? await en : undefined, rtl: ur ? await ur : undefined };
|
|
49
47
|
schedule();
|
|
@@ -52,7 +50,6 @@ export function SpellHighlightPlugin() {
|
|
|
52
50
|
active = false;
|
|
53
51
|
clearTimeout(timer);
|
|
54
52
|
unregister();
|
|
55
|
-
unsubscribe();
|
|
56
53
|
CSS.highlights.delete(HIGHLIGHT);
|
|
57
54
|
};
|
|
58
55
|
}, [editor]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spellDictionaries.d.ts","sourceRoot":"","sources":["../../src/spellcheck/spellDictionaries.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"spellDictionaries.d.ts","sourceRoot":"","sources":["../../src/spellcheck/spellDictionaries.ts"],"names":[],"mappings":"AAEA,4DAA4D;AAC5D,MAAM,MAAM,aAAa,GAAG,IAAI,GAAG,IAAI,GAAG,cAAc,CAAC;AAEzD,wFAAwF;AACxF,MAAM,WAAW,aAAa;IAC5B,uDAAuD;IACvD,GAAG,EAAE,MAAM,CAAC;IACZ,qEAAqE;IACrE,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,OAAO;IACtB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CACjC;AAED,KAAK,MAAM,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,CAAC;AAmB3C;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAGpG;AAED,2DAA2D;AAC3D,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAEnE;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAS3E;AAED,gEAAgE;AAChE,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AASD,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CAMhD;AAED,0DAA0D;AAC1D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,QAAQ,EAAE,CAEzE"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import nspell from 'nspell';
|
|
2
|
-
import { isAcceptedWord } from './userWords.js';
|
|
3
2
|
const loaders = new Map();
|
|
4
3
|
const spellers = new Map();
|
|
5
4
|
async function fetchText(url) {
|
|
@@ -39,13 +38,7 @@ export function getSpeller(language) {
|
|
|
39
38
|
return null;
|
|
40
39
|
let speller = spellers.get(language);
|
|
41
40
|
if (!speller) {
|
|
42
|
-
speller = loader()
|
|
43
|
-
.then(({ aff, dic }) => nspell(aff, dic))
|
|
44
|
-
.then((base) => ({
|
|
45
|
-
// Words the user added or ignored count as correct, whatever the dictionary says.
|
|
46
|
-
correct: (word) => isAcceptedWord(language, word) || base.correct(word),
|
|
47
|
-
suggest: (word) => base.suggest(word),
|
|
48
|
-
}));
|
|
41
|
+
speller = loader().then(({ aff, dic }) => nspell(aff, dic));
|
|
49
42
|
spellers.set(language, speller);
|
|
50
43
|
}
|
|
51
44
|
return speller;
|
package/package.json
CHANGED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { FindMatch } from '../find/findReplaceActions.js';
|
|
2
|
-
/** A word under the pointer, as a Lexical range, with the direction of its block. */
|
|
3
|
-
export interface WordAtPoint {
|
|
4
|
-
word: string;
|
|
5
|
-
direction: 'ltr' | 'rtl';
|
|
6
|
-
match: FindMatch;
|
|
7
|
-
}
|
|
8
|
-
/** The DOM text node and offset under a viewport point, or null if the point is not on text. */
|
|
9
|
-
export declare function textPointAt(x: number, y: number): {
|
|
10
|
-
node: Node;
|
|
11
|
-
offset: number;
|
|
12
|
-
} | null;
|
|
13
|
-
/** The whole word around an offset in a Lexical text node. Must run inside an editor read or update. */
|
|
14
|
-
export declare function $wordAtDomPoint(domText: Node, offset: number): WordAtPoint | null;
|
|
15
|
-
//# sourceMappingURL=spellingMenu.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"spellingMenu.d.ts","sourceRoot":"","sources":["../../src/spellcheck/spellingMenu.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAG5D,qFAAqF;AACrF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,KAAK,GAAG,KAAK,CAAC;IACzB,KAAK,EAAE,SAAS,CAAC;CAClB;AAED,gGAAgG;AAChG,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAUvF;AAED,wGAAwG;AACxG,wBAAgB,eAAe,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAWjF"}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { $getNearestNodeFromDOMNode, $isTextNode } from 'lexical';
|
|
2
|
-
import { wordsIn } from './spellDictionaries.js';
|
|
3
|
-
/** The DOM text node and offset under a viewport point, or null if the point is not on text. */
|
|
4
|
-
export function textPointAt(x, y) {
|
|
5
|
-
const doc = document;
|
|
6
|
-
if (doc.caretPositionFromPoint) {
|
|
7
|
-
const position = doc.caretPositionFromPoint(x, y);
|
|
8
|
-
return position && position.offsetNode.nodeType === Node.TEXT_NODE ? { node: position.offsetNode, offset: position.offset } : null;
|
|
9
|
-
}
|
|
10
|
-
const range = doc.caretRangeFromPoint?.(x, y);
|
|
11
|
-
return range && range.startContainer.nodeType === Node.TEXT_NODE ? { node: range.startContainer, offset: range.startOffset } : null;
|
|
12
|
-
}
|
|
13
|
-
/** The whole word around an offset in a Lexical text node. Must run inside an editor read or update. */
|
|
14
|
-
export function $wordAtDomPoint(domText, offset) {
|
|
15
|
-
const node = $getNearestNodeFromDOMNode(domText);
|
|
16
|
-
if (!$isTextNode(node))
|
|
17
|
-
return null;
|
|
18
|
-
const text = node.getTextContent();
|
|
19
|
-
const span = wordsIn(text).find((s) => offset >= s.start && offset <= s.end);
|
|
20
|
-
if (!span)
|
|
21
|
-
return null;
|
|
22
|
-
return {
|
|
23
|
-
word: span.word,
|
|
24
|
-
direction: node.getParentOrThrow().getDirection() === 'rtl' ? 'rtl' : 'ltr',
|
|
25
|
-
match: { anchorKey: node.getKey(), anchorOffset: span.start, focusKey: node.getKey(), focusOffset: span.end },
|
|
26
|
-
};
|
|
27
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import type { SpellLanguage } from './spellDictionaries.js';
|
|
2
|
-
/**
|
|
3
|
-
* Where words the user has added to the dictionary are kept. Like the
|
|
4
|
-
* auto-correct stores: the host can pass several, and words are added to the
|
|
5
|
-
* first one that accepts writes.
|
|
6
|
-
*/
|
|
7
|
-
export interface UserWordStore {
|
|
8
|
-
readonly id: string;
|
|
9
|
-
readonly readOnly?: boolean;
|
|
10
|
-
load(language: SpellLanguage): Promise<string[]>;
|
|
11
|
-
append?(language: SpellLanguage, word: string): Promise<void>;
|
|
12
|
-
}
|
|
13
|
-
/** Words added to the dictionary, kept in this browser's localStorage. */
|
|
14
|
-
export declare function localStorageUserWordStore(options?: {
|
|
15
|
-
key?: string;
|
|
16
|
-
}): UserWordStore;
|
|
17
|
-
/**
|
|
18
|
-
* Words served by an API. `load` calls `GET {url}?language=xx` and expects a JSON
|
|
19
|
-
* array of strings; `append` calls `POST {url}` with `{ language, word }`.
|
|
20
|
-
*/
|
|
21
|
-
export declare function apiUserWordStore(options: {
|
|
22
|
-
id?: string;
|
|
23
|
-
url: string;
|
|
24
|
-
headers?: Record<string, string>;
|
|
25
|
-
fetch?: typeof fetch;
|
|
26
|
-
}): UserWordStore;
|
|
27
|
-
/** Calls `listener` whenever accepted or ignored words change. Returns an unsubscribe function. */
|
|
28
|
-
export declare function onSpellWordsChange(listener: () => void): () => void;
|
|
29
|
-
/** Whether the speller should treat the word as correct: it is in the user's dictionary or ignored this session. */
|
|
30
|
-
export declare function isAcceptedWord(language: SpellLanguage, word: string): boolean;
|
|
31
|
-
/** Loads the user's words for each language from the stores (in order). */
|
|
32
|
-
export declare function loadUserWords(stores: UserWordStore[]): Promise<void>;
|
|
33
|
-
/** Adds a word to the first store that accepts writes. Returns false if none does. */
|
|
34
|
-
export declare function addUserWord(stores: UserWordStore[], language: SpellLanguage, word: string): Promise<boolean>;
|
|
35
|
-
/** Loads the session's ignored words. Call once at startup. */
|
|
36
|
-
export declare function restoreIgnoredWords(): void;
|
|
37
|
-
/** Ignores a word for the rest of this browser session (kept in sessionStorage). */
|
|
38
|
-
export declare function ignoreWord(language: SpellLanguage, word: string): void;
|
|
39
|
-
//# sourceMappingURL=userWords.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"userWords.d.ts","sourceRoot":"","sources":["../../src/spellcheck/userWords.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,IAAI,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACjD,MAAM,CAAC,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/D;AAoBD,0EAA0E;AAC1E,wBAAgB,yBAAyB,CAAC,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,aAAa,CAoBvF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IACxC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB,GAAG,aAAa,CAmBhB;AAWD,mGAAmG;AACnG,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAGnE;AAED,oHAAoH;AACpH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAE7E;AAED,2EAA2E;AAC3E,wBAAsB,aAAa,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ1E;AAED,sFAAsF;AACtF,wBAAsB,WAAW,CAAC,MAAM,EAAE,aAAa,EAAE,EAAE,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOlH;AAWD,+DAA+D;AAC/D,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C;AAED,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAUtE"}
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
const LOCAL_KEY = 'likhari-user-words';
|
|
2
|
-
const SESSION_KEY = 'likhari-spell-ignore';
|
|
3
|
-
const LANGUAGES = ['en', 'ur', 'pa-shahmukhi'];
|
|
4
|
-
function readStored(key) {
|
|
5
|
-
try {
|
|
6
|
-
const parsed = JSON.parse(localStorage.getItem(key) ?? '[]');
|
|
7
|
-
return Array.isArray(parsed) ? parsed.filter((e) => typeof e?.word === 'string') : [];
|
|
8
|
-
}
|
|
9
|
-
catch {
|
|
10
|
-
return [];
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
/** Words added to the dictionary, kept in this browser's localStorage. */
|
|
14
|
-
export function localStorageUserWordStore(options = {}) {
|
|
15
|
-
const key = options.key ?? LOCAL_KEY;
|
|
16
|
-
return {
|
|
17
|
-
id: 'local-storage',
|
|
18
|
-
async load(language) {
|
|
19
|
-
return readStored(key)
|
|
20
|
-
.filter((e) => e.language === language)
|
|
21
|
-
.map((e) => e.word);
|
|
22
|
-
},
|
|
23
|
-
async append(language, word) {
|
|
24
|
-
const all = readStored(key);
|
|
25
|
-
if (all.some((e) => e.language === language && e.word === word))
|
|
26
|
-
return;
|
|
27
|
-
all.push({ language, word });
|
|
28
|
-
try {
|
|
29
|
-
localStorage.setItem(key, JSON.stringify(all));
|
|
30
|
-
}
|
|
31
|
-
catch {
|
|
32
|
-
// Storage full or disabled: the word stays for this session only.
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Words served by an API. `load` calls `GET {url}?language=xx` and expects a JSON
|
|
39
|
-
* array of strings; `append` calls `POST {url}` with `{ language, word }`.
|
|
40
|
-
*/
|
|
41
|
-
export function apiUserWordStore(options) {
|
|
42
|
-
const doFetch = options.fetch ?? fetch;
|
|
43
|
-
return {
|
|
44
|
-
id: options.id ?? 'api',
|
|
45
|
-
async load(language) {
|
|
46
|
-
const response = await doFetch(`${options.url}?language=${encodeURIComponent(language)}`, { headers: options.headers });
|
|
47
|
-
if (!response.ok)
|
|
48
|
-
throw new Error(`User word API failed: ${response.status}`);
|
|
49
|
-
const data = await response.json();
|
|
50
|
-
return Array.isArray(data) ? data.filter((w) => typeof w === 'string') : [];
|
|
51
|
-
},
|
|
52
|
-
async append(language, word) {
|
|
53
|
-
const response = await doFetch(options.url, {
|
|
54
|
-
method: 'POST',
|
|
55
|
-
headers: { 'Content-Type': 'application/json', ...options.headers },
|
|
56
|
-
body: JSON.stringify({ language, word }),
|
|
57
|
-
});
|
|
58
|
-
if (!response.ok)
|
|
59
|
-
throw new Error(`User word API rejected the word: ${response.status}`);
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
// Words the user accepted (kept in their store), and words ignored for this session.
|
|
64
|
-
const accepted = new Map(LANGUAGES.map((l) => [l, new Set()]));
|
|
65
|
-
const ignored = new Map(LANGUAGES.map((l) => [l, new Set()]));
|
|
66
|
-
const listeners = new Set();
|
|
67
|
-
function notify() {
|
|
68
|
-
for (const listener of listeners)
|
|
69
|
-
listener();
|
|
70
|
-
}
|
|
71
|
-
/** Calls `listener` whenever accepted or ignored words change. Returns an unsubscribe function. */
|
|
72
|
-
export function onSpellWordsChange(listener) {
|
|
73
|
-
listeners.add(listener);
|
|
74
|
-
return () => listeners.delete(listener);
|
|
75
|
-
}
|
|
76
|
-
/** Whether the speller should treat the word as correct: it is in the user's dictionary or ignored this session. */
|
|
77
|
-
export function isAcceptedWord(language, word) {
|
|
78
|
-
return accepted.get(language)?.has(word) === true || ignored.get(language)?.has(word) === true;
|
|
79
|
-
}
|
|
80
|
-
/** Loads the user's words for each language from the stores (in order). */
|
|
81
|
-
export async function loadUserWords(stores) {
|
|
82
|
-
for (const language of LANGUAGES) {
|
|
83
|
-
const set = accepted.get(language);
|
|
84
|
-
set.clear();
|
|
85
|
-
const results = await Promise.allSettled(stores.map((store) => store.load(language)));
|
|
86
|
-
for (const result of results)
|
|
87
|
-
if (result.status === 'fulfilled')
|
|
88
|
-
for (const word of result.value)
|
|
89
|
-
set.add(word);
|
|
90
|
-
}
|
|
91
|
-
notify();
|
|
92
|
-
}
|
|
93
|
-
/** Adds a word to the first store that accepts writes. Returns false if none does. */
|
|
94
|
-
export async function addUserWord(stores, language, word) {
|
|
95
|
-
const writable = stores.find((store) => !store.readOnly && store.append);
|
|
96
|
-
if (!writable?.append)
|
|
97
|
-
return false;
|
|
98
|
-
await writable.append(language, word);
|
|
99
|
-
accepted.get(language)?.add(word);
|
|
100
|
-
notify();
|
|
101
|
-
return true;
|
|
102
|
-
}
|
|
103
|
-
function readIgnored() {
|
|
104
|
-
try {
|
|
105
|
-
const parsed = JSON.parse(sessionStorage.getItem(SESSION_KEY) ?? '[]');
|
|
106
|
-
return Array.isArray(parsed) ? parsed.filter((e) => typeof e?.word === 'string') : [];
|
|
107
|
-
}
|
|
108
|
-
catch {
|
|
109
|
-
return [];
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
/** Loads the session's ignored words. Call once at startup. */
|
|
113
|
-
export function restoreIgnoredWords() {
|
|
114
|
-
for (const { language, word } of readIgnored())
|
|
115
|
-
ignored.get(language)?.add(word);
|
|
116
|
-
notify();
|
|
117
|
-
}
|
|
118
|
-
/** Ignores a word for the rest of this browser session (kept in sessionStorage). */
|
|
119
|
-
export function ignoreWord(language, word) {
|
|
120
|
-
ignored.get(language)?.add(word);
|
|
121
|
-
const all = readIgnored();
|
|
122
|
-
if (!all.some((e) => e.language === language && e.word === word))
|
|
123
|
-
all.push({ language, word });
|
|
124
|
-
try {
|
|
125
|
-
sessionStorage.setItem(SESSION_KEY, JSON.stringify(all));
|
|
126
|
-
}
|
|
127
|
-
catch {
|
|
128
|
-
// Session storage unavailable: the word is ignored until the page reloads.
|
|
129
|
-
}
|
|
130
|
-
notify();
|
|
131
|
-
}
|
|
132
|
-
restoreIgnoredWords();
|