@tiptap/extension-details 2.24.2 → 3.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/helpers/isNodeVisible.ts","../src/helpers/findClosestVisibleNode.ts","../src/helpers/setGapCursor.ts","../src/details.ts"],"sourcesContent":["import { Editor } from '@tiptap/core'\n\nexport const isNodeVisible = (position: number, editor: Editor): boolean => {\n const node = editor.view.domAtPos(position).node as HTMLElement\n const isOpen = node.offsetParent !== null\n\n return isOpen\n}\n","import { Editor, Predicate } from '@tiptap/core'\nimport { Node as ProseMirrorNode, ResolvedPos } from '@tiptap/pm/model'\n\nimport { isNodeVisible } from './isNodeVisible.js'\n\nexport const findClosestVisibleNode = ($pos: ResolvedPos, predicate: Predicate, editor: Editor): ({\n pos: number,\n start: number,\n depth: number,\n node: ProseMirrorNode,\n} | undefined) => {\n for (let i = $pos.depth; i > 0; i -= 1) {\n const node = $pos.node(i)\n const match = predicate(node)\n const isVisible = isNodeVisible($pos.start(i), editor)\n\n if (match && isVisible) {\n return {\n pos: i > 0 ? $pos.before(i) : 0,\n start: $pos.start(i),\n depth: i,\n node,\n }\n }\n }\n}\n","import { Editor, findChildren, findParentNode } from '@tiptap/core'\nimport { GapCursor } from '@tiptap/pm/gapcursor'\nimport { ResolvedPos } from '@tiptap/pm/model'\nimport { Selection } from '@tiptap/pm/state'\n\nimport { isNodeVisible } from './isNodeVisible.js'\n\nexport const setGapCursor = (editor: Editor, direction: 'down' | 'right') => {\n const { state, view, extensionManager } = editor\n const { schema, selection } = state\n const { empty, $anchor } = selection\n const hasGapCursorExtension = !!extensionManager.extensions.find(extension => extension.name === 'gapCursor')\n\n if (\n !empty\n || $anchor.parent.type !== schema.nodes.detailsSummary\n || !hasGapCursorExtension\n ) {\n return false\n }\n\n if (\n direction === 'right'\n && $anchor.parentOffset !== ($anchor.parent.nodeSize - 2)\n ) {\n return false\n }\n\n const details = findParentNode(node => node.type === schema.nodes.details)(selection)\n\n if (!details) {\n return false\n }\n\n const detailsContent = findChildren(details.node, node => node.type === schema.nodes.detailsContent)\n\n if (!detailsContent.length) {\n return false\n }\n\n const isOpen = isNodeVisible(details.start + detailsContent[0].pos + 1, editor)\n\n if (isOpen) {\n return false\n }\n\n const $position = state.doc.resolve(details.pos + details.node.nodeSize)\n const $validPosition = GapCursor.findFrom($position, 1, false) as unknown as (null | ResolvedPos)\n\n if (!$validPosition) {\n return false\n }\n\n const { tr } = state\n const gapCursorSelection = new GapCursor($validPosition) as Selection\n\n tr.setSelection(gapCursorSelection)\n tr.scrollIntoView()\n view.dispatch(tr)\n\n return true\n}\n","import {\n defaultBlockAt,\n findChildren,\n findParentNode,\n isActive,\n mergeAttributes,\n Node,\n} from '@tiptap/core'\nimport {\n Plugin,\n PluginKey,\n Selection,\n TextSelection,\n} from '@tiptap/pm/state'\nimport type { ViewMutationRecord } from '@tiptap/pm/view'\n\nimport { findClosestVisibleNode } from './helpers/findClosestVisibleNode.js'\nimport { isNodeVisible } from './helpers/isNodeVisible.js'\nimport { setGapCursor } from './helpers/setGapCursor.js'\n\nexport interface DetailsOptions {\n /**\n * Specify if the open status should be saved in the document. Defaults to `false`.\n */\n persist: boolean,\n /**\n * Specifies a CSS class that is set when toggling the content. Defaults to `is-open`.\n */\n openClassName: string,\n /**\n * Custom HTML attributes that should be added to the rendered HTML tag.\n */\n HTMLAttributes: {\n [key: string]: any\n },\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n details: {\n /**\n * Set a details node\n */\n setDetails: () => ReturnType,\n /**\n * Unset a details node\n */\n unsetDetails: () => ReturnType,\n }\n }\n}\n\nexport const Details = Node.create<DetailsOptions>({\n name: 'details',\n\n content: 'detailsSummary detailsContent',\n\n group: 'block',\n\n defining: true,\n\n isolating: true,\n\n allowGapCursor: false,\n\n addOptions() {\n return {\n persist: false,\n openClassName: 'is-open',\n HTMLAttributes: {},\n }\n },\n\n addAttributes() {\n if (!this.options.persist) {\n return []\n }\n\n return {\n open: {\n default: false,\n parseHTML: element => element.hasAttribute('open'),\n renderHTML: ({ open }) => {\n if (!open) {\n return {}\n }\n\n return { open: '' }\n },\n },\n }\n },\n\n parseHTML() {\n return [\n {\n tag: 'details',\n },\n ]\n },\n\n renderHTML({ HTMLAttributes }) {\n return [\n 'details',\n mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),\n 0,\n ]\n },\n\n addNodeView() {\n return ({\n editor,\n getPos,\n node,\n HTMLAttributes,\n }) => {\n const dom = document.createElement('div')\n const attributes = mergeAttributes(\n this.options.HTMLAttributes,\n HTMLAttributes,\n {\n 'data-type': this.name,\n },\n )\n\n Object.entries(attributes).forEach(([key, value]) => dom.setAttribute(key, value))\n\n const toggle = document.createElement('button')\n\n toggle.type = 'button'\n\n dom.append(toggle)\n\n const content = document.createElement('div')\n\n dom.append(content)\n\n const toggleDetailsContent = (setToValue?: boolean) => {\n if (setToValue !== undefined) {\n if (setToValue) {\n if (dom.classList.contains(this.options.openClassName)) {\n return\n }\n dom.classList.add(this.options.openClassName)\n } else {\n if (!dom.classList.contains(this.options.openClassName)) {\n return\n }\n dom.classList.remove(this.options.openClassName)\n }\n } else {\n dom.classList.toggle(this.options.openClassName)\n }\n\n const event = new Event('toggleDetailsContent')\n const detailsContent = content.querySelector(':scope > div[data-type=\"detailsContent\"]')\n\n detailsContent?.dispatchEvent(event)\n }\n\n if (node.attrs.open) {\n setTimeout(() => toggleDetailsContent())\n }\n\n toggle.addEventListener('click', () => {\n toggleDetailsContent()\n\n if (!this.options.persist) {\n editor.commands\n .focus(undefined, { scrollIntoView: false })\n\n return\n }\n\n if (editor.isEditable && typeof getPos === 'function') {\n const { from, to } = editor.state.selection\n\n editor\n .chain()\n .command(({ tr }) => {\n const pos = getPos()\n const currentNode = tr.doc.nodeAt(pos)\n\n if (currentNode?.type !== this.type) {\n return false\n }\n\n tr.setNodeMarkup(pos, undefined, {\n open: !currentNode.attrs.open,\n })\n\n return true\n })\n .setTextSelection({\n from,\n to,\n })\n .focus(undefined, { scrollIntoView: false })\n .run()\n }\n })\n\n return {\n dom,\n contentDOM: content,\n ignoreMutation(mutation: ViewMutationRecord) {\n if (mutation.type === 'selection') {\n return false\n }\n\n return !dom.contains(mutation.target) || dom === mutation.target\n },\n update: updatedNode => {\n if (updatedNode.type !== this.type) {\n return false\n }\n\n // Only update the open state if set\n if (updatedNode.attrs.open !== undefined) {\n toggleDetailsContent(updatedNode.attrs.open)\n }\n\n return true\n },\n }\n }\n },\n\n addCommands() {\n return {\n setDetails: () => ({ state, chain }) => {\n const { schema, selection } = state\n const { $from, $to } = selection\n const range = $from.blockRange($to)\n\n if (!range) {\n return false\n }\n\n const slice = state.doc.slice(range.start, range.end)\n const match = schema.nodes.detailsContent.contentMatch.matchFragment(slice.content)\n\n if (!match) {\n return false\n }\n\n const content = slice.toJSON()?.content || []\n\n return chain()\n .insertContentAt({ from: range.start, to: range.end }, {\n type: this.name,\n content: [\n {\n type: 'detailsSummary',\n },\n {\n type: 'detailsContent',\n content,\n },\n ],\n })\n .setTextSelection(range.start + 2)\n .run()\n },\n\n unsetDetails: () => ({ state, chain }) => {\n const { selection, schema } = state\n const details = findParentNode(node => node.type === this.type)(selection)\n\n if (!details) {\n return false\n }\n\n const detailsSummaries = findChildren(details.node, node => node.type === schema.nodes.detailsSummary)\n const detailsContents = findChildren(details.node, node => node.type === schema.nodes.detailsContent)\n\n if (!detailsSummaries.length || !detailsContents.length) {\n return false\n }\n\n const detailsSummary = detailsSummaries[0]\n const detailsContent = detailsContents[0]\n const from = details.pos\n const $from = state.doc.resolve(from)\n const to = from + details.node.nodeSize\n const range = { from, to }\n const content = detailsContent.node.content.toJSON() as [] || []\n const defaultTypeForSummary = $from.parent.type.contentMatch.defaultType\n\n // TODO: this may break for some custom schemas\n const summaryContent = defaultTypeForSummary?.create(null, detailsSummary.node.content).toJSON()\n const mergedContent = [\n summaryContent,\n ...content,\n ]\n\n return chain()\n .insertContentAt(range, mergedContent)\n .setTextSelection(from + 1)\n .run()\n },\n }\n },\n\n addKeyboardShortcuts() {\n return {\n Backspace: () => {\n const { schema, selection } = this.editor.state\n const { empty, $anchor } = selection\n\n if (!empty || $anchor.parent.type !== schema.nodes.detailsSummary) {\n return false\n }\n\n // for some reason safari removes the whole text content within a `<summary>`tag on backspace\n // so we have to remove the text manually\n // see: https://discuss.prosemirror.net/t/safari-backspace-bug-with-details-tag/4223\n if ($anchor.parentOffset !== 0) {\n return this.editor.commands.command(({ tr }) => {\n const from = $anchor.pos - 1\n const to = $anchor.pos\n\n tr.delete(from, to)\n\n return true\n })\n }\n\n return this.editor.commands.unsetDetails()\n },\n\n // Creates a new node below it if it is closed.\n // Otherwise inside `DetailsContent`.\n Enter: ({ editor }) => {\n const { state, view } = editor\n const { schema, selection } = state\n const { $head } = selection\n\n if ($head.parent.type !== schema.nodes.detailsSummary) {\n return false\n }\n\n const isVisible = isNodeVisible($head.after() + 1, editor)\n const above = isVisible\n ? state.doc.nodeAt($head.after())\n : $head.node(-2)\n\n if (!above) {\n return false\n }\n\n const after = isVisible\n ? 0\n : $head.indexAfter(-1)\n const type = defaultBlockAt(above.contentMatchAt(after))\n\n if (!type || !above.canReplaceWith(after, after, type)) {\n return false\n }\n\n const node = type.createAndFill()\n\n if (!node) {\n return false\n }\n\n const pos = isVisible\n ? $head.after() + 1\n : $head.after(-1)\n const tr = state.tr.replaceWith(pos, pos, node)\n const $pos = tr.doc.resolve(pos)\n const newSelection = Selection.near($pos, 1)\n\n tr.setSelection(newSelection)\n tr.scrollIntoView()\n view.dispatch(tr)\n\n return true\n },\n\n // The default gapcursor implementation can’t handle hidden content, so we need to fix this.\n ArrowRight: ({ editor }) => {\n return setGapCursor(editor, 'right')\n },\n\n // The default gapcursor implementation can’t handle hidden content, so we need to fix this.\n ArrowDown: ({ editor }) => {\n return setGapCursor(editor, 'down')\n },\n }\n },\n\n addProseMirrorPlugins() {\n return [\n // This plugin prevents text selections within the hidden content in `DetailsContent`.\n // The cursor is moved to the next visible position.\n new Plugin({\n key: new PluginKey('detailsSelection'),\n appendTransaction: (transactions, oldState, newState) => {\n const { editor, type } = this\n const selectionSet = transactions.some(transaction => transaction.selectionSet)\n\n if (\n !selectionSet\n || !oldState.selection.empty\n || !newState.selection.empty\n ) {\n return\n }\n\n const detailsIsActive = isActive(newState, type.name)\n\n if (!detailsIsActive) {\n return\n }\n\n const { $from } = newState.selection\n const isVisible = isNodeVisible($from.pos, editor)\n\n if (isVisible) {\n return\n }\n\n const details = findClosestVisibleNode($from, node => node.type === type, editor)\n\n if (!details) {\n return\n }\n\n const detailsSummaries = findChildren(details.node, node => node.type === newState.schema.nodes.detailsSummary)\n\n if (!detailsSummaries.length) {\n return\n }\n\n const detailsSummary = detailsSummaries[0]\n const selectionDirection = oldState.selection.from < newState.selection.from\n ? 'forward'\n : 'backward'\n const correctedPosition = selectionDirection === 'forward'\n ? details.start + detailsSummary.pos\n : details.pos + detailsSummary.pos + detailsSummary.node.nodeSize\n const selection = TextSelection.create(newState.doc, correctedPosition)\n const transaction = newState.tr.setSelection(selection)\n\n return transaction\n },\n }),\n ]\n },\n})\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"sources":["../src/details.ts","../src/helpers/isNodeVisible.ts","../src/helpers/findClosestVisibleNode.ts","../src/helpers/setGapCursor.ts","../src/index.ts"],"sourcesContent":["import { defaultBlockAt, findChildren, findParentNode, isActive, mergeAttributes, Node } from '@tiptap/core'\nimport { Plugin, PluginKey, Selection, TextSelection } from '@tiptap/pm/state'\nimport type { ViewMutationRecord } from '@tiptap/pm/view'\n\nimport { findClosestVisibleNode } from './helpers/findClosestVisibleNode.js'\nimport { isNodeVisible } from './helpers/isNodeVisible.js'\nimport { setGapCursor } from './helpers/setGapCursor.js'\n\nexport interface DetailsOptions {\n /**\n * Specify if the open status should be saved in the document. Defaults to `false`.\n */\n persist: boolean\n /**\n * Specifies a CSS class that is set when toggling the content. Defaults to `is-open`.\n */\n openClassName: string\n /**\n * Custom HTML attributes that should be added to the rendered HTML tag.\n */\n HTMLAttributes: {\n [key: string]: any\n }\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n details: {\n /**\n * Set a details node\n */\n setDetails: () => ReturnType\n /**\n * Unset a details node\n */\n unsetDetails: () => ReturnType\n }\n }\n}\n\nexport const Details = Node.create<DetailsOptions>({\n name: 'details',\n\n content: 'detailsSummary detailsContent',\n\n group: 'block',\n\n defining: true,\n\n isolating: true,\n\n // @ts-ignore: allowGapCursor is not a valid option by default, dts on build doesnt pick this up\n allowGapCursor: false,\n\n addOptions() {\n return {\n persist: false,\n openClassName: 'is-open',\n HTMLAttributes: {},\n }\n },\n\n addAttributes() {\n if (!this.options.persist) {\n return []\n }\n\n return {\n open: {\n default: false,\n parseHTML: element => element.hasAttribute('open'),\n renderHTML: ({ open }) => {\n if (!open) {\n return {}\n }\n\n return { open: '' }\n },\n },\n }\n },\n\n parseHTML() {\n return [\n {\n tag: 'details',\n },\n ]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['details', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]\n },\n\n addNodeView() {\n return ({ editor, getPos, node, HTMLAttributes }) => {\n const dom = document.createElement('div')\n const attributes = mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {\n 'data-type': this.name,\n })\n\n Object.entries(attributes).forEach(([key, value]) => dom.setAttribute(key, value))\n\n const toggle = document.createElement('button')\n\n toggle.type = 'button'\n\n dom.append(toggle)\n\n const content = document.createElement('div')\n\n dom.append(content)\n\n const toggleDetailsContent = (setToValue?: boolean) => {\n if (setToValue !== undefined) {\n if (setToValue) {\n if (dom.classList.contains(this.options.openClassName)) {\n return\n }\n dom.classList.add(this.options.openClassName)\n } else {\n if (!dom.classList.contains(this.options.openClassName)) {\n return\n }\n dom.classList.remove(this.options.openClassName)\n }\n } else {\n dom.classList.toggle(this.options.openClassName)\n }\n\n const event = new Event('toggleDetailsContent')\n const detailsContent = content.querySelector(':scope > div[data-type=\"detailsContent\"]')\n\n detailsContent?.dispatchEvent(event)\n }\n\n if (node.attrs.open) {\n setTimeout(() => toggleDetailsContent())\n }\n\n toggle.addEventListener('click', () => {\n toggleDetailsContent()\n\n if (!this.options.persist) {\n editor.commands.focus(undefined, { scrollIntoView: false })\n\n return\n }\n\n if (editor.isEditable && typeof getPos === 'function') {\n const { from, to } = editor.state.selection\n\n editor\n .chain()\n .command(({ tr }) => {\n const pos = getPos()\n if (pos === undefined) {\n return false\n }\n\n const currentNode = tr.doc.nodeAt(pos)\n\n if (currentNode?.type !== this.type) {\n return false\n }\n\n tr.setNodeMarkup(pos, undefined, {\n open: !currentNode.attrs.open,\n })\n\n return true\n })\n .setTextSelection({\n from,\n to,\n })\n .focus(undefined, { scrollIntoView: false })\n .run()\n }\n })\n\n return {\n dom,\n contentDOM: content,\n ignoreMutation(mutation: ViewMutationRecord) {\n if (mutation.type === 'selection') {\n return false\n }\n\n return !dom.contains(mutation.target) || dom === mutation.target\n },\n update: updatedNode => {\n if (updatedNode.type !== this.type) {\n return false\n }\n\n // Only update the open state if set\n if (updatedNode.attrs.open !== undefined) {\n toggleDetailsContent(updatedNode.attrs.open)\n }\n\n return true\n },\n }\n }\n },\n\n addCommands() {\n return {\n setDetails:\n () =>\n ({ state, chain }) => {\n const { schema, selection } = state\n const { $from, $to } = selection\n const range = $from.blockRange($to)\n\n if (!range) {\n return false\n }\n\n const slice = state.doc.slice(range.start, range.end)\n const match = schema.nodes.detailsContent.contentMatch.matchFragment(slice.content)\n\n if (!match) {\n return false\n }\n\n const content = slice.toJSON()?.content || []\n\n return chain()\n .insertContentAt(\n { from: range.start, to: range.end },\n {\n type: this.name,\n content: [\n {\n type: 'detailsSummary',\n },\n {\n type: 'detailsContent',\n content,\n },\n ],\n },\n )\n .setTextSelection(range.start + 2)\n .run()\n },\n\n unsetDetails:\n () =>\n ({ state, chain }) => {\n const { selection, schema } = state\n const details = findParentNode(node => node.type === this.type)(selection)\n\n if (!details) {\n return false\n }\n\n const detailsSummaries = findChildren(details.node, node => node.type === schema.nodes.detailsSummary)\n const detailsContents = findChildren(details.node, node => node.type === schema.nodes.detailsContent)\n\n if (!detailsSummaries.length || !detailsContents.length) {\n return false\n }\n\n const detailsSummary = detailsSummaries[0]\n const detailsContent = detailsContents[0]\n const from = details.pos\n const $from = state.doc.resolve(from)\n const to = from + details.node.nodeSize\n const range = { from, to }\n const content = (detailsContent.node.content.toJSON() as []) || []\n const defaultTypeForSummary = $from.parent.type.contentMatch.defaultType\n\n // TODO: this may break for some custom schemas\n const summaryContent = defaultTypeForSummary?.create(null, detailsSummary.node.content).toJSON()\n const mergedContent = [summaryContent, ...content]\n\n return chain()\n .insertContentAt(range, mergedContent)\n .setTextSelection(from + 1)\n .run()\n },\n }\n },\n\n addKeyboardShortcuts() {\n return {\n Backspace: () => {\n const { schema, selection } = this.editor.state\n const { empty, $anchor } = selection\n\n if (!empty || $anchor.parent.type !== schema.nodes.detailsSummary) {\n return false\n }\n\n // for some reason safari removes the whole text content within a `<summary>`tag on backspace\n // so we have to remove the text manually\n // see: https://discuss.prosemirror.net/t/safari-backspace-bug-with-details-tag/4223\n if ($anchor.parentOffset !== 0) {\n return this.editor.commands.command(({ tr }) => {\n const from = $anchor.pos - 1\n const to = $anchor.pos\n\n tr.delete(from, to)\n\n return true\n })\n }\n\n return this.editor.commands.unsetDetails()\n },\n\n // Creates a new node below it if it is closed.\n // Otherwise inside `DetailsContent`.\n Enter: ({ editor }) => {\n const { state, view } = editor\n const { schema, selection } = state\n const { $head } = selection\n\n if ($head.parent.type !== schema.nodes.detailsSummary) {\n return false\n }\n\n const isVisible = isNodeVisible($head.after() + 1, editor)\n const above = isVisible ? state.doc.nodeAt($head.after()) : $head.node(-2)\n\n if (!above) {\n return false\n }\n\n const after = isVisible ? 0 : $head.indexAfter(-1)\n const type = defaultBlockAt(above.contentMatchAt(after))\n\n if (!type || !above.canReplaceWith(after, after, type)) {\n return false\n }\n\n const node = type.createAndFill()\n\n if (!node) {\n return false\n }\n\n const pos = isVisible ? $head.after() + 1 : $head.after(-1)\n const tr = state.tr.replaceWith(pos, pos, node)\n const $pos = tr.doc.resolve(pos)\n const newSelection = Selection.near($pos, 1)\n\n tr.setSelection(newSelection)\n tr.scrollIntoView()\n view.dispatch(tr)\n\n return true\n },\n\n // The default gapcursor implementation can’t handle hidden content, so we need to fix this.\n ArrowRight: ({ editor }) => {\n return setGapCursor(editor, 'right')\n },\n\n // The default gapcursor implementation can’t handle hidden content, so we need to fix this.\n ArrowDown: ({ editor }) => {\n return setGapCursor(editor, 'down')\n },\n }\n },\n\n addProseMirrorPlugins() {\n return [\n // This plugin prevents text selections within the hidden content in `DetailsContent`.\n // The cursor is moved to the next visible position.\n new Plugin({\n key: new PluginKey('detailsSelection'),\n appendTransaction: (transactions, oldState, newState) => {\n const { editor, type } = this\n const selectionSet = transactions.some(transaction => transaction.selectionSet)\n\n if (!selectionSet || !oldState.selection.empty || !newState.selection.empty) {\n return\n }\n\n const detailsIsActive = isActive(newState, type.name)\n\n if (!detailsIsActive) {\n return\n }\n\n const { $from } = newState.selection\n const isVisible = isNodeVisible($from.pos, editor)\n\n if (isVisible) {\n return\n }\n\n const details = findClosestVisibleNode($from, node => node.type === type, editor)\n\n if (!details) {\n return\n }\n\n const detailsSummaries = findChildren(\n details.node,\n node => node.type === newState.schema.nodes.detailsSummary,\n )\n\n if (!detailsSummaries.length) {\n return\n }\n\n const detailsSummary = detailsSummaries[0]\n const selectionDirection = oldState.selection.from < newState.selection.from ? 'forward' : 'backward'\n const correctedPosition =\n selectionDirection === 'forward'\n ? details.start + detailsSummary.pos\n : details.pos + detailsSummary.pos + detailsSummary.node.nodeSize\n const selection = TextSelection.create(newState.doc, correctedPosition)\n const transaction = newState.tr.setSelection(selection)\n\n return transaction\n },\n }),\n ]\n },\n})\n","import type { Editor } from '@tiptap/core'\n\nexport const isNodeVisible = (position: number, editor: Editor): boolean => {\n const node = editor.view.domAtPos(position).node as HTMLElement\n const isOpen = node.offsetParent !== null\n\n return isOpen\n}\n","import type { Editor, Predicate } from '@tiptap/core'\nimport type { Node as ProseMirrorNode, ResolvedPos } from '@tiptap/pm/model'\n\nimport { isNodeVisible } from './isNodeVisible.js'\n\nexport const findClosestVisibleNode = (\n $pos: ResolvedPos,\n predicate: Predicate,\n editor: Editor,\n):\n | {\n pos: number\n start: number\n depth: number\n node: ProseMirrorNode\n }\n | undefined => {\n for (let i = $pos.depth; i > 0; i -= 1) {\n const node = $pos.node(i)\n const match = predicate(node)\n const isVisible = isNodeVisible($pos.start(i), editor)\n\n if (match && isVisible) {\n return {\n pos: i > 0 ? $pos.before(i) : 0,\n start: $pos.start(i),\n depth: i,\n node,\n }\n }\n }\n}\n","import type { Editor } from '@tiptap/core'\nimport { findChildren, findParentNode } from '@tiptap/core'\nimport { GapCursor } from '@tiptap/pm/gapcursor'\nimport type { ResolvedPos } from '@tiptap/pm/model'\nimport type { Selection } from '@tiptap/pm/state'\n\nimport { isNodeVisible } from './isNodeVisible.js'\n\nexport const setGapCursor = (editor: Editor, direction: 'down' | 'right') => {\n const { state, view, extensionManager } = editor\n const { schema, selection } = state\n const { empty, $anchor } = selection\n const hasGapCursorExtension = !!extensionManager.extensions.find(extension => extension.name === 'gapCursor')\n\n if (!empty || $anchor.parent.type !== schema.nodes.detailsSummary || !hasGapCursorExtension) {\n return false\n }\n\n if (direction === 'right' && $anchor.parentOffset !== $anchor.parent.nodeSize - 2) {\n return false\n }\n\n const details = findParentNode(node => node.type === schema.nodes.details)(selection)\n\n if (!details) {\n return false\n }\n\n const detailsContent = findChildren(details.node, node => node.type === schema.nodes.detailsContent)\n\n if (!detailsContent.length) {\n return false\n }\n\n const isOpen = isNodeVisible(details.start + detailsContent[0].pos + 1, editor)\n\n if (isOpen) {\n return false\n }\n\n const $position = state.doc.resolve(details.pos + details.node.nodeSize)\n const $validPosition = GapCursor.findFrom($position, 1, false) as unknown as null | ResolvedPos\n\n if (!$validPosition) {\n return false\n }\n\n const { tr } = state\n const gapCursorSelection = new GapCursor($validPosition) as Selection\n\n tr.setSelection(gapCursorSelection)\n tr.scrollIntoView()\n view.dispatch(tr)\n\n return true\n}\n","import { Details } from './details.js'\n\nexport * from './details.js'\n\nexport default Details\n"],"mappings":"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","names":["findChildren","findParentNode","findParentNode","findChildren","transaction"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tiptap/extension-details",
3
3
  "description": "details extension for tiptap",
4
- "version": "2.24.2",
4
+ "version": "3.0.0-beta.10",
5
5
  "homepage": "https://tiptap.dev/api/nodes/details",
6
6
  "keywords": [
7
7
  "tiptap",
@@ -15,26 +15,30 @@
15
15
  "type": "module",
16
16
  "exports": {
17
17
  ".": {
18
- "types": "./dist/index.d.ts",
18
+ "types": {
19
+ "import": "./dist/index.d.ts",
20
+ "require": "./dist/index.d.cts"
21
+ },
19
22
  "import": "./dist/index.js",
20
23
  "require": "./dist/index.cjs"
21
24
  }
22
25
  },
23
26
  "main": "dist/index.cjs",
24
27
  "module": "dist/index.js",
25
- "umd": "dist/index.umd.js",
26
28
  "types": "dist/index.d.ts",
27
29
  "files": [
28
30
  "src",
29
31
  "dist"
30
32
  ],
31
33
  "devDependencies": {
32
- "@tiptap/core": "^2.24.2",
33
- "@tiptap/extension-text-style": "^2.24.2"
34
+ "@tiptap/core": "3.0.0-beta.10",
35
+ "@tiptap/pm": "3.0.0-beta.10",
36
+ "@tiptap/extension-text-style": "3.0.0-beta.10"
34
37
  },
35
38
  "peerDependencies": {
36
- "@tiptap/core": "^2.7.0",
37
- "@tiptap/extension-text-style": "^2.7.0"
39
+ "@tiptap/core": "3.0.0-beta.10",
40
+ "@tiptap/extension-text-style": "3.0.0-beta.10",
41
+ "@tiptap/pm": "3.0.0-beta.10"
38
42
  },
39
43
  "repository": {
40
44
  "type": "git",
@@ -42,7 +46,7 @@
42
46
  "directory": "packages/extension-details"
43
47
  },
44
48
  "scripts": {
45
- "clean": "rm -rf dist",
46
- "build": "npm run clean && rollup -c"
49
+ "build": "tsup",
50
+ "lint": "prettier ./src/ --check && eslint --cache --quiet --no-error-on-unmatched-pattern ./src/"
47
51
  }
48
52
  }
package/src/details.ts CHANGED
@@ -1,17 +1,5 @@
1
- import {
2
- defaultBlockAt,
3
- findChildren,
4
- findParentNode,
5
- isActive,
6
- mergeAttributes,
7
- Node,
8
- } from '@tiptap/core'
9
- import {
10
- Plugin,
11
- PluginKey,
12
- Selection,
13
- TextSelection,
14
- } from '@tiptap/pm/state'
1
+ import { defaultBlockAt, findChildren, findParentNode, isActive, mergeAttributes, Node } from '@tiptap/core'
2
+ import { Plugin, PluginKey, Selection, TextSelection } from '@tiptap/pm/state'
15
3
  import type { ViewMutationRecord } from '@tiptap/pm/view'
16
4
 
17
5
  import { findClosestVisibleNode } from './helpers/findClosestVisibleNode.js'
@@ -22,17 +10,17 @@ export interface DetailsOptions {
22
10
  /**
23
11
  * Specify if the open status should be saved in the document. Defaults to `false`.
24
12
  */
25
- persist: boolean,
13
+ persist: boolean
26
14
  /**
27
15
  * Specifies a CSS class that is set when toggling the content. Defaults to `is-open`.
28
16
  */
29
- openClassName: string,
17
+ openClassName: string
30
18
  /**
31
19
  * Custom HTML attributes that should be added to the rendered HTML tag.
32
20
  */
33
21
  HTMLAttributes: {
34
22
  [key: string]: any
35
- },
23
+ }
36
24
  }
37
25
 
38
26
  declare module '@tiptap/core' {
@@ -41,11 +29,11 @@ declare module '@tiptap/core' {
41
29
  /**
42
30
  * Set a details node
43
31
  */
44
- setDetails: () => ReturnType,
32
+ setDetails: () => ReturnType
45
33
  /**
46
34
  * Unset a details node
47
35
  */
48
- unsetDetails: () => ReturnType,
36
+ unsetDetails: () => ReturnType
49
37
  }
50
38
  }
51
39
  }
@@ -61,6 +49,7 @@ export const Details = Node.create<DetailsOptions>({
61
49
 
62
50
  isolating: true,
63
51
 
52
+ // @ts-ignore: allowGapCursor is not a valid option by default, dts on build doesnt pick this up
64
53
  allowGapCursor: false,
65
54
 
66
55
  addOptions() {
@@ -100,28 +89,15 @@ export const Details = Node.create<DetailsOptions>({
100
89
  },
101
90
 
102
91
  renderHTML({ HTMLAttributes }) {
103
- return [
104
- 'details',
105
- mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
106
- 0,
107
- ]
92
+ return ['details', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]
108
93
  },
109
94
 
110
95
  addNodeView() {
111
- return ({
112
- editor,
113
- getPos,
114
- node,
115
- HTMLAttributes,
116
- }) => {
96
+ return ({ editor, getPos, node, HTMLAttributes }) => {
117
97
  const dom = document.createElement('div')
118
- const attributes = mergeAttributes(
119
- this.options.HTMLAttributes,
120
- HTMLAttributes,
121
- {
122
- 'data-type': this.name,
123
- },
124
- )
98
+ const attributes = mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
99
+ 'data-type': this.name,
100
+ })
125
101
 
126
102
  Object.entries(attributes).forEach(([key, value]) => dom.setAttribute(key, value))
127
103
 
@@ -166,8 +142,7 @@ export const Details = Node.create<DetailsOptions>({
166
142
  toggleDetailsContent()
167
143
 
168
144
  if (!this.options.persist) {
169
- editor.commands
170
- .focus(undefined, { scrollIntoView: false })
145
+ editor.commands.focus(undefined, { scrollIntoView: false })
171
146
 
172
147
  return
173
148
  }
@@ -179,6 +154,10 @@ export const Details = Node.create<DetailsOptions>({
179
154
  .chain()
180
155
  .command(({ tr }) => {
181
156
  const pos = getPos()
157
+ if (pos === undefined) {
158
+ return false
159
+ }
160
+
182
161
  const currentNode = tr.doc.nodeAt(pos)
183
162
 
184
163
  if (currentNode?.type !== this.type) {
@@ -228,77 +207,81 @@ export const Details = Node.create<DetailsOptions>({
228
207
 
229
208
  addCommands() {
230
209
  return {
231
- setDetails: () => ({ state, chain }) => {
232
- const { schema, selection } = state
233
- const { $from, $to } = selection
234
- const range = $from.blockRange($to)
235
-
236
- if (!range) {
237
- return false
238
- }
210
+ setDetails:
211
+ () =>
212
+ ({ state, chain }) => {
213
+ const { schema, selection } = state
214
+ const { $from, $to } = selection
215
+ const range = $from.blockRange($to)
216
+
217
+ if (!range) {
218
+ return false
219
+ }
239
220
 
240
- const slice = state.doc.slice(range.start, range.end)
241
- const match = schema.nodes.detailsContent.contentMatch.matchFragment(slice.content)
221
+ const slice = state.doc.slice(range.start, range.end)
222
+ const match = schema.nodes.detailsContent.contentMatch.matchFragment(slice.content)
242
223
 
243
- if (!match) {
244
- return false
245
- }
224
+ if (!match) {
225
+ return false
226
+ }
246
227
 
247
- const content = slice.toJSON()?.content || []
228
+ const content = slice.toJSON()?.content || []
248
229
 
249
- return chain()
250
- .insertContentAt({ from: range.start, to: range.end }, {
251
- type: this.name,
252
- content: [
230
+ return chain()
231
+ .insertContentAt(
232
+ { from: range.start, to: range.end },
253
233
  {
254
- type: 'detailsSummary',
234
+ type: this.name,
235
+ content: [
236
+ {
237
+ type: 'detailsSummary',
238
+ },
239
+ {
240
+ type: 'detailsContent',
241
+ content,
242
+ },
243
+ ],
255
244
  },
256
- {
257
- type: 'detailsContent',
258
- content,
259
- },
260
- ],
261
- })
262
- .setTextSelection(range.start + 2)
263
- .run()
264
- },
245
+ )
246
+ .setTextSelection(range.start + 2)
247
+ .run()
248
+ },
265
249
 
266
- unsetDetails: () => ({ state, chain }) => {
267
- const { selection, schema } = state
268
- const details = findParentNode(node => node.type === this.type)(selection)
250
+ unsetDetails:
251
+ () =>
252
+ ({ state, chain }) => {
253
+ const { selection, schema } = state
254
+ const details = findParentNode(node => node.type === this.type)(selection)
269
255
 
270
- if (!details) {
271
- return false
272
- }
256
+ if (!details) {
257
+ return false
258
+ }
273
259
 
274
- const detailsSummaries = findChildren(details.node, node => node.type === schema.nodes.detailsSummary)
275
- const detailsContents = findChildren(details.node, node => node.type === schema.nodes.detailsContent)
260
+ const detailsSummaries = findChildren(details.node, node => node.type === schema.nodes.detailsSummary)
261
+ const detailsContents = findChildren(details.node, node => node.type === schema.nodes.detailsContent)
276
262
 
277
- if (!detailsSummaries.length || !detailsContents.length) {
278
- return false
279
- }
263
+ if (!detailsSummaries.length || !detailsContents.length) {
264
+ return false
265
+ }
280
266
 
281
- const detailsSummary = detailsSummaries[0]
282
- const detailsContent = detailsContents[0]
283
- const from = details.pos
284
- const $from = state.doc.resolve(from)
285
- const to = from + details.node.nodeSize
286
- const range = { from, to }
287
- const content = detailsContent.node.content.toJSON() as [] || []
288
- const defaultTypeForSummary = $from.parent.type.contentMatch.defaultType
289
-
290
- // TODO: this may break for some custom schemas
291
- const summaryContent = defaultTypeForSummary?.create(null, detailsSummary.node.content).toJSON()
292
- const mergedContent = [
293
- summaryContent,
294
- ...content,
295
- ]
296
-
297
- return chain()
298
- .insertContentAt(range, mergedContent)
299
- .setTextSelection(from + 1)
300
- .run()
301
- },
267
+ const detailsSummary = detailsSummaries[0]
268
+ const detailsContent = detailsContents[0]
269
+ const from = details.pos
270
+ const $from = state.doc.resolve(from)
271
+ const to = from + details.node.nodeSize
272
+ const range = { from, to }
273
+ const content = (detailsContent.node.content.toJSON() as []) || []
274
+ const defaultTypeForSummary = $from.parent.type.contentMatch.defaultType
275
+
276
+ // TODO: this may break for some custom schemas
277
+ const summaryContent = defaultTypeForSummary?.create(null, detailsSummary.node.content).toJSON()
278
+ const mergedContent = [summaryContent, ...content]
279
+
280
+ return chain()
281
+ .insertContentAt(range, mergedContent)
282
+ .setTextSelection(from + 1)
283
+ .run()
284
+ },
302
285
  }
303
286
  },
304
287
 
@@ -341,17 +324,13 @@ export const Details = Node.create<DetailsOptions>({
341
324
  }
342
325
 
343
326
  const isVisible = isNodeVisible($head.after() + 1, editor)
344
- const above = isVisible
345
- ? state.doc.nodeAt($head.after())
346
- : $head.node(-2)
327
+ const above = isVisible ? state.doc.nodeAt($head.after()) : $head.node(-2)
347
328
 
348
329
  if (!above) {
349
330
  return false
350
331
  }
351
332
 
352
- const after = isVisible
353
- ? 0
354
- : $head.indexAfter(-1)
333
+ const after = isVisible ? 0 : $head.indexAfter(-1)
355
334
  const type = defaultBlockAt(above.contentMatchAt(after))
356
335
 
357
336
  if (!type || !above.canReplaceWith(after, after, type)) {
@@ -364,9 +343,7 @@ export const Details = Node.create<DetailsOptions>({
364
343
  return false
365
344
  }
366
345
 
367
- const pos = isVisible
368
- ? $head.after() + 1
369
- : $head.after(-1)
346
+ const pos = isVisible ? $head.after() + 1 : $head.after(-1)
370
347
  const tr = state.tr.replaceWith(pos, pos, node)
371
348
  const $pos = tr.doc.resolve(pos)
372
349
  const newSelection = Selection.near($pos, 1)
@@ -400,11 +377,7 @@ export const Details = Node.create<DetailsOptions>({
400
377
  const { editor, type } = this
401
378
  const selectionSet = transactions.some(transaction => transaction.selectionSet)
402
379
 
403
- if (
404
- !selectionSet
405
- || !oldState.selection.empty
406
- || !newState.selection.empty
407
- ) {
380
+ if (!selectionSet || !oldState.selection.empty || !newState.selection.empty) {
408
381
  return
409
382
  }
410
383
 
@@ -427,19 +400,21 @@ export const Details = Node.create<DetailsOptions>({
427
400
  return
428
401
  }
429
402
 
430
- const detailsSummaries = findChildren(details.node, node => node.type === newState.schema.nodes.detailsSummary)
403
+ const detailsSummaries = findChildren(
404
+ details.node,
405
+ node => node.type === newState.schema.nodes.detailsSummary,
406
+ )
431
407
 
432
408
  if (!detailsSummaries.length) {
433
409
  return
434
410
  }
435
411
 
436
412
  const detailsSummary = detailsSummaries[0]
437
- const selectionDirection = oldState.selection.from < newState.selection.from
438
- ? 'forward'
439
- : 'backward'
440
- const correctedPosition = selectionDirection === 'forward'
441
- ? details.start + detailsSummary.pos
442
- : details.pos + detailsSummary.pos + detailsSummary.node.nodeSize
413
+ const selectionDirection = oldState.selection.from < newState.selection.from ? 'forward' : 'backward'
414
+ const correctedPosition =
415
+ selectionDirection === 'forward'
416
+ ? details.start + detailsSummary.pos
417
+ : details.pos + detailsSummary.pos + detailsSummary.node.nodeSize
443
418
  const selection = TextSelection.create(newState.doc, correctedPosition)
444
419
  const transaction = newState.tr.setSelection(selection)
445
420
 
@@ -1,14 +1,20 @@
1
- import { Editor, Predicate } from '@tiptap/core'
2
- import { Node as ProseMirrorNode, ResolvedPos } from '@tiptap/pm/model'
1
+ import type { Editor, Predicate } from '@tiptap/core'
2
+ import type { Node as ProseMirrorNode, ResolvedPos } from '@tiptap/pm/model'
3
3
 
4
4
  import { isNodeVisible } from './isNodeVisible.js'
5
5
 
6
- export const findClosestVisibleNode = ($pos: ResolvedPos, predicate: Predicate, editor: Editor): ({
7
- pos: number,
8
- start: number,
9
- depth: number,
10
- node: ProseMirrorNode,
11
- } | undefined) => {
6
+ export const findClosestVisibleNode = (
7
+ $pos: ResolvedPos,
8
+ predicate: Predicate,
9
+ editor: Editor,
10
+ ):
11
+ | {
12
+ pos: number
13
+ start: number
14
+ depth: number
15
+ node: ProseMirrorNode
16
+ }
17
+ | undefined => {
12
18
  for (let i = $pos.depth; i > 0; i -= 1) {
13
19
  const node = $pos.node(i)
14
20
  const match = predicate(node)
@@ -1,4 +1,4 @@
1
- import { Editor } from '@tiptap/core'
1
+ import type { Editor } from '@tiptap/core'
2
2
 
3
3
  export const isNodeVisible = (position: number, editor: Editor): boolean => {
4
4
  const node = editor.view.domAtPos(position).node as HTMLElement
@@ -1,7 +1,8 @@
1
- import { Editor, findChildren, findParentNode } from '@tiptap/core'
1
+ import type { Editor } from '@tiptap/core'
2
+ import { findChildren, findParentNode } from '@tiptap/core'
2
3
  import { GapCursor } from '@tiptap/pm/gapcursor'
3
- import { ResolvedPos } from '@tiptap/pm/model'
4
- import { Selection } from '@tiptap/pm/state'
4
+ import type { ResolvedPos } from '@tiptap/pm/model'
5
+ import type { Selection } from '@tiptap/pm/state'
5
6
 
6
7
  import { isNodeVisible } from './isNodeVisible.js'
7
8
 
@@ -11,18 +12,11 @@ export const setGapCursor = (editor: Editor, direction: 'down' | 'right') => {
11
12
  const { empty, $anchor } = selection
12
13
  const hasGapCursorExtension = !!extensionManager.extensions.find(extension => extension.name === 'gapCursor')
13
14
 
14
- if (
15
- !empty
16
- || $anchor.parent.type !== schema.nodes.detailsSummary
17
- || !hasGapCursorExtension
18
- ) {
15
+ if (!empty || $anchor.parent.type !== schema.nodes.detailsSummary || !hasGapCursorExtension) {
19
16
  return false
20
17
  }
21
18
 
22
- if (
23
- direction === 'right'
24
- && $anchor.parentOffset !== ($anchor.parent.nodeSize - 2)
25
- ) {
19
+ if (direction === 'right' && $anchor.parentOffset !== $anchor.parent.nodeSize - 2) {
26
20
  return false
27
21
  }
28
22
 
@@ -45,7 +39,7 @@ export const setGapCursor = (editor: Editor, direction: 'down' | 'right') => {
45
39
  }
46
40
 
47
41
  const $position = state.doc.resolve(details.pos + details.node.nodeSize)
48
- const $validPosition = GapCursor.findFrom($position, 1, false) as unknown as (null | ResolvedPos)
42
+ const $validPosition = GapCursor.findFrom($position, 1, false) as unknown as null | ResolvedPos
49
43
 
50
44
  if (!$validPosition) {
51
45
  return false
@@ -1 +0,0 @@
1
- {"version":3,"file":"details.d.ts","sourceRoot":"","sources":["../src/details.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,IAAI,EACL,MAAM,cAAc,CAAA;AAarB,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,cAAc,EAAE;QACd,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KACnB,CAAC;CACH;AAED,OAAO,QAAQ,cAAc,CAAC;IAC5B,UAAU,QAAQ,CAAC,UAAU;QAC3B,OAAO,EAAE;YACP;;eAEG;YACH,UAAU,EAAE,MAAM,UAAU,CAAC;YAC7B;;eAEG;YACH,YAAY,EAAE,MAAM,UAAU,CAAC;SAChC,CAAA;KACF;CACF;AAED,eAAO,MAAM,OAAO,2BA8YlB,CAAA"}
@@ -1,9 +0,0 @@
1
- import { Editor, Predicate } from '@tiptap/core';
2
- import { Node as ProseMirrorNode, ResolvedPos } from '@tiptap/pm/model';
3
- export declare const findClosestVisibleNode: ($pos: ResolvedPos, predicate: Predicate, editor: Editor) => ({
4
- pos: number;
5
- start: number;
6
- depth: number;
7
- node: ProseMirrorNode;
8
- } | undefined);
9
- //# sourceMappingURL=findClosestVisibleNode.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"findClosestVisibleNode.d.ts","sourceRoot":"","sources":["../../src/helpers/findClosestVisibleNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,EAAE,IAAI,IAAI,eAAe,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAIvE,eAAO,MAAM,sBAAsB,SAAU,WAAW,aAAa,SAAS,UAAU,MAAM,KAAG,CAAC;IAChG,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,eAAe,CAAC;CACvB,GAAG,SAAS,CAeZ,CAAA"}
@@ -1,3 +0,0 @@
1
- import { Editor } from '@tiptap/core';
2
- export declare const isNodeVisible: (position: number, editor: Editor) => boolean;
3
- //# sourceMappingURL=isNodeVisible.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"isNodeVisible.d.ts","sourceRoot":"","sources":["../../src/helpers/isNodeVisible.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAErC,eAAO,MAAM,aAAa,aAAc,MAAM,UAAU,MAAM,KAAG,OAKhE,CAAA"}
@@ -1,3 +0,0 @@
1
- import { Editor } from '@tiptap/core';
2
- export declare const setGapCursor: (editor: Editor, direction: "down" | "right") => boolean;
3
- //# sourceMappingURL=setGapCursor.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"setGapCursor.d.ts","sourceRoot":"","sources":["../../src/helpers/setGapCursor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAgC,MAAM,cAAc,CAAA;AAOnE,eAAO,MAAM,YAAY,WAAY,MAAM,aAAa,MAAM,GAAG,OAAO,YAsDvE,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,cAAc,cAAc,CAAA;AAE5B,eAAe,OAAO,CAAA"}