@inshapardaz/likhari-react 0.1.21-dev.205 → 0.1.21-dev.208

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.
@@ -1 +1 @@
1
- {"version":3,"file":"EditorRoot.d.ts","sourceRoot":"","sources":["../src/EditorRoot.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"EditorRoot.d.ts","sourceRoot":"","sources":["../src/EditorRoot.tsx"],"names":[],"mappings":"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"}
@@ -293,7 +293,7 @@ export const EditorRoot = forwardRef(function EditorRoot({ documentId, autoCorre
293
293
  const portalTargetSelector = `#${CSS.escape(portalTargetId)}`;
294
294
  return (_jsx(EditorThemeProvider, { theme: theme, colorScheme: colorScheme, accentColor: accentColor, scopeElementId: portalTargetId, children: _jsxs("div", { className: "likhari-root", dir: dir, ref: rootElementRef, "data-document-id": documentId, style: { height: typeof height === 'number' ? `${height}px` : height }, children: [_jsx("div", { id: portalTargetId, className: "likhari-portal-target" }), _jsx(PortalTargetContext.Provider, { value: portalTargetSelector, children: _jsxs(UiStringsContext.Provider, { value: strings, children: [_jsx(ImageOptionsContext.Provider, { value: imageOptions, children: _jsxs(LexicalComposer, { initialConfig: initialConfig, children: [_jsx(Toolbar, { config: config, onSave: handleSave, findOpen: openPanel === 'find', onToggleFind: () => togglePanel('find'), spellOpen: openPanel === 'spell', onToggleSpell: () => togglePanel('spell'), autoCorrectOpen: openPanel === 'autocorrect', onToggleAutoCorrect: () => togglePanel('autocorrect'), isDirty: isDirty, showSave: showSave, fontOptions: fontOptions, direction: dir, locale: locale, drafts: autosave
295
295
  ? { currentId: draftId, maxBytes: autosaveMaxBytes, adoptOnRestore: !documentId, onRestored: handleDraftRestored }
296
- : undefined }), documentId && (_jsx(DraftRestore, { draftId: documentId, mode: restoreDraft, initialJson: initialContentJsonRef.current, locale: locale, onRestored: handleDraftRestored })), _jsxs("div", { className: "likhari-canvas-frame", children: [config.findReplace && openPanel === 'find' && _jsx(FindReplaceBar, { strings: strings, dir: dir, onClose: () => setOpenPanel(null) }), config.language.spellCheck && openPanel === 'spell' && (_jsx(SpellcheckPanel, { strings: strings, dir: dir, onClose: () => setOpenPanel(null) })), config.language.autocorrect && openPanel === 'autocorrect' && (_jsx(AutoCorrectPanel, { strings: strings, dir: dir, stores: stores, onSaved: () => setAutoCorrectVersion((v) => v + 1), onClose: () => setOpenPanel(null) })), _jsx("div", { className: "likhari-canvas", children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "likhari-content-editable", dir: dir, spellCheck: false, "aria-label": strings.editor.contentLabel }), placeholder: _jsx("div", { className: "likhari-placeholder", children: resolvedPlaceholder }), ErrorBoundary: LexicalErrorBoundary }) })] }), config.history && _jsx(HistoryPlugin, {}), (config.lists.bullet || config.lists.numbered || config.lists.check) && _jsx(ListPlugin, {}), config.lists.check && _jsx(CheckListPlugin, {}), config.links && _jsx(LinkPlugin, {}), config.links && _jsx(LinkPastePlugin, {}), config.blocks.pageBreak && _jsx(PageBreakPlugin, {}), config.language.spellCheck && _jsx(SpellHighlightPlugin, {}), config.language.autocorrect && _jsx(AutoCorrectPlugin, { stores: stores, version: autoCorrectVersion, enabled: true, urduNormalization: urduNormalization, punctuation: punctuation }), config.columns && _jsx(LayoutPlugin, {}), config.footnotes && _jsx(FootnotePlugin, {}), config.poetry.enabled && _jsx(PoetryPlugin, {}), config.poetry.enabled && _jsx(PoetryResizer, {}), config.tables && _jsx(TablePlugin, { hasCellMerge: true, hasTabHandler: true }), config.blocks.horizontalRule && _jsx(HorizontalRulePlugin, {}), _jsx(EditorInstancePlugin, { instanceRef: editorInstanceRef }), _jsx(OnChangePlugin, { onChange: handleChange })] }) }), _jsx(LeaveDialog, { opened: leavePrompt !== null, canSave: Boolean(onSave), canSaveDraft: autosave || navigationGuard === 'save-draft', draftFailed: leavePrompt?.draftFailed ?? false, onSave: () => {
296
+ : undefined }), documentId && (_jsx(DraftRestore, { draftId: documentId, mode: restoreDraft, initialJson: initialContentJsonRef.current, locale: locale, onRestored: handleDraftRestored })), _jsxs("div", { className: "likhari-canvas-frame", children: [config.findReplace && openPanel === 'find' && _jsx(FindReplaceBar, { strings: strings, dir: dir, onClose: () => setOpenPanel(null) }), config.language.spellCheck && openPanel === 'spell' && (_jsx(SpellcheckPanel, { strings: strings, dir: dir, onClose: () => setOpenPanel(null) })), config.language.autocorrect && openPanel === 'autocorrect' && (_jsx(AutoCorrectPanel, { strings: strings, dir: dir, stores: stores, onSaved: () => setAutoCorrectVersion((v) => v + 1), onClose: () => setOpenPanel(null) })), _jsx("div", { className: "likhari-canvas", children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "likhari-content-editable", dir: dir, spellCheck: false, "aria-label": strings.editor.contentLabel }), placeholder: _jsx("div", { className: "likhari-placeholder", children: resolvedPlaceholder }), ErrorBoundary: LexicalErrorBoundary }) })] }), config.history && _jsx(HistoryPlugin, {}), (config.lists.bullet || config.lists.numbered || config.lists.check) && _jsx(ListPlugin, {}), config.lists.check && _jsx(CheckListPlugin, {}), config.links && _jsx(LinkPlugin, {}), config.links && _jsx(LinkPastePlugin, {}), config.blocks.pageBreak && _jsx(PageBreakPlugin, {}), config.language.spellCheck && _jsx(SpellHighlightPlugin, {}), (config.language.autocorrect || config.language.textCleanup) && _jsx(AutoCorrectPlugin, { stores: stores, version: autoCorrectVersion, enabled: true, typing: config.language.autocorrect, urduNormalization: urduNormalization, punctuation: punctuation }), config.columns && _jsx(LayoutPlugin, {}), config.footnotes && _jsx(FootnotePlugin, {}), config.poetry.enabled && _jsx(PoetryPlugin, {}), config.poetry.enabled && _jsx(PoetryResizer, {}), config.tables && _jsx(TablePlugin, { hasCellMerge: true, hasTabHandler: true }), config.blocks.horizontalRule && _jsx(HorizontalRulePlugin, {}), _jsx(EditorInstancePlugin, { instanceRef: editorInstanceRef }), _jsx(OnChangePlugin, { onChange: handleChange })] }) }), _jsx(LeaveDialog, { opened: leavePrompt !== null, canSave: Boolean(onSave), canSaveDraft: autosave || navigationGuard === 'save-draft', draftFailed: leavePrompt?.draftFailed ?? false, onSave: () => {
297
297
  handleSave();
298
298
  settleLeave(true);
299
299
  }, onSaveDraft: () => {
@@ -12,10 +12,13 @@ export declare const CORRECT_DOCUMENT_COMMAND: LexicalCommand<void>;
12
12
  * the table reloads). The table used depends on the block's direction: RTL
13
13
  * blocks use the Urdu and Shahmukhi tables, LTR blocks the English one.
14
14
  */
15
- export declare function AutoCorrectPlugin({ stores, version, enabled, urduNormalization, punctuation, }: {
15
+ export declare function AutoCorrectPlugin({ stores, version, enabled, typing, urduNormalization, punctuation, }: {
16
16
  stores: AutoCorrectStore[];
17
17
  version: number;
18
+ /** Whether the tables and the whole-document command are active. */
18
19
  enabled: boolean;
20
+ /** Whether corrections run as the user types (auto-correct), not only on demand. */
21
+ typing: boolean;
19
22
  /** Urdu normalisation for right-to-left text; character mapping always applies. */
20
23
  urduNormalization?: UrduNormalizationOptions;
21
24
  /** Common punctuation fixes (from the bundled Urdu rule list). */
@@ -1 +1 @@
1
- {"version":3,"file":"AutoCorrectPlugin.d.ts","sourceRoot":"","sources":["../../src/autocorrect/AutoCorrectPlugin.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA4E,KAAK,cAAc,EAAE,MAAM,SAAS,CAAC;AAQxH,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,iBAAiB,EACjB,WAAW,GACZ,EAAE;IACD,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,mFAAmF;IACnF,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;IAC7C,kEAAkE;IAClE,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC,QA0FA"}
1
+ {"version":3,"file":"AutoCorrectPlugin.d.ts","sourceRoot":"","sources":["../../src/autocorrect/AutoCorrectPlugin.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA4E,KAAK,cAAc,EAAE,MAAM,SAAS,CAAC;AAUxH,OAAO,EAA0B,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACrF,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAiB,KAAK,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAG9F,sFAAsF;AACtF,kFAAkF;AAClF,eAAO,MAAM,wBAAwB,EAAE,cAAc,CAAC,IAAI,CAA6C,CAAC;AAMxG;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,OAAO,EACP,OAAO,EACP,MAAM,EACN,iBAAiB,EACjB,WAAW,GACZ,EAAE;IACD,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,OAAO,EAAE,OAAO,CAAC;IACjB,oFAAoF;IACpF,MAAM,EAAE,OAAO,CAAC;IAChB,mFAAmF;IACnF,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;IAC7C,kEAAkE;IAClE,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC,QA4FA"}
@@ -1,7 +1,8 @@
1
1
  import { useEffect, useRef } from 'react';
2
2
  import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
3
3
  import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_EDITOR, createCommand } from 'lexical';
4
- import { $correctDocument, $correctPunctuationBeforeCaret, $correctPunctuationDocument, $correctWordBeforeCaret, $preservingSelection, } from './autoCorrectActions.js';
4
+ import { $correctDocument, $correctPunctuationBeforeCaret, $correctPunctuationDocument, $correctWordBeforeCaret, $preservingSelection, $spacePunctuationDocument, } from './autoCorrectActions.js';
5
+ import { $cleanUpText } from '../cleanup/textCleanupActions.js';
5
6
  import { activePunctuationRules } from './punctuationRules.js';
6
7
  import { loadAutoCorrections } from './autoCorrectStores.js';
7
8
  import { normalizeUrdu } from '../normalization/urduNormalize.js';
@@ -18,7 +19,7 @@ const RTL_LANGUAGES = ['ur', 'pa-shahmukhi'];
18
19
  * the table reloads). The table used depends on the block's direction: RTL
19
20
  * blocks use the Urdu and Shahmukhi tables, LTR blocks the English one.
20
21
  */
21
- export function AutoCorrectPlugin({ stores, version, enabled, urduNormalization, punctuation, }) {
22
+ export function AutoCorrectPlugin({ stores, version, enabled, typing, urduNormalization, punctuation, }) {
22
23
  const [editor] = useLexicalComposerContext();
23
24
  // Effects below run once per editor, so they reach the latest options through this ref.
24
25
  const normalizeOptions = useRef(urduNormalization);
@@ -55,15 +56,18 @@ export function AutoCorrectPlugin({ stores, version, enabled, urduNormalization,
55
56
  return editor.registerCommand(CORRECT_DOCUMENT_COMMAND, () => {
56
57
  editor.update(() => {
57
58
  $preservingSelection(() => {
59
+ $cleanUpText();
60
+ if (punctuation?.spacing)
61
+ $spacePunctuationDocument();
58
62
  $correctPunctuationDocument(punctuationRules.current);
59
63
  $correctDocument(tables.current.get('en') ?? new Map(), rtlTable.current, normalizeRtl);
60
64
  });
61
65
  }, { discrete: true });
62
66
  return true;
63
67
  }, COMMAND_PRIORITY_EDITOR);
64
- }, [editor, enabled]);
68
+ }, [editor, enabled, punctuation?.spacing]);
65
69
  useEffect(() => {
66
- if (!enabled)
70
+ if (!enabled || !typing)
67
71
  return;
68
72
  const tableFor = (rtl) => (rtl ? rtlTable.current : tables.current.get('en'));
69
73
  const onKeyDown = (event) => {
@@ -99,6 +103,6 @@ export function AutoCorrectPlugin({ stores, version, enabled, urduNormalization,
99
103
  unregister();
100
104
  attach(null);
101
105
  };
102
- }, [editor, enabled]);
106
+ }, [editor, enabled, typing]);
103
107
  return null;
104
108
  }
@@ -1,4 +1,4 @@
1
- import type { PunctuationRule } from './punctuationRules.js';
1
+ import { type PunctuationRule } from './punctuationRules.js';
2
2
  /**
3
3
  * Corrects the word that ends just before the caret, when the character just
4
4
  * typed (the word boundary: space or punctuation) is not a word character. The
@@ -30,4 +30,6 @@ export declare function $correctPunctuationDocument(rules: PunctuationRule[]): n
30
30
  * leave the caret at the last match, or at the end of the document.
31
31
  */
32
32
  export declare function $preservingSelection(change: () => void): void;
33
+ /** Applies urduhack-style punctuation spacing to every text node. Returns how many changed. */
34
+ export declare function $spacePunctuationDocument(): number;
33
35
  //# sourceMappingURL=autoCorrectActions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"autoCorrectActions.d.ts","sourceRoot":"","sources":["../../src/autocorrect/autoCorrectActions.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAM1D;;;;;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"}
1
+ {"version":3,"file":"autoCorrectActions.d.ts","sourceRoot":"","sources":["../../src/autocorrect/autoCorrectActions.ts"],"names":[],"mappings":"AAWA,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAMhF;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAiCjH;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACxB,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACxB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GACtC,MAAM,CA8BR;AAQD;;;;GAIG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,OAAO,CAqBhF;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,MAAM,CA2B5E;AAID;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,IAAI,GAAG,IAAI,CAoB7D;AAED,+FAA+F;AAC/F,wBAAgB,yBAAyB,IAAI,MAAM,CAWlD"}
@@ -1,6 +1,7 @@
1
1
  import { $createRangeSelection, $getNodeByKey, $getRoot, $getSelection, $isRangeSelection, $isTextNode, $setSelection, } from 'lexical';
2
2
  import { $locate, $replaceMatch, $textGroups } from '../find/findReplaceActions.js';
3
3
  import { wordsIn } from '../spellcheck/spellDictionaries.js';
4
+ import { spacePunctuationText } from './punctuationRules.js';
4
5
  /** The part of a word that counts as word characters at its end. */
5
6
  const WORD_END = /[\p{L}\p{M}'’]+$/u;
6
7
  const WORD_CHAR = /[\p{L}\p{M}'’]/u;
@@ -166,3 +167,16 @@ export function $preservingSelection(change) {
166
167
  selection.focus.set(saved.focus.key, saved.focus.offset, saved.focus.type);
167
168
  $setSelection(selection);
168
169
  }
170
+ /** Applies urduhack-style punctuation spacing to every text node. Returns how many changed. */
171
+ export function $spacePunctuationDocument() {
172
+ let changed = 0;
173
+ for (const node of $getRoot().getAllTextNodes()) {
174
+ const text = node.getTextContent();
175
+ const spaced = spacePunctuationText(text);
176
+ if (spaced !== text) {
177
+ node.setTextContent(spaced);
178
+ changed += 1;
179
+ }
180
+ }
181
+ return changed;
182
+ }
@@ -16,7 +16,11 @@ export interface PunctuationOptions {
16
16
  enabled?: boolean;
17
17
  /** Replace a straight double quote (") with a closing curly quote (”). On by default. */
18
18
  straightDoubleQuote?: boolean;
19
+ /** Space after Urdu punctuation and remove spaces before it, as urduhack's punctuations_space. Off by default. */
20
+ spacing?: boolean;
19
21
  }
20
22
  /** The rules in use for the options, longest match first, so `۔"` wins over `"`. */
21
23
  export declare function activePunctuationRules(options?: PunctuationOptions): PunctuationRule[];
24
+ /** Spaces after Urdu punctuation (not inside numbers), and none before it. */
25
+ export declare function spacePunctuationText(text: string): string;
22
26
  //# sourceMappingURL=punctuationRules.d.ts.map
@@ -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;CAC/B;AAED,oFAAoF;AACpF,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,kBAAuB,GAAG,eAAe,EAAE,CAK1F"}
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"}
@@ -43,3 +43,11 @@ export function activePunctuationRules(options = {}) {
43
43
  rules.push({ incorrect: '"', correct: '”', completeWord: false });
44
44
  return rules.sort((a, b) => b.incorrect.length - a.incorrect.length);
45
45
  }
46
+ /** Space after Urdu punctuation, when the next character is not punctuation, a digit or a space. */
47
+ const SPACE_AFTER_PUNCTUATION = /(?<=[؛،٫؟۔٪])(?=[^؛،٫؟۔٪0-9 \n])/gu;
48
+ /** Spaces before Urdu punctuation. */
49
+ const SPACE_BEFORE_PUNCTUATION = /\s+([؛،٫؟۔٪])/gu;
50
+ /** Spaces after Urdu punctuation (not inside numbers), and none before it. */
51
+ export function spacePunctuationText(text) {
52
+ return text.replace(SPACE_AFTER_PUNCTUATION, ' ').replace(SPACE_BEFORE_PUNCTUATION, '$1');
53
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Tidies the whole document in place: Unicode NFC normalisation (Arabic-script
3
+ * text can store the same visible letters in different code points), collapsed
4
+ * repeated spaces, and empty text nodes removed. Formatting is kept, since each
5
+ * text node is only changed, never replaced. Returns how many text nodes changed.
6
+ */
7
+ export declare function $cleanUpText(): number;
8
+ //# sourceMappingURL=textCleanupActions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"textCleanupActions.d.ts","sourceRoot":"","sources":["../../src/cleanup/textCleanupActions.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAMrC"}
@@ -0,0 +1,28 @@
1
+ import { $getRoot } from 'lexical';
2
+ /** Runs of two or more spaces or tabs, collapsed to one space. */
3
+ const REPEATED_SPACE = /[ \t]{2,}/g;
4
+ /**
5
+ * Tidies the whole document in place: Unicode NFC normalisation (Arabic-script
6
+ * text can store the same visible letters in different code points), collapsed
7
+ * repeated spaces, and empty text nodes removed. Formatting is kept, since each
8
+ * text node is only changed, never replaced. Returns how many text nodes changed.
9
+ */
10
+ export function $cleanUpText() {
11
+ let changed = 0;
12
+ for (const node of $getRoot().getAllTextNodes()) {
13
+ if (cleanNode(node))
14
+ changed += 1;
15
+ }
16
+ return changed;
17
+ }
18
+ function cleanNode(node) {
19
+ const text = node.getTextContent();
20
+ const cleaned = text.normalize('NFC').replace(REPEATED_SPACE, ' ');
21
+ if (cleaned === text)
22
+ return false;
23
+ if (cleaned === '')
24
+ node.remove();
25
+ else
26
+ node.setTextContent(cleaned);
27
+ return true;
28
+ }
@@ -638,7 +638,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, findOpen = false, o
638
638
  config.columns ||
639
639
  config.footnotes ||
640
640
  config.poetry.enabled;
641
- const showLanguageGroup = config.language.autocorrect || config.language.spellCheck;
641
+ const showLanguageGroup = config.language.autocorrect || config.language.textCleanup || config.language.spellCheck;
642
642
  // The "script & cleanup" and "indent/outdent" groups (UI spec §3.3) render
643
643
  // as normal inline toolbar groups whenever there's room, and only move
644
644
  // into the "..." overflow menu when the measured toolbar content doesn't
@@ -812,7 +812,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, findOpen = false, o
812
812
  showStubInsertGroup && (_jsxs("div", { className: "likhari-toolbar-group likhari-toolbar-group--collapse-tablet", children: [(config.images.linked || config.images.embedded) && (_jsx(ToolbarButton, { icon: IconPhoto, title: strings.toolbar.insertImage, onClick: openImageDialog })), config.blocks.pageBreak && (_jsx(ToolbarButton, { icon: IconPageBreak, title: strings.toolbar.insertPageBreak, onClick: () => editor.dispatchCommand(INSERT_PAGE_BREAK_COMMAND, undefined) })), config.tables && _jsx(ToolbarButton, { icon: IconTable, title: strings.toolbar.insertTable, onClick: openTableDialog }), 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.horizontalRule && (_jsx(ToolbarButton, { icon: IconSeparatorHorizontal, title: strings.toolbar.insertHorizontalRule, onClick: () => editor.dispatchCommand(INSERT_HORIZONTAL_RULE_COMMAND, undefined) })), config.findReplace && (_jsx(ToolbarButton, { icon: IconSearch, title: strings.findReplace.toggle, active: findOpen, onClick: onToggleFind })), config.poetry.enabled && locale !== 'en' && (_jsx(ToolbarButton, { icon: IconFeather, title: strings.toolbar.insertPoetryCouplet, onClick: () => editor.dispatchCommand(INSERT_POETRY_COUPLET_COMMAND, { layout: config.poetry.defaultLayout }) }))] }, "stubInsert")),
813
813
  config.tables && state.inTable && (_jsx("div", { className: "likhari-toolbar-group", children: _jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 240, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx("button", { type: "button", className: "likhari-toolbar-button", "data-active": "true", "aria-pressed": "true", "aria-haspopup": "menu", "aria-label": strings.toolbar.tableOptions, title: strings.toolbar.tableOptions, onMouseDown: (e) => e.preventDefault(), children: _jsx(IconTableOptions, { size: ICON_SIZE, stroke: ICON_STROKE }) }) }), _jsx(Menu.Dropdown, { children: _jsx(TableMenuItems, { strings: strings, direction: direction, rows: state.tableRows, columns: state.tableColumns, canMergeCells: state.canMergeCells, canUnmergeCell: state.canUnmergeCell, onAction: runMenuAction }) })] }) }, "tableActions")),
814
814
  config.poetry.enabled && state.inPoetry && (_jsx("div", { className: "likhari-toolbar-group", children: _jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 200, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx("button", { type: "button", className: "likhari-toolbar-button", "data-active": "true", "aria-pressed": "true", "aria-haspopup": "menu", "aria-label": strings.toolbar.poetryOptions, title: strings.toolbar.poetryOptions, onMouseDown: (e) => e.preventDefault(), children: _jsx(IconFeather, { size: ICON_SIZE, stroke: ICON_STROKE }) }) }), _jsx(Menu.Dropdown, { children: _jsx(PoetryMenuItems, { strings: strings, layout: state.poetryLayout, scale: state.poetryScale, centered: state.poetryCentered, onAction: runMenuAction }) })] }) }, "poetryActions")),
815
- showLanguageGroup && (_jsxs("div", { className: "likhari-toolbar-group likhari-toolbar-group--collapse-tablet", children: [config.language.autocorrect && (_jsx(ToolbarButton, { icon: IconWand, title: strings.toolbar.autocorrect, active: autoCorrectOpen, onClick: onToggleAutoCorrect })), config.language.autocorrect && (_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 }))] }, "language")),
815
+ showLanguageGroup && (_jsxs("div", { className: "likhari-toolbar-group likhari-toolbar-group--collapse-tablet", 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 }))] }, "language")),
816
816
  ];
817
817
  return (_jsxs("div", { className: "likhari-toolbar", role: "toolbar", "aria-label": strings.toolbar.ariaLabel, ref: toolbarContainerRef, children: [fixedGroups, movableGroups
818
818
  .filter((group) => !overflowGroupKeys.has(group.key))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inshapardaz/likhari-react",
3
- "version": "0.1.21-dev.205",
3
+ "version": "0.1.21-dev.208",
4
4
  "description": "React implementation of the Likhari editor, built on @lexical/react.",
5
5
  "license": "MIT",
6
6
  "repository": {