@inshapardaz/likhari-react 0.1.21 → 0.1.22-dev.218

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.
Files changed (55) hide show
  1. package/dist/EditorRoot.d.ts +12 -0
  2. package/dist/EditorRoot.d.ts.map +1 -1
  3. package/dist/EditorRoot.js +21 -4
  4. package/dist/autocorrect/AutoCorrectPlugin.d.ts +27 -0
  5. package/dist/autocorrect/AutoCorrectPlugin.d.ts.map +1 -0
  6. package/dist/autocorrect/AutoCorrectPlugin.js +108 -0
  7. package/dist/autocorrect/autoCorrectActions.d.ts +35 -0
  8. package/dist/autocorrect/autoCorrectActions.d.ts.map +1 -0
  9. package/dist/autocorrect/autoCorrectActions.js +182 -0
  10. package/dist/autocorrect/autoCorrectStores.d.ts +52 -0
  11. package/dist/autocorrect/autoCorrectStores.d.ts.map +1 -0
  12. package/dist/autocorrect/autoCorrectStores.js +115 -0
  13. package/dist/autocorrect/punctuationRules.d.ts +26 -0
  14. package/dist/autocorrect/punctuationRules.d.ts.map +1 -0
  15. package/dist/autocorrect/punctuationRules.js +53 -0
  16. package/dist/cleanup/textCleanupActions.d.ts +8 -0
  17. package/dist/cleanup/textCleanupActions.d.ts.map +1 -0
  18. package/dist/cleanup/textCleanupActions.js +28 -0
  19. package/dist/components/AutoCorrectPanel.d.ts +15 -0
  20. package/dist/components/AutoCorrectPanel.d.ts.map +1 -0
  21. package/dist/components/AutoCorrectPanel.js +44 -0
  22. package/dist/components/SpellcheckPanel.d.ts +15 -0
  23. package/dist/components/SpellcheckPanel.d.ts.map +1 -0
  24. package/dist/components/SpellcheckPanel.js +91 -0
  25. package/dist/components/Toolbar.d.ts +10 -1
  26. package/dist/components/Toolbar.d.ts.map +1 -1
  27. package/dist/components/Toolbar.js +125 -109
  28. package/dist/find/findReplaceActions.d.ts +13 -0
  29. package/dist/find/findReplaceActions.d.ts.map +1 -1
  30. package/dist/find/findReplaceActions.js +2 -2
  31. package/dist/i18n/strings.d.ts +34 -0
  32. package/dist/i18n/strings.d.ts.map +1 -1
  33. package/dist/i18n/strings.js +107 -5
  34. package/dist/index.d.ts +7 -0
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +3 -0
  37. package/dist/normalization/urduNormalize.d.ts +24 -0
  38. package/dist/normalization/urduNormalize.d.ts.map +1 -0
  39. package/dist/normalization/urduNormalize.js +116 -0
  40. package/dist/spellcheck/SpellHighlightPlugin.d.ts +8 -0
  41. package/dist/spellcheck/SpellHighlightPlugin.d.ts.map +1 -0
  42. package/dist/spellcheck/SpellHighlightPlugin.js +60 -0
  43. package/dist/spellcheck/spellDictionaries.d.ts.map +1 -1
  44. package/dist/spellcheck/spellDictionaries.js +8 -1
  45. package/dist/spellcheck/spellcheckActions.d.ts +21 -0
  46. package/dist/spellcheck/spellcheckActions.d.ts.map +1 -0
  47. package/dist/spellcheck/spellcheckActions.js +38 -0
  48. package/dist/spellcheck/spellingMenu.d.ts +15 -0
  49. package/dist/spellcheck/spellingMenu.d.ts.map +1 -0
  50. package/dist/spellcheck/spellingMenu.js +27 -0
  51. package/dist/spellcheck/userWords.d.ts +39 -0
  52. package/dist/spellcheck/userWords.d.ts.map +1 -0
  53. package/dist/spellcheck/userWords.js +132 -0
  54. package/dist/theme/editor.css +185 -0
  55. package/package.json +1 -1
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Common Urdu punctuation and character fixes, as a snapshot of
3
+ * https://api.nawishta.co.uk/tools/ur/spellchecker/punctuation (fetched 2026-10-04).
4
+ * Bundled so the editor works offline and does not depend on that service;
5
+ * refresh the list when the service changes.
6
+ */
7
+ export interface PunctuationRule {
8
+ incorrect: string;
9
+ correct: string;
10
+ /** When true, the match must be a whole word (not part of a longer one). */
11
+ completeWord: boolean;
12
+ }
13
+ export declare const URDU_PUNCTUATION_RULES: PunctuationRule[];
14
+ export interface PunctuationOptions {
15
+ /** Apply the punctuation fixes. On by default. */
16
+ enabled?: boolean;
17
+ /** Replace a straight double quote (") with a closing curly quote (”). On by default. */
18
+ straightDoubleQuote?: boolean;
19
+ /** Space after Urdu punctuation and remove spaces before it, as urduhack's punctuations_space. Off by default. */
20
+ spacing?: boolean;
21
+ }
22
+ /** The rules in use for the options, longest match first, so `۔"` wins over `"`. */
23
+ export declare function activePunctuationRules(options?: PunctuationOptions): PunctuationRule[];
24
+ /** Spaces after Urdu punctuation (not inside numbers), and none before it. */
25
+ export declare function spacePunctuationText(text: string): string;
26
+ //# sourceMappingURL=punctuationRules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"punctuationRules.d.ts","sourceRoot":"","sources":["../../src/autocorrect/punctuationRules.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,eAAO,MAAM,sBAAsB,EAAE,eAAe,EAmCnD,CAAC;AAEF,MAAM,WAAW,kBAAkB;IACjC,kDAAkD;IAClD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yFAAyF;IACzF,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,kHAAkH;IAClH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,oFAAoF;AACpF,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,kBAAuB,GAAG,eAAe,EAAE,CAK1F;AAOD,8EAA8E;AAC9E,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD"}
@@ -0,0 +1,53 @@
1
+ export const URDU_PUNCTUATION_RULES = [
2
+ { incorrect: ".", correct: "۔", completeWord: false },
3
+ { incorrect: ",", correct: "،", completeWord: false },
4
+ { incorrect: " ۔", correct: "۔", completeWord: false },
5
+ { incorrect: " - ", correct: "۔ ", completeWord: false },
6
+ { incorrect: " ،", correct: "،", completeWord: false },
7
+ { incorrect: "?", correct: "؟", completeWord: false },
8
+ { incorrect: " ؟", correct: "؟", completeWord: false },
9
+ { incorrect: " !", correct: "!", completeWord: false },
10
+ { incorrect: "( ", correct: "(", completeWord: false },
11
+ { incorrect: " (", correct: "(", completeWord: false },
12
+ { incorrect: "ه", correct: "ہ", completeWord: false },
13
+ { incorrect: "ک", correct: "ک", completeWord: false },
14
+ { incorrect: "ئو", correct: "ؤ", completeWord: false },
15
+ { incorrect: "’’", correct: "”", completeWord: false },
16
+ { incorrect: "‘‘", correct: "“", completeWord: false },
17
+ { incorrect: "…", correct: "۔۔۔", completeWord: false },
18
+ { incorrect: "——", correct: "۔۔۔", completeWord: false },
19
+ { incorrect: "—", correct: "۔۔۔", completeWord: false },
20
+ { incorrect: " ۔۔۔", correct: "۔۔۔ ", completeWord: false },
21
+ { incorrect: "،،", correct: "“", completeWord: false },
22
+ { incorrect: "۔\"", correct: "۔“", completeWord: false },
23
+ { incorrect: "، \"", correct: "۔ ”", completeWord: false },
24
+ { incorrect: "؟\"", correct: "؟“", completeWord: false },
25
+ { incorrect: "!\"", correct: "!“", completeWord: false },
26
+ { incorrect: "-", correct: "۔", completeWord: false },
27
+ { incorrect: " ، ", correct: "، ", completeWord: false },
28
+ { incorrect: " ۔", correct: "۔ ", completeWord: false },
29
+ { incorrect: " : ", correct: ": ", completeWord: false },
30
+ { incorrect: "آ", correct: "آ", completeWord: false },
31
+ { incorrect: "ؤ", correct: "ؤ", completeWord: false },
32
+ { incorrect: " ِ", correct: "ِ", completeWord: false },
33
+ { incorrect: " ً", correct: "ً", completeWord: false },
34
+ { incorrect: " :", correct: ":", completeWord: false },
35
+ { incorrect: "” ", correct: "”", completeWord: false },
36
+ ];
37
+ /** The rules in use for the options, longest match first, so `۔"` wins over `"`. */
38
+ export function activePunctuationRules(options = {}) {
39
+ if (options.enabled === false)
40
+ return [];
41
+ const rules = URDU_PUNCTUATION_RULES.filter((rule) => rule.incorrect !== rule.correct);
42
+ if (options.straightDoubleQuote !== false)
43
+ rules.push({ incorrect: '"', correct: '”', completeWord: false });
44
+ return rules.sort((a, b) => b.incorrect.length - a.incorrect.length);
45
+ }
46
+ /** Space after Urdu punctuation, when the next character is not punctuation, a digit or a space. */
47
+ const SPACE_AFTER_PUNCTUATION = /(?<=[؛،٫؟۔٪])(?=[^؛،٫؟۔٪0-9 \n])/gu;
48
+ /** Spaces before Urdu punctuation. */
49
+ const SPACE_BEFORE_PUNCTUATION = /\s+([؛،٫؟۔٪])/gu;
50
+ /** Spaces after Urdu punctuation (not inside numbers), and none before it. */
51
+ export function spacePunctuationText(text) {
52
+ return text.replace(SPACE_AFTER_PUNCTUATION, ' ').replace(SPACE_BEFORE_PUNCTUATION, '$1');
53
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Tidies the whole document in place: Unicode NFC normalisation (Arabic-script
3
+ * text can store the same visible letters in different code points), collapsed
4
+ * repeated spaces, and empty text nodes removed. Formatting is kept, since each
5
+ * text node is only changed, never replaced. Returns how many text nodes changed.
6
+ */
7
+ export declare function $cleanUpText(): number;
8
+ //# sourceMappingURL=textCleanupActions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"textCleanupActions.d.ts","sourceRoot":"","sources":["../../src/cleanup/textCleanupActions.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAMrC"}
@@ -0,0 +1,28 @@
1
+ import { $getRoot } from 'lexical';
2
+ /** Runs of two or more spaces or tabs, collapsed to one space. */
3
+ const REPEATED_SPACE = /[ \t]{2,}/g;
4
+ /**
5
+ * Tidies the whole document in place: Unicode NFC normalisation (Arabic-script
6
+ * text can store the same visible letters in different code points), collapsed
7
+ * repeated spaces, and empty text nodes removed. Formatting is kept, since each
8
+ * text node is only changed, never replaced. Returns how many text nodes changed.
9
+ */
10
+ export function $cleanUpText() {
11
+ let changed = 0;
12
+ for (const node of $getRoot().getAllTextNodes()) {
13
+ if (cleanNode(node))
14
+ changed += 1;
15
+ }
16
+ return changed;
17
+ }
18
+ function cleanNode(node) {
19
+ const text = node.getTextContent();
20
+ const cleaned = text.normalize('NFC').replace(REPEATED_SPACE, ' ');
21
+ if (cleaned === text)
22
+ return false;
23
+ if (cleaned === '')
24
+ node.remove();
25
+ else
26
+ node.setTextContent(cleaned);
27
+ return true;
28
+ }
@@ -0,0 +1,15 @@
1
+ import type { Strings } from '../i18n/strings.js';
2
+ import { type AutoCorrectStore } from '../autocorrect/autoCorrectStores.js';
3
+ /**
4
+ * Adds one correction (a typed word and its replacement) to the first store
5
+ * that accepts writes. Saving bumps the editor's correction version, so the new
6
+ * entry applies to the next word the user types.
7
+ */
8
+ export declare function AutoCorrectPanel({ strings, dir, stores, onSaved, onClose, }: {
9
+ strings: Strings;
10
+ dir: 'ltr' | 'rtl';
11
+ stores: AutoCorrectStore[];
12
+ onSaved: () => void;
13
+ onClose: () => void;
14
+ }): import("react").JSX.Element;
15
+ //# sourceMappingURL=AutoCorrectPanel.d.ts.map
@@ -0,0 +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;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"}
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { IconX } from '@tabler/icons-react';
4
+ import { appendAutoCorrection } from '../autocorrect/autoCorrectStores.js';
5
+ const LANGUAGES = [
6
+ { value: 'en', label: 'English' },
7
+ { value: 'ur', label: 'اردو' },
8
+ { value: 'pa-shahmukhi', label: 'پنجابی (شاہ مکھی)' },
9
+ ];
10
+ /**
11
+ * Adds one correction (a typed word and its replacement) to the first store
12
+ * that accepts writes. Saving bumps the editor's correction version, so the new
13
+ * entry applies to the next word the user types.
14
+ */
15
+ export function AutoCorrectPanel({ strings, dir, stores, onSaved, onClose, }) {
16
+ const t = strings.autoCorrect;
17
+ const [language, setLanguage] = useState('en');
18
+ const [from, setFrom] = useState('');
19
+ const [to, setTo] = useState('');
20
+ const [status, setStatus] = useState('idle');
21
+ const save = async () => {
22
+ const entry = { from: from.trim(), to: to.trim() };
23
+ if (entry.from === '' || entry.to === '' || entry.from === entry.to)
24
+ return;
25
+ try {
26
+ const written = await appendAutoCorrection(stores, language, entry);
27
+ if (!written) {
28
+ setStatus('nowhere');
29
+ return;
30
+ }
31
+ setStatus('saved');
32
+ setFrom('');
33
+ setTo('');
34
+ onSaved();
35
+ }
36
+ catch {
37
+ setStatus('failed');
38
+ }
39
+ };
40
+ return (_jsxs("div", { className: "likhari-autocorrect-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("label", { className: "likhari-autocorrect-field", children: [t.language, _jsx("select", { value: language, onChange: (e) => {
41
+ setLanguage(e.target.value);
42
+ setStatus('idle');
43
+ }, children: LANGUAGES.map((l) => (_jsx("option", { value: l.value, children: l.label }, l.value))) })] }), _jsxs("label", { className: "likhari-autocorrect-field", children: [t.from, _jsx("input", { value: from, onChange: (e) => setFrom(e.target.value), placeholder: t.fromPlaceholder })] }), _jsxs("label", { className: "likhari-autocorrect-field", children: [t.to, _jsx("input", { value: to, onChange: (e) => setTo(e.target.value), placeholder: t.toPlaceholder })] }), _jsxs("div", { className: "likhari-autocorrect-actions", children: [_jsx("button", { type: "button", onClick: save, disabled: from.trim() === '' || to.trim() === '', children: t.save }), _jsxs("span", { className: "likhari-spell-note", "aria-live": "polite", children: [status === 'saved' && t.saved, status === 'nowhere' && t.noWritableStore, status === 'failed' && t.failed] })] })] }));
44
+ }
@@ -0,0 +1,15 @@
1
+ import type { Strings } from '../i18n/strings.js';
2
+ import { type UserWordStore } from '../spellcheck/userWords.js';
3
+ /**
4
+ * Steps through the misspelled words one at a time, like the find widget.
5
+ * Each word is selected in the editor as it comes up, and its suggestions
6
+ * can replace it. Checking reads the document on demand, so it never flags
7
+ * words while the user is still typing.
8
+ */
9
+ export declare function SpellcheckPanel({ strings, dir, dictionaryStores, onClose, }: {
10
+ strings: Strings;
11
+ dir: 'ltr' | 'rtl';
12
+ dictionaryStores: UserWordStore[];
13
+ onClose: () => void;
14
+ }): import("react").JSX.Element;
15
+ //# sourceMappingURL=SpellcheckPanel.d.ts.map
@@ -0,0 +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;AAI/C,OAAO,EAA2B,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAgBtF;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,EAC9B,OAAO,EACP,GAAG,EACH,gBAAgB,EAChB,OAAO,GACR,EAAE;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,KAAK,GAAG,KAAK,CAAC;IACnB,gBAAgB,EAAE,aAAa,EAAE,CAAC;IAClC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,+BAgKA"}
@@ -0,0 +1,91 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
4
+ import { IconChevronDown, IconX } from '@tabler/icons-react';
5
+ import { $replaceMatch, $selectMatch } from '../find/findReplaceActions.js';
6
+ import { $collectMisspellings } from '../spellcheck/spellcheckActions.js';
7
+ import { getSpeller, hasSpellDictionary } from '../spellcheck/spellDictionaries.js';
8
+ import { addUserWord, ignoreWord } from '../spellcheck/userWords.js';
9
+ const LANGUAGES = [
10
+ { value: 'en', label: 'English' },
11
+ { value: 'ur', label: 'اردو' },
12
+ { value: 'pa-shahmukhi', label: 'پنجابی (شاہ مکھی)' },
13
+ ];
14
+ const SUGGESTIONS_PER_WORD = 3;
15
+ /**
16
+ * Steps through the misspelled words one at a time, like the find widget.
17
+ * Each word is selected in the editor as it comes up, and its suggestions
18
+ * can replace it. Checking reads the document on demand, so it never flags
19
+ * words while the user is still typing.
20
+ */
21
+ export function SpellcheckPanel({ strings, dir, dictionaryStores, onClose, }) {
22
+ const [editor] = useLexicalComposerContext();
23
+ const t = strings.spellcheck;
24
+ const [language, setLanguage] = useState('en');
25
+ const [items, setItems] = useState(null);
26
+ const [index, setIndex] = useState(0);
27
+ const [checking, setChecking] = useState(false);
28
+ const current = items?.[index];
29
+ // Selects an item in the editor and focuses the editor, so the selection is
30
+ // drawn (Lexical only draws it while the editor has focus).
31
+ const show = (list, at) => {
32
+ const item = list[at];
33
+ if (!item)
34
+ return;
35
+ editor.update(() => $selectMatch(item.match), { discrete: true });
36
+ editor.getElementByKey(item.match.anchorKey)?.scrollIntoView({ block: 'center' });
37
+ editor.focus();
38
+ };
39
+ const check = async () => {
40
+ const speller = getSpeller(language);
41
+ if (!speller)
42
+ return;
43
+ setChecking(true);
44
+ const resolved = await speller;
45
+ const found = editor.getEditorState().read(() => $collectMisspellings({ ltr: resolved, rtl: resolved }));
46
+ const list = found.map((m) => ({
47
+ ...m,
48
+ suggestions: resolved.suggest(m.word).slice(0, SUGGESTIONS_PER_WORD),
49
+ }));
50
+ setItems(list);
51
+ setIndex(0);
52
+ setChecking(false);
53
+ show(list, 0);
54
+ };
55
+ const step = (delta) => {
56
+ if (!items || items.length === 0)
57
+ return;
58
+ const next = (index + delta + items.length) % items.length;
59
+ setIndex(next);
60
+ show(items, next);
61
+ };
62
+ // Replacing changes the document, so the list is checked again afterwards.
63
+ // Only a word still at the same place is replaced, in case the document
64
+ // changed since the check.
65
+ const replace = async (item, suggestion) => {
66
+ const speller = getSpeller(language);
67
+ if (!speller)
68
+ return;
69
+ const resolved = await speller;
70
+ editor.update(() => {
71
+ const match = $collectMisspellings({ ltr: resolved, rtl: resolved }).find((m) => m.word === item.word && m.match.anchorKey === item.match.anchorKey && m.match.anchorOffset === item.match.anchorOffset);
72
+ if (match)
73
+ $replaceMatch(match.match, suggestion);
74
+ }, { discrete: true });
75
+ await check();
76
+ };
77
+ const status = items === null ? '' : items.length === 0 ? t.noMisspellings : `${index + 1} / ${items.length}`;
78
+ 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
+ setLanguage(e.target.value);
80
+ 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 }), _jsxs("div", { className: "likhari-spell-suggestions", children: [_jsx("button", { type: "button", className: "likhari-spell-suggestion", onClick: () => {
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 })] }), 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
+ }
@@ -1,12 +1,21 @@
1
+ import { type UserWordStore } from '../spellcheck/userWords.js';
1
2
  import { type DraftsToolbarOptions } from './DraftsDialog.js';
2
3
  import type { ResolvedEditorFeatureConfig } from '@inshapardaz/likhari-core';
3
4
  import { type FontOption } from '../fonts/index.js';
4
5
  import type { Locale } from '../i18n/strings.js';
5
6
  export interface ToolbarProps {
6
7
  config: ResolvedEditorFeatureConfig;
8
+ /** Where words added to the dictionary are saved. */
9
+ dictionaryStores?: UserWordStore[];
7
10
  /** Whether the find-and-replace widget is open, and how to toggle it (owned by EditorRoot). */
8
11
  findOpen?: boolean;
9
12
  onToggleFind?: () => void;
13
+ /** Whether the spellcheck panel is open, and how to toggle it (owned by EditorRoot). */
14
+ spellOpen?: boolean;
15
+ onToggleSpell?: () => void;
16
+ /** Whether the add-auto-correction panel is open, and how to toggle it (owned by EditorRoot). */
17
+ autoCorrectOpen?: boolean;
18
+ onToggleAutoCorrect?: () => void;
10
19
  onSave?: () => void;
11
20
  isDirty?: boolean;
12
21
  showSave?: boolean;
@@ -19,5 +28,5 @@ export interface ToolbarProps {
19
28
  /** Autosave drafts: when set, the toolbar offers a Drafts button that lists and restores them. */
20
29
  drafts?: DraftsToolbarOptions;
21
30
  }
22
- export declare function Toolbar({ config, onSave, isDirty, showSave, findOpen, onToggleFind, fontOptions, direction, locale, drafts }: ToolbarProps): import("react").JSX.Element;
31
+ export declare function Toolbar({ config, dictionaryStores, onSave, isDirty, showSave, findOpen, onToggleFind, spellOpen, onToggleSpell, autoCorrectOpen, onToggleAutoCorrect, fontOptions, direction, locale, drafts }: ToolbarProps): import("react").JSX.Element;
23
32
  //# sourceMappingURL=Toolbar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"AAqFA,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,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,WAAkC,EAAE,SAAiB,EAAE,MAAa,EAAE,MAAM,EAAE,EAAE,YAAY,+BAskCxL"}
1
+ {"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"AA2CA,OAAO,EAA2B,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAgDtF,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;AA6hBvD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,2BAA2B,CAAC;IACpC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,aAAa,EAAE,CAAC;IACnC,+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,gBAAqB,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,+BA2mC/R"}