@inshapardaz/likhari-react 0.1.19-dev.186 → 0.1.19-dev.187

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":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAY1D,OAAO,KAAK,EAA8B,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAEjF,OAAO,EAAyB,KAAK,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AACvF,OAAO,EAAwB,KAAK,mBAAmB,EAAE,KAAK,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAKzH,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAOjE,OAAO,EAAgB,KAAK,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAchF,OAAO,EAAgC,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAMnE,yEAAyE;AACzE,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,YAAY,GAAG,KAAK,CAAC;AAEnE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,QAAQ,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,aAAa,CAAC,EAAE,uBAAuB,CAAC;IACxC,KAAK,CAAC,EAAE,oBAAoB,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC/B;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAClD;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,KAAK,IAAI,CAAC;IACrD;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC;;;;;;;;;;;;;;;OAeG;IACH,eAAe,CAAC,EAAE,mBAAmB,CAAC;IACtC,uGAAuG;IACvG,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3E,6EAA6E;IAC7E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,SAAS;IACxB,UAAU,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;IACrC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IAClD,iBAAiB,IAAI,OAAO,CAAC;IAC7B,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,KAAK,IAAI,IAAI,CAAC;CACf;AAQD,eAAO,MAAM,UAAU,uGAqYrB,CAAC"}
1
+ {"version":3,"file":"EditorRoot.d.ts","sourceRoot":"","sources":["../src/EditorRoot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAY1D,OAAO,KAAK,EAA8B,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAEjF,OAAO,EAAyB,KAAK,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AACvF,OAAO,EAAwB,KAAK,mBAAmB,EAAE,KAAK,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAKzH,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAOjE,OAAO,EAAgB,KAAK,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAehF,OAAO,EAAgC,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAMnE,yEAAyE;AACzE,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,YAAY,GAAG,KAAK,CAAC;AAEnE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,QAAQ,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,aAAa,CAAC,EAAE,uBAAuB,CAAC;IACxC,KAAK,CAAC,EAAE,oBAAoB,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC/B;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAClD;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,KAAK,IAAI,CAAC;IACrD;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC;;;;;;;;;;;;;;;OAeG;IACH,eAAe,CAAC,EAAE,mBAAmB,CAAC;IACtC,uGAAuG;IACvG,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3E,6EAA6E;IAC7E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,SAAS;IACxB,UAAU,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;IACrC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IAClD,iBAAiB,IAAI,OAAO,CAAC;IAC7B,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,KAAK,IAAI,IAAI,CAAC;CACf;AAQD,eAAO,MAAM,UAAU,uGAwYrB,CAAC"}
@@ -28,6 +28,7 @@ import { PoetryResizer } from './blocks/PoetryResizer.js';
28
28
  import { DraftRestore } from './components/DraftRestore.js';
29
29
  import { FindReplaceBar } from './components/FindReplaceBar.js';
30
30
  import { SpellcheckPanel } from './components/SpellcheckPanel.js';
31
+ import { SpellHighlightPlugin } from './spellcheck/SpellHighlightPlugin.js';
31
32
  import { LeaveDialog } from './components/LeaveDialog.js';
32
33
  import { clearDraft, createDraftId, hasDraftContent, pruneDrafts, writeDraft, } from './persistence/draftStorage.js';
33
34
  import { ImageOptionsContext } from './image/ImageOptionsContext.js';
@@ -291,7 +292,7 @@ export const EditorRoot = forwardRef(function EditorRoot({ documentId, initialCo
291
292
  setFindOpen(false);
292
293
  }, isDirty: isDirty, showSave: showSave, fontOptions: fontOptions, direction: dir, locale: locale, drafts: autosave
293
294
  ? { currentId: draftId, maxBytes: autosaveMaxBytes, adoptOnRestore: !documentId, onRestored: handleDraftRestored }
294
- : undefined }), documentId && (_jsx(DraftRestore, { draftId: documentId, mode: restoreDraft, initialJson: initialContentJsonRef.current, locale: locale, onRestored: handleDraftRestored })), _jsxs("div", { className: "likhari-canvas-frame", children: [config.findReplace && findOpen && _jsx(FindReplaceBar, { strings: strings, dir: dir, onClose: () => setFindOpen(false) }), config.language.spellCheck && spellOpen && _jsx(SpellcheckPanel, { strings: strings, onClose: () => setSpellOpen(false) }), _jsx("div", { className: "likhari-canvas", children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "likhari-content-editable", dir: dir, "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.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: () => {
295
+ : undefined }), documentId && (_jsx(DraftRestore, { draftId: documentId, mode: restoreDraft, initialJson: initialContentJsonRef.current, locale: locale, onRestored: handleDraftRestored })), _jsxs("div", { className: "likhari-canvas-frame", children: [config.findReplace && findOpen && _jsx(FindReplaceBar, { strings: strings, dir: dir, onClose: () => setFindOpen(false) }), config.language.spellCheck && spellOpen && _jsx(SpellcheckPanel, { strings: strings, onClose: () => setSpellOpen(false) }), _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.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: () => {
295
296
  handleSave();
296
297
  settleLeave(true);
297
298
  }, onSaveDraft: () => {
@@ -29,7 +29,7 @@ export function SpellcheckPanel({ strings, onClose }) {
29
29
  return;
30
30
  setChecking(true);
31
31
  const resolved = await speller;
32
- const found = editor.getEditorState().read(() => $collectMisspellings(resolved));
32
+ const found = editor.getEditorState().read(() => $collectMisspellings({ ltr: resolved, rtl: resolved }));
33
33
  setItems(found.map((m) => ({
34
34
  ...m,
35
35
  suggestions: resolved.suggest(m.word).slice(0, SUGGESTIONS_PER_WORD),
@@ -50,7 +50,7 @@ export function SpellcheckPanel({ strings, onClose }) {
50
50
  editor.update(() => {
51
51
  // The list can be stale if the document changed since it was checked:
52
52
  // only replace if the same word is still at the same place.
53
- const current = $collectMisspellings(resolved).find((m) => m.word === item.word && m.match.anchorKey === item.match.anchorKey && m.match.anchorOffset === item.match.anchorOffset);
53
+ const current = $collectMisspellings({ ltr: resolved, rtl: resolved }).find((m) => m.word === item.word && m.match.anchorKey === item.match.anchorKey && m.match.anchorOffset === item.match.anchorOffset);
54
54
  if (current)
55
55
  $replaceMatch(current.match, suggestion);
56
56
  }, { discrete: true });
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Underlines misspelled words the way the browser does, without changing the
3
+ * document: ranges are set on the rendered text through the CSS Custom
4
+ * Highlight API. Checking is debounced so typing stays smooth. In browsers
5
+ * without the API, nothing is underlined.
6
+ */
7
+ export declare function SpellHighlightPlugin(): null;
8
+ //# sourceMappingURL=SpellHighlightPlugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpellHighlightPlugin.d.ts","sourceRoot":"","sources":["../../src/spellcheck/SpellHighlightPlugin.tsx"],"names":[],"mappings":"AASA;;;;;GAKG;AACH,wBAAgB,oBAAoB,SAgDnC"}
@@ -0,0 +1,57 @@
1
+ import { useEffect } from 'react';
2
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
3
+ import { $collectMisspellings } from './spellcheckActions.js';
4
+ import { getSpeller } from './spellDictionaries.js';
5
+ /** The CSS Custom Highlight name; editor.css styles it as a red wavy underline. */
6
+ const HIGHLIGHT = 'likhari-spelling';
7
+ const DEBOUNCE_MS = 300;
8
+ /**
9
+ * Underlines misspelled words the way the browser does, without changing the
10
+ * document: ranges are set on the rendered text through the CSS Custom
11
+ * Highlight API. Checking is debounced so typing stays smooth. In browsers
12
+ * without the API, nothing is underlined.
13
+ */
14
+ export function SpellHighlightPlugin() {
15
+ const [editor] = useLexicalComposerContext();
16
+ useEffect(() => {
17
+ if (typeof CSS === 'undefined' || !('highlights' in CSS))
18
+ return;
19
+ let checkers = null;
20
+ let timer;
21
+ let active = true;
22
+ const paint = () => {
23
+ if (!active || !checkers)
24
+ return;
25
+ const found = editor.getEditorState().read(() => $collectMisspellings(checkers));
26
+ const ranges = [];
27
+ for (const misspelling of found) {
28
+ for (const segment of misspelling.segments) {
29
+ const text = editor.getElementByKey(segment.key)?.firstChild;
30
+ if (!text || text.nodeType !== Node.TEXT_NODE)
31
+ continue;
32
+ const range = document.createRange();
33
+ range.setStart(text, segment.start);
34
+ range.setEnd(text, segment.end);
35
+ ranges.push(range);
36
+ }
37
+ }
38
+ CSS.highlights.set(HIGHLIGHT, new Highlight(...ranges));
39
+ };
40
+ const schedule = () => {
41
+ clearTimeout(timer);
42
+ timer = setTimeout(paint, DEBOUNCE_MS);
43
+ };
44
+ const unregister = editor.registerUpdateListener(schedule);
45
+ Promise.all([getSpeller('en'), getSpeller('ur')]).then(async ([en, ur]) => {
46
+ checkers = { ltr: en ? await en : undefined, rtl: ur ? await ur : undefined };
47
+ schedule();
48
+ });
49
+ return () => {
50
+ active = false;
51
+ clearTimeout(timer);
52
+ unregister();
53
+ CSS.highlights.delete(HIGHLIGHT);
54
+ };
55
+ }, [editor]);
56
+ return null;
57
+ }
@@ -1,9 +1,21 @@
1
1
  import { type FindMatch } from '../find/findReplaceActions.js';
2
2
  import { type Speller } from './spellDictionaries.js';
3
+ /** The checker for each text direction: English for LTR blocks, Urdu for RTL. */
4
+ export interface Checkers {
5
+ ltr?: Speller;
6
+ rtl?: Speller;
7
+ }
8
+ /** The part of a misspelled word that falls inside one text node. */
9
+ export interface Segment {
10
+ key: string;
11
+ start: number;
12
+ end: number;
13
+ }
3
14
  export interface Misspelling {
4
15
  word: string;
5
16
  match: FindMatch;
17
+ segments: Segment[];
6
18
  }
7
- /** Every word in the document the speller does not recognise, with its position. */
8
- export declare function $collectMisspellings(speller: Speller): Misspelling[];
19
+ /** Every word in the document its block's checker does not recognise. */
20
+ export declare function $collectMisspellings(checkers: Checkers): Misspelling[];
9
21
  //# sourceMappingURL=spellcheckActions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"spellcheckActions.d.ts","sourceRoot":"","sources":["../../src/spellcheck/spellcheckActions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAClF,OAAO,EAAW,KAAK,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAE5D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,SAAS,CAAC;CAClB;AAED,oFAAoF;AACpF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,WAAW,EAAE,CAepE"}
1
+ {"version":3,"file":"spellcheckActions.d.ts","sourceRoot":"","sources":["../../src/spellcheck/spellcheckActions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAClF,OAAO,EAAW,KAAK,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAE5D,iFAAiF;AACjF,MAAM,WAAW,QAAQ;IACvB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,qEAAqE;AACrE,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,SAAS,CAAC;IACjB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,yEAAyE;AACzE,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,GAAG,WAAW,EAAE,CAmBtE"}
@@ -1,9 +1,13 @@
1
1
  import { $textGroups, $locate } from '../find/findReplaceActions.js';
2
2
  import { wordsIn } from './spellDictionaries.js';
3
- /** Every word in the document the speller does not recognise, with its position. */
4
- export function $collectMisspellings(speller) {
3
+ /** Every word in the document its block's checker does not recognise. */
4
+ export function $collectMisspellings(checkers) {
5
5
  const found = [];
6
6
  for (const group of $textGroups()) {
7
+ const direction = group[0].getParentOrThrow().getDirection();
8
+ const speller = direction === 'rtl' ? checkers.rtl : checkers.ltr;
9
+ if (!speller)
10
+ continue;
7
11
  const text = group.map((node) => node.getTextContent()).join('');
8
12
  for (const span of wordsIn(text)) {
9
13
  if (speller.correct(span.word))
@@ -13,8 +17,22 @@ export function $collectMisspellings(speller) {
13
17
  found.push({
14
18
  word: span.word,
15
19
  match: { anchorKey: start.key, anchorOffset: start.offset, focusKey: end.key, focusOffset: end.offset },
20
+ segments: segmentsOf(group, span.start, span.end),
16
21
  });
17
22
  }
18
23
  }
19
24
  return found;
20
25
  }
26
+ function segmentsOf(group, start, end) {
27
+ const segments = [];
28
+ let offset = 0;
29
+ for (const node of group) {
30
+ const length = node.getTextContent().length;
31
+ const from = Math.max(start, offset);
32
+ const to = Math.min(end, offset + length);
33
+ if (to > from)
34
+ segments.push({ key: node.getKey(), start: from - offset, end: to - offset });
35
+ offset += length;
36
+ }
37
+ return segments;
38
+ }
@@ -963,3 +963,10 @@
963
963
  .likhari-spell-suggestion {
964
964
  font-size: 12px;
965
965
  }
966
+
967
+ /* Misspelled words, underlined like the browser's own spellcheck. Styled
968
+ through the CSS Custom Highlight API by SpellHighlightPlugin. */
969
+ ::highlight(likhari-spelling) {
970
+ text-decoration: red wavy underline;
971
+ text-decoration-skip-ink: none;
972
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inshapardaz/likhari-react",
3
- "version": "0.1.19-dev.186",
3
+ "version": "0.1.19-dev.187",
4
4
  "description": "React implementation of the Likhari editor, built on @lexical/react.",
5
5
  "license": "MIT",
6
6
  "repository": {