🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP →
Sign In

@tiptap/core

Package Overview
Dependencies
Maintainers
6
Versions
498
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tiptap/core - npm Package Compare versions

Comparing version
3.24.0
to
3.25.0
+27
src/utilities/attrsEqual.ts
/**
* Compare two attribute objects for equality.
* Handles null/undefined and asserts key presence in both objects so that
* `{ foo: undefined }` and `{ bar: undefined }` are not treated as equal.
*/
export function attrsEqual(
a: Record<string, any> | null | undefined,
b: Record<string, any> | null | undefined,
): boolean {
if (a === b) {
return true
}
if (!a || !b) {
return false
}
const keysA = Object.keys(a)
const keysB = Object.keys(b)
if (keysA.length !== keysB.length) {
return false
}
return keysA.every(
key => Object.prototype.hasOwnProperty.call(b, key) && Object.is(a[key], b[key]),
)
}
import { attrsEqual } from './attrsEqual.js'
/**
* Compare two arrays of mark objects for equality.
* Marks are compared by type and attributes (using attrsEqual),
* so key ordering in attrs does not matter.
*/
export function marksEqual(
a: { type: string; attrs?: Record<string, any> }[],
b: { type: string; attrs?: Record<string, any> }[],
): boolean {
if (a.length !== b.length) {
return false
}
return a.every((mark, i) => {
const other = b[i]
return mark.type === other.type && attrsEqual(mark.attrs, other.attrs)
})
}
+3
-3
{
"name": "@tiptap/core",
"version": "3.24.0",
"version": "3.25.0",
"description": "headless rich text editor",

@@ -61,6 +61,6 @@ "keywords": [

"devDependencies": {
"@tiptap/pm": "^3.24.0"
"@tiptap/pm": "^3.25.0"
},
"peerDependencies": {
"@tiptap/pm": "3.24.0"
"@tiptap/pm": "3.25.0"
},

@@ -67,0 +67,0 @@ "scripts": {

@@ -7,6 +7,9 @@ import type { RawCommands } from '../types.js'

/**
* Remove all marks in the current selection.
* Remove all clearable marks in the current selection.
* Marks with `clearable: false` are preserved
* @param options.ignoreClearable If true, removes all marks regardless of `clearable` setting. Defaults to `false`.
* @example editor.commands.unsetAllMarks()
* @example editor.commands.unsetAllMarks({ ignoreClearable: true })
*/
unsetAllMarks: () => ReturnType
unsetAllMarks: (options?: { ignoreClearable?: boolean }) => ReturnType
}

@@ -17,4 +20,5 @@ }

export const unsetAllMarks: RawCommands['unsetAllMarks'] =
() =>
({ tr, dispatch }) => {
(options = {}) =>
({ tr, dispatch, editor }) => {
const { ignoreClearable = false } = options
const { selection } = tr

@@ -27,5 +31,13 @@ const { empty, ranges } = selection

const { nonClearableMarks } = editor.extensionManager
if (dispatch) {
const clearableMarkTypes = Object.values(editor.schema.marks).filter(
markType => ignoreClearable || !nonClearableMarks.includes(markType.name),
)
ranges.forEach(range => {
tr.removeMark(range.$from.pos, range.$to.pos)
for (const markType of clearableMarkTypes) {
tr.removeMark(range.$from.pos, range.$to.pos, markType)
}
})

@@ -32,0 +44,0 @@ }

@@ -50,2 +50,4 @@ import { keymap } from '@tiptap/pm/keymap'

nonClearableMarks: string[] = []
constructor(extensions: Extensions, editor: Editor) {

@@ -468,2 +470,11 @@ this.editor = editor

}
const clearable =
callOrReturn(
getExtensionField<MarkConfig['clearable']>(extension, 'clearable', context),
) ?? true
if (!clearable) {
this.nonClearableMarks.push(extension.name)
}
}

@@ -470,0 +481,0 @@

@@ -29,14 +29,21 @@ import { Plugin, PluginKey } from '@tiptap/pm/state'

const { doc, selection } = state
const { ranges } = selection
const from = Math.min(...ranges.map(range => range.$from.pos))
const to = Math.max(...ranges.map(range => range.$to.pos))
const textSerializers = getTextSerializersFromSchema(schema)
const range = { from, to }
const { blockSeparator } = this.options
const options = {
...(blockSeparator !== undefined ? { blockSeparator } : {}),
textSerializers,
}
return getTextBetween(doc, range, {
...(this.options.blockSeparator !== undefined
? { blockSeparator: this.options.blockSeparator }
: {}),
textSerializers,
})
// Serialize each selection range independently and join the results.
// CellSelection exposes one range per selected cell; flattening to
// min(from)/max(to) would pull in unselected cells between them.
// Sort by document position so reverse selections (e.g. dragging
// upward) still emit text in document order.
const sortedRanges = [...selection.ranges].sort((a, b) => a.$from.pos - b.$from.pos)
return sortedRanges
.map(({ $from, $to }) =>
getTextBetween(doc, { from: $from.pos, to: $to.pos }, options),
)
.join(blockSeparator ?? '\n\n')
},

@@ -43,0 +50,0 @@ },

@@ -408,2 +408,3 @@ import type { Node as PMNode } from '@tiptap/pm/model'

this.element = options.element
this.element.draggable = false
this.contentElement = options.contentElement

@@ -410,0 +411,0 @@

@@ -34,2 +34,9 @@ import type { DOMOutputSpec, Mark as ProseMirrorMark, MarkSpec, MarkType } from '@tiptap/pm/model'

/**
* Whether this mark is removed by `unsetAllMarks`.
* Set to `false` for semantic marks (comments, suggestions) that should survive "clear formatting".
* @default true
*/
clearable?: boolean | (() => boolean)
/**
* Inclusive

@@ -36,0 +43,0 @@ */

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

export * from './attrsEqual.js'
export * from './callOrReturn.js'

@@ -24,2 +25,3 @@ export * from './canInsertNode.js'

export * as markdown from './markdown/index.js'
export * from './marksEqual.js'
export * from './mergeAttributes.js'

@@ -26,0 +28,0 @@ export * from './mergeDeep.js'

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display