@inshapardaz/likhari-react 0.1.49 → 0.1.51-dev.407
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/autocorrect/AutoCorrectPlugin.d.ts.map +1 -1
- package/dist/autocorrect/AutoCorrectPlugin.js +25 -5
- package/dist/autocorrect/autoCorrectActions.d.ts.map +1 -1
- package/dist/autocorrect/autoCorrectActions.js +5 -3
- package/dist/autocorrect/autoCorrectStores.d.ts +17 -1
- package/dist/autocorrect/autoCorrectStores.d.ts.map +1 -1
- package/dist/autocorrect/autoCorrectStores.js +68 -12
- package/dist/autocorrect/punctuationRules.d.ts +8 -0
- package/dist/autocorrect/punctuationRules.d.ts.map +1 -1
- package/dist/autocorrect/punctuationRules.js +13 -2
- package/dist/cleanup/textCleanupActions.d.ts +3 -2
- package/dist/cleanup/textCleanupActions.d.ts.map +1 -1
- package/dist/cleanup/textCleanupActions.js +21 -3
- package/dist/components/AutoCorrectPanel.d.ts.map +1 -1
- package/dist/components/AutoCorrectPanel.js +3 -2
- package/dist/components/Toolbar.d.ts.map +1 -1
- package/dist/components/Toolbar.js +111 -35
- package/dist/components/ToolbarSettingsPanel.d.ts +17 -0
- package/dist/components/ToolbarSettingsPanel.d.ts.map +1 -0
- package/dist/components/ToolbarSettingsPanel.js +12 -0
- package/dist/i18n/strings.d.ts +18 -0
- package/dist/i18n/strings.d.ts.map +1 -1
- package/dist/i18n/strings.js +48 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/join/joinLinesActions.d.ts.map +1 -1
- package/dist/join/joinLinesActions.js +12 -14
- package/dist/theme/editor.css +54 -0
- package/dist/toolbar/toolbarVisibility.d.ts +23 -0
- package/dist/toolbar/toolbarVisibility.d.ts.map +1 -0
- package/dist/toolbar/toolbarVisibility.js +40 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AutoCorrectPlugin.d.ts","sourceRoot":"","sources":["../../src/autocorrect/AutoCorrectPlugin.tsx"],"names":[],"mappings":"AAEA,OAAO,EAQL,KAAK,cAAc,EACpB,MAAM,SAAS,CAAC;AAWjB,OAAO,EAA0B,KAAK,kBAAkB,
|
|
1
|
+
{"version":3,"file":"AutoCorrectPlugin.d.ts","sourceRoot":"","sources":["../../src/autocorrect/AutoCorrectPlugin.tsx"],"names":[],"mappings":"AAEA,OAAO,EAQL,KAAK,cAAc,EACpB,MAAM,SAAS,CAAC;AAWjB,OAAO,EAA0B,KAAK,kBAAkB,EAAwB,MAAM,oBAAoB,CAAC;AAC3G,OAAO,EAAiD,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC3G,OAAO,EAAiB,KAAK,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAG9F,sFAAsF;AACtF,kFAAkF;AAClF,eAAO,MAAM,wBAAwB,EAAE,cAAc,CAAC,IAAI,CAA6C,CAAC;AAqBxG;;;;;;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,QAgHA"}
|
|
@@ -4,7 +4,7 @@ import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_EDITOR, BLUR_COMMAND
|
|
|
4
4
|
import { $correctDocument, $correctPunctuationBeforeCaret, $correctPunctuationDocument, $correctWordBeforeCaret, $correctWordEndingAtCaret, $preservingSelection, $spacePunctuationDocument, } from './autoCorrectActions.js';
|
|
5
5
|
import { $cleanUpText } from '../cleanup/textCleanupActions.js';
|
|
6
6
|
import { activePunctuationRules } from './punctuationRules.js';
|
|
7
|
-
import {
|
|
7
|
+
import { loadAutoCorrectEntries } from './autoCorrectStores.js';
|
|
8
8
|
import { normalizeUrdu } from '../normalization/urduNormalize.js';
|
|
9
9
|
/** Keys that end a word: a space or punctuation, in Latin and Arabic-script forms. */
|
|
10
10
|
/** Corrects every word in the document, e.g. after pasting or loading content. */
|
|
@@ -12,6 +12,21 @@ export const CORRECT_DOCUMENT_COMMAND = createCommand('CORRECT_DOCUMENT_COMMAND'
|
|
|
12
12
|
const BOUNDARY_KEYS = new Set([' ', '.', ',', ';', ':', '!', '?', '،', '؛', '؟', '۔']);
|
|
13
13
|
const LANGUAGES = ['en', 'ur', 'pa-shahmukhi'];
|
|
14
14
|
const RTL_LANGUAGES = ['ur', 'pa-shahmukhi'];
|
|
15
|
+
/** The whole-word entries (the default) as a from-to table. */
|
|
16
|
+
function wholeWordTable(entries) {
|
|
17
|
+
const table = new Map();
|
|
18
|
+
for (const entry of entries)
|
|
19
|
+
if (entry.wholeWord !== false)
|
|
20
|
+
table.set(entry.from, entry.to);
|
|
21
|
+
return table;
|
|
22
|
+
}
|
|
23
|
+
/** Entries marked "not whole word only", as substring-matching rules, longest first. */
|
|
24
|
+
function substringAutoCorrectRules(entries) {
|
|
25
|
+
return entries
|
|
26
|
+
.filter((entry) => entry.wholeWord === false)
|
|
27
|
+
.map((entry) => ({ incorrect: entry.from, correct: entry.to, completeWord: false }))
|
|
28
|
+
.sort((a, b) => b.incorrect.length - a.incorrect.length);
|
|
29
|
+
}
|
|
15
30
|
/**
|
|
16
31
|
* Corrects typed words as the user finishes them, the way the reference editor
|
|
17
32
|
* does, but from any set of stores. Corrections are loaded once per change
|
|
@@ -26,17 +41,21 @@ export function AutoCorrectPlugin({ stores, version, enabled, typing, urduNormal
|
|
|
26
41
|
normalizeOptions.current = urduNormalization;
|
|
27
42
|
const punctuationRules = useRef(activePunctuationRules(punctuation));
|
|
28
43
|
punctuationRules.current = activePunctuationRules(punctuation);
|
|
44
|
+
const combinedRules = () => [...punctuationRules.current, ...substringRules.current].sort((a, b) => b.incorrect.length - a.incorrect.length);
|
|
29
45
|
const normalizeRtl = (text) => normalizeUrdu(text, normalizeOptions.current);
|
|
30
46
|
const tables = useRef(new Map());
|
|
31
47
|
const rtlTable = useRef(new Map());
|
|
48
|
+
// Corrections marked "not whole word only" apply even inside a longer word, so they
|
|
49
|
+
// run through the same substring-matching machinery as the punctuation rules.
|
|
50
|
+
const substringRules = useRef([]);
|
|
32
51
|
useEffect(() => {
|
|
33
52
|
if (!enabled)
|
|
34
53
|
return;
|
|
35
54
|
let active = true;
|
|
36
|
-
Promise.all(LANGUAGES.map(async (language) => [language, await
|
|
55
|
+
Promise.all(LANGUAGES.map(async (language) => [language, await loadAutoCorrectEntries(stores, language)])).then((loaded) => {
|
|
37
56
|
if (!active)
|
|
38
57
|
return;
|
|
39
|
-
tables.current = new Map(loaded);
|
|
58
|
+
tables.current = new Map(loaded.map(([language, entries]) => [language, wholeWordTable(entries)]));
|
|
40
59
|
// Urdu and Shahmukhi share right-to-left blocks, so both apply there; Urdu wins a clash.
|
|
41
60
|
const combined = new Map();
|
|
42
61
|
for (const language of RTL_LANGUAGES) {
|
|
@@ -45,6 +64,7 @@ export function AutoCorrectPlugin({ stores, version, enabled, typing, urduNormal
|
|
|
45
64
|
combined.set(from, to);
|
|
46
65
|
}
|
|
47
66
|
rtlTable.current = combined;
|
|
67
|
+
substringRules.current = loaded.flatMap(([, entries]) => substringAutoCorrectRules(entries));
|
|
48
68
|
});
|
|
49
69
|
return () => {
|
|
50
70
|
active = false;
|
|
@@ -59,7 +79,7 @@ export function AutoCorrectPlugin({ stores, version, enabled, typing, urduNormal
|
|
|
59
79
|
$cleanUpText();
|
|
60
80
|
if (punctuation?.spacing)
|
|
61
81
|
$spacePunctuationDocument();
|
|
62
|
-
$correctPunctuationDocument(
|
|
82
|
+
$correctPunctuationDocument(combinedRules());
|
|
63
83
|
$correctDocument(tables.current.get('en') ?? new Map(), rtlTable.current, normalizeRtl);
|
|
64
84
|
});
|
|
65
85
|
}, { discrete: true });
|
|
@@ -81,7 +101,7 @@ export function AutoCorrectPlugin({ stores, version, enabled, typing, urduNormal
|
|
|
81
101
|
if (!$isRangeSelection(selection))
|
|
82
102
|
return;
|
|
83
103
|
// Punctuation can end in any character (e.g. the letter ه), so every key is checked.
|
|
84
|
-
$correctPunctuationBeforeCaret(
|
|
104
|
+
$correctPunctuationBeforeCaret(combinedRules());
|
|
85
105
|
if (!boundary)
|
|
86
106
|
return;
|
|
87
107
|
const block = selection.anchor.getNode().getTopLevelElement();
|
|
@@ -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,
|
|
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,CAsBhF;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,MAAM,CA4B5E;AAID;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,IAAI,GAAG,IAAI,CAoB7D;AAED,+FAA+F;AAC/F,wBAAgB,yBAAyB,IAAI,MAAM,CAWlD;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAuBnH"}
|
|
@@ -100,10 +100,11 @@ export function $correctPunctuationBeforeCaret(rules) {
|
|
|
100
100
|
if (!rule)
|
|
101
101
|
return false;
|
|
102
102
|
const start = before.length - rule.incorrect.length;
|
|
103
|
-
|
|
103
|
+
const replacement = rule.resolve ? rule.resolve(before.slice(0, start)) : rule.correct;
|
|
104
|
+
$replaceMatch({ anchorKey: node.getKey(), anchorOffset: start, focusKey: node.getKey(), focusOffset: before.length }, replacement);
|
|
104
105
|
const after = $getSelection();
|
|
105
106
|
if ($isRangeSelection(after)) {
|
|
106
|
-
const caret = start +
|
|
107
|
+
const caret = start + replacement.length;
|
|
107
108
|
after.anchor.set(node.getKey(), caret, 'text');
|
|
108
109
|
after.focus.set(node.getKey(), caret, 'text');
|
|
109
110
|
}
|
|
@@ -121,7 +122,8 @@ export function $correctPunctuationDocument(rules) {
|
|
|
121
122
|
for (const rule of rules) {
|
|
122
123
|
let index = text.indexOf(rule.incorrect);
|
|
123
124
|
while (index !== -1) {
|
|
124
|
-
|
|
125
|
+
const correct = rule.resolve ? rule.resolve(text.slice(0, index)) : rule.correct;
|
|
126
|
+
candidates.push({ start: index, end: index + rule.incorrect.length, correct });
|
|
125
127
|
index = text.indexOf(rule.incorrect, index + 1);
|
|
126
128
|
}
|
|
127
129
|
}
|
|
@@ -3,6 +3,8 @@ import type { SpellLanguage } from '../spellcheck/spellDictionaries.js';
|
|
|
3
3
|
export interface AutoCorrectEntry {
|
|
4
4
|
from: string;
|
|
5
5
|
to: string;
|
|
6
|
+
/** Whole word only (default), or applied even inside a longer word. */
|
|
7
|
+
wholeWord?: boolean;
|
|
6
8
|
}
|
|
7
9
|
/**
|
|
8
10
|
* Where auto-corrections come from and go. The host supplies any number of
|
|
@@ -45,7 +47,21 @@ export declare function fileAutoCorrectStore(options: {
|
|
|
45
47
|
url: string;
|
|
46
48
|
fetch?: typeof fetch;
|
|
47
49
|
}): AutoCorrectStore;
|
|
48
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* The bundled Urdu auto-correct corpus from
|
|
52
|
+
* https://api.nawishta.co.uk/tools/ur/spellchecker/autocorrect, which serves a
|
|
53
|
+
* fixed Urdu list regardless of the `language` requested — so this store only
|
|
54
|
+
* answers for `'ur'`. Read-only: it has no documented endpoint for adding
|
|
55
|
+
* entries, so pair it with a writable store (e.g. `localStorageAutoCorrectStore`)
|
|
56
|
+
* for corrections added through the editor.
|
|
57
|
+
*/
|
|
58
|
+
export declare function nawishtaAutoCorrectStore(options?: {
|
|
59
|
+
url?: string;
|
|
60
|
+
fetch?: typeof fetch;
|
|
61
|
+
}): AutoCorrectStore;
|
|
62
|
+
/** Every correction for one language, merged across stores (the first listed wins a clash). Failing stores are skipped. */
|
|
63
|
+
export declare function loadAutoCorrectEntries(stores: AutoCorrectStore[], language: SpellLanguage): Promise<AutoCorrectEntry[]>;
|
|
64
|
+
/** The whole-word corrections for one language, merged across stores, as a from-to table. */
|
|
49
65
|
export declare function loadAutoCorrections(stores: AutoCorrectStore[], language: SpellLanguage): Promise<Map<string, string>>;
|
|
50
66
|
/** Saves a correction to the first store that accepts writes. Returns false if none does. */
|
|
51
67
|
export declare function appendAutoCorrection(stores: AutoCorrectStore[], language: SpellLanguage, entry: AutoCorrectEntry): Promise<boolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autoCorrectStores.d.ts","sourceRoot":"","sources":["../../src/autocorrect/autoCorrectStores.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAErE,8DAA8D;AAC9D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"autoCorrectStores.d.ts","sourceRoot":"","sources":["../../src/autocorrect/autoCorrectStores.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAErE,8DAA8D;AAC9D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,uEAAuE;IACvE,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,IAAI,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAC3D,MAAM,CAAC,CAAC,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1E;AAmBD;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,gBAAgB,CA6B7F;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAC3C,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,gBAAgB,CAmBnB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;CAAE,GAAG,gBAAgB,CAoBlH;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;CAAO,GAAG,gBAAgB,CAwC/G;AAED,2HAA2H;AAC3H,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,EAAE,EAAE,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAa7H;AAED,6FAA6F;AAC7F,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,gBAAgB,EAAE,EAAE,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAK3H;AAED,6FAA6F;AAC7F,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,gBAAgB,EAAE,EAAE,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAKzI"}
|
|
@@ -4,7 +4,8 @@ function isEntry(value) {
|
|
|
4
4
|
value !== null &&
|
|
5
5
|
typeof value.from === 'string' &&
|
|
6
6
|
typeof value.to === 'string' &&
|
|
7
|
-
value.from !== ''
|
|
7
|
+
value.from !== '' &&
|
|
8
|
+
(value.wholeWord === undefined || typeof value.wholeWord === 'boolean'));
|
|
8
9
|
}
|
|
9
10
|
/**
|
|
10
11
|
* Corrections kept in this browser's localStorage. Storage can be disabled or
|
|
@@ -27,11 +28,11 @@ export function localStorageAutoCorrectStore(options = {}) {
|
|
|
27
28
|
async load(language) {
|
|
28
29
|
return readAll()
|
|
29
30
|
.filter((e) => e.language === language)
|
|
30
|
-
.map(({ from, to }) => ({ from, to }));
|
|
31
|
+
.map(({ from, to, wholeWord }) => ({ from, to, wholeWord }));
|
|
31
32
|
},
|
|
32
33
|
async append(language, entry) {
|
|
33
34
|
const all = readAll().filter((e) => !(e.language === language && e.from === entry.from));
|
|
34
|
-
all.push({ language, from: entry.from, to: entry.to });
|
|
35
|
+
all.push({ language, from: entry.from, to: entry.to, wholeWord: entry.wholeWord });
|
|
35
36
|
try {
|
|
36
37
|
localStorage.setItem(key, JSON.stringify(all));
|
|
37
38
|
}
|
|
@@ -55,13 +56,13 @@ export function apiAutoCorrectStore(options) {
|
|
|
55
56
|
if (!response.ok)
|
|
56
57
|
throw new Error(`Auto-correct API failed: ${response.status}`);
|
|
57
58
|
const data = await response.json();
|
|
58
|
-
return Array.isArray(data) ? data.filter(isEntry).map(({ from, to }) => ({ from, to })) : [];
|
|
59
|
+
return Array.isArray(data) ? data.filter(isEntry).map(({ from, to, wholeWord }) => ({ from, to, wholeWord })) : [];
|
|
59
60
|
},
|
|
60
61
|
async append(language, entry) {
|
|
61
62
|
const response = await doFetch(options.url, {
|
|
62
63
|
method: 'POST',
|
|
63
64
|
headers: { 'Content-Type': 'application/json', ...options.headers },
|
|
64
|
-
body: JSON.stringify({ language, from: entry.from, to: entry.to }),
|
|
65
|
+
body: JSON.stringify({ language, from: entry.from, to: entry.to, wholeWord: entry.wholeWord }),
|
|
65
66
|
});
|
|
66
67
|
if (!response.ok)
|
|
67
68
|
throw new Error(`Auto-correct API rejected the entry: ${response.status}`);
|
|
@@ -88,21 +89,76 @@ export function fileAutoCorrectStore(options) {
|
|
|
88
89
|
id: options.id ?? 'file',
|
|
89
90
|
readOnly: true,
|
|
90
91
|
async load(language) {
|
|
91
|
-
return (await all())
|
|
92
|
+
return (await all())
|
|
93
|
+
.filter((e) => e.language === language)
|
|
94
|
+
.map(({ from, to, wholeWord }) => ({ from, to, wholeWord }));
|
|
92
95
|
},
|
|
93
96
|
};
|
|
94
97
|
}
|
|
95
|
-
/**
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
/**
|
|
99
|
+
* The bundled Urdu auto-correct corpus from
|
|
100
|
+
* https://api.nawishta.co.uk/tools/ur/spellchecker/autocorrect, which serves a
|
|
101
|
+
* fixed Urdu list regardless of the `language` requested — so this store only
|
|
102
|
+
* answers for `'ur'`. Read-only: it has no documented endpoint for adding
|
|
103
|
+
* entries, so pair it with a writable store (e.g. `localStorageAutoCorrectStore`)
|
|
104
|
+
* for corrections added through the editor.
|
|
105
|
+
*/
|
|
106
|
+
export function nawishtaAutoCorrectStore(options = {}) {
|
|
107
|
+
const url = options.url ?? 'https://api.nawishta.co.uk/tools/ur/spellchecker/autocorrect';
|
|
108
|
+
const doFetch = options.fetch ?? fetch;
|
|
109
|
+
let cached = null;
|
|
110
|
+
function isNawishtaEntry(value) {
|
|
111
|
+
return (typeof value === 'object' &&
|
|
112
|
+
value !== null &&
|
|
113
|
+
typeof value.incorrectText === 'string' &&
|
|
114
|
+
typeof value.correctText === 'string' &&
|
|
115
|
+
value.incorrectText !== '');
|
|
116
|
+
}
|
|
117
|
+
const load = () => {
|
|
118
|
+
cached ?? (cached = doFetch(url).then(async (response) => {
|
|
119
|
+
if (!response.ok)
|
|
120
|
+
throw new Error(`Nawishta auto-correct API failed: ${response.status}`);
|
|
121
|
+
const data = await response.json();
|
|
122
|
+
return Array.isArray(data)
|
|
123
|
+
? data
|
|
124
|
+
.filter(isNawishtaEntry)
|
|
125
|
+
.map(({ incorrectText, correctText, completeWord }) => ({ from: incorrectText, to: correctText, wholeWord: completeWord }))
|
|
126
|
+
: [];
|
|
127
|
+
}));
|
|
128
|
+
return cached;
|
|
129
|
+
};
|
|
130
|
+
return {
|
|
131
|
+
id: 'nawishta',
|
|
132
|
+
readOnly: true,
|
|
133
|
+
async load(language) {
|
|
134
|
+
return language === 'ur' ? load() : [];
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/** Every correction for one language, merged across stores (the first listed wins a clash). Failing stores are skipped. */
|
|
139
|
+
export async function loadAutoCorrectEntries(stores, language) {
|
|
140
|
+
const seen = new Set();
|
|
141
|
+
const merged = [];
|
|
98
142
|
const results = await Promise.allSettled(stores.map((store) => store.load(language)));
|
|
99
143
|
results.forEach((result) => {
|
|
100
144
|
if (result.status !== 'fulfilled')
|
|
101
145
|
return;
|
|
102
|
-
for (const entry of result.value)
|
|
103
|
-
if (
|
|
104
|
-
|
|
146
|
+
for (const entry of result.value) {
|
|
147
|
+
if (seen.has(entry.from))
|
|
148
|
+
continue;
|
|
149
|
+
seen.add(entry.from);
|
|
150
|
+
merged.push(entry);
|
|
151
|
+
}
|
|
105
152
|
});
|
|
153
|
+
return merged;
|
|
154
|
+
}
|
|
155
|
+
/** The whole-word corrections for one language, merged across stores, as a from-to table. */
|
|
156
|
+
export async function loadAutoCorrections(stores, language) {
|
|
157
|
+
const entries = await loadAutoCorrectEntries(stores, language);
|
|
158
|
+
const table = new Map();
|
|
159
|
+
for (const entry of entries)
|
|
160
|
+
if (entry.wholeWord !== false)
|
|
161
|
+
table.set(entry.from, entry.to);
|
|
106
162
|
return table;
|
|
107
163
|
}
|
|
108
164
|
/** Saves a correction to the first store that accepts writes. Returns false if none does. */
|
|
@@ -9,6 +9,14 @@ export interface PunctuationRule {
|
|
|
9
9
|
correct: string;
|
|
10
10
|
/** When true, the match must be a whole word (not part of a longer one). */
|
|
11
11
|
completeWord: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* When set, chooses the replacement from the text immediately before the
|
|
14
|
+
* match instead of using the fixed `correct` form. Used for the straight
|
|
15
|
+
* double quote, which opens or closes depending on what comes before it —
|
|
16
|
+
* macOS's native text substitution already does this, so without it the
|
|
17
|
+
* same editor behaves differently depending on OS.
|
|
18
|
+
*/
|
|
19
|
+
resolve?: (before: string) => string;
|
|
12
20
|
}
|
|
13
21
|
export declare const URDU_PUNCTUATION_RULES: PunctuationRule[];
|
|
14
22
|
export interface PunctuationOptions {
|
|
@@ -1 +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;
|
|
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;IACtB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;CACtC;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;AAcD,oFAAoF;AACpF,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,kBAAuB,GAAG,eAAe,EAAE,CAO1F;AAOD,8EAA8E;AAC9E,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD"}
|
|
@@ -34,13 +34,24 @@ export const URDU_PUNCTUATION_RULES = [
|
|
|
34
34
|
{ incorrect: " :", correct: ":", completeWord: false },
|
|
35
35
|
{ incorrect: "” ", correct: "”", completeWord: false },
|
|
36
36
|
];
|
|
37
|
+
/**
|
|
38
|
+
* A double quote opens (“) when it starts a quotation — at the start of the
|
|
39
|
+
* text, or after whitespace, an opening bracket, a dash, or another opening
|
|
40
|
+
* quote — and otherwise closes (”), the same heuristic macOS's native
|
|
41
|
+
* substitution uses.
|
|
42
|
+
*/
|
|
43
|
+
const OPENING_QUOTE_CONTEXT = /[\s([{—–-“‘]$/u;
|
|
44
|
+
function resolveDoubleQuote(before) {
|
|
45
|
+
return before === '' || OPENING_QUOTE_CONTEXT.test(before) ? '“' : '”';
|
|
46
|
+
}
|
|
37
47
|
/** The rules in use for the options, longest match first, so `۔"` wins over `"`. */
|
|
38
48
|
export function activePunctuationRules(options = {}) {
|
|
39
49
|
if (options.enabled === false)
|
|
40
50
|
return [];
|
|
41
51
|
const rules = URDU_PUNCTUATION_RULES.filter((rule) => rule.incorrect !== rule.correct);
|
|
42
|
-
if (options.straightDoubleQuote !== false)
|
|
43
|
-
rules.push({ incorrect: '"', correct: '”', completeWord: false });
|
|
52
|
+
if (options.straightDoubleQuote !== false) {
|
|
53
|
+
rules.push({ incorrect: '"', correct: '”', completeWord: false, resolve: resolveDoubleQuote });
|
|
54
|
+
}
|
|
44
55
|
return rules.sort((a, b) => b.incorrect.length - a.incorrect.length);
|
|
45
56
|
}
|
|
46
57
|
/** Space after Urdu punctuation, when the next character is not punctuation, a digit or a space. */
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tidies the whole document in place: Unicode NFC normalisation (Arabic-script
|
|
3
3
|
* text can store the same visible letters in different code points), collapsed
|
|
4
|
-
* repeated spaces,
|
|
5
|
-
*
|
|
4
|
+
* repeated spaces, empty text nodes removed, and runs of two or more blank
|
|
5
|
+
* paragraphs collapsed to one. Formatting is kept, since each text node is only
|
|
6
|
+
* changed, never replaced. Returns how many text nodes and blank lines changed.
|
|
6
7
|
*/
|
|
7
8
|
export declare function $cleanUpText(): number;
|
|
8
9
|
//# sourceMappingURL=textCleanupActions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textCleanupActions.d.ts","sourceRoot":"","sources":["../../src/cleanup/textCleanupActions.ts"],"names":[],"mappings":"AAKA
|
|
1
|
+
{"version":3,"file":"textCleanupActions.d.ts","sourceRoot":"","sources":["../../src/cleanup/textCleanupActions.ts"],"names":[],"mappings":"AAKA;;;;;;GAMG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAOrC"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { $getRoot } from 'lexical';
|
|
1
|
+
import { $getRoot, $isParagraphNode } from 'lexical';
|
|
2
2
|
/** Runs of two or more spaces or tabs, collapsed to one space. */
|
|
3
3
|
const REPEATED_SPACE = /[ \t]{2,}/g;
|
|
4
4
|
/**
|
|
5
5
|
* Tidies the whole document in place: Unicode NFC normalisation (Arabic-script
|
|
6
6
|
* text can store the same visible letters in different code points), collapsed
|
|
7
|
-
* repeated spaces,
|
|
8
|
-
*
|
|
7
|
+
* repeated spaces, empty text nodes removed, and runs of two or more blank
|
|
8
|
+
* paragraphs collapsed to one. Formatting is kept, since each text node is only
|
|
9
|
+
* changed, never replaced. Returns how many text nodes and blank lines changed.
|
|
9
10
|
*/
|
|
10
11
|
export function $cleanUpText() {
|
|
11
12
|
let changed = 0;
|
|
@@ -13,8 +14,25 @@ export function $cleanUpText() {
|
|
|
13
14
|
if (cleanNode(node))
|
|
14
15
|
changed += 1;
|
|
15
16
|
}
|
|
17
|
+
changed += $collapseBlankParagraphs();
|
|
16
18
|
return changed;
|
|
17
19
|
}
|
|
20
|
+
/** Removes every blank paragraph that directly follows another blank one, keeping the first. */
|
|
21
|
+
function $collapseBlankParagraphs() {
|
|
22
|
+
let removed = 0;
|
|
23
|
+
let previousWasBlank = false;
|
|
24
|
+
for (const child of $getRoot().getChildren()) {
|
|
25
|
+
const isBlank = $isParagraphNode(child) && child.getTextContent() === '';
|
|
26
|
+
if (isBlank && previousWasBlank) {
|
|
27
|
+
child.remove();
|
|
28
|
+
removed += 1;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
previousWasBlank = isBlank;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return removed;
|
|
35
|
+
}
|
|
18
36
|
function cleanNode(node) {
|
|
19
37
|
const text = node.getTextContent();
|
|
20
38
|
const cleaned = text.normalize('NFC').replace(REPEATED_SPACE, ' ');
|
|
@@ -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;AAC/F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAQrE;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,GAAG,EACH,MAAM,EACN,WAAgB,EAChB,eAAsB,EACtB,OAAO,EACP,OAAO,GACR,EAAE;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,KAAK,GAAG,KAAK,CAAC;IACnB,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,sFAAsF;IACtF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,aAAa,CAAC;IAChC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,+
|
|
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;AAC/F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAQrE;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,GAAG,EACH,MAAM,EACN,WAAgB,EAChB,eAAsB,EACtB,OAAO,EACP,OAAO,GACR,EAAE;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,KAAK,GAAG,KAAK,CAAC;IACnB,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,sFAAsF;IACtF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,aAAa,CAAC;IAChC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,+BA0EA"}
|
|
@@ -17,9 +17,10 @@ export function AutoCorrectPanel({ strings, dir, stores, initialFrom = '', initi
|
|
|
17
17
|
const [language, setLanguage] = useState(initialLanguage);
|
|
18
18
|
const [from, setFrom] = useState(initialFrom);
|
|
19
19
|
const [to, setTo] = useState('');
|
|
20
|
+
const [wholeWord, setWholeWord] = useState(true);
|
|
20
21
|
const [status, setStatus] = useState('idle');
|
|
21
22
|
const save = async () => {
|
|
22
|
-
const entry = { from: from.trim(), to: to.trim() };
|
|
23
|
+
const entry = { from: from.trim(), to: to.trim(), wholeWord };
|
|
23
24
|
if (entry.from === '' || entry.to === '' || entry.from === entry.to)
|
|
24
25
|
return;
|
|
25
26
|
try {
|
|
@@ -40,5 +41,5 @@ export function AutoCorrectPanel({ strings, dir, stores, initialFrom = '', initi
|
|
|
40
41
|
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
42
|
setLanguage(e.target.value);
|
|
42
43
|
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
|
+
}, 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("label", { className: "likhari-autocorrect-field likhari-autocorrect-checkbox", children: [_jsx("input", { type: "checkbox", checked: wholeWord, onChange: (e) => setWholeWord(e.target.checked) }), t.wholeWord] }), _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
45
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"AA2CA,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACjF,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnF,OAAO,EAA2B,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAmDtF,OAAO,EAAgB,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"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"}
|