@fileverse-dev/ddoc 4.6.1-patch-3 → 4.6.1-ps.0

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,4 +1,4 @@
1
- import { k as Ql, b as Zl, s as eh } from "./index-DRl2meX_.mjs";
1
+ import { k as Ql, b as Zl, s as eh } from "./index-EZXg-EBW.mjs";
2
2
  let An = [], Xr = [];
3
3
  (() => {
4
4
  let s = "lc,34,7n,7,7b,19,,,,2,,2,,,20,b,1c,l,g,,2t,7,2,6,2,2,,4,z,,u,r,2j,b,1m,9,9,,o,4,,9,,3,,5,17,3,3b,f,,w,1j,,,,4,8,4,,3,7,a,2,t,,1m,,,,2,4,8,,9,,a,2,q,,2,2,1l,,4,2,4,2,2,3,3,,u,2,3,,b,2,1l,,4,5,,2,4,,k,2,m,6,,,1m,,,2,,4,8,,7,3,a,2,u,,1n,,,,c,,9,,14,,3,,1l,3,5,3,,4,7,2,b,2,t,,1m,,2,,2,,3,,5,2,7,2,b,2,s,2,1l,2,,,2,4,8,,9,,a,2,t,,20,,4,,2,3,,,8,,29,,2,7,c,8,2q,,2,9,b,6,22,2,r,,,,,,1j,e,,5,,2,5,b,,10,9,,2u,4,,6,,2,2,2,p,2,4,3,g,4,d,,2,2,6,,f,,jj,3,qa,3,t,3,t,2,u,2,1s,2,,7,8,,2,b,9,,19,3,3b,2,y,,3a,3,4,2,9,,6,3,63,2,2,,1m,,,7,,,,,2,8,6,a,2,,1c,h,1r,4,1c,7,,,5,,14,9,c,2,w,4,2,2,,3,1k,,,2,3,,,3,1m,8,2,2,48,3,,d,,7,4,,6,,3,2,5i,1m,,5,ek,,5f,x,2da,3,3x,,2o,w,fe,6,2x,2,n9w,4,,a,w,2,28,2,7k,,3,,4,,p,2,5,,47,2,q,i,d,,12,8,p,b,1a,3,1c,,2,4,2,2,13,,1v,6,2,2,2,2,c,,8,,1b,,1f,,,3,2,2,5,2,,,16,2,8,,6m,,2,,4,,fn4,,kh,g,g,g,a6,2,gt,,6a,,45,5,1ae,3,,2,5,4,14,3,4,,4l,2,fx,4,ar,2,49,b,4w,,1i,f,1k,3,1d,4,2,2,1x,3,10,5,,8,1q,,c,2,1g,9,a,4,2,,2n,3,2,,,2,6,,4g,,3,8,l,2,1l,2,,,,,m,,e,7,3,5,5f,8,2,3,,,n,,29,,2,6,,,2,,,2,,2,6j,,2,4,6,2,,2,r,2,2d,8,2,,,2,2y,,,,2,6,,,2t,3,2,4,,5,77,9,,2,6t,,a,2,,,4,,40,4,2,2,4,,w,a,14,6,2,4,8,,9,6,2,3,1a,d,,2,ba,7,,6,,,2a,m,2,7,,2,,2,3e,6,3,,,2,,7,,,20,2,3,,,,9n,2,f0b,5,1n,7,t4,,1r,4,29,,f5k,2,43q,,,3,4,5,8,8,2,7,u,4,44,3,1iz,1j,4,1e,8,,e,,m,5,,f,11s,7,,h,2,7,,2,,5,79,7,c5,4,15s,7,31,7,240,5,gx7k,2o,3k,6o".split(",").map((e) => e ? parseInt(e, 36) : 1);
package/dist/index.es.js CHANGED
@@ -1,4 +1,4 @@
1
- import { D as a, a as o, E as t, P as d, f as r, i, g as n, j as m, h as E, m as c, e as l, l as p, c as u, d as C, u as f, v as D } from "./index-DRl2meX_.mjs";
1
+ import { D as a, a as o, E as t, P as d, f as r, i, g as n, j as m, h as E, m as c, e as l, l as p, c as u, d as C, u as f, v as D } from "./index-EZXg-EBW.mjs";
2
2
  export {
3
3
  a as DdocEditor,
4
4
  o as DdocExportModal,
@@ -3,7 +3,7 @@ var Wr = (t, e, O) => e in t ? Vr(t, e, { enumerable: !0, configurable: !0, writ
3
3
  var gt = (t, e, O) => Wr(t, typeof e != "symbol" ? e + "" : e, O);
4
4
  import { jsx as Cr } from "react/jsx-runtime";
5
5
  import { useRef as pe, useEffect as St } from "react";
6
- import { s as y, A as pO, T as dO, a as Gr, S as et, b as Lr, P as fO, E as j, i as QO, c as _, D as le, k as tt, F as Ar, M as Pt, W as Er, R as Ur, d as mO, N as oe, e as V, f as Mr, g as Ot, h as ce, p as $O, j as Y, t as h, l as Ir, I as rt, L as at, m as ye, n as ue, o as Qe, q as gO, r as te, u as SO, v as Br, w as Nr, x as PO, y as kO, z as Dr, B as Fr, C as Kr, G as Oe, H as ZO, J as Jr, K as kt, O as Hr, Q as ea, U as ta, V as Oa, X as ra, Y as aa, Z as ia, _ as sa, $ as na, a0 as la, a1 as oa, a2 as ca } from "./index-BaJBrKSQ.mjs";
6
+ import { s as y, A as pO, T as dO, a as Gr, S as et, b as Lr, P as fO, E as j, i as QO, c as _, D as le, k as tt, F as Ar, M as Pt, W as Er, R as Ur, d as mO, N as oe, e as V, f as Mr, g as Ot, h as ce, p as $O, j as Y, t as h, l as Ir, I as rt, L as at, m as ye, n as ue, o as Qe, q as gO, r as te, u as SO, v as Br, w as Nr, x as PO, y as kO, z as Dr, B as Fr, C as Kr, G as Oe, H as ZO, J as Jr, K as kt, O as Hr, Q as ea, U as ta, V as Oa, X as ra, Y as aa, Z as ia, _ as sa, $ as na, a0 as la, a1 as oa, a2 as ca } from "./index-K2dDPRDs.mjs";
7
7
  class ua {
8
8
  /**
9
9
  Create a new completion context. (Mostly useful for testing
@@ -5,3 +5,16 @@ export declare const shouldShow: ({ editor }: {
5
5
  editor: Editor;
6
6
  }) => boolean;
7
7
  export declare const shouldShowIgnoringFocus: (editor: Editor) => boolean;
8
+ /**
9
+ * Whether the bubble menu should take itself out of the way.
10
+ *
11
+ * It floats at an inline `z-index: 61` while @fileverse/ui dialogs stack at
12
+ * `z-50`, so a modal cannot paint over it — anything that owns the screen has
13
+ * to hide the menu explicitly or it overlaps the dialog.
14
+ */
15
+ export declare const isBubbleMenuHidden: ({ isCommentOpen, isLinkPopupOpen, isBubbleMenuSuppressed, isCustomSpacingOpen, }: {
16
+ isCommentOpen: boolean;
17
+ isLinkPopupOpen: boolean;
18
+ isBubbleMenuSuppressed: boolean;
19
+ isCustomSpacingOpen: boolean;
20
+ }) => boolean;
@@ -0,0 +1,14 @@
1
+ import { Editor } from '@tiptap/core';
2
+
3
+ export declare const CustomSpacingDialog: ({ editor, open, onOpenChange, }: {
4
+ editor: Editor | null;
5
+ open: boolean;
6
+ onOpenChange: (open: boolean) => void;
7
+ }) => import("react/jsx-runtime").JSX.Element;
8
+ /**
9
+ * The single mounted instance, wired to the store. Subscribing here rather
10
+ * than at the mount site keeps the open/close re-render off the editor tree.
11
+ */
12
+ export declare const CustomSpacingDialogHost: ({ editor, }: {
13
+ editor: Editor | null;
14
+ }) => import("react/jsx-runtime").JSX.Element;
@@ -1,7 +1,9 @@
1
+ import { Editor } from '@tiptap/core';
1
2
  import { IEditorToolElement } from '../editor-utils';
2
3
 
3
- export declare const LineHeightDropdown: ({ tool, currentLineHeight, onSetLineHeight, }: {
4
+ export declare const LineHeightDropdown: ({ tool, editor, currentLineHeight, onSetLineHeight, }: {
4
5
  tool: IEditorToolElement;
6
+ editor: Editor | null;
5
7
  currentLineHeight?: string;
6
8
  onSetLineHeight: (lineHeight: string) => void;
7
9
  }) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,21 @@
1
+ import { Editor } from '@tiptap/core';
2
+
3
+ export type SpacingToggle = {
4
+ edge: 'before' | 'after';
5
+ label: string;
6
+ onSelect: () => void;
7
+ };
8
+ /**
9
+ * The Google Docs "Add / Remove space before|after paragraph" pair, shared by
10
+ * the toolbar dropdown and the bubble menu so the label logic exists once.
11
+ *
12
+ * The label reflects what the block actually renders with, stylesheet
13
+ * included, so a fresh paragraph offers "Remove" first — its gap comes from
14
+ * editor.css rather than from an attribute.
15
+ *
16
+ * Both halves write an explicit value. Removing writes 0, because null hands
17
+ * the block back to the stylesheet — the very gap being removed. Adding writes
18
+ * SPACING_ADD_PT for the mirror-image reason: "Add" is only offered when the
19
+ * stylesheet already gives nothing, so null would leave it at zero.
20
+ */
21
+ export declare const getSpacingToggles: (editor: Editor | null) => SpacingToggle[];
@@ -150,12 +150,13 @@ export declare const FontSizePicker: ({ editor, setVisibility, elementRef, curre
150
150
  currentSize?: string;
151
151
  onSetFontSize: (fontSize: string) => void;
152
152
  }) => import("react/jsx-runtime").JSX.Element;
153
- export declare const LineHeightPicker: ({ setVisibility, elementRef, currentLineHeight, onSetLineHeight, }: {
153
+ export declare const LineHeightPicker: ({ editor, setVisibility, elementRef, currentLineHeight, onSetLineHeight, onOpenCustomSpacing, }: {
154
154
  editor: Editor;
155
155
  elementRef: React.RefObject<HTMLDivElement>;
156
156
  setVisibility: Dispatch<SetStateAction<IEditorTool>>;
157
157
  currentLineHeight?: string;
158
158
  onSetLineHeight: (lineHeight: string) => void;
159
+ onOpenCustomSpacing?: () => void;
159
160
  }) => import("react/jsx-runtime").JSX.Element;
160
161
  export declare const TextFormatingPopup: ({ editor, isOpen, setIsOpen, setToolVisibility, }: {
161
162
  editor: Editor | null;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Paragraph spacing read straight out of the OOXML.
3
+ *
4
+ * Mammoth is a *semantic* converter — its README is explicit that it uses "the
5
+ * semantic information in the document, and ignoring other details" — so it
6
+ * never parses w:spacing at all. Structure still comes from mammoth; only the
7
+ * presentational values we model are read here, from the same arrayBuffer.
8
+ */
9
+ export type DocxParagraphSpacing = {
10
+ spaceBefore: number | null;
11
+ spaceAfter: number | null;
12
+ lineHeight: string | null;
13
+ /** Paragraph text, used to verify alignment against mammoth's output. */
14
+ text: string;
15
+ };
16
+ /**
17
+ * One entry per w:p, in document order — the same order mammoth emits its
18
+ * blocks in, which is what makes them zippable.
19
+ */
20
+ export declare const readDocxSpacing: (documentXml: string, stylesXml: string) => DocxParagraphSpacing[];
21
+ /**
22
+ * Zip the spacing onto mammoth's HTML as inline styles, which the LineHeight
23
+ * and ParagraphSpacing attributes already parse back on import.
24
+ *
25
+ * Alignment is positional, so it is verified rather than trusted: mammoth
26
+ * relocates text boxes and appends footnotes, and `ignoreEmptyParagraphs`
27
+ * has to stay off for the counts to line up at all. On any divergence the
28
+ * HTML is returned untouched — no spacing is better than spacing on the
29
+ * wrong paragraphs, which is silent and hard to trace.
30
+ */
31
+ export declare const applyDocxSpacingToHtml: (html: string, spacings: DocxParagraphSpacing[]) => string;
32
+ /**
33
+ * Read the spacing out of the .docx archive and zip it onto mammoth's HTML.
34
+ *
35
+ * Reuses the arrayBuffer mammoth is already given, so the file is not read
36
+ * twice. Every failure path returns the original HTML: losing spacing is
37
+ * recoverable, losing the import is not.
38
+ */
39
+ export declare const readDocxSpacingFromArchive: (arrayBuffer: ArrayBuffer, html: string) => Promise<string>;
@@ -18,5 +18,12 @@ declare module '@tiptap/core' {
18
18
  * Carrying the current line-height onto the next block is handled by the
19
19
  * dBlock `Enter` handler (extensions/d-block/dblock.ts), alongside the
20
20
  * fontFamily/fontSize persistence — not by an inheritance plugin here.
21
+ *
22
+ * Scope is selection-based, like textAlign: a collapsed cursor styles the block
23
+ * it sits in, and whole-document is Cmd+A. It used to restyle the entire
24
+ * document on a collapsed cursor, which stopped being defensible once the
25
+ * selection-scoped "Custom spacing" item joined these presets in one dropdown.
21
26
  */
27
+ /** 1.15 in the UI. Exported so serializers can tell "default" from "authored". */
28
+ export declare const DEFAULT_LINE_HEIGHT = "138%";
22
29
  export declare const LineHeight: Extension<any, any>;
@@ -0,0 +1,31 @@
1
+ import { Extension } from '@tiptap/core';
2
+
3
+ export type ParagraphSpacingAttrs = {
4
+ /** Space above the block, in pt. `null` unsets it; omit to leave as-is. */
5
+ spaceBefore?: number | null;
6
+ /** Space below the block, in pt. `null` unsets it; omit to leave as-is. */
7
+ spaceAfter?: number | null;
8
+ };
9
+ declare module '@tiptap/core' {
10
+ interface Commands<ReturnType> {
11
+ paragraphSpacing: {
12
+ /**
13
+ * Set space before/after on the blocks in the current selection.
14
+ */
15
+ setParagraphSpacing: (attrs: ParagraphSpacingAttrs) => ReturnType;
16
+ /**
17
+ * Clear both space before and space after on the current selection.
18
+ */
19
+ unsetParagraphSpacing: () => ReturnType;
20
+ };
21
+ }
22
+ }
23
+ /**
24
+ * `spaceBefore` / `spaceAfter` as inline margins, in pt.
25
+ *
26
+ * Unlike `lineHeight`, the default is `null` — an untouched block renders no
27
+ * inline margin at all, so the stylesheet in editor.css (including its
28
+ * responsive overrides and :first-child resets) stays in charge. `0` is a
29
+ * distinct, explicit value: it renders `margin-*: 0` and overrides that CSS.
30
+ */
31
+ export declare const ParagraphSpacing: Extension<any, any>;
@@ -7,7 +7,7 @@ export type EditorCommand = {
7
7
  isEnabled: boolean;
8
8
  current?: string | null;
9
9
  };
10
- export type EditorCommandId = 'edit.undo' | 'edit.redo' | 'edit.selectAll' | 'edit.cut' | 'edit.copy' | 'edit.paste' | 'edit.pasteWithoutFormatting' | 'edit.delete' | 'edit.findReplace' | 'insert.image' | 'insert.table' | 'insert.link' | 'insert.comment' | 'insert.callout' | 'insert.quote' | 'insert.code' | 'insert.codeBlock' | 'insert.video' | 'insert.mermaid' | 'insert.plainText' | 'insert.tweet' | 'insert.soundcloud' | 'insert.divider' | 'insert.pageBreak' | 'insert.columns2' | 'insert.columns3' | 'format.bold' | 'format.italic' | 'format.underline' | 'format.strike' | 'format.superscript' | 'format.subscript' | 'format.heading' | 'format.align' | 'format.direction' | 'format.lineHeight' | 'format.fontFamily' | 'format.fontSize.increase' | 'format.fontSize.decrease' | 'format.list.bullet' | 'format.list.numbered' | 'format.list.check' | 'format.clearFormatting' | 'table.addRowAbove' | 'table.addRowBelow' | 'table.mergeCells' | 'table.deleteRow' | 'table.addColumnLeft' | 'table.addColumnRight' | 'table.deleteColumn' | 'table.toggleHeaderRow' | 'table.toggleHeaderColumn' | 'table.toggleHeaderCell' | 'table.deleteTable';
10
+ export type EditorCommandId = 'edit.undo' | 'edit.redo' | 'edit.selectAll' | 'edit.cut' | 'edit.copy' | 'edit.paste' | 'edit.pasteWithoutFormatting' | 'edit.delete' | 'edit.findReplace' | 'insert.image' | 'insert.table' | 'insert.link' | 'insert.comment' | 'insert.callout' | 'insert.quote' | 'insert.code' | 'insert.codeBlock' | 'insert.video' | 'insert.mermaid' | 'insert.plainText' | 'insert.tweet' | 'insert.soundcloud' | 'insert.divider' | 'insert.pageBreak' | 'insert.columns2' | 'insert.columns3' | 'format.bold' | 'format.italic' | 'format.underline' | 'format.strike' | 'format.superscript' | 'format.subscript' | 'format.heading' | 'format.align' | 'format.direction' | 'format.lineHeight' | 'format.customSpacing' | 'format.fontFamily' | 'format.fontSize.increase' | 'format.fontSize.decrease' | 'format.list.bullet' | 'format.list.numbered' | 'format.list.check' | 'format.clearFormatting' | 'table.addRowAbove' | 'table.addRowBelow' | 'table.mergeCells' | 'table.deleteRow' | 'table.addColumnLeft' | 'table.addColumnRight' | 'table.deleteColumn' | 'table.toggleHeaderRow' | 'table.toggleHeaderColumn' | 'table.toggleHeaderCell' | 'table.deleteTable';
11
11
  export type UseEditorCommandsOptions = UploadImageOptions;
12
12
  /**
13
13
  * Reactive, editor-derived command registry (architecture doc §2 bucket 1).
@@ -0,0 +1,14 @@
1
+ type Store = {
2
+ isCustomSpacingOpen: boolean;
3
+ setCustomSpacingOpen: (val: boolean) => void;
4
+ };
5
+ /**
6
+ * Open/closed state for the one CustomSpacingDialog, which ddoc-editor mounts.
7
+ * Module-level like search-replace-store: the entry points are scattered (the
8
+ * toolbar dropdown, the bubble menu, and a host app's own menu rendered
9
+ * outside the editor tree), so they share a flag rather than each owning a
10
+ * dialog instance.
11
+ */
12
+ export declare const useCustomSpacingStore: import('zustand').UseBoundStore<import('zustand').StoreApi<Store>>;
13
+ export declare const openCustomSpacingDialog: () => void;
14
+ export {};
@@ -6,8 +6,6 @@ export interface AwarenessIdentity {
6
6
  isEns: string | boolean;
7
7
  }
8
8
  export declare const PLACEHOLDER_COLOR = "#9CA3AF";
9
- export declare const COLLAB_PRESENCE_COLORS: readonly ["#30bced", "#6eeb83", "#fa69d1", "#ecd444", "#ee6352", "#db3041", "#0ad7f2", "#1bff39"];
10
9
  export declare function buildIdentityMap(states: Map<number, Record<string, unknown>>): Map<string, AwarenessIdentity>;
11
10
  export declare function mergePresence(roomMembers: string[], identityBySocketId: Map<string, AwarenessIdentity>): IDocCollabUsers[];
12
- export declare function assignSessionColors(collaborators: IDocCollabUsers[], colorByName: Map<string, string>): IDocCollabUsers[];
13
11
  export declare function identitySignature(roomMembers: string[], identityBySocketId: Map<string, AwarenessIdentity>): string;
@@ -62,7 +62,6 @@ export declare class SocketClient {
62
62
  private roomId;
63
63
  roomMembers: string[];
64
64
  private _lastPresenceSignature;
65
- private _presenceColorByName;
66
65
  private _onPresenceChange?;
67
66
  private _pendingAwarenessUpdates;
68
67
  private collaborationKeyPair;
@@ -10,6 +10,14 @@ export declare const getFontSizeOptions: (editor?: Editor) => {
10
10
  }[];
11
11
  export declare const getCurrentFontSize: (editor: Editor | null, currentSize: string) => string;
12
12
  export declare const uiValueToPercentage: (uiValue: string) => string;
13
+ /**
14
+ * CSS line-height comes in three shapes and ddoc stores percentages, so the
15
+ * attribute needs one meaning. A unitless ratio is exactly percentage / 100,
16
+ * so it normalises losslessly — Google Docs pastes `line-height:1.38`, which
17
+ * is `138%`. Absolute units cannot become a ratio without knowing the font
18
+ * size, so they are passed through untouched rather than guessed at.
19
+ */
20
+ export declare const normalizeLineHeight: (value: string | null | undefined) => string | null;
13
21
  export declare const percentageToUiValue: (percentage: string) => string;
14
22
  export declare const LINE_HEIGHT_OPTIONS: {
15
23
  value: string;
@@ -23,4 +31,52 @@ export declare const getLineHeightOptions: () => {
23
31
  uiValue: string;
24
32
  description: string;
25
33
  }[];
34
+ export declare const SPACING_MIN_PT = 0;
35
+ export declare const SPACING_MAX_PT = 100;
36
+ /**
37
+ * What "Add space before/after paragraph" writes, matching Google Docs.
38
+ *
39
+ * A real value rather than null: null would hand the block back to the
40
+ * stylesheet, and the toggle only offers "Add" when the stylesheet is already
41
+ * giving it nothing — so restoring null would leave the gap at zero and the
42
+ * menu item would appear to do nothing.
43
+ */
44
+ export declare const SPACING_ADD_PT = 12;
45
+ /** A value shared by every block in the selection, or `'mixed'`. */
46
+ export type SpacingReading<T> = T | 'mixed';
47
+ export type SpacingSelection = {
48
+ spaceBefore: SpacingReading<number | null>;
49
+ spaceAfter: SpacingReading<number | null>;
50
+ lineHeight: SpacingReading<string | null>;
51
+ };
52
+ /**
53
+ * Read spacing across the current selection, the single place the dialog and
54
+ * any read-back should use. `'mixed'` means the selected blocks disagree — the
55
+ * caller must render that as an empty field and leave the attribute alone on
56
+ * apply, rather than stamping one block's value onto the rest.
57
+ */
58
+ export declare const readSpacingSelection: (editor: Editor | null) => SpacingSelection;
26
59
  export declare const getCurrentLineHeight: (editor: Editor | null, currentLineHeight?: string) => string;
60
+ export type EffectiveSpacing = {
61
+ spaceBefore: SpacingReading<number>;
62
+ spaceAfter: SpacingReading<number>;
63
+ };
64
+ /**
65
+ * The spacing a block actually renders with: its explicit attribute when set,
66
+ * otherwise whatever the stylesheet resolves to.
67
+ *
68
+ * Read from the rendered DOM because there is no single default to hardcode —
69
+ * the gap depends on viewport, schema version, element type, and whether the
70
+ * block is the first or last child. Both the "add/remove space" menu items and
71
+ * the custom spacing dialog need the real number, not the attribute.
72
+ */
73
+ export declare const readEffectiveSpacing: (editor: Editor | null) => EffectiveSpacing;
74
+ /**
75
+ * Which half of the Google-Docs-style toggle to offer.
76
+ *
77
+ * "Add space before paragraph" only appears once the block genuinely has no
78
+ * gap — the stylesheet's default counts, so a fresh paragraph offers "Remove"
79
+ * first. A mixed selection counts as having a gap, since removing is the
80
+ * action that leaves every block in the same state.
81
+ */
82
+ export declare const spacingToggleAction: (effective: SpacingReading<number>) => "add" | "remove";
@@ -1,7 +1,7 @@
1
1
  import { jsx as m, jsxs as L } from "react/jsx-runtime";
2
2
  import { useRef as D, useState as E, useEffect as A } from "react";
3
3
  import { DynamicDropdown as P, Tooltip as O, IconButton as T } from "@fileverse/ui";
4
- import { a3 as _, a4 as w, c as R } from "./index-BaJBrKSQ.mjs";
4
+ import { a3 as _, a4 as w, c as R } from "./index-K2dDPRDs.mjs";
5
5
  const p = (e, n, o = n) => {
6
6
  const { state: i } = e, l = i.changeByRange((t) => {
7
7
  const d = i.sliceDoc(t.from, t.to), r = i.sliceDoc(