Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@doist/typist

Package Overview
Dependencies
Maintainers
12
Versions
121
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@doist/typist - npm Package Compare versions

Comparing version
13.0.0
to
13.0.1
+6
-0
CHANGELOG.md

@@ -0,1 +1,7 @@

## [13.0.1](https://github.com/Doist/typist/compare/v13.0.0...v13.0.1) (2026-06-11)
### Bug Fixes
* replace wrapping empty textblock when inserting block-only Markdown content ([#1372](https://github.com/Doist/typist/issues/1372)) ([9ed9697](https://github.com/Doist/typist/commit/9ed969732647541c15c4548df4da20af92ccfe31))
## [13.0.0](https://github.com/Doist/typist/compare/v12.0.0...v13.0.0) (2026-06-09)

@@ -2,0 +8,0 @@

+12
-1

@@ -20,3 +20,3 @@ import { parseHtmlToElement } from "../../../../helpers/dom.js";

};
const { from, to } = typeof position === "number" ? {
let { from, to } = typeof position === "number" ? {
from: position,

@@ -30,2 +30,13 @@ to: position

const content = DOMParser.fromSchema(editor.schema).parseSlice(parseHtmlToElement(htmlContent), options.parseOptions);
let isOnlyBlockContent = content.content.childCount > 0;
content.content.forEach((node) => {
isOnlyBlockContent = isOnlyBlockContent ? node.isBlock : false;
});
if (from === to && isOnlyBlockContent) {
const { parent } = tr.doc.resolve(from);
if (parent.isTextblock && !parent.type.spec.code && !parent.childCount) {
from -= 1;
to += 1;
}
}
tr.replaceRange(from, to, content);

@@ -32,0 +43,0 @@ if (options.updateSelection) selectionToInsertionEnd(tr, tr.steps.length - 1, -1);

+1
-1

@@ -1,1 +0,1 @@

{"version":3,"file":"insert-markdown-content-at.js","names":[],"sources":["../../../../../src/extensions/core/extra-editor-commands/commands/insert-markdown-content-at.ts"],"sourcesContent":["import { RawCommands, selectionToInsertionEnd } from '@tiptap/core'\nimport { DOMParser } from '@tiptap/pm/model'\n\nimport { parseHtmlToElement } from '../../../../helpers/dom'\nimport { getHTMLSerializerInstance } from '../../../../serializers/html/html'\n\nimport type { Range } from '@tiptap/core'\nimport type { ParseOptions } from '@tiptap/pm/model'\n\n/**\n * Augment the official `@tiptap/core` module with extra commands so that the compiler knows about\n * them. For this to work externally, a wildcard export needs to be added to the root `index.ts`.\n */\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n insertMarkdownContentAt: {\n /**\n * Inserts the provided Markdown content as HTML into the editor at a specific position.\n *\n * @param position The position or range the Markdown will be inserted in.\n * @param markdown The Markdown content to parse and insert as HTML.\n * @param options An optional object with the following parameters:\n * @param options.parseOptions The parse options to use when the HTML content is parsed by ProseMirror.\n * @param options.updateSelection Whether the selection should move to the newly inserted content.\n */\n insertMarkdownContentAt: (\n position: number | Range,\n markdown: string,\n options?: {\n parseOptions?: ParseOptions\n updateSelection?: boolean\n },\n ) => ReturnType\n }\n }\n}\n\n/**\n * Inserts the provided Markdown content as HTML into the editor at a specific position.\n *\n * The solution for this function was inspired by how ProseMirror pastes content from the clipboard,\n * and how Tiptap inserts content with the `insertContentAt` command.\n */\nfunction insertMarkdownContentAt(\n position: number | Range,\n markdown: string,\n options?: {\n parseOptions?: ParseOptions\n updateSelection?: boolean\n },\n): ReturnType<RawCommands['insertMarkdownContentAt']> {\n return ({ editor, tr, dispatch }) => {\n // Check if the transaction should be dispatched\n // ref: https://tiptap.dev/api/commands#dry-run-for-commands\n if (dispatch) {\n // Default values for command options must be set here\n // (they do not work if set in the function signature)\n options = {\n parseOptions: {},\n updateSelection: true,\n ...options,\n }\n\n // Get the start and end positions from the provided position\n const { from, to } =\n typeof position === 'number'\n ? { from: position, to: position }\n : { from: position.from, to: position.to }\n\n // Parse the Markdown to HTML and then then into ProseMirror nodes\n const htmlContent = getHTMLSerializerInstance(editor.schema).serialize(markdown)\n const content = DOMParser.fromSchema(editor.schema).parseSlice(\n parseHtmlToElement(htmlContent),\n options.parseOptions,\n )\n\n // Inserts the content into the editor while preserving the current selection\n tr.replaceRange(from, to, content)\n\n // Set the text cursor to the end of the inserted content\n if (options.updateSelection) {\n selectionToInsertionEnd(tr, tr.steps.length - 1, -1)\n }\n }\n\n return true\n }\n}\n\nexport { insertMarkdownContentAt }\n"],"mappings":";;;;;;;;;;;AA2CA,SAAS,wBACL,UACA,UACA,SAIkD;CAClD,QAAQ,EAAE,QAAQ,IAAI,eAAe;EAGjC,IAAI,UAAU;GAGV,UAAU;IACN,cAAc,CAAC;IACf,iBAAiB;IACjB,GAAG;GACP;GAGA,MAAM,EAAE,MAAM,OACV,OAAO,aAAa,WACd;IAAE,MAAM;IAAU,IAAI;GAAS,IAC/B;IAAE,MAAM,SAAS;IAAM,IAAI,SAAS;GAAG;GAGjD,MAAM,cAAc,0BAA0B,OAAO,MAAM,CAAC,CAAC,UAAU,QAAQ;GAC/E,MAAM,UAAU,UAAU,WAAW,OAAO,MAAM,CAAC,CAAC,WAChD,mBAAmB,WAAW,GAC9B,QAAQ,YACZ;GAGA,GAAG,aAAa,MAAM,IAAI,OAAO;GAGjC,IAAI,QAAQ,iBACR,wBAAwB,IAAI,GAAG,MAAM,SAAS,GAAG,EAAE;EAE3D;EAEA,OAAO;CACX;AACJ"}
{"version":3,"file":"insert-markdown-content-at.js","names":[],"sources":["../../../../../src/extensions/core/extra-editor-commands/commands/insert-markdown-content-at.ts"],"sourcesContent":["import { RawCommands, selectionToInsertionEnd } from '@tiptap/core'\nimport { DOMParser } from '@tiptap/pm/model'\n\nimport { parseHtmlToElement } from '../../../../helpers/dom'\nimport { getHTMLSerializerInstance } from '../../../../serializers/html/html'\n\nimport type { Range } from '@tiptap/core'\nimport type { ParseOptions } from '@tiptap/pm/model'\n\n/**\n * Augment the official `@tiptap/core` module with extra commands so that the compiler knows about\n * them. For this to work externally, a wildcard export needs to be added to the root `index.ts`.\n */\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n insertMarkdownContentAt: {\n /**\n * Inserts the provided Markdown content as HTML into the editor at a specific position.\n *\n * @param position The position or range the Markdown will be inserted in.\n * @param markdown The Markdown content to parse and insert as HTML.\n * @param options An optional object with the following parameters:\n * @param options.parseOptions The parse options to use when the HTML content is parsed by ProseMirror.\n * @param options.updateSelection Whether the selection should move to the newly inserted content.\n */\n insertMarkdownContentAt: (\n position: number | Range,\n markdown: string,\n options?: {\n parseOptions?: ParseOptions\n updateSelection?: boolean\n },\n ) => ReturnType\n }\n }\n}\n\n/**\n * Inserts the provided Markdown content as HTML into the editor at a specific position.\n *\n * The solution for this function was inspired by how ProseMirror pastes content from the clipboard,\n * and how Tiptap inserts content with the `insertContentAt` command.\n */\nfunction insertMarkdownContentAt(\n position: number | Range,\n markdown: string,\n options?: {\n parseOptions?: ParseOptions\n updateSelection?: boolean\n },\n): ReturnType<RawCommands['insertMarkdownContentAt']> {\n return ({ editor, tr, dispatch }) => {\n // Check if the transaction should be dispatched\n // ref: https://tiptap.dev/api/commands#dry-run-for-commands\n if (dispatch) {\n // Default values for command options must be set here\n // (they do not work if set in the function signature)\n options = {\n parseOptions: {},\n updateSelection: true,\n ...options,\n }\n\n // Get the start and end positions from the provided position\n let { from, to } =\n typeof position === 'number'\n ? { from: position, to: position }\n : { from: position.from, to: position.to }\n\n // Parse the Markdown to HTML and then then into ProseMirror nodes\n const htmlContent = getHTMLSerializerInstance(editor.schema).serialize(markdown)\n const content = DOMParser.fromSchema(editor.schema).parseSlice(\n parseHtmlToElement(htmlContent),\n options.parseOptions,\n )\n\n // Check if the parsed content is non-empty and composed of block nodes only (the\n // initial value guards against an empty insert deleting the wrapping textblock below)\n let isOnlyBlockContent = content.content.childCount > 0\n\n content.content.forEach((node) => {\n isOnlyBlockContent = isOnlyBlockContent ? node.isBlock : false\n })\n\n // Expand the insertion range to replace the wrapping empty textblock when only block\n // content is inserted at a cursor position, mirroring the official `insertContentAt`\n // command behaviour (e.g., a table pasted into an empty paragraph should replace the\n // paragraph, instead of keeping the empty paragraph below the table)\n if (from === to && isOnlyBlockContent) {\n const { parent } = tr.doc.resolve(from)\n\n const isEmptyTextBlock =\n parent.isTextblock && !parent.type.spec.code && !parent.childCount\n\n if (isEmptyTextBlock) {\n from -= 1\n to += 1\n }\n }\n\n // Inserts the content into the editor while preserving the current selection\n tr.replaceRange(from, to, content)\n\n // Set the text cursor to the end of the inserted content\n if (options.updateSelection) {\n selectionToInsertionEnd(tr, tr.steps.length - 1, -1)\n }\n }\n\n return true\n }\n}\n\nexport { insertMarkdownContentAt }\n"],"mappings":";;;;;;;;;;;AA2CA,SAAS,wBACL,UACA,UACA,SAIkD;CAClD,QAAQ,EAAE,QAAQ,IAAI,eAAe;EAGjC,IAAI,UAAU;GAGV,UAAU;IACN,cAAc,CAAC;IACf,iBAAiB;IACjB,GAAG;GACP;GAGA,IAAI,EAAE,MAAM,OACR,OAAO,aAAa,WACd;IAAE,MAAM;IAAU,IAAI;GAAS,IAC/B;IAAE,MAAM,SAAS;IAAM,IAAI,SAAS;GAAG;GAGjD,MAAM,cAAc,0BAA0B,OAAO,MAAM,CAAC,CAAC,UAAU,QAAQ;GAC/E,MAAM,UAAU,UAAU,WAAW,OAAO,MAAM,CAAC,CAAC,WAChD,mBAAmB,WAAW,GAC9B,QAAQ,YACZ;GAIA,IAAI,qBAAqB,QAAQ,QAAQ,aAAa;GAEtD,QAAQ,QAAQ,SAAS,SAAS;IAC9B,qBAAqB,qBAAqB,KAAK,UAAU;GAC7D,CAAC;GAMD,IAAI,SAAS,MAAM,oBAAoB;IACnC,MAAM,EAAE,WAAW,GAAG,IAAI,QAAQ,IAAI;IAKtC,IAFI,OAAO,eAAe,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,OAAO,YAEtC;KAClB,QAAQ;KACR,MAAM;IACV;GACJ;GAGA,GAAG,aAAa,MAAM,IAAI,OAAO;GAGjC,IAAI,QAAQ,iBACR,wBAAwB,IAAI,GAAG,MAAM,SAAS,GAAG,EAAE;EAE3D;EAEA,OAAO;CACX;AACJ"}
{
"name": "@doist/typist",
"description": "The mighty Tiptap-based rich-text editor React component that powers Doist products.",
"version": "13.0.0",
"version": "13.0.1",
"license": "MIT",

@@ -107,3 +107,3 @@ "homepage": "https://typist.doist.dev/",

"devDependencies": {
"@doist/reactist": "30.1.4",
"@doist/reactist": "33.0.1",
"@mdx-js/react": "3.1.1",

@@ -121,3 +121,3 @@ "@semantic-release/changelog": "6.0.3",

"@types/lodash-es": "4.17.12",
"@types/react": "18.3.30",
"@types/react": "18.3.31",
"@types/react-dom": "18.3.7",

@@ -124,0 +124,0 @@ "@types/react-syntax-highlighter": "15.5.13",