@inshapardaz/likhari-react 0.1.25-dev.224 → 0.1.25-dev.228

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.
@@ -7,7 +7,9 @@ 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 CompletionStore } from './autocomplete/completionStores.js';
10
11
  import { type UserWordStore } from './spellcheck/userWords.js';
12
+ import type { SpellLanguage } from './spellcheck/spellDictionaries.js';
11
13
  import { type Locale } from './i18n/index.js';
12
14
  /** What happens when the user is about to leave with unsaved changes. */
13
15
  export type NavigationGuardMode = 'confirm' | 'save-draft' | 'off';
@@ -21,6 +23,10 @@ export interface EditorRootProps {
21
23
  autoCorrectStores?: AutoCorrectStore[];
22
24
  /** Where words added to the spelling dictionary are saved, in priority order. Pass a stable array. */
23
25
  dictionaryStores?: UserWordStore[];
26
+ /** Where autocomplete words come from, combined per language. Defaults to the dictionaries and the user's words. Pass a stable array. */
27
+ completionStores?: CompletionStore[];
28
+ /** The language autocomplete works in: 'auto' follows each block's direction (English for LTR, Urdu for RTL). */
29
+ autoCompleteLanguage?: SpellLanguage | 'auto';
24
30
  /** Urdu normalisation applied as part of auto-correct. Diacritics are kept unless `removeDiacritics` is set. */
25
31
  urduNormalization?: UrduNormalizationOptions;
26
32
  /** Common punctuation fixes, and whether a straight " becomes ”. Both on by default. */
@@ -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;AAIzE,OAAO,EAA4C,KAAK,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAatG,OAAO,EAAgC,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAMnE,yEAAyE;AACzE,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,YAAY,GAAG,KAAK,CAAC;AAEnE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,QAAQ,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AASD,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mGAAmG;IACnG,iBAAiB,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACvC,sGAAsG;IACtG,gBAAgB,CAAC,EAAE,aAAa,EAAE,CAAC;IACnC,gHAAgH;IAChH,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;IAC7C,wFAAwF;IACxF,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,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;IAC3B;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC;;;;;;;;;;;;;;;OAeG;IACH,eAAe,CAAC,EAAE,mBAAmB,CAAC;IACtC,uGAAuG;IACvG,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3E,6EAA6E;IAC7E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,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,uGAkbrB,CAAC"}
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;AAIzE,OAAO,EAA6B,KAAK,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClG,OAAO,EAA4C,KAAK,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACtG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAYpE,OAAO,EAAgC,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAMnE,yEAAyE;AACzE,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,YAAY,GAAG,KAAK,CAAC;AAEnE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,QAAQ,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAWD,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mGAAmG;IACnG,iBAAiB,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACvC,sGAAsG;IACtG,gBAAgB,CAAC,EAAE,aAAa,EAAE,CAAC;IACnC,yIAAyI;IACzI,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;IACrC,iHAAiH;IACjH,oBAAoB,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;IAC9C,gHAAgH;IAChH,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;IAC7C,wFAAwF;IACxF,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,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;IAC3B;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC;;;;;;;;;;;;;;;OAeG;IACH,eAAe,CAAC,EAAE,mBAAmB,CAAC;IACtC,uGAAuG;IACvG,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3E,6EAA6E;IAC7E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,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,uGAubrB,CAAC"}
@@ -33,6 +33,7 @@ import { localStorageAutoCorrectStore } from './autocorrect/autoCorrectStores.js
33
33
  import { SpellcheckPanel } from './components/SpellcheckPanel.js';
34
34
  import { SpellHighlightPlugin } from './spellcheck/SpellHighlightPlugin.js';
35
35
  import { AutocompletePlugin } from './autocomplete/AutocompletePlugin.js';
36
+ import { dictionaryCompletionStore } from './autocomplete/completionStores.js';
36
37
  import { loadUserWords, localStorageUserWordStore } from './spellcheck/userWords.js';
37
38
  import { LeaveDialog } from './components/LeaveDialog.js';
38
39
  import { clearDraft, createDraftId, hasDraftContent, pruneDrafts, writeDraft, } from './persistence/draftStorage.js';
@@ -44,6 +45,8 @@ const DEFAULT_AUTOSAVE_MAX_BYTES = 2000000;
44
45
  const DEFAULT_AUTOSAVE_MAX_DRAFTS = 20;
45
46
  /** Used when the host passes no stores: corrections are kept in this browser. */
46
47
  const DEFAULT_AUTOCORRECT_STORES = [localStorageAutoCorrectStore()];
48
+ /** Used when the host passes no completion stores: the dictionary's words and the user's added words. */
49
+ const DEFAULT_COMPLETION_STORES = [dictionaryCompletionStore()];
47
50
  /** Used when the host passes no dictionary stores: added words are kept in this browser. */
48
51
  const DEFAULT_DICTIONARY_STORES = [localStorageUserWordStore()];
49
52
  function initialEditorStateJson(initialContent) {
@@ -52,7 +55,7 @@ function initialEditorStateJson(initialContent) {
52
55
  const state = defaultFormatRegistry.parse(initialContent.format, initialContent.value);
53
56
  return JSON.stringify(state);
54
57
  }
55
- export const EditorRoot = forwardRef(function EditorRoot({ documentId, autoCorrectStores, dictionaryStores, 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) {
58
+ export const EditorRoot = forwardRef(function EditorRoot({ documentId, autoCorrectStores, dictionaryStores, completionStores, autoCompleteLanguage = 'auto', 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) {
56
59
  const config = useMemo(() => resolveFeatureConfig(featureConfig, featurePreset), [featureConfig, featurePreset]);
57
60
  const strings = useMemo(() => getStrings(locale), [locale]);
58
61
  const resolvedPlaceholder = placeholder ?? strings.editor.placeholder;
@@ -88,6 +91,7 @@ export const EditorRoot = forwardRef(function EditorRoot({ documentId, autoCorre
88
91
  const [autoCorrectVersion, setAutoCorrectVersion] = useState(0);
89
92
  const stores = autoCorrectStores ?? DEFAULT_AUTOCORRECT_STORES;
90
93
  const wordStores = dictionaryStores ?? DEFAULT_DICTIONARY_STORES;
94
+ const resolvedCompletionStores = completionStores ?? DEFAULT_COMPLETION_STORES;
91
95
  useEffect(() => {
92
96
  void loadUserWords(wordStores);
93
97
  }, [wordStores]);
@@ -306,7 +310,7 @@ export const EditorRoot = forwardRef(function EditorRoot({ documentId, autoCorre
306
310
  const portalTargetSelector = `#${CSS.escape(portalTargetId)}`;
307
311
  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, dictionaryStores: wordStores, onAddAutoCorrect: config.language.autocorrect ? openAutoCorrectFor : undefined, 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
308
312
  ? { currentId: draftId, maxBytes: autosaveMaxBytes, adoptOnRestore: !documentId, onRestored: handleDraftRestored }
309
- : 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, dictionaryStores: wordStores, onAddAutoCorrect: config.language.autocorrect ? openAutoCorrectFor : undefined, onClose: () => setOpenPanel(null) })), config.language.autocorrect && openPanel === 'autocorrect' && (_jsx(AutoCorrectPanel, { strings: strings, dir: dir, stores: stores, initialFrom: autoCorrectPrefill?.from, initialLanguage: autoCorrectPrefill?.language, onSaved: () => setAutoCorrectVersion((v) => v + 1), onClose: () => setOpenPanel(null) }, autoCorrectPrefill?.id ?? 0)), _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.autocomplete && _jsx(AutocompletePlugin, { label: strings.autoComplete.label }), (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: () => {
313
+ : 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, dictionaryStores: wordStores, onAddAutoCorrect: config.language.autocorrect ? openAutoCorrectFor : undefined, onClose: () => setOpenPanel(null) })), config.language.autocorrect && openPanel === 'autocorrect' && (_jsx(AutoCorrectPanel, { strings: strings, dir: dir, stores: stores, initialFrom: autoCorrectPrefill?.from, initialLanguage: autoCorrectPrefill?.language, onSaved: () => setAutoCorrectVersion((v) => v + 1), onClose: () => setOpenPanel(null) }, autoCorrectPrefill?.id ?? 0)), _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.autocomplete && (_jsx(AutocompletePlugin, { label: strings.autoComplete.label, stores: resolvedCompletionStores, language: autoCompleteLanguage })), (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: () => {
310
314
  handleSave();
311
315
  settleLeave(true);
312
316
  }, onSaveDraft: () => {
@@ -1,3 +1,5 @@
1
+ import type { SpellLanguage } from '../spellcheck/spellDictionaries.js';
2
+ import type { CompletionStore } from './completionStores.js';
1
3
  /**
2
4
  * Suggests completions for the word being typed, from the dictionary's words and
3
5
  * the user's own. Shown only while typing, at the caret. Tab or Enter accepts the
@@ -5,7 +7,10 @@
5
7
  * moving the caret) dismisses it. Left-to-right text uses the English dictionary,
6
8
  * right-to-left text the Urdu one.
7
9
  */
8
- export declare function AutocompletePlugin({ label }: {
10
+ export declare function AutocompletePlugin({ label, stores, language: chosenLanguage, }: {
9
11
  label: string;
12
+ stores: CompletionStore[];
13
+ /** The language to complete in, or 'auto' to follow the block's direction. */
14
+ language: SpellLanguage | 'auto';
10
15
  }): import("react").JSX.Element | null;
11
16
  //# sourceMappingURL=AutocompletePlugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AutocompletePlugin.d.ts","sourceRoot":"","sources":["../../src/autocomplete/AutocompletePlugin.tsx"],"names":[],"mappings":"AAgCA;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,sCA4J9D"}
1
+ {"version":3,"file":"AutocompletePlugin.d.ts","sourceRoot":"","sources":["../../src/autocomplete/AutocompletePlugin.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAGrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAqB1D;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,MAAM,EACN,QAAQ,EAAE,cAAc,GACzB,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,8EAA8E;IAC9E,QAAQ,EAAE,aAAa,GAAG,MAAM,CAAC;CAClC,sCAsLA"}
@@ -3,12 +3,12 @@ import { useEffect, useRef, useState } from 'react';
3
3
  import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
4
4
  import { $getSelection, $isRangeSelection, $isTextNode, COMMAND_PRIORITY_HIGH, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND, KEY_ENTER_COMMAND, KEY_TAB_COMMAND, } from 'lexical';
5
5
  import { $replaceMatch } from '../find/findReplaceActions.js';
6
- import { getDictionaryWords } from '../spellcheck/spellDictionaries.js';
7
- import { acceptedWords, onSpellWordsChange } from '../spellcheck/userWords.js';
6
+ import { onSpellWordsChange } from '../spellcheck/userWords.js';
8
7
  import { WordIndex } from './wordIndex.js';
9
8
  /** The letters at the end of the text before the caret. */
10
9
  const WORD_END = /[\p{L}\p{M}]+$/u;
11
10
  const LETTER = /[\p{L}\p{M}]/u;
11
+ const MOVEMENT_KEYS = new Set(['ArrowLeft', 'ArrowRight', 'Home', 'End', 'PageUp', 'PageDown']);
12
12
  /** Suggestions only appear once the typed part of a word is this long. */
13
13
  const MIN_PREFIX = 2;
14
14
  /**
@@ -18,8 +18,13 @@ const MIN_PREFIX = 2;
18
18
  * moving the caret) dismisses it. Left-to-right text uses the English dictionary,
19
19
  * right-to-left text the Urdu one.
20
20
  */
21
- export function AutocompletePlugin({ label }) {
21
+ export function AutocompletePlugin({ label, stores, language: chosenLanguage, }) {
22
22
  const [editor] = useLexicalComposerContext();
23
+ // Effects run once per editor, so they read the latest props through refs.
24
+ const storesRef = useRef(stores);
25
+ storesRef.current = stores;
26
+ const languageRef = useRef(chosenLanguage);
27
+ languageRef.current = chosenLanguage;
23
28
  const [popup, setPopup] = useState(null);
24
29
  const popupRef = useRef(null);
25
30
  const typing = useRef(false);
@@ -32,10 +37,7 @@ export function AutocompletePlugin({ label }) {
32
37
  const indexFor = (language) => {
33
38
  let index = indexes.current.get(language);
34
39
  if (!index) {
35
- const words = getDictionaryWords(language);
36
- if (!words)
37
- return null;
38
- index = words.then((dictionary) => new WordIndex([...dictionary, ...acceptedWords(language)]));
40
+ index = Promise.all(storesRef.current.map((store) => store.load(language).catch(() => []))).then((lists) => new WordIndex(lists.flat()));
39
41
  indexes.current.set(language, index);
40
42
  }
41
43
  return index;
@@ -58,38 +60,56 @@ export function AutocompletePlugin({ label }) {
58
60
  const after = text.charAt(selection.anchor.offset);
59
61
  if (!prefix || prefix.length < MIN_PREFIX || (after !== '' && LETTER.test(after)))
60
62
  return;
61
- const rtl = node.getParentOrThrow().getDirection() === 'rtl';
62
- target = { prefix, language: rtl ? 'ur' : 'en', dir: rtl ? 'rtl' : 'ltr' };
63
+ target = { prefix, rtl: node.getParentOrThrow().getDirection() === 'rtl' };
63
64
  });
64
65
  if (target === null)
65
66
  return hide();
66
- const { prefix, language, dir } = target;
67
- const index = indexFor(language);
68
- if (!index)
69
- return hide();
70
- void index.then((loaded) => {
67
+ const { prefix, rtl } = target;
68
+ const language = languageRef.current === 'auto' ? (rtl ? 'ur' : 'en') : languageRef.current;
69
+ void indexFor(language).then((loaded) => {
70
+ // Same word as the open list: keep the user's highlight rather than resetting it.
71
+ const open = popupRef.current;
72
+ if (open && open.prefix === prefix && open.dir === (rtl ? 'rtl' : 'ltr'))
73
+ return;
71
74
  const items = loaded.complete(prefix);
72
75
  if (items.length === 0)
73
76
  return hide();
74
77
  const rect = caretRect(editor);
75
- show({ items, index: 0, x: rect.left, y: rect.bottom, dir });
78
+ show({ prefix, items, index: 0, selected: false, x: rect.left, y: rect.bottom, dir: rtl ? 'rtl' : 'ltr' });
76
79
  });
77
80
  };
78
- const move = (delta) => {
81
+ // Handles an arrow key for the list, and cancels the browser's own caret movement.
82
+ const move = (delta, event) => {
79
83
  const current = popupRef.current;
80
- if (!current)
84
+ // Still loading the list for the word being typed: the arrow is for the list, not the caret.
85
+ if (!current && prefixAtCaret(editor) === null)
81
86
  return false;
82
- const count = current.items.length;
83
- show({ ...current, index: (current.index + delta + count) % count });
87
+ event?.preventDefault();
88
+ if (current) {
89
+ const count = current.items.length;
90
+ show({ ...current, index: (current.index + delta + count) % count, selected: true });
91
+ }
84
92
  return true;
85
93
  };
86
94
  const onKeyDown = (event) => {
95
+ // Up, Down, Tab and Enter act on the list through their editor commands, which
96
+ // run after this listener, so the list must stay open for them.
97
+ const current = popupRef.current;
98
+ if (current && (event.key === 'Tab' || (event.key === 'Enter' && current.selected)))
99
+ return;
100
+ if ((event.key === 'ArrowUp' || event.key === 'ArrowDown') && (current || prefixAtCaret(editor) !== null))
101
+ return;
87
102
  if (event.key === 'Escape' && popupRef.current) {
88
103
  event.preventDefault();
89
104
  show(null);
90
105
  return;
91
106
  }
92
- // Printable keys and Backspace are typing; any other key (arrows, Home, ...) moves the caret.
107
+ // Left, Right, Home and End move the caret away from the word, so the list closes.
108
+ if (MOVEMENT_KEYS.has(event.key)) {
109
+ typing.current = false;
110
+ return hide();
111
+ }
112
+ // Printable keys and Backspace are typing; anything else (Enter, Tab, ...) ends the word.
93
113
  typing.current = (event.key.length === 1 && !event.ctrlKey && !event.metaKey) || event.key === 'Backspace';
94
114
  if (!typing.current)
95
115
  hide();
@@ -120,10 +140,10 @@ export function AutocompletePlugin({ label }) {
120
140
  setTimeout(refresh, 0);
121
141
  });
122
142
  const unregisterCommands = [
123
- editor.registerCommand(KEY_ARROW_DOWN_COMMAND, () => move(1), COMMAND_PRIORITY_HIGH),
124
- editor.registerCommand(KEY_ARROW_UP_COMMAND, () => move(-1), COMMAND_PRIORITY_HIGH),
143
+ editor.registerCommand(KEY_ARROW_DOWN_COMMAND, (event) => move(1, event), COMMAND_PRIORITY_HIGH),
144
+ editor.registerCommand(KEY_ARROW_UP_COMMAND, (event) => move(-1, event), COMMAND_PRIORITY_HIGH),
125
145
  editor.registerCommand(KEY_TAB_COMMAND, (event) => acceptHighlighted(event), COMMAND_PRIORITY_HIGH),
126
- editor.registerCommand(KEY_ENTER_COMMAND, (event) => acceptHighlighted(event), COMMAND_PRIORITY_HIGH),
146
+ editor.registerCommand(KEY_ENTER_COMMAND, (event) => (popupRef.current?.selected ? acceptHighlighted(event) : false), COMMAND_PRIORITY_HIGH),
127
147
  ];
128
148
  function acceptHighlighted(event) {
129
149
  const current = popupRef.current;
@@ -143,6 +163,10 @@ export function AutocompletePlugin({ label }) {
143
163
  show(null);
144
164
  };
145
165
  }, [editor]);
166
+ // The index is per store set and language; a change of stores starts again.
167
+ useEffect(() => {
168
+ indexes.current.clear();
169
+ }, [stores]);
146
170
  if (!popup)
147
171
  return null;
148
172
  return (_jsx("div", { className: "likhari-autocomplete", role: "listbox", "aria-label": label, dir: popup.dir, style: { position: 'fixed', left: popup.x, top: popup.y + 4 }, children: popup.items.map((item, i) => (_jsx("div", { role: "option", "aria-selected": i === popup.index, className: i === popup.index ? 'likhari-autocomplete-item likhari-autocomplete-item--active' : 'likhari-autocomplete-item', onMouseDown: (event) => {
@@ -150,6 +174,25 @@ export function AutocompletePlugin({ label }) {
150
174
  acceptItem(editor, item);
151
175
  }, children: item }, item))) }));
152
176
  }
177
+ /** The typed part of the word at the caret, when the caret is at its end and the word is long enough to complete. */
178
+ function prefixAtCaret(editor) {
179
+ let prefix = null;
180
+ editor.getEditorState().read(() => {
181
+ const selection = $getSelection();
182
+ if (!$isRangeSelection(selection) || !selection.isCollapsed())
183
+ return;
184
+ const node = selection.anchor.getNode();
185
+ if (!$isTextNode(node))
186
+ return;
187
+ const text = node.getTextContent();
188
+ const before = text.slice(0, selection.anchor.offset);
189
+ const word = WORD_END.exec(before)?.[0];
190
+ const after = text.charAt(selection.anchor.offset);
191
+ if (word && word.length >= MIN_PREFIX && !(after !== '' && LETTER.test(after)))
192
+ prefix = word;
193
+ });
194
+ return prefix;
195
+ }
153
196
  /** Where to put the popup: under the caret, or the editor's top-left if the caret has no position yet. */
154
197
  function caretRect(editor) {
155
198
  const selection = window.getSelection();
@@ -0,0 +1,25 @@
1
+ import { type SpellLanguage } from '../spellcheck/spellDictionaries.js';
2
+ /**
3
+ * Where autocomplete words come from. The host can pass several; their words
4
+ * are combined per language. Like the other stores, a failing store is skipped.
5
+ */
6
+ export interface CompletionStore {
7
+ readonly id: string;
8
+ load(language: SpellLanguage): Promise<string[]>;
9
+ }
10
+ /**
11
+ * The default: the dictionary's words plus the words the user has added. Nothing
12
+ * to configure; it follows whichever spelling dictionaries are registered.
13
+ */
14
+ export declare function dictionaryCompletionStore(): CompletionStore;
15
+ /**
16
+ * Words served by an API. `load` calls `GET {url}?language=xx` and expects a JSON
17
+ * array of strings.
18
+ */
19
+ export declare function apiCompletionStore(options: {
20
+ id?: string;
21
+ url: string;
22
+ headers?: Record<string, string>;
23
+ fetch?: typeof fetch;
24
+ }): CompletionStore;
25
+ //# sourceMappingURL=completionStores.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completionStores.d.ts","sourceRoot":"","sources":["../../src/autocomplete/completionStores.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAGzF;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAClD;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,eAAe,CAS3D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,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,eAAe,CAWlB"}
@@ -0,0 +1,33 @@
1
+ import { getDictionaryWords } from '../spellcheck/spellDictionaries.js';
2
+ import { acceptedWords } from '../spellcheck/userWords.js';
3
+ /**
4
+ * The default: the dictionary's words plus the words the user has added. Nothing
5
+ * to configure; it follows whichever spelling dictionaries are registered.
6
+ */
7
+ export function dictionaryCompletionStore() {
8
+ return {
9
+ id: 'dictionary',
10
+ async load(language) {
11
+ const words = getDictionaryWords(language);
12
+ const dictionary = words ? await words : [];
13
+ return [...dictionary, ...acceptedWords(language)];
14
+ },
15
+ };
16
+ }
17
+ /**
18
+ * Words served by an API. `load` calls `GET {url}?language=xx` and expects a JSON
19
+ * array of strings.
20
+ */
21
+ export function apiCompletionStore(options) {
22
+ const doFetch = options.fetch ?? fetch;
23
+ return {
24
+ id: options.id ?? 'api',
25
+ async load(language) {
26
+ const response = await doFetch(`${options.url}?language=${encodeURIComponent(language)}`, { headers: options.headers });
27
+ if (!response.ok)
28
+ throw new Error(`Completion API failed: ${response.status}`);
29
+ const data = await response.json();
30
+ return Array.isArray(data) ? data.filter((w) => typeof w === 'string') : [];
31
+ },
32
+ };
33
+ }
package/dist/index.d.ts CHANGED
@@ -16,4 +16,6 @@ export type { UrduNormalizationOptions } from './normalization/urduNormalize.js'
16
16
  export type { PunctuationOptions, PunctuationRule } from './autocorrect/punctuationRules.js';
17
17
  export { localStorageUserWordStore, apiUserWordStore, ignoreWord, addUserWord, onSpellWordsChange } from './spellcheck/userWords.js';
18
18
  export type { UserWordStore } from './spellcheck/userWords.js';
19
+ export { dictionaryCompletionStore, apiCompletionStore } from './autocomplete/completionStores.js';
20
+ export type { CompletionStore } from './autocomplete/completionStores.js';
19
21
  //# sourceMappingURL=index.d.ts.map
@@ -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;AAC1F,OAAO,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,UAAU,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAClI,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,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;AAC1F,OAAO,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,UAAU,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAClI,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAChG,YAAY,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC"}
package/dist/index.js CHANGED
@@ -7,3 +7,4 @@ export { registerSpellDictionary, hasSpellDictionary } from './spellcheck/spellD
7
7
  export { localStorageAutoCorrectStore, apiAutoCorrectStore, fileAutoCorrectStore } from './autocorrect/autoCorrectStores.js';
8
8
  export { normalizeUrdu, normalizeUrduCharacters, removeUrduDiacritics, replaceUrduDigits } from './normalization/urduNormalize.js';
9
9
  export { localStorageUserWordStore, apiUserWordStore, ignoreWord, addUserWord, onSpellWordsChange } from './spellcheck/userWords.js';
10
+ export { dictionaryCompletionStore, apiCompletionStore } from './autocomplete/completionStores.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inshapardaz/likhari-react",
3
- "version": "0.1.25-dev.224",
3
+ "version": "0.1.25-dev.228",
4
4
  "description": "React implementation of the Likhari editor, built on @lexical/react.",
5
5
  "license": "MIT",
6
6
  "repository": {