@inshapardaz/likhari-react 0.1.50 → 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.
Files changed (31) hide show
  1. package/dist/autocorrect/AutoCorrectPlugin.d.ts.map +1 -1
  2. package/dist/autocorrect/AutoCorrectPlugin.js +25 -5
  3. package/dist/autocorrect/autoCorrectActions.d.ts.map +1 -1
  4. package/dist/autocorrect/autoCorrectActions.js +5 -3
  5. package/dist/autocorrect/autoCorrectStores.d.ts +17 -1
  6. package/dist/autocorrect/autoCorrectStores.d.ts.map +1 -1
  7. package/dist/autocorrect/autoCorrectStores.js +68 -12
  8. package/dist/autocorrect/punctuationRules.d.ts +8 -0
  9. package/dist/autocorrect/punctuationRules.d.ts.map +1 -1
  10. package/dist/autocorrect/punctuationRules.js +13 -2
  11. package/dist/cleanup/textCleanupActions.d.ts +3 -2
  12. package/dist/cleanup/textCleanupActions.d.ts.map +1 -1
  13. package/dist/cleanup/textCleanupActions.js +21 -3
  14. package/dist/components/AutoCorrectPanel.d.ts.map +1 -1
  15. package/dist/components/AutoCorrectPanel.js +3 -2
  16. package/dist/components/Toolbar.d.ts.map +1 -1
  17. package/dist/components/Toolbar.js +107 -29
  18. package/dist/components/ToolbarSettingsPanel.d.ts +17 -0
  19. package/dist/components/ToolbarSettingsPanel.d.ts.map +1 -0
  20. package/dist/components/ToolbarSettingsPanel.js +12 -0
  21. package/dist/i18n/strings.d.ts +18 -0
  22. package/dist/i18n/strings.d.ts.map +1 -1
  23. package/dist/i18n/strings.js +48 -0
  24. package/dist/index.d.ts +1 -1
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +1 -1
  27. package/dist/theme/editor.css +54 -0
  28. package/dist/toolbar/toolbarVisibility.d.ts +23 -0
  29. package/dist/toolbar/toolbarVisibility.d.ts.map +1 -0
  30. package/dist/toolbar/toolbarVisibility.js +40 -0
  31. 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,EAAE,MAAM,oBAAoB,CAAC;AACrF,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAiB,KAAK,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAG9F,sFAAsF;AACtF,kFAAkF;AAClF,eAAO,MAAM,wBAAwB,EAAE,cAAc,CAAC,IAAI,CAA6C,CAAC;AAMxG;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,OAAO,EACP,OAAO,EACP,MAAM,EACN,iBAAiB,EACjB,WAAW,GACZ,EAAE;IACD,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,OAAO,EAAE,OAAO,CAAC;IACjB,oFAAoF;IACpF,MAAM,EAAE,OAAO,CAAC;IAChB,mFAAmF;IACnF,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;IAC7C,kEAAkE;IAClE,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC,QA0GA"}
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 { loadAutoCorrections } from './autoCorrectStores.js';
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 loadAutoCorrections(stores, language)])).then((loaded) => {
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(punctuationRules.current);
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(punctuationRules.current);
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,CAqBhF;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,MAAM,CA2B5E;AAID;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,IAAI,GAAG,IAAI,CAoB7D;AAED,+FAA+F;AAC/F,wBAAgB,yBAAyB,IAAI,MAAM,CAWlD;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"}
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
- $replaceMatch({ anchorKey: node.getKey(), anchorOffset: start, focusKey: node.getKey(), focusOffset: before.length }, rule.correct);
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 + rule.correct.length;
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
- candidates.push({ start: index, end: index + rule.incorrect.length, correct: rule.correct });
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
- /** Corrections for one language, merged across stores. Failing stores are skipped. */
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;CACZ;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;AAkBD;;;;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,CAkBlH;AAED,sFAAsF;AACtF,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,gBAAgB,EAAE,EAAE,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAQ3H;AAED,6FAA6F;AAC7F,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,gBAAgB,EAAE,EAAE,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAKzI"}
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()).filter((e) => e.language === language).map(({ from, to }) => ({ from, to }));
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
- /** Corrections for one language, merged across stores. Failing stores are skipped. */
96
- export async function loadAutoCorrections(stores, language) {
97
- const table = new Map();
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 (!table.has(entry.from))
104
- table.set(entry.from, entry.to);
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;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"}
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, and empty text nodes removed. Formatting is kept, since each
5
- * text node is only changed, never replaced. Returns how many text nodes changed.
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;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAMrC"}
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, and empty text nodes removed. Formatting is kept, since each
8
- * text node is only changed, never replaced. Returns how many text nodes changed.
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,+BAqEA"}
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":"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"}
1
+ {"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Fragment, forwardRef, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from 'react';
3
- import { Menu, Select, Toolbar as MantineToolbar } from '@mantine/core';
3
+ import { Menu, Popover, Select, Toolbar as MantineToolbar } from '@mantine/core';
4
4
  import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
5
5
  import { $getSelection, $isRangeSelection, $isElementNode, $setSelection, $getRoot, $selectAll, $getNearestNodeFromDOMNode, $createTextNode, KEY_MODIFIER_COMMAND, COMMAND_PRIORITY_NORMAL, FORMAT_TEXT_COMMAND, FORMAT_ELEMENT_COMMAND, INDENT_CONTENT_COMMAND, OUTDENT_CONTENT_COMMAND, UNDO_COMMAND, REDO_COMMAND, SELECTION_CHANGE_COMMAND, CAN_UNDO_COMMAND, CAN_REDO_COMMAND, COMMAND_PRIORITY_LOW, } from 'lexical';
6
6
  import { $getSelectionStyleValueForProperty, $patchStyleText, $setBlocksType } from '@lexical/selection';
@@ -31,12 +31,14 @@ import { $canMergeSelectedCells, $canUnmergeSelectedCell, $deleteTable, $deleteT
31
31
  import { $createLinkNode, $isLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link';
32
32
  import { LinkDialog } from './LinkDialog.js';
33
33
  import { DraftsDialog } from './DraftsDialog.js';
34
+ import { ToolbarSettingsPanel } from './ToolbarSettingsPanel.js';
35
+ import { useToolbarItemVisibility } from '../toolbar/toolbarVisibility.js';
34
36
  import { normalizeLinkUrl } from '../utils/linkUrl.js';
35
37
  import { CANVAS_FONT_DEFAULTS, DEFAULT_FONT_OPTIONS, FONT_SIZES_PX, ZOOM_LEVELS_PERCENT } from '../fonts/index.js';
36
38
  import { setStyleProperty } from '../utils/style.js';
37
39
  import { useStrings } from '../i18n/useStrings.js';
38
40
  import { usePortalTarget } from '../PortalTargetContext.js';
39
- import { IconAbc, IconAlignCenter, IconAlignJustified, IconAlignLeft, IconAlignRight, IconArrowBackUp, IconArrowForwardUp, IconArrowsJoin, IconBold, IconClearFormatting, IconDeviceFloppy, IconBook, IconChevronDown, IconChevronUp, IconDots, IconHelpCircle, IconHistory, IconExternalLink, IconFeather, IconH1, IconH2, IconH3, IconH4, IconH5, IconH6, IconIndentDecrease, IconIndentIncrease, IconItalic, IconLetterCase, IconLetterCaseLower, IconLetterCaseUpper, IconLink, IconLinkOff, IconList, IconListCheck, IconListNumbers, IconPageBreak, IconPencil, IconPhoto, IconPilcrow, IconQuote, IconSeparatorHorizontal, IconSparkles, IconStrikethrough, IconSubscript, IconSuperscript, IconArrowMergeBoth, IconArrowsSplit2, IconColumnInsertLeft, IconColumnInsertRight, IconColumnRemove, IconColumns, IconClipboard, IconCopy, IconCut, IconSearch, IconSelectAll, IconColumns1, IconColumns2, IconNumber1Small, IconRowInsertBottom, IconRowInsertTop, IconRowRemove, IconArrowsShuffle, IconTable, IconTableMinus, IconTableOptions, IconMinus, IconPlus, IconTextSize, IconTypography, IconUnderline, IconWand, } from '@tabler/icons-react';
41
+ import { IconAbc, IconAlignCenter, IconAlignJustified, IconAlignLeft, IconAlignRight, IconArrowBackUp, IconArrowForwardUp, IconArrowsJoin, IconBold, IconClearFormatting, IconDeviceFloppy, IconBook, IconChevronDown, IconChevronUp, IconDots, IconHelpCircle, IconHistory, IconExternalLink, IconFeather, IconH1, IconH2, IconH3, IconH4, IconH5, IconH6, IconIndentDecrease, IconIndentIncrease, IconItalic, IconLetterCase, IconLetterCaseLower, IconLetterCaseUpper, IconLink, IconLinkOff, IconList, IconListCheck, IconListNumbers, IconPageBreak, IconPencil, IconPhoto, IconPilcrow, IconQuote, IconSeparatorHorizontal, IconSettings, IconSparkles, IconStrikethrough, IconSubscript, IconSuperscript, IconArrowMergeBoth, IconArrowsSplit2, IconColumnInsertLeft, IconColumnInsertRight, IconColumnRemove, IconColumns, IconClipboard, IconCopy, IconCut, IconSearch, IconSelectAll, IconColumns1, IconColumns2, IconNumber1Small, IconRowInsertBottom, IconRowInsertTop, IconRowRemove, IconArrowsShuffle, IconTable, IconTableMinus, IconTableOptions, IconMinus, IconPlus, IconTextSize, IconTypography, IconUnderline, IconWand, } from '@tabler/icons-react';
40
42
  import { IconCoupletCenterNarrow, IconCoupletCenterWide, IconCoupletDelete, IconCoupletGutterNarrow, IconCoupletGutterWide, IconCoupletSpacingLoose, IconCoupletSpacingTight, IconCoupletStaggerNarrow, IconCoupletStaggerWide, IconPoetryDelete, } from '../icons/poetryIcons.js';
41
43
  /** Sentinel for "no inline style": Lexical returns the default it is given when
42
44
  * the selection has none, so this tells "unstyled" (show the canvas default)
@@ -277,6 +279,8 @@ export function Toolbar({ config, toolbarStyle, dictionaryStores = [], thesaurus
277
279
  const strings = useStrings(locale);
278
280
  const portalTarget = usePortalTarget();
279
281
  const [draftsOpen, setDraftsOpen] = useState(false);
282
+ const [settingsOpen, setSettingsOpen] = useState(false);
283
+ const { isHidden, toggle: toggleHidden } = useToolbarItemVisibility();
280
284
  const rtl = direction === 'rtl';
281
285
  const ALIGN_ICONS = rtl ? ALIGN_ICONS_RTL : ALIGN_ICONS_LTR;
282
286
  // Responsive "priority+" overflow (UI spec §3.3): the toolbar's outer
@@ -289,6 +293,7 @@ export function Toolbar({ config, toolbarStyle, dictionaryStores = [], thesaurus
289
293
  // join lines) is lower priority still, so it is first to move.
290
294
  const toolbarContainerRef = useRef(null);
291
295
  const toolbarMeasureRef = useRef(null);
296
+ const toolbarOverflowMeasureRef = useRef(null);
292
297
  const [overflowGroupKeys, setOverflowGroupKeys] = useState(EMPTY_OVERFLOW);
293
298
  const updateToolbar = useCallback(() => {
294
299
  editor.getEditorState().read(() => {
@@ -845,9 +850,9 @@ export function Toolbar({ config, toolbarStyle, dictionaryStores = [], thesaurus
845
850
  (config.poetry.enabled && locale !== 'en') ||
846
851
  (config.tables && state.inTable) ||
847
852
  (config.poetry.enabled && state.inPoetry);
848
- const moreItems = [];
853
+ const allMoreItems = [];
849
854
  if (fmt.superscript) {
850
- moreItems.push({
855
+ allMoreItems.push({
851
856
  key: 'superscript',
852
857
  icon: IconSuperscript,
853
858
  label: strings.toolbar.superscript,
@@ -856,7 +861,7 @@ export function Toolbar({ config, toolbarStyle, dictionaryStores = [], thesaurus
856
861
  });
857
862
  }
858
863
  if (fmt.subscript) {
859
- moreItems.push({
864
+ allMoreItems.push({
860
865
  key: 'subscript',
861
866
  icon: IconSubscript,
862
867
  label: strings.toolbar.subscript,
@@ -865,14 +870,16 @@ export function Toolbar({ config, toolbarStyle, dictionaryStores = [], thesaurus
865
870
  });
866
871
  }
867
872
  if (fmt.caseTransforms) {
868
- moreItems.push({ key: 'upper', icon: IconLetterCaseUpper, label: strings.toolbar.uppercase, onClick: () => applyCaseTransform('upper') }, { key: 'lower', icon: IconLetterCaseLower, label: strings.toolbar.lowercase, onClick: () => applyCaseTransform('lower') }, { key: 'capitalize', icon: IconLetterCase, label: strings.toolbar.capitalize, onClick: () => applyCaseTransform('capitalize') });
873
+ allMoreItems.push({ key: 'upper', icon: IconLetterCaseUpper, label: strings.toolbar.uppercase, onClick: () => applyCaseTransform('upper') }, { key: 'lower', icon: IconLetterCaseLower, label: strings.toolbar.lowercase, onClick: () => applyCaseTransform('lower') }, { key: 'capitalize', icon: IconLetterCase, label: strings.toolbar.capitalize, onClick: () => applyCaseTransform('capitalize') });
869
874
  }
870
875
  if (fmt.clearFormatting) {
871
- moreItems.push({ key: 'clear', icon: IconClearFormatting, label: strings.toolbar.clearFormatting, onClick: clearFormatting });
876
+ allMoreItems.push({ key: 'clear', icon: IconClearFormatting, label: strings.toolbar.clearFormatting, onClick: clearFormatting });
872
877
  }
878
+ // The user can hide any of these individually from the toolbar settings panel.
879
+ const moreItems = allMoreItems.filter((item) => !isHidden(item.key));
873
880
  // UI spec §5: indent/outdent arrows are direction-relative and swap in RTL
874
881
  // (outdent moves content toward the start edge, which is the right in RTL).
875
- const indentItems = config.indent
882
+ const allIndentItems = config.indent
876
883
  ? [
877
884
  {
878
885
  key: 'outdent',
@@ -888,6 +895,7 @@ export function Toolbar({ config, toolbarStyle, dictionaryStores = [], thesaurus
888
895
  },
889
896
  ]
890
897
  : [];
898
+ const indentItems = allIndentItems.filter((item) => !isHidden(item.key));
891
899
  // Grouped for the dropdown when options declare groups (Latin / Urdu).
892
900
  const fontData = (() => {
893
901
  const data = [];
@@ -933,22 +941,31 @@ export function Toolbar({ config, toolbarStyle, dictionaryStores = [], thesaurus
933
941
  ];
934
942
  const formattingValue = state.listType ?? state.blockType;
935
943
  const AlignIcon = ALIGN_ICONS[state.elementFormat] ?? IconAlignLeft;
936
- const formatSection = (showFormattingGroup || fmt.bold || fmt.italic || fmt.underline || fmt.strikethrough || moreItems.length > 0) && (_jsxs(Fragment, { children: [showFormattingGroup && (_jsx(MantineToolbar.Group, { children: _jsx(ToolbarSelect, { icon: IconPilcrow, label: strings.toolbar.formattingLabel, width: 148, value: formattingValue, data: formattingOptions, comingSoon: strings.toolbar.comingSoon, renderOption: iconOptionRenderer(FORMATTING_ICONS, IconPilcrow), onMouseDown: snapshotSelection, onChange: withRefocus((v) => applyFormatting(v)) }) })), (fmt.bold || fmt.italic || fmt.underline || fmt.strikethrough || moreItems.length > 0) && (_jsxs(MantineToolbar.Group, { className: "likhari-toolbar-cluster", children: [fmt.bold && _jsx(ToolbarButton, { icon: IconBold, title: strings.toolbar.bold, active: state.activeFormats.has('bold'), onClick: () => formatText('bold') }), fmt.italic && (_jsx(ToolbarButton, { icon: IconItalic, title: strings.toolbar.italic, active: state.activeFormats.has('italic'), onClick: () => formatText('italic') })), fmt.underline && (_jsx(ToolbarButton, { icon: IconUnderline, title: strings.toolbar.underline, active: state.activeFormats.has('underline'), onClick: () => formatText('underline') })), fmt.strikethrough && (_jsx(ToolbarButton, { icon: IconStrikethrough, title: strings.toolbar.strikethrough, active: state.activeFormats.has('strikethrough'), onClick: () => formatText('strikethrough') })), moreItems.length > 0 && (_jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 210, closeOnItemClick: true, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx(ToolbarButton, { icon: IconDots, title: strings.toolbar.moreFormatting }) }), _jsx(Menu.Dropdown, { children: moreItems.map((item) => (_jsx(MoreMenuItem, { icon: item.icon, label: item.label, active: item.active, onClick: runMenuItem(item.onClick) }, item.key))) })] }))] }))] }, "format"));
937
- const alignSection = (showAlignGroup || indentItems.length > 0) && (_jsxs(Fragment, { children: [showAlignGroup && (_jsx(MantineToolbar.Group, { children: _jsx(ToolbarSelect, { icon: AlignIcon, label: strings.toolbar.alignment, width: 138, value: state.elementFormat || 'start', data: alignOptions, renderOption: iconOptionRenderer(ALIGN_ICONS, IconAlignLeft), onMouseDown: snapshotSelection, onChange: withRefocus((v) => formatElement(v)) }) })), indentItems.length > 0 && (_jsx(MantineToolbar.Group, { className: "likhari-toolbar-cluster", children: indentItems.map((item) => (_jsx(ToolbarButton, { icon: item.icon, title: item.label, onClick: item.onClick }, item.key))) }))] }, "align"));
938
- const fontSection = (config.font.family || (config.font.size && !uniform)) && (_jsxs(MantineToolbar.Group, { children: [config.font.family && (_jsx(ToolbarSelect, { icon: IconTypography, label: strings.toolbar.fontFamily, width: 150, value: fontOptions.some((f) => f.family === shownFontFamily) ? shownFontFamily : null, placeholder: strings.toolbar.fontFamilyPlaceholder, data: fontData, searchable: true, noMatchMessage: strings.toolbar.noMatch, renderOption: ({ option }) => _jsx("span", { style: { fontFamily: option.value }, children: option.label }), onMouseDown: snapshotSelection, onChange: uniform
944
+ const showParagraphStyle = showFormattingGroup && !isHidden('paragraphStyle');
945
+ const showBold = fmt.bold && !isHidden('bold');
946
+ const showItalic = fmt.italic && !isHidden('italic');
947
+ const showUnderline = fmt.underline && !isHidden('underline');
948
+ const showStrikethrough = fmt.strikethrough && !isHidden('strikethrough');
949
+ const formatSection = (showParagraphStyle || showBold || showItalic || showUnderline || showStrikethrough || moreItems.length > 0) && (_jsxs(Fragment, { children: [showParagraphStyle && (_jsx(MantineToolbar.Group, { children: _jsx(ToolbarSelect, { icon: IconPilcrow, label: strings.toolbar.formattingLabel, width: 148, value: formattingValue, data: formattingOptions, comingSoon: strings.toolbar.comingSoon, renderOption: iconOptionRenderer(FORMATTING_ICONS, IconPilcrow), onMouseDown: snapshotSelection, onChange: withRefocus((v) => applyFormatting(v)) }) })), (showBold || showItalic || showUnderline || showStrikethrough || moreItems.length > 0) && (_jsxs(MantineToolbar.Group, { className: "likhari-toolbar-cluster", children: [showBold && _jsx(ToolbarButton, { icon: IconBold, title: strings.toolbar.bold, active: state.activeFormats.has('bold'), onClick: () => formatText('bold') }), showItalic && (_jsx(ToolbarButton, { icon: IconItalic, title: strings.toolbar.italic, active: state.activeFormats.has('italic'), onClick: () => formatText('italic') })), showUnderline && (_jsx(ToolbarButton, { icon: IconUnderline, title: strings.toolbar.underline, active: state.activeFormats.has('underline'), onClick: () => formatText('underline') })), showStrikethrough && (_jsx(ToolbarButton, { icon: IconStrikethrough, title: strings.toolbar.strikethrough, active: state.activeFormats.has('strikethrough'), onClick: () => formatText('strikethrough') })), moreItems.length > 0 && (_jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 210, closeOnItemClick: true, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx(ToolbarButton, { icon: IconDots, title: strings.toolbar.moreFormatting }) }), _jsx(Menu.Dropdown, { children: moreItems.map((item) => (_jsx(MoreMenuItem, { icon: item.icon, label: item.label, active: item.active, onClick: runMenuItem(item.onClick) }, item.key))) })] }))] }))] }, "format"));
950
+ const showAlignSelect = showAlignGroup && !isHidden('alignment');
951
+ const alignSection = (showAlignSelect || indentItems.length > 0) && (_jsxs(Fragment, { children: [showAlignSelect && (_jsx(MantineToolbar.Group, { children: _jsx(ToolbarSelect, { icon: AlignIcon, label: strings.toolbar.alignment, width: 138, value: state.elementFormat || 'start', data: alignOptions, renderOption: iconOptionRenderer(ALIGN_ICONS, IconAlignLeft), onMouseDown: snapshotSelection, onChange: withRefocus((v) => formatElement(v)) }) })), indentItems.length > 0 && (_jsx(MantineToolbar.Group, { className: "likhari-toolbar-cluster", children: indentItems.map((item) => (_jsx(ToolbarButton, { icon: item.icon, title: item.label, onClick: item.onClick }, item.key))) }))] }, "align"));
952
+ const showFontFamily = config.font.family && !isHidden('fontFamily');
953
+ const showFontSize = config.font.size && !uniform && !isHidden('fontSize');
954
+ const fontSection = (showFontFamily || showFontSize) && (_jsxs(MantineToolbar.Group, { children: [showFontFamily && (_jsx(ToolbarSelect, { icon: IconTypography, label: strings.toolbar.fontFamily, width: 150, value: fontOptions.some((f) => f.family === shownFontFamily) ? shownFontFamily : null, placeholder: strings.toolbar.fontFamilyPlaceholder, data: fontData, searchable: true, noMatchMessage: strings.toolbar.noMatch, renderOption: ({ option }) => _jsx("span", { style: { fontFamily: option.value }, children: option.label }), onMouseDown: snapshotSelection, onChange: uniform
939
955
  ? // A view setting: leave the editor state and selection alone, just hand the caret back.
940
956
  (v) => {
941
957
  onUniformFontFamilyChange?.(v);
942
958
  editor.focus();
943
959
  }
944
- : withRefocus((v) => applyFont('font-family', v)) })), config.font.size && !uniform && (_jsx(ToolbarSelect, { icon: IconTextSize, label: strings.toolbar.fontSize, width: 84, value: FONT_SIZES_PX.some((px) => `${px}px` === shownFontSize) ? shownFontSize : null, placeholder: strings.toolbar.fontSizePlaceholder, data: FONT_SIZES_PX.map((px) => ({ value: `${px}px`, label: String(px) })), onMouseDown: snapshotSelection, onChange: withRefocus((v) => applyFont('font-size', v)) }))] }, "font"));
960
+ : withRefocus((v) => applyFont('font-family', v)) })), showFontSize && (_jsx(ToolbarSelect, { icon: IconTextSize, label: strings.toolbar.fontSize, width: 84, value: FONT_SIZES_PX.some((px) => `${px}px` === shownFontSize) ? shownFontSize : null, placeholder: strings.toolbar.fontSizePlaceholder, data: FONT_SIZES_PX.map((px) => ({ value: `${px}px`, label: String(px) })), onMouseDown: snapshotSelection, onChange: withRefocus((v) => applyFont('font-size', v)) }))] }, "font"));
945
961
  /** The insert commands. `contextual` also puts the link, table and poetry option menus among them (the classic
946
962
  * toolbar); the ribbon shows those on their own tabs instead. */
947
963
  const insertButtons = (contextual) => (_jsxs(_Fragment, { children: [config.links &&
964
+ !isHidden('link') &&
948
965
  (contextual && state.isLink ? (_jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 240, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx(ToolbarButton, { icon: IconLink, title: strings.toolbar.linkOptions, active: true, "aria-haspopup": "menu" }) }), _jsx(Menu.Dropdown, { children: _jsx(LinkMenuItems, { url: state.linkUrl, onEdit: () => {
949
966
  restoreSelection();
950
967
  openLinkDialog();
951
- }, onRemove: removeLink, strings: strings }) })] })) : (_jsx(ToolbarButton, { icon: IconLink, title: strings.toolbar.insertLink, onClick: openLinkDialog }))), (config.images.linked || config.images.embedded) && (_jsx(ToolbarButton, { icon: IconPhoto, title: strings.toolbar.insertImage, onClick: openImageDialog })), config.blocks.horizontalRule && (_jsx(ToolbarButton, { icon: IconSeparatorHorizontal, title: strings.toolbar.insertHorizontalRule, onClick: () => editor.dispatchCommand(INSERT_HORIZONTAL_RULE_COMMAND, undefined) })), config.tables && _jsx(ToolbarButton, { icon: IconTable, title: strings.toolbar.insertTable, onClick: openTableDialog }), contextual && config.tables && state.inTable && (_jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 240, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx(ToolbarButton, { icon: IconTableOptions, title: strings.toolbar.tableOptions, active: true, "aria-haspopup": "menu" }) }), _jsx(Menu.Dropdown, { children: _jsx(TableMenuItems, { strings: strings, direction: direction, rows: state.tableRows, columns: state.tableColumns, canMergeCells: state.canMergeCells, canUnmergeCell: state.canUnmergeCell, onAction: runMenuAction }) })] })), config.columns && _jsx(ToolbarButton, { icon: IconColumns, title: strings.toolbar.insertColumns, onClick: openLayoutDialog }), config.footnotes && (_jsx(ToolbarButton, { icon: IconNumber1Small, title: strings.toolbar.insertFootnote, onClick: () => editor.dispatchCommand(INSERT_FOOTNOTE_COMMAND, undefined) })), config.blocks.pageBreak && (_jsx(ToolbarButton, { icon: IconPageBreak, title: strings.toolbar.insertPageBreak, onClick: () => editor.dispatchCommand(INSERT_PAGE_BREAK_COMMAND, undefined) })), config.poetry.enabled && locale !== 'en' && (_jsx(ToolbarButton, { icon: IconFeather, title: strings.toolbar.insertPoetryCouplet, onClick: () => editor.dispatchCommand(INSERT_POETRY_COUPLET_COMMAND, { layout: config.poetry.defaultLayout }) })), contextual && config.poetry.enabled && state.inPoetry && (_jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 200, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx(ToolbarButton, { icon: IconFeather, title: strings.toolbar.poetryOptions, active: true, "aria-haspopup": "menu" }) }), _jsx(Menu.Dropdown, { children: _jsx(PoetryMenuItems, { strings: strings, layout: state.poetryLayout, scale: state.poetryScale, centered: state.poetryCentered, onAction: runMenuAction }) })] }))] }));
968
+ }, onRemove: removeLink, strings: strings }) })] })) : (_jsx(ToolbarButton, { icon: IconLink, title: strings.toolbar.insertLink, onClick: openLinkDialog }))), (config.images.linked || config.images.embedded) && !isHidden('image') && (_jsx(ToolbarButton, { icon: IconPhoto, title: strings.toolbar.insertImage, onClick: openImageDialog })), config.blocks.horizontalRule && !isHidden('horizontalRule') && (_jsx(ToolbarButton, { icon: IconSeparatorHorizontal, title: strings.toolbar.insertHorizontalRule, onClick: () => editor.dispatchCommand(INSERT_HORIZONTAL_RULE_COMMAND, undefined) })), config.tables && !isHidden('table') && _jsx(ToolbarButton, { icon: IconTable, title: strings.toolbar.insertTable, onClick: openTableDialog }), contextual && config.tables && state.inTable && (_jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 240, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx(ToolbarButton, { icon: IconTableOptions, title: strings.toolbar.tableOptions, active: true, "aria-haspopup": "menu" }) }), _jsx(Menu.Dropdown, { children: _jsx(TableMenuItems, { strings: strings, direction: direction, rows: state.tableRows, columns: state.tableColumns, canMergeCells: state.canMergeCells, canUnmergeCell: state.canUnmergeCell, onAction: runMenuAction }) })] })), config.columns && !isHidden('columns') && _jsx(ToolbarButton, { icon: IconColumns, title: strings.toolbar.insertColumns, onClick: openLayoutDialog }), config.footnotes && !isHidden('footnote') && (_jsx(ToolbarButton, { icon: IconNumber1Small, title: strings.toolbar.insertFootnote, onClick: () => editor.dispatchCommand(INSERT_FOOTNOTE_COMMAND, undefined) })), config.blocks.pageBreak && !isHidden('pageBreak') && (_jsx(ToolbarButton, { icon: IconPageBreak, title: strings.toolbar.insertPageBreak, onClick: () => editor.dispatchCommand(INSERT_PAGE_BREAK_COMMAND, undefined) })), config.poetry.enabled && locale !== 'en' && !isHidden('poetry') && (_jsx(ToolbarButton, { icon: IconFeather, title: strings.toolbar.insertPoetryCouplet, onClick: () => editor.dispatchCommand(INSERT_POETRY_COUPLET_COMMAND, { layout: config.poetry.defaultLayout }) })), contextual && config.poetry.enabled && state.inPoetry && (_jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 200, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx(ToolbarButton, { icon: IconFeather, title: strings.toolbar.poetryOptions, active: true, "aria-haspopup": "menu" }) }), _jsx(Menu.Dropdown, { children: _jsx(PoetryMenuItems, { strings: strings, layout: state.poetryLayout, scale: state.poetryScale, centered: state.poetryCentered, onAction: runMenuAction }) })] }))] }));
952
969
  const insertSection = showInsertGroup && (_jsx(MantineToolbar.Group, { className: "likhari-toolbar-cluster", children: insertButtons(true) }, "insert"));
953
970
  const joinLines = () => {
954
971
  editor.update(() => {
@@ -1031,23 +1048,79 @@ export function Toolbar({ config, toolbarStyle, dictionaryStores = [], thesaurus
1031
1048
  });
1032
1049
  // The tools group is always built, even when every command in it is off, so the ribbon asks first.
1033
1050
  const hasToolsGroup = config.language.autocorrect || config.language.textCleanup || config.language.spellCheck || config.joinLines;
1034
- const toolsSection = (_jsxs(MantineToolbar.Group, { className: "likhari-toolbar-cluster", children: [config.language.autocorrect && (_jsx(ToolbarButton, { icon: IconWand, title: strings.toolbar.autocorrect, active: autoCorrectOpen, onClick: onToggleAutoCorrect })), config.language.textCleanup && (_jsx(ToolbarButton, { icon: IconSparkles, title: strings.autoCorrect.correctDocument, onClick: () => editor.dispatchCommand(CORRECT_DOCUMENT_COMMAND, undefined) })), config.language.spellCheck && (_jsx(ToolbarButton, { icon: IconAbc, title: strings.toolbar.spellChecker, active: spellOpen, onClick: onToggleSpell })), config.joinLines && state.canJoinLines && (_jsx(ToolbarButton, { icon: IconArrowsJoin, title: strings.toolbar.joinLines, onClick: joinLines }))] }, "tools"));
1051
+ const toolsSection = (_jsxs(MantineToolbar.Group, { className: "likhari-toolbar-cluster", children: [config.language.autocorrect && !isHidden('autocorrect') && (_jsx(ToolbarButton, { icon: IconWand, title: strings.toolbar.autocorrect, active: autoCorrectOpen, onClick: onToggleAutoCorrect })), config.language.textCleanup && !isHidden('textCleanup') && (_jsx(ToolbarButton, { icon: IconSparkles, title: strings.autoCorrect.correctDocument, onClick: () => editor.dispatchCommand(CORRECT_DOCUMENT_COMMAND, undefined) })), config.language.spellCheck && !isHidden('spellCheck') && (_jsx(ToolbarButton, { icon: IconAbc, title: strings.toolbar.spellChecker, active: spellOpen, onClick: onToggleSpell })), config.joinLines && state.canJoinLines && !isHidden('joinLines') && (_jsx(ToolbarButton, { icon: IconArrowsJoin, title: strings.toolbar.joinLines, onClick: joinLines }))] }, "tools"));
1035
1052
  // Uniform mode's zoom: [-] 100% [+], stepping through the preset levels; the
1036
1053
  // percentage resets to 100%. Always laid out left-to-right, like a number.
1037
- const showZoom = uniform && config.font.size;
1054
+ const showZoom = uniform && config.font.size && !isHidden('zoom');
1038
1055
  const zoomOutTo = [...ZOOM_LEVELS_PERCENT].reverse().find((level) => level < zoom);
1039
1056
  const zoomInTo = ZOOM_LEVELS_PERCENT.find((level) => level > zoom);
1040
- const hasEndUtilities = Boolean(drafts || config.findReplace || helpUrl !== false);
1041
1057
  const zoomGroup = showZoom && (_jsxs(MantineToolbar.Group, { className: "likhari-toolbar-cluster", role: "group", "aria-label": strings.toolbar.zoom, dir: "ltr", children: [_jsx(ToolbarButton, { icon: IconMinus, title: strings.toolbar.zoomOut, disabled: zoomOutTo === undefined, onClick: () => zoomOutTo !== undefined && onZoomChange?.(zoomOutTo) }), _jsxs(MantineToolbar.Toggle, { title: strings.toolbar.zoomReset, "aria-label": `${strings.toolbar.zoomReset} (${zoom}%)`, onMouseDown: (e) => e.preventDefault(), onClick: () => onZoomChange?.(100), style: { width: 'auto', minWidth: 52, padding: '0 6px', fontSize: 12 }, children: [zoom, "%"] }), _jsx(ToolbarButton, { icon: IconPlus, title: strings.toolbar.zoomIn, disabled: zoomInTo === undefined, onClick: () => zoomInTo !== undefined && onZoomChange?.(zoomInTo) })] }, "zoom"));
1042
- const draftsButton = drafts && _jsx(ToolbarButton, { icon: IconHistory, title: strings.toolbar.drafts, onClick: () => setDraftsOpen(true) });
1043
- const findButton = config.findReplace && (_jsx(ToolbarButton, { icon: IconSearch, title: strings.findReplace.toggle, active: findOpen, onClick: onToggleFind }));
1044
- const helpButton = helpUrl !== false && (_jsx(MantineToolbar.Toggle, { component: "a", href: helpUrl ?? DEFAULT_HELP_URL, target: "_blank", rel: "noopener noreferrer", "aria-label": strings.toolbar.help, title: strings.toolbar.help, children: _jsx(IconHelpCircle, { size: ICON_SIZE, stroke: ICON_STROKE }) }));
1045
- // Drafts, search and help: the far-end group (see `endMovable` below).
1046
- const utilitiesGroup = hasEndUtilities && (_jsxs(MantineToolbar.Group, { className: "likhari-toolbar-cluster", children: [draftsButton, findButton, helpButton] }, "end"));
1047
- const saveGroup = showSave && (_jsx(MantineToolbar.Group, { className: "likhari-toolbar-cluster", children: _jsx(ToolbarButton, { icon: IconDeviceFloppy, title: strings.toolbar.save, dirty: Boolean(isDirty),
1058
+ const draftsButton = drafts && !isHidden('drafts') && _jsx(ToolbarButton, { icon: IconHistory, title: strings.toolbar.drafts, onClick: () => setDraftsOpen(true) });
1059
+ const findButton = config.findReplace && !isHidden('find') && (_jsx(ToolbarButton, { icon: IconSearch, title: strings.findReplace.toggle, active: findOpen, onClick: onToggleFind }));
1060
+ const helpButton = helpUrl !== false && !isHidden('help') && (_jsx(MantineToolbar.Toggle, { component: "a", href: helpUrl ?? DEFAULT_HELP_URL, target: "_blank", rel: "noopener noreferrer", "aria-label": strings.toolbar.help, title: strings.toolbar.help, children: _jsx(IconHelpCircle, { size: ICON_SIZE, stroke: ICON_STROKE }) }));
1061
+ // Every item the settings panel can show/hide, grouped the way it appears in the
1062
+ // toolbar. Built from what the host's EditorFeatureConfig makes available — a
1063
+ // feature the host turned off is never offered here, since this panel only lets
1064
+ // the user declutter what the host already enabled.
1065
+ const settingsGroup = (id, label, items) => {
1066
+ const present = items.filter((item) => Boolean(item));
1067
+ return present.length > 0 ? { id, label, items: present } : null;
1068
+ };
1069
+ const t = strings.toolbar;
1070
+ const settingsGroups = [
1071
+ settingsGroup('formatting', strings.toolbarSettings.groups.formatting, [
1072
+ showFormattingGroup && { id: 'paragraphStyle', label: t.formattingLabel },
1073
+ fmt.bold && { id: 'bold', label: t.bold },
1074
+ fmt.italic && { id: 'italic', label: t.italic },
1075
+ fmt.underline && { id: 'underline', label: t.underline },
1076
+ fmt.strikethrough && { id: 'strikethrough', label: t.strikethrough },
1077
+ ...allMoreItems.map((item) => ({ id: item.key, label: item.label })),
1078
+ ]),
1079
+ settingsGroup('alignment', strings.toolbarSettings.groups.alignment, [
1080
+ showAlignGroup && { id: 'alignment', label: t.alignment },
1081
+ ...allIndentItems.map((item) => ({ id: item.key, label: item.label })),
1082
+ ]),
1083
+ settingsGroup('font', strings.toolbarSettings.groups.font, [
1084
+ config.font.family && { id: 'fontFamily', label: t.fontFamily },
1085
+ config.font.size && !uniform && { id: 'fontSize', label: t.fontSize },
1086
+ ]),
1087
+ settingsGroup('insert', strings.toolbarSettings.groups.insert, [
1088
+ config.links && { id: 'link', label: t.insertLink },
1089
+ (config.images.linked || config.images.embedded) && { id: 'image', label: t.insertImage },
1090
+ config.blocks.horizontalRule && { id: 'horizontalRule', label: t.insertHorizontalRule },
1091
+ config.tables && { id: 'table', label: t.insertTable },
1092
+ config.columns && { id: 'columns', label: t.insertColumns },
1093
+ config.footnotes && { id: 'footnote', label: t.insertFootnote },
1094
+ config.blocks.pageBreak && { id: 'pageBreak', label: t.insertPageBreak },
1095
+ config.poetry.enabled && locale !== 'en' && { id: 'poetry', label: t.insertPoetryCouplet },
1096
+ ]),
1097
+ settingsGroup('tools', strings.toolbarSettings.groups.tools, [
1098
+ config.language.autocorrect && { id: 'autocorrect', label: t.autocorrect },
1099
+ config.language.textCleanup && { id: 'textCleanup', label: strings.autoCorrect.correctDocument },
1100
+ config.language.spellCheck && { id: 'spellCheck', label: t.spellChecker },
1101
+ config.joinLines && { id: 'joinLines', label: t.joinLines },
1102
+ ]),
1103
+ settingsGroup('zoom', strings.toolbarSettings.groups.zoom, [uniform && config.font.size && { id: 'zoom', label: t.zoom }]),
1104
+ settingsGroup('utilities', strings.toolbarSettings.groups.utilities, [
1105
+ drafts && { id: 'drafts', label: t.drafts },
1106
+ config.findReplace && { id: 'find', label: strings.findReplace.toggle },
1107
+ helpUrl !== false && { id: 'help', label: t.help },
1108
+ ]),
1109
+ settingsGroup('history', strings.toolbarSettings.groups.history, [
1110
+ showSave && { id: 'save', label: t.save },
1111
+ config.history && { id: 'undo', label: t.undo },
1112
+ config.history && { id: 'redo', label: t.redo },
1113
+ ]),
1114
+ ].filter((g) => g !== null);
1115
+ const settingsButton = (_jsxs(Popover, { opened: settingsOpen, onChange: setSettingsOpen, position: "bottom-end", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 280, children: [_jsx(Popover.Target, { children: _jsx(ToolbarButton, { icon: IconSettings, title: strings.toolbar.settings, active: settingsOpen, onClick: () => setSettingsOpen((v) => !v) }) }), _jsx(Popover.Dropdown, { p: 0, children: _jsx(ToolbarSettingsPanel, { strings: strings, dir: direction, groups: settingsGroups, isHidden: isHidden, onToggle: toggleHidden, onClose: () => setSettingsOpen(false) }) })] }));
1116
+ // Drafts, search, help and the settings gear: the far-end group (see `endMovable` below).
1117
+ const utilitiesGroup = (_jsxs(MantineToolbar.Group, { className: "likhari-toolbar-cluster", children: [draftsButton, findButton, helpButton, settingsButton] }, "end"));
1118
+ const saveGroup = showSave && !isHidden('save') && (_jsx(MantineToolbar.Group, { className: "likhari-toolbar-cluster", children: _jsx(ToolbarButton, { icon: IconDeviceFloppy, title: strings.toolbar.save, dirty: Boolean(isDirty),
1048
1119
  // Nothing to save until the content has changed since it was loaded or last saved.
1049
1120
  disabled: !isDirty, onClick: onSave }) }, "save"));
1050
- const historyGroup = config.history && (_jsxs(MantineToolbar.Group, { className: "likhari-toolbar-cluster", children: [_jsx(ToolbarButton, { icon: IconArrowBackUp, title: strings.toolbar.undo, disabled: !state.canUndo, onClick: () => editor.dispatchCommand(UNDO_COMMAND, undefined) }), _jsx(ToolbarButton, { icon: IconArrowForwardUp, title: strings.toolbar.redo, disabled: !state.canRedo, onClick: () => editor.dispatchCommand(REDO_COMMAND, undefined) })] }, "history"));
1121
+ const showUndo = !isHidden('undo');
1122
+ const showRedo = !isHidden('redo');
1123
+ const historyGroup = config.history && (showUndo || showRedo) && (_jsxs(MantineToolbar.Group, { className: "likhari-toolbar-cluster", children: [showUndo && (_jsx(ToolbarButton, { icon: IconArrowBackUp, title: strings.toolbar.undo, disabled: !state.canUndo, onClick: () => editor.dispatchCommand(UNDO_COMMAND, undefined) })), showRedo && (_jsx(ToolbarButton, { icon: IconArrowForwardUp, title: strings.toolbar.redo, disabled: !state.canRedo, onClick: () => editor.dispatchCommand(REDO_COMMAND, undefined) }))] }, "history"));
1051
1124
  const startSections = [
1052
1125
  saveGroup,
1053
1126
  historyGroup,
@@ -1079,10 +1152,9 @@ export function Toolbar({ config, toolbarStyle, dictionaryStores = [], thesaurus
1079
1152
  const measure = toolbarMeasureRef.current;
1080
1153
  if (!container || !measure)
1081
1154
  return;
1082
- // Rough width of the "..." button itself (icon button + its gap) —
1083
- // only charged against the budget once something has actually moved
1084
- // into it, since it isn't rendered at all otherwise.
1085
- const OVERFLOW_BUTTON_WIDTH = 38;
1155
+ // Fallback only, in case the overflow button's own hidden measurement
1156
+ // element (below) isn't mounted yet on the very first pass.
1157
+ const OVERFLOW_BUTTON_FALLBACK_WIDTH = 38;
1086
1158
  const recompute = () => {
1087
1159
  const available = container.clientWidth;
1088
1160
  const total = measure.scrollWidth;
@@ -1094,6 +1166,10 @@ export function Toolbar({ config, toolbarStyle, dictionaryStores = [], thesaurus
1094
1166
  measure.querySelectorAll('[data-group-key]').forEach((el) => {
1095
1167
  widths.set(el.dataset.groupKey, el.getBoundingClientRect().width);
1096
1168
  });
1169
+ // Measured from a real (hidden) copy of the "..." button, in the toolbar's own
1170
+ // CSS, rather than guessed — only charged against the budget once something
1171
+ // has actually moved into it, since it isn't rendered at all otherwise.
1172
+ const overflowButtonWidth = toolbarOverflowMeasureRef.current?.scrollWidth ?? OVERFLOW_BUTTON_FALLBACK_WIDTH;
1097
1173
  let remaining = total;
1098
1174
  const next = new Set();
1099
1175
  for (const key of movableGroupKeys.split(',').filter(Boolean)) {
@@ -1106,13 +1182,15 @@ export function Toolbar({ config, toolbarStyle, dictionaryStores = [], thesaurus
1106
1182
  next.add(key);
1107
1183
  }
1108
1184
  if (next.size > 0)
1109
- remaining += OVERFLOW_BUTTON_WIDTH;
1185
+ remaining += overflowButtonWidth;
1110
1186
  setOverflowGroupKeys((prev) => (setsEqual(prev, next) ? prev : next));
1111
1187
  };
1112
1188
  recompute();
1113
1189
  const ro = new ResizeObserver(recompute);
1114
1190
  ro.observe(container);
1115
1191
  ro.observe(measure);
1192
+ if (toolbarOverflowMeasureRef.current)
1193
+ ro.observe(toolbarOverflowMeasureRef.current);
1116
1194
  return () => ro.disconnect();
1117
1195
  });
1118
1196
  const isVisible = (section) => !overflowGroupKeys.has(section.key);
@@ -1255,5 +1333,5 @@ export function Toolbar({ config, toolbarStyle, dictionaryStores = [], thesaurus
1255
1333
  setRibbonPeek(false);
1256
1334
  }, children: _jsx(MantineToolbar, { "aria-label": strings.toolbar.ariaLabel, className: bordered ? 'likhari-toolbar likhari-ribbon-panel' : 'likhari-toolbar likhari-toolbar--plain likhari-ribbon-panel', withBorder: bordered, variant: variant, color: "var(--editor-accent)", children: withDividers(activeTab.groups) }) })), contextMenuEl, dialogsEl] }));
1257
1335
  }
1258
- return (_jsxs(MantineToolbar, { ref: toolbarContainerRef, "aria-label": strings.toolbar.ariaLabel, className: bordered ? 'likhari-toolbar' : 'likhari-toolbar likhari-toolbar--plain', withBorder: bordered, variant: variant, color: "var(--editor-accent)", children: [withDividers(startSections), visibleMovable.map((section) => (_jsxs(Fragment, { children: [_jsx(MantineToolbar.Divider, {}), section.node] }, section.key))), hiddenMovable.length > 0 && (_jsxs(_Fragment, { children: [_jsx(MantineToolbar.Divider, {}), _jsxs(Menu, { opened: overflowMenuOpen, onChange: handleOverflowMenuChange, position: "bottom-end", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: "max-content", closeOnItemClick: false, children: [_jsx(Menu.Target, { children: _jsx(ToolbarButton, { icon: IconDots, title: strings.toolbar.moreFormatting }) }), _jsx(Menu.Dropdown, { p: 6, children: _jsx(MantineToolbar, { withBorder: false, variant: variant, color: "var(--editor-accent)", style: { maxWidth: 320 }, children: withDividers(hiddenMovable.map((section) => section.node)) }) })] })] })), _jsx("span", { "aria-hidden": "true", style: { flex: '1 1 auto' } }), visibleEnd.map((section) => section.node), contextMenuEl, overflowEnabled && (_jsxs(MantineToolbar, { "aria-hidden": "true", ref: toolbarMeasureRef, withBorder: bordered, variant: variant, color: "var(--editor-accent)", style: { position: 'absolute', visibility: 'hidden', pointerEvents: 'none', top: 0, left: 0, flexWrap: 'nowrap', width: 'max-content' }, children: [withDividers(startSections), movableSections.map((section) => (_jsxs(Fragment, { children: [_jsx(MantineToolbar.Divider, {}), _jsx("span", { "data-group-key": section.key, style: { display: 'inline-flex' }, children: section.node })] }, section.key)))] })), dialogsEl] }));
1336
+ return (_jsxs(MantineToolbar, { ref: toolbarContainerRef, "aria-label": strings.toolbar.ariaLabel, className: bordered ? 'likhari-toolbar' : 'likhari-toolbar likhari-toolbar--plain', withBorder: bordered, variant: variant, color: "var(--editor-accent)", children: [withDividers(startSections), visibleMovable.map((section) => (_jsxs(Fragment, { children: [_jsx(MantineToolbar.Divider, {}), section.node] }, section.key))), hiddenMovable.length > 0 && (_jsxs(_Fragment, { children: [_jsx(MantineToolbar.Divider, {}), _jsxs(Menu, { opened: overflowMenuOpen, onChange: handleOverflowMenuChange, position: "bottom-end", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: "max-content", closeOnItemClick: false, children: [_jsx(Menu.Target, { children: _jsx(ToolbarButton, { icon: IconDots, title: strings.toolbar.moreFormatting }) }), _jsx(Menu.Dropdown, { p: 6, children: _jsx(MantineToolbar, { withBorder: false, variant: variant, color: "var(--editor-accent)", style: { maxWidth: 320 }, children: withDividers(hiddenMovable.map((section) => section.node)) }) })] })] })), _jsx("span", { "aria-hidden": "true", style: { flex: '1 1 auto' } }), visibleEnd.map((section) => section.node), contextMenuEl, overflowEnabled && (_jsxs(MantineToolbar, { "aria-hidden": "true", ref: toolbarMeasureRef, className: bordered ? 'likhari-toolbar' : 'likhari-toolbar likhari-toolbar--plain', withBorder: bordered, variant: variant, color: "var(--editor-accent)", style: { position: 'absolute', visibility: 'hidden', pointerEvents: 'none', top: 0, left: 0, flexWrap: 'nowrap', width: 'max-content' }, children: [withDividers(startSections), mainMovable.map((section) => (_jsxs(Fragment, { children: [_jsx(MantineToolbar.Divider, {}), _jsx("span", { "data-group-key": section.key, style: { display: 'inline-flex' }, children: section.node })] }, section.key))), endMovable.map((section) => (_jsx("span", { "data-group-key": section.key, style: { display: 'inline-flex' }, children: section.node }, section.key)))] })), overflowEnabled && (_jsxs(MantineToolbar, { "aria-hidden": "true", ref: toolbarOverflowMeasureRef, className: bordered ? 'likhari-toolbar' : 'likhari-toolbar likhari-toolbar--plain', withBorder: bordered, variant: variant, color: "var(--editor-accent)", style: { position: 'absolute', visibility: 'hidden', pointerEvents: 'none', top: 0, left: 0, width: 'max-content' }, children: [_jsx(MantineToolbar.Divider, {}), _jsx(ToolbarButton, { icon: IconDots, title: "Toolbar overflow measurement" })] })), dialogsEl] }));
1259
1337
  }
@@ -0,0 +1,17 @@
1
+ import type { Strings } from '../i18n/strings.js';
2
+ import type { ToolbarVisibilityGroup } from '../toolbar/toolbarVisibility.js';
3
+ /**
4
+ * Lets the user show or hide individual toolbar buttons and dropdowns, grouped
5
+ * the way they appear in the toolbar. The choice is read from and saved to
6
+ * localStorage by the caller (`useToolbarItemVisibility`); this component only
7
+ * renders the checkboxes and reports clicks.
8
+ */
9
+ export declare function ToolbarSettingsPanel({ strings, dir, groups, isHidden, onToggle, onClose, }: {
10
+ strings: Strings;
11
+ dir: 'ltr' | 'rtl';
12
+ groups: ToolbarVisibilityGroup[];
13
+ isHidden: (id: string) => boolean;
14
+ onToggle: (id: string) => void;
15
+ onClose: () => void;
16
+ }): import("react").JSX.Element;
17
+ //# sourceMappingURL=ToolbarSettingsPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolbarSettingsPanel.d.ts","sourceRoot":"","sources":["../../src/components/ToolbarSettingsPanel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAE3E;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,OAAO,EACP,GAAG,EACH,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,OAAO,GACR,EAAE;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,KAAK,GAAG,KAAK,CAAC;IACnB,MAAM,EAAE,sBAAsB,EAAE,CAAC;IACjC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IAClC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,+BAyBA"}
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { IconX } from '@tabler/icons-react';
3
+ /**
4
+ * Lets the user show or hide individual toolbar buttons and dropdowns, grouped
5
+ * the way they appear in the toolbar. The choice is read from and saved to
6
+ * localStorage by the caller (`useToolbarItemVisibility`); this component only
7
+ * renders the checkboxes and reports clicks.
8
+ */
9
+ export function ToolbarSettingsPanel({ strings, dir, groups, isHidden, onToggle, onClose, }) {
10
+ const t = strings.toolbarSettings;
11
+ return (_jsxs("div", { className: "likhari-toolbar-settings-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 }) })] }), _jsx("div", { className: "likhari-toolbar-settings-groups", children: groups.map((group) => (_jsxs("fieldset", { className: "likhari-toolbar-settings-group", children: [_jsx("legend", { children: group.label }), group.items.map((item) => (_jsxs("label", { className: "likhari-toolbar-settings-item", children: [_jsx("input", { type: "checkbox", checked: !isHidden(item.id), onChange: () => onToggle(item.id) }), item.label] }, item.id)))] }, group.id))) })] }));
12
+ }
@@ -74,6 +74,8 @@ export interface Strings {
74
74
  clearFormatting: string;
75
75
  outdent: string;
76
76
  indent: string;
77
+ /** Opens/closes the toolbar settings panel (show/hide individual toolbar items). */
78
+ settings: string;
77
79
  /** The ribbon layout (`toolbarStyle.layout: 'ribbon'`): its tab strip and tabs. */
78
80
  ribbon: {
79
81
  /** aria-label on the tab strip. */
@@ -110,6 +112,21 @@ export interface Strings {
110
112
  right: string;
111
113
  };
112
114
  };
115
+ /** The toolbar settings panel (the gear button): show/hide individual toolbar buttons and dropdowns. Grouped the way they appear in the toolbar; group labels are below. */
116
+ toolbarSettings: {
117
+ title: string;
118
+ close: string;
119
+ groups: {
120
+ formatting: string;
121
+ alignment: string;
122
+ font: string;
123
+ insert: string;
124
+ tools: string;
125
+ zoom: string;
126
+ utilities: string;
127
+ history: string;
128
+ };
129
+ };
113
130
  /** The popup `EditorRef.confirmDiscard()` shows when the user is about to leave with unsaved changes (lexical-editor-spec.md §6.4). */
114
131
  leaveDialog: {
115
132
  title: string;
@@ -221,6 +238,7 @@ export interface Strings {
221
238
  failed: string;
222
239
  close: string;
223
240
  correctDocument: string;
241
+ wholeWord: string;
224
242
  };
225
243
  contextMenu: {
226
244
  cut: string;
@@ -1 +1 @@
1
- {"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../../src/i18n/strings.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../../src/i18n/strings.ts"],"names":[],"mappings":"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"}
@@ -62,6 +62,7 @@ const en = {
62
62
  clearFormatting: 'Clear formatting',
63
63
  outdent: 'Outdent',
64
64
  indent: 'Indent',
65
+ settings: 'Toolbar settings',
65
66
  ribbon: {
66
67
  tabsLabel: 'Ribbon tabs',
67
68
  quickAccess: 'Quick access',
@@ -92,6 +93,20 @@ const en = {
92
93
  right: 'Align right',
93
94
  },
94
95
  },
96
+ toolbarSettings: {
97
+ title: 'Toolbar settings',
98
+ close: 'Close',
99
+ groups: {
100
+ formatting: 'Formatting',
101
+ alignment: 'Alignment & indent',
102
+ font: 'Font',
103
+ insert: 'Insert',
104
+ tools: 'Tools',
105
+ zoom: 'Zoom',
106
+ utilities: 'Drafts, search & help',
107
+ history: 'Save, undo & redo',
108
+ },
109
+ },
95
110
  leaveDialog: {
96
111
  title: 'Unsaved changes',
97
112
  message: 'You have unsaved changes. What would you like to do before leaving?',
@@ -196,6 +211,7 @@ const en = {
196
211
  failed: 'Could not save the correction',
197
212
  close: 'Close',
198
213
  correctDocument: 'Correct whole document',
214
+ wholeWord: 'Whole word only',
199
215
  },
200
216
  contextMenu: {
201
217
  cut: 'Cut',
@@ -360,6 +376,7 @@ const ur = {
360
376
  clearFormatting: 'فارمیٹنگ ختم کریں',
361
377
  outdent: 'حاشیہ کم کریں',
362
378
  indent: 'حاشیہ بڑھائیں',
379
+ settings: 'ٹول بار کی ترتیبات',
363
380
  ribbon: {
364
381
  tabsLabel: 'ربن کے ٹیب',
365
382
  quickAccess: 'فوری رسائی',
@@ -390,6 +407,20 @@ const ur = {
390
407
  right: 'دائیں سیدھ',
391
408
  },
392
409
  },
410
+ toolbarSettings: {
411
+ title: 'ٹول بار کی ترتیبات',
412
+ close: 'بند کریں',
413
+ groups: {
414
+ formatting: 'فارمیٹنگ',
415
+ alignment: 'سیدھ اور حاشیہ',
416
+ font: 'فونٹ',
417
+ insert: 'داخل کریں',
418
+ tools: 'آلات',
419
+ zoom: 'زوم',
420
+ utilities: 'ڈرافٹس، تلاش اور مدد',
421
+ history: 'محفوظ کریں، واپس اور آگے',
422
+ },
423
+ },
393
424
  leaveDialog: {
394
425
  title: 'غیر محفوظ شدہ تبدیلیاں',
395
426
  message: 'آپ کی تبدیلیاں محفوظ نہیں ہوئیں۔ جانے سے پہلے آپ کیا کرنا چاہیں گے؟',
@@ -494,6 +525,7 @@ const ur = {
494
525
  failed: 'تصحیح محفوظ نہیں ہو سکی',
495
526
  close: 'بند کریں',
496
527
  correctDocument: 'تمام متن کی تصحیح کریں',
528
+ wholeWord: 'صرف پورا لفظ',
497
529
  },
498
530
  contextMenu: {
499
531
  cut: 'کاٹیں',
@@ -658,6 +690,7 @@ const paShahmukhi = {
658
690
  clearFormatting: 'فارمیٹنگ ہٹاؤ',
659
691
  outdent: 'حاشیہ گھٹاؤ',
660
692
  indent: 'حاشیہ ودھاؤ',
693
+ settings: 'ٹول بار دیاں ترتیباں',
661
694
  ribbon: {
662
695
  tabsLabel: 'ربن دے ٹیب',
663
696
  quickAccess: 'جھٹ رسائی',
@@ -688,6 +721,20 @@ const paShahmukhi = {
688
721
  right: 'سجے سیدھ',
689
722
  },
690
723
  },
724
+ toolbarSettings: {
725
+ title: 'ٹول بار دیاں ترتیباں',
726
+ close: 'بند کرو',
727
+ groups: {
728
+ formatting: 'فارمیٹنگ',
729
+ alignment: 'سیدھ تے حاشیہ',
730
+ font: 'فونٹ',
731
+ insert: 'داخل کرو',
732
+ tools: 'اوزار',
733
+ zoom: 'زوم',
734
+ utilities: 'ڈرافٹس، تلاش تے مدد',
735
+ history: 'سانبھو، واپس تے اگے',
736
+ },
737
+ },
691
738
  leaveDialog: {
692
739
  title: 'غیر محفوظ تبدیلیاں',
693
740
  message: 'تہاڈیاں تبدیلیاں سنبھالیاں نئیں گئیاں۔ جان توں پہلاں تسی کی کرنا چاہو گے؟',
@@ -792,6 +839,7 @@ const paShahmukhi = {
792
839
  failed: 'درستی سانبھی نئیں جا سکی',
793
840
  close: 'بند کرو',
794
841
  correctDocument: 'پورے متن دی درستی کرو',
842
+ wholeWord: 'صرف پورا لفظ',
795
843
  },
796
844
  contextMenu: {
797
845
  cut: 'کٹو',
package/dist/index.d.ts CHANGED
@@ -11,7 +11,7 @@ export type { FontOption, FontMode } from './fonts/index.js';
11
11
  export { STRINGS, getStrings, useStrings, useUiStrings, UiStringsContext } from './i18n/index.js';
12
12
  export type { Locale, Strings } from './i18n/index.js';
13
13
  export { registerSpellDictionary, hasSpellDictionary, setEnglishDictionaryBaseUrl, type HunspellFiles, type SpellLanguage } from './spellcheck/spellDictionaries.js';
14
- export { localStorageAutoCorrectStore, apiAutoCorrectStore, fileAutoCorrectStore } from './autocorrect/autoCorrectStores.js';
14
+ export { localStorageAutoCorrectStore, apiAutoCorrectStore, fileAutoCorrectStore, nawishtaAutoCorrectStore } from './autocorrect/autoCorrectStores.js';
15
15
  export type { AutoCorrectStore, AutoCorrectEntry } from './autocorrect/autoCorrectStores.js';
16
16
  export { normalizeUrdu, normalizeUrduCharacters, removeUrduDiacritics, replaceUrduDigits } from './normalization/urduNormalize.js';
17
17
  export type { UrduNormalizationOptions } from './normalization/urduNormalize.js';
@@ -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,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,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,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAC1G,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACpD,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,2BAA2B,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAClK,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"}
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,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,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,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAC1G,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACpD,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,2BAA2B,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAClK,OAAO,EAAE,4BAA4B,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AACpJ,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
@@ -5,7 +5,7 @@ export { ImageNode, $createImageNode, $isImageNode } from './image/ImageNode.js'
5
5
  export { DEFAULT_FONT_OPTIONS, URDU_WEB_FONT_OPTIONS, FONT_SIZES_PX, ZOOM_LEVELS_PERCENT } from './fonts/index.js';
6
6
  export { STRINGS, getStrings, useStrings, useUiStrings, UiStringsContext } from './i18n/index.js';
7
7
  export { registerSpellDictionary, hasSpellDictionary, setEnglishDictionaryBaseUrl } from './spellcheck/spellDictionaries.js';
8
- export { localStorageAutoCorrectStore, apiAutoCorrectStore, fileAutoCorrectStore } from './autocorrect/autoCorrectStores.js';
8
+ export { localStorageAutoCorrectStore, apiAutoCorrectStore, fileAutoCorrectStore, nawishtaAutoCorrectStore } from './autocorrect/autoCorrectStores.js';
9
9
  export { normalizeUrdu, normalizeUrduCharacters, removeUrduDiacritics, replaceUrduDigits } from './normalization/urduNormalize.js';
10
10
  export { localStorageUserWordStore, apiUserWordStore, ignoreWord, addUserWord, onSpellWordsChange } from './spellcheck/userWords.js';
11
11
  export { dictionaryCompletionStore, apiCompletionStore } from './autocomplete/completionStores.js';
@@ -1076,6 +1076,16 @@
1076
1076
  color: inherit;
1077
1077
  }
1078
1078
 
1079
+ .likhari-autocorrect-checkbox {
1080
+ flex-direction: row;
1081
+ align-items: center;
1082
+ gap: 6px;
1083
+ }
1084
+
1085
+ .likhari-autocorrect-checkbox input {
1086
+ width: auto;
1087
+ }
1088
+
1079
1089
  .likhari-autocorrect-actions {
1080
1090
  display: flex;
1081
1091
  flex-direction: column;
@@ -1097,6 +1107,50 @@
1097
1107
  cursor: default;
1098
1108
  }
1099
1109
 
1110
+ /* Toolbar settings panel: a Mantine Popover dropdown anchored on the gear button. */
1111
+ .likhari-toolbar-settings-panel {
1112
+ width: 280px;
1113
+ max-height: min(70vh, 480px);
1114
+ overflow-y: auto;
1115
+ padding: 8px;
1116
+ font-size: 13px;
1117
+ background: var(--editor-paper-dim);
1118
+ color: inherit;
1119
+ }
1120
+
1121
+ .likhari-toolbar-settings-groups {
1122
+ display: flex;
1123
+ flex-direction: column;
1124
+ gap: 10px;
1125
+ }
1126
+
1127
+ .likhari-toolbar-settings-group {
1128
+ border: 1px solid var(--editor-line);
1129
+ border-radius: 6px;
1130
+ padding: 6px 8px;
1131
+ margin: 0;
1132
+ }
1133
+
1134
+ .likhari-toolbar-settings-group legend {
1135
+ padding: 0 4px;
1136
+ font-size: 11px;
1137
+ text-transform: uppercase;
1138
+ letter-spacing: 0.02em;
1139
+ opacity: 0.7;
1140
+ }
1141
+
1142
+ .likhari-toolbar-settings-item {
1143
+ display: flex;
1144
+ align-items: center;
1145
+ gap: 6px;
1146
+ padding: 3px 2px;
1147
+ cursor: pointer;
1148
+ }
1149
+
1150
+ .likhari-toolbar-settings-item input {
1151
+ width: auto;
1152
+ }
1153
+
1100
1154
  /* Autocomplete suggestions, shown under the caret while typing. */
1101
1155
  .likhari-autocomplete {
1102
1156
  z-index: 20;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Which toolbar items the user has chosen to hide, kept in localStorage so the
3
+ * choice survives a reload. A feature a host turned off (`EditorFeatureConfig`)
4
+ * is never offered here and is unaffected by it — this only lets the user declutter
5
+ * what the host already made available, it does not re-gate availability.
6
+ */
7
+ export declare function useToolbarItemVisibility(): {
8
+ hidden: ReadonlySet<string>;
9
+ isHidden: (id: string) => boolean;
10
+ toggle: (id: string) => void;
11
+ };
12
+ /** One toggleable row in the toolbar settings panel. */
13
+ export interface ToolbarVisibilityItem {
14
+ id: string;
15
+ label: string;
16
+ }
17
+ /** A labelled group of toggleable rows, as shown in the toolbar settings panel. */
18
+ export interface ToolbarVisibilityGroup {
19
+ id: string;
20
+ label: string;
21
+ items: ToolbarVisibilityItem[];
22
+ }
23
+ //# sourceMappingURL=toolbarVisibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolbarVisibility.d.ts","sourceRoot":"","sources":["../../src/toolbar/toolbarVisibility.ts"],"names":[],"mappings":"AAqBA;;;;;GAKG;AACH,wBAAgB,wBAAwB,IAAI;IAAE,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IAAC,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,CAc3I;AAED,wDAAwD;AACxD,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAED,mFAAmF;AACnF,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,qBAAqB,EAAE,CAAC;CAChC"}
@@ -0,0 +1,40 @@
1
+ import { useState } from 'react';
2
+ const STORAGE_KEY = 'likhari-toolbar-hidden-items';
3
+ function readHidden() {
4
+ try {
5
+ const parsed = JSON.parse(localStorage.getItem(STORAGE_KEY) ?? '[]');
6
+ return new Set(Array.isArray(parsed) ? parsed.filter((id) => typeof id === 'string') : []);
7
+ }
8
+ catch {
9
+ return new Set();
10
+ }
11
+ }
12
+ function writeHidden(hidden) {
13
+ try {
14
+ localStorage.setItem(STORAGE_KEY, JSON.stringify([...hidden]));
15
+ }
16
+ catch {
17
+ // Storage disabled or full: the choice applies for this session only.
18
+ }
19
+ }
20
+ /**
21
+ * Which toolbar items the user has chosen to hide, kept in localStorage so the
22
+ * choice survives a reload. A feature a host turned off (`EditorFeatureConfig`)
23
+ * is never offered here and is unaffected by it — this only lets the user declutter
24
+ * what the host already made available, it does not re-gate availability.
25
+ */
26
+ export function useToolbarItemVisibility() {
27
+ const [hidden, setHidden] = useState(() => (typeof localStorage === 'undefined' ? new Set() : readHidden()));
28
+ const toggle = (id) => {
29
+ setHidden((prev) => {
30
+ const next = new Set(prev);
31
+ if (next.has(id))
32
+ next.delete(id);
33
+ else
34
+ next.add(id);
35
+ writeHidden(next);
36
+ return next;
37
+ });
38
+ };
39
+ return { hidden, isHidden: (id) => hidden.has(id), toggle };
40
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inshapardaz/likhari-react",
3
- "version": "0.1.50",
3
+ "version": "0.1.51-dev.407",
4
4
  "description": "React implementation of the Likhari editor, built on @lexical/react.",
5
5
  "license": "MIT",
6
6
  "repository": {