Merge branch 'main' into feature/tippy-menus

This commit is contained in:
Philipp Kühn 2021-04-16 09:30:42 +02:00
commit cadabd09e0
210 changed files with 2049 additions and 894 deletions

View File

@ -38,6 +38,7 @@ module.exports = {
'no-alert': 'off',
'no-console': ['warn', { allow: ['warn', 'error'] }],
semi: ['error', 'never'],
'import/order': 'off',
'import/extensions': 'off',
'import/no-extraneous-dependencies': 'off',
'import/no-unresolved': 'off',

113
README.md
View File

@ -1,78 +1,65 @@
# tiptap 2 (Sponsors preview!)
# tiptap 2
A headless, framework-agnostic and extendable rich text editor, based on [ProseMirror](https://github.com/ProseMirror/prosemirror).
[![Build Status](https://github.com/ueberdosis/tiptap-next/workflows/build/badge.svg)](https://github.com/ueberdosis/tiptap-next/actions)
[![Version](https://img.shields.io/npm/v/@tiptap/core.svg?label=version)](https://www.npmjs.com/package/@tiptap/core)
[![Downloads](https://img.shields.io/npm/dm/@tiptap/core.svg)](https://npmcharts.com/compare/@tiptap/core?minimal=true)
[![License](https://img.shields.io/npm/l/@tiptap/core.svg)](https://www.npmjs.com/package/@tiptap/core)
[![Build Status](https://github.com/ueberdosis/tiptap-next/workflows/build/badge.svg)](https://github.com/ueberdosis/tiptap-next/actions)
[![Sponsor](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub)](https://github.com/sponsors/ueberdosis)
[![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg?sanitize=true)](https://discord.gg/WtJ49jGshW)
[![Sponsor](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub)](https://github.com/sponsors/ueberdosis)
A headless and extendable rich text editor, based on [ProseMirror](https://github.com/ProseMirror/prosemirror), which is already in use at many well-known companies such as *New York Times*, *The Guardian* or *Atlassian*.
> If youre looking for tiptap 1, [click here](https://github.com/ueberdosis/tiptap/tree/v1).
## Feedback
We are looking for your feedback to improve tiptap 2 before the first public release! Share everything that helps to make it better for everyone!
* Join the Discord server! [Link](https://discord.gg/WtJ49jGshW)
* Create issues on GitHub! [Link](https://github.com/ueberdosis/tiptap-next/issues)
* Send an email! [humans@tiptap.dev](mailto:humans@tiptap.dev)
* Follow us on Twitter! [@tiptap_editor](https://twitter.com/tiptap_editor) [@hanspagel](https://twitter.com/hanspagel) [@_philippkuehn](https://twitter.com/_philippkuehn)
## Why we built tiptap
We were looking for a text editor for [Vue.js](https://github.com/vuejs/vue) and found some solutions that didnt really satisfy us. An editor should be easy to extend and not based on old dependencies such as jQuery. For React there is already a great editor called [Slate.js](https://github.com/ianstormtaylor/slate), which impresses with its modularity. We came across [ProseMirror](https://github.com/prosemirror) and decided to build on it. ProseMirror is a toolkit for building rich text editors that are already in use at many well-known companies such as *Atlassian* or *New York Times*.
### What does `headless` mean?
With headless components you'll have (almost) full control over markup and styling. We dont want to tell you what a menu should look like or where it should be rendered in the DOM. Thats all up to you. There is also a [great article about headless components](https://adamwathan.me/renderless-components-in-vuejs/) by Adam Wathan.
### How is the data stored under the hood?
You can save your data as a raw `HTML` string or can get a `JSON`-serializable representation of your document. And of course, you can also pass `HTML` or `JSON` content back to the editor.
## 💖 Sponsor the development
Are you using tiptap in production? We need your sponsorship to maintain, update and develop tiptap. [Become a Sponsor now!](https://github.com/sponsors/ueberdosis)
## Examples
Have a look at the [examples to see tiptap in action](https://next.tiptap.dev/examples).
## Documentation
To check out some live examples, visit [next.tiptap.dev](https://next.tiptap.dev/).
The full documentation is a available on [next.tiptap.dev](https://next.tiptap.dev/installation).
## Community
For help, discussion about best practices, or any other conversation that would benefit from being searchable:
[Discuss tiptap on GitHub](https://github.com/ueberdosis/tiptap/discussions)
For casual chit-chat with others using the framework:
[Join the tiptap Discord Server](https://discord.gg/WtJ49jGshW)
## 💖 Sponsors
* [überdosis](https://ueberdosis.io/)
* [Ziff Media Group](https://ziffmedia.com/)
* [Incyte Studios](https://incytestudios.com/)
* [dotCMS](http://dotcms.com/)
* [Restruct](https://restruct.nl/)
[mymind](https://mymind.com/),
[DocIQ](https://www.dociq.io/),
[Apostrophe CMS](https://apostrophecms.com/),
[Ycode](https://www.ycode.com/),
[@impactvelocity](https://github.com/impactvelocity),
[Flow Mobile](https://www.flowmobile.app/),
[Gretel](http://www.gretel.co/),
[Omics Data Automation](https://www.omicsautomation.com/),
[Novadiscovery](http://www.novadiscovery.com/),
[Atlan Technologies](https://atlan.com/),
[Gamma](https://gamma.app/),
[Kirchner Consulting](https://kirchnerconsulting.ch/),
[IT Xpert](https://itxpert.ch/) and hundreds of awesome inviduals.
Using tiptap in production? Invest in the future of tiptap and [become a sponsor!](https://github.com/sponsors/ueberdosis)
## Contributing
Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
## Maintainers
- [Philipp Kühn](https://github.com/philippkuehn) (developer)
- [Hans Pagel](https://github.com/hanspagel) (maintainer)
## Premium Sponsors
- [überdosis](https://ueberdosis.io/)
- [mymind](https://mymind.com/)
- [DocIQ](https://www.dociq.io/)
- [Apostrophe CMS](https://apostrophecms.com/)
- [Ycode](https://www.ycode.com/)
- [@impactvelocity](https://github.com/impactvelocity)
- [Flow Mobile](https://www.flowmobile.app/)
## Credits
- [Sam Willis](https://github.com/samwillis)
- [Christoph Flathmann](https://github.com/Chrissi2812)
- [Erick Wilder](https://github.com/erickwilder)
- [Marius Tolzmann](https://github.com/mariux)
- [jjangga0214](https://github.com/jjangga0214)
- [Maya Nedeljkovich](https://github.com/mayacoda)
- [Ryan Bliss](https://github.com/ryanbliss)
- [Gregor](https://github.com/gambolputty)
- [All Contributors](../../contributors)
## Links
- https://github.com/ueberdosis/awesome-tiptap
## Become a sponsor
Your benefits as a sponsor:
* Give back to the open source community
* Get early access to private repositories
* Ensure the further maintenace and development of tiptap
* Your issues and pull requests get a `sponsor 💖` label
* Get a sponsor badge in all your comments on GitHub
* Show support in your GitHub profile
* Receive monthly reports about our open source work
Does that sound good? [Become a sponsor!](https://github.com/sponsors/ueberdosis)
## Contributors
[Sam Willis](https://github.com/samwillis),
[Christoph Flathmann](https://github.com/Chrissi2812),
[Erick Wilder](https://github.com/erickwilder),
[Marius Tolzmann](https://github.com/mariux),
[jjangga0214](https://github.com/jjangga0214),
[Maya Nedeljkovich](https://github.com/mayacoda),
[Ryan Bliss](https://github.com/ryanbliss),
[Gregor](https://github.com/gambolputty) and [many more](../../contributors).
## License
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

View File

@ -16,10 +16,10 @@ import Text from '@tiptap/extension-text'
import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'
import CodeBlockComponent from './CodeBlockComponent'
// install all highlight.js languages
import 'lowlight'
// load all highlight.js languages
import lowlight from 'lowlight'
// or install specific languages only
// load specific languages only
// import lowlight from 'lowlight/lib/core'
// import javascript from 'highlight.js/lib/languages/javascript'
// lowlight.registerLanguage('javascript', javascript)
@ -41,11 +41,13 @@ export default {
Document,
Paragraph,
Text,
CodeBlockLowlight.extend({
addNodeView() {
return VueNodeViewRenderer(CodeBlockComponent)
},
}),
CodeBlockLowlight
.extend({
addNodeView() {
return VueNodeViewRenderer(CodeBlockComponent)
},
})
.configure({ lowlight }),
],
content: `
<p>

View File

@ -70,7 +70,7 @@ export default {
this.editor = new Editor({
extensions: [
...defaultExtensions().filter(extension => extension.config.name !== 'history'),
...defaultExtensions().filter(extension => extension.name !== 'history'),
Highlight,
TaskList,
TaskItem,

View File

@ -6,128 +6,129 @@ export const SmilieReplacer = Extension.create({
addInputRules() {
return [
new InputRule(/-___-/, '😑'),
new InputRule(/:'-\)/, '😂'),
new InputRule(/':-\)/, '😅'),
new InputRule(/':-D/, '😅'),
new InputRule(/>:-\)/, '😆'),
new InputRule(/-__-/, '😑'),
new InputRule(/':-\(/, '😓'),
new InputRule(/:'-\(/, '😢'),
new InputRule(/>:-\(/, '😠'),
new InputRule(/O:-\)/, '😇'),
new InputRule(/0:-3/, '😇'),
new InputRule(/0:-\)/, '😇'),
new InputRule(/0;\^\)/, '😇'),
new InputRule(/O;-\)/, '😇'),
new InputRule(/0;-\)/, '😇'),
new InputRule(/O:-3/, '😇'),
new InputRule(/:'\)/, '😂'),
new InputRule(/:-D/, '😃'),
new InputRule(/':\)/, '😅'),
new InputRule(/'=\)/, '😅'),
new InputRule(/':D/, '😅'),
new InputRule(/'=D/, '😅'),
new InputRule(/>:\)/, '😆'),
new InputRule(/>;\)/, '😆'),
new InputRule(/>=\)/, '😆'),
new InputRule(/;-\)/, '😉'),
new InputRule(/\*-\)/, '😉'),
new InputRule(/;-\]/, '😉'),
new InputRule(/;\^\)/, '😉'),
new InputRule(/B-\)/, '😎'),
new InputRule(/8-\)/, '😎'),
new InputRule(/B-D/, '😎'),
new InputRule(/8-D/, '😎'),
new InputRule(/:-\*/, '😘'),
new InputRule(/:\^\*/, '😘'),
new InputRule(/:-\)/, '🙂'),
new InputRule(/-_-/, '😑'),
new InputRule(/:-X/, '😶'),
new InputRule(/:-#/, '😶'),
new InputRule(/:-x/, '😶'),
new InputRule(/>.</, '😣'),
new InputRule(/:-O/, '😮'),
new InputRule(/:-o/, '😮'),
new InputRule(/O_O/, '😮'),
new InputRule(/>:O/, '😮'),
new InputRule(/:-P/, '😛'),
new InputRule(/:-p/, '😛'),
new InputRule(/:-Þ/, '😛'),
new InputRule(/:-þ/, '😛'),
new InputRule(/:-b/, '😛'),
new InputRule(/>:P/, '😜'),
new InputRule(/X-P/, '😜'),
new InputRule(/x-p/, '😜'),
new InputRule(/':\(/, '😓'),
new InputRule(/'=\(/, '😓'),
new InputRule(/>:\\/, '😕'),
new InputRule(/>:\//, '😕'),
new InputRule(/:-\//, '😕'),
new InputRule(/:-./, '😕'),
new InputRule(/>:\[/, '😞'),
new InputRule(/:-\(/, '😞'),
new InputRule(/:-\[/, '😞'),
new InputRule(/:'\(/, '😢'),
new InputRule(/;-\(/, '😢'),
new InputRule(/#-\)/, '😵'),
new InputRule(/%-\)/, '😵'),
new InputRule(/X-\)/, '😵'),
new InputRule(/>:\(/, '😠'),
new InputRule(/0:3/, '😇'),
new InputRule(/0:\)/, '😇'),
new InputRule(/O:\)/, '😇'),
new InputRule(/O=\)/, '😇'),
new InputRule(/O:3/, '😇'),
new InputRule(/<\/3/, '💔'),
new InputRule(/:D/, '😃'),
new InputRule(/=D/, '😃'),
new InputRule(/;\)/, '😉'),
new InputRule(/\*\)/, '😉'),
new InputRule(/;\]/, '😉'),
new InputRule(/;D/, '😉'),
new InputRule(/B\)/, '😎'),
new InputRule(/8\)/, '😎'),
new InputRule(/:\*/, '😘'),
new InputRule(/=\*/, '😘'),
new InputRule(/:\)/, '🙂'),
new InputRule(/=\]/, '🙂'),
new InputRule(/=\)/, '🙂'),
new InputRule(/:\]/, '🙂'),
new InputRule(/:X/, '😶'),
new InputRule(/:#/, '😶'),
new InputRule(/=X/, '😶'),
new InputRule(/=x/, '😶'),
new InputRule(/:x/, '😶'),
new InputRule(/=#/, '😶'),
new InputRule(/:O/, '😮'),
new InputRule(/:o/, '😮'),
new InputRule(/:P/, '😛'),
new InputRule(/=P/, '😛'),
new InputRule(/:p/, '😛'),
new InputRule(/=p/, '😛'),
new InputRule(/:Þ/, '😛'),
new InputRule(/:þ/, '😛'),
new InputRule(/:b/, '😛'),
new InputRule(/d:/, '😛'),
new InputRule(/:\//, '😕'),
new InputRule(/:\\/, '😕'),
new InputRule(/=\//, '😕'),
new InputRule(/=\\/, '😕'),
new InputRule(/:L/, '😕'),
new InputRule(/=L/, '😕'),
new InputRule(/:\(/, '😞'),
new InputRule(/:\[/, '😞'),
new InputRule(/=\(/, '😞'),
new InputRule(/;\(/, '😢'),
new InputRule(/D:/, '😨'),
new InputRule(/:\$/, '😳'),
new InputRule(/=\$/, '😳'),
new InputRule(/#\)/, '😵'),
new InputRule(/%\)/, '😵'),
new InputRule(/X\)/, '😵'),
new InputRule(/:@/, '😠'),
new InputRule(/<3/, '❤️'),
new InputRule(/-___- /, '😑 '),
new InputRule(/:'-\) /, '😂 '),
new InputRule(/':-\) /, '😅 '),
new InputRule(/':-D /, '😅 '),
new InputRule(/>:-\) /, '😆 '),
new InputRule(/-__- /, '😑 '),
new InputRule(/':-\( /, '😓 '),
new InputRule(/:'-\( /, '😢 '),
new InputRule(/>:-\( /, '😠 '),
new InputRule(/O:-\) /, '😇 '),
new InputRule(/0:-3 /, '😇 '),
new InputRule(/0:-\) /, '😇 '),
new InputRule(/0;\^\) /, '😇 '),
new InputRule(/O;-\) /, '😇 '),
new InputRule(/0;-\) /, '😇 '),
new InputRule(/O:-3 /, '😇 '),
new InputRule(/:'\) /, '😂 '),
new InputRule(/:-D /, '😃 '),
new InputRule(/':\) /, '😅 '),
new InputRule(/'=\) /, '😅 '),
new InputRule(/':D /, '😅 '),
new InputRule(/'=D /, '😅 '),
new InputRule(/>:\) /, '😆 '),
new InputRule(/>;\) /, '😆 '),
new InputRule(/>=\) /, '😆 '),
new InputRule(/;-\) /, '😉 '),
new InputRule(/\*-\) /, '😉 '),
new InputRule(/;-\] /, '😉 '),
new InputRule(/;\^\) /, '😉 '),
new InputRule(/B-\) /, '😎 '),
new InputRule(/8-\) /, '😎 '),
new InputRule(/B-D /, '😎 '),
new InputRule(/8-D /, '😎 '),
new InputRule(/:-\* /, '😘 '),
new InputRule(/:\^\* /, '😘 '),
new InputRule(/:-\) /, '🙂 '),
new InputRule(/-_- /, '😑 '),
new InputRule(/:-X /, '😶 '),
new InputRule(/:-# /, '😶 '),
new InputRule(/:-x /, '😶 '),
new InputRule(/>.< /, '😣 '),
new InputRule(/:-O /, '😮 '),
new InputRule(/:-o /, '😮 '),
new InputRule(/O_O /, '😮 '),
new InputRule(/>:O /, '😮 '),
new InputRule(/:-P /, '😛 '),
new InputRule(/:-p /, '😛 '),
new InputRule(/:-Þ /, '😛 '),
new InputRule(/:-þ /, '😛 '),
new InputRule(/:-b /, '😛 '),
new InputRule(/>:P /, '😜 '),
new InputRule(/X-P /, '😜 '),
new InputRule(/x-p /, '😜 '),
new InputRule(/':\( /, '😓 '),
new InputRule(/'=\( /, '😓 '),
new InputRule(/>:\\ /, '😕 '),
new InputRule(/>:\/ /, '😕 '),
new InputRule(/:-\/ /, '😕 '),
new InputRule(/:-. /, '😕 '),
new InputRule(/>:\[ /, '😞 '),
new InputRule(/:-\( /, '😞 '),
new InputRule(/:-\[ /, '😞 '),
new InputRule(/:'\( /, '😢 '),
new InputRule(/;-\( /, '😢 '),
new InputRule(/#-\) /, '😵 '),
new InputRule(/%-\) /, '😵 '),
new InputRule(/X-\) /, '😵 '),
new InputRule(/>:\( /, '😠 '),
new InputRule(/0:3 /, '😇 '),
new InputRule(/0:\) /, '😇 '),
new InputRule(/O:\) /, '😇 '),
new InputRule(/O=\) /, '😇 '),
new InputRule(/O:3 /, '😇 '),
new InputRule(/<\/3 /, '💔 '),
new InputRule(/:D /, '😃 '),
new InputRule(/=D /, '😃 '),
new InputRule(/;\) /, '😉 '),
new InputRule(/\*\) /, '😉 '),
new InputRule(/;\] /, '😉 '),
new InputRule(/;D /, '😉 '),
new InputRule(/B\) /, '😎 '),
new InputRule(/8\) /, '😎 '),
new InputRule(/:\* /, '😘 '),
new InputRule(/=\* /, '😘 '),
new InputRule(/:\) /, '🙂 '),
new InputRule(/=\] /, '🙂 '),
new InputRule(/=\) /, '🙂 '),
new InputRule(/:\] /, '🙂 '),
new InputRule(/:X /, '😶 '),
new InputRule(/:# /, '😶 '),
new InputRule(/=X /, '😶 '),
new InputRule(/=x /, '😶 '),
new InputRule(/:x /, '😶 '),
new InputRule(/=# /, '😶 '),
new InputRule(/:O /, '😮 '),
new InputRule(/:o /, '😮 '),
new InputRule(/:P /, '😛 '),
new InputRule(/=P /, '😛 '),
new InputRule(/:p /, '😛 '),
new InputRule(/=p /, '😛 '),
new InputRule(/:Þ /, '😛 '),
new InputRule(/:þ /, '😛 '),
new InputRule(/:b /, '😛 '),
new InputRule(/d: /, '😛 '),
new InputRule(/:\/ /, '😕 '),
new InputRule(/:\\ /, '😕 '),
new InputRule(/=\/ /, '😕 '),
new InputRule(/=\\ /, '😕 '),
new InputRule(/:L /, '😕 '),
new InputRule(/=L /, '😕 '),
new InputRule(/:\( /, '😞 '),
new InputRule(/:\[ /, '😞 '),
new InputRule(/=\( /, '😞 '),
new InputRule(/;\( /, '😢 '),
new InputRule(/D: /, '😨 '),
new InputRule(/:\$ /, '😳 '),
new InputRule(/=\$ /, '😳 '),
new InputRule(/#\) /, '😵 '),
new InputRule(/%\) /, '😵 '),
new InputRule(/X\) /, '😵 '),
new InputRule(/:@ /, '😠 '),
new InputRule(/<3 /, '❤️ '),
new InputRule(/\/shrug/, '¯\\_(ツ)_/¯'),
]
},
})

View File

@ -17,7 +17,7 @@ const CustomDocument = Document.extend({
})
const CustomTaskItem = TaskItem.extend({
content: 'text*',
content: 'inline*',
})
export default {

View File

@ -0,0 +1,69 @@
<template>
<div v-if="editor">
<button @click="editor.chain().focus().setWordBreak().run()" :class="{ 'is-active': editor.isActive('wordBreak') }">
wbr
</button>
<editor-content :editor="editor" />
<h2>HTML</h2>
{{ editor.getHTML() }}
</div>
</template>
<script>
import { Editor, EditorContent } from '@tiptap/vue-2'
import { defaultExtensions } from '@tiptap/starter-kit'
import { WordBreak } from './word-break'
export default {
components: {
EditorContent,
},
data() {
return {
editor: null,
}
},
mounted() {
this.editor = new Editor({
extensions: [
...defaultExtensions(),
WordBreak,
],
content: `
<p>super<wbr>longword</p>
`,
})
},
beforeDestroy() {
this.editor.destroy()
},
}
</script>
<style lang="scss" scoped>
::v-deep {
.ProseMirror {
> * + * {
margin-top: 0.75em;
}
}
.word-break {
display: inline-block;
height: 1em;
margin: 0 0.1em;
line-height: 1em;
border: 1px dashed #868e96;
color: #868e96;
&::before {
content: '-';
}
}
}
</style>

View File

@ -0,0 +1,71 @@
import { Command, Node, mergeAttributes } from '@tiptap/core'
import { exitCode } from 'prosemirror-commands'
export interface WordBreakOptions {
HTMLAttributes: {
[key: string]: any
},
}
declare module '@tiptap/core' {
interface Commands {
wordBreak: {
/**
* Add a hard break
*/
setWordBreak: () => Command,
}
}
}
export const WordBreak = Node.create<WordBreakOptions>({
name: 'wordBreak',
defaultOptions: {
HTMLAttributes: {},
},
inline: true,
group: 'inline',
selectable: false,
parseHTML() {
return [
{ tag: 'wbr' },
]
},
renderHTML({ HTMLAttributes }) {
return ['wbr', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)]
},
addCommands() {
return {
setWordBreak: () => ({ commands, state, dispatch }) => {
return commands.first([
() => exitCode(state, dispatch),
() => {
if (dispatch) {
state.tr.replaceSelectionWith(this.type.create()).scrollIntoView()
}
return true
},
])
},
}
},
addNodeView() {
return () => {
const dom = document.createElement('span')
dom.classList.add('word-break')
return {
dom,
}
}
},
})

View File

@ -125,4 +125,61 @@ context('/demos/Nodes/CodeBlock', () => {
.should('contain', 'Code')
})
})
it('reverts the markdown shortcut when pressing backspace', () => {
cy.get('.ProseMirror').then(([{ editor }]) => {
editor.commands.clearContent()
cy.get('.ProseMirror')
.type('``` {backspace}')
cy.get('.ProseMirror pre')
.should('not.exist')
})
})
it('removes the code block when pressing backspace', () => {
cy.get('.ProseMirror').then(([{ editor }]) => {
editor.commands.clearContent()
cy.get('.ProseMirror pre')
.should('not.exist')
cy.get('.ProseMirror')
.type('Paragraph{enter}``` A{backspace}{backspace}')
cy.get('.ProseMirror pre')
.should('not.exist')
})
})
it('removes the code block when pressing backspace, even with blank lines', () => {
cy.get('.ProseMirror').then(([{ editor }]) => {
editor.commands.clearContent()
cy.get('.ProseMirror pre')
.should('not.exist')
cy.get('.ProseMirror')
.type('Paragraph{enter}{enter}``` A{backspace}{backspace}')
cy.get('.ProseMirror pre')
.should('not.exist')
})
})
it('removes the code block when pressing backspace, even at start of document', () => {
cy.get('.ProseMirror').then(([{ editor }]) => {
editor.commands.clearContent()
cy.get('.ProseMirror pre')
.should('not.exist')
cy.get('.ProseMirror')
.type('``` A{leftArrow}{backspace}')
cy.get('.ProseMirror pre')
.should('not.exist')
})
})
})

View File

@ -15,10 +15,10 @@ import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'
// install all highlight.js languages
import 'lowlight'
// load all highlight.js languages
import lowlight from 'lowlight'
// or install specific languages only
// load specific languages only
// import lowlight from 'lowlight/lib/core'
// import javascript from 'highlight.js/lib/languages/javascript'
// lowlight.registerLanguage('javascript', javascript)
@ -40,7 +40,9 @@ export default {
Document,
Paragraph,
Text,
CodeBlockLowlight,
CodeBlockLowlight.configure({
lowlight,
}),
],
content: `
<p>

View File

@ -1,5 +0,0 @@
# Advanced Example
Use a custom list of extensions.
<demo name="HandleExtensions" />

View File

@ -182,6 +182,8 @@ new Editor({
})
```
You can use that to hook into event handlers and pass - for example - a custom paste handler, too.
### Parse options
Passed content is parsed by ProseMirror. To hook into the parsing, you can pass `parseOptions` which are then handled by [ProseMirror](https://prosemirror.net/docs/ref/#model.ParseOptions).

View File

@ -1,4 +1,4 @@
# Concept
# Introduction
tiptap is a friendly wrapper around [ProseMirror](https://ProseMirror.net). Although tiptap tries to hide most of the complexity of ProseMirror, its built on top of its APIs and we recommend you to read through the [ProseMirror Guide](https://ProseMirror.net/docs/guide/) for advanced usage.
### Structure
@ -20,7 +20,8 @@ ProseMirror has its own vocabulary and youll stumble upon all those words now
| State | Everything to describe the current content and selection of your editor. |
| Transaction | A change to the state (updated selection, content, …) |
| Extension | Registeres new functionality. |
| Node | Adds blocks, like heading, paragraph. |
| Mark | Adds inline formatting, for example bold or italic. |
| Node | A type of content, for example a heading or a paragraph. |
| Mark | Can be applied to nodes, for example for inline formatting. |
| Command | Execute an action inside the editor, that somehow changes the state. |
| Decoration | Styling on top of the document, for example to highlight mistakes. |

View File

@ -3,7 +3,7 @@
## toc
## Introduction
Marks add new capabilities to tiptap. Marks are like different types of inline styling, for example bold, italic or highlights.
One or multiple marks can be applied to [nodes](/api/nodes), for example to add inline formatting like bold and italic, or other additional information.
## List of supported marks
| Title | Default Extension | Source Code |

View File

@ -3,7 +3,7 @@
## toc
## Introduction
Nodes add new capabilities to tiptap. Nodes are like block types, for example a paragraph, heading, or code block.
If you think of the document as a tree, then nodes are just a type of content in that tree. Examples of nodes are paragraphs, headings, or code blocks. But nodes dont have to be blocks. They can also be rendered inline with the text, for example for **@mentions**.
## List of supported nodes
| Title | Default Extension | Source Code |

View File

@ -6,7 +6,7 @@ There is no extension or example yet, but its definitely on our list to build
If you want to give it a shot yourself, you could start altering the [`Mention`](/api/nodes/mention) node. This uses the [`Suggestion`](/api/utilities/suggestion) utility, which should help with an autocomplete and such things.
:::pro Fund the development ♥
We need your support to maintain, update, support and develop tiptap 2. If youre waiting for this extension, [become a sponsor and fund our work](/sponsor).
We need your support to maintain, update, support and develop tiptap. If youre waiting for this extension, [become a sponsor and fund our work](/sponsor).
:::
## Bring your own emoji picker

View File

@ -1,7 +1,7 @@
# Hashtag
:::pro Fund the development ♥
We need your support to maintain, update, support and develop tiptap 2. If youre waiting for this extension, [become a sponsor and fund our work](/sponsor).
We need your support to maintain, update, support and develop tiptap. If youre waiting for this extension, [become a sponsor and fund our work](/sponsor).
:::
TODO

View File

@ -39,7 +39,7 @@ yarn add @tiptap/vue-2
And yes, we plan to support React, too. Meanwhile, you can roll your own `ReactRenderer`, but dont forget to share it with the community.
Its also possible to use plain JavaScript, but that is probably a lot more work.
Its also possible to use Vanilla JavaScript, but that is probably a lot more work.
## Settings
| Option | Type | Default | Description |

View File

@ -4,7 +4,7 @@
This extension renders a task item list element, which is a `<li>` tag with a `data-type` attribute set to `taskItem`. It also renders a checkbox inside the list element, which updates a `checked` attribute.
This extension doesnt require any JavaScript framework, its based on plain JavaScript.
This extension doesnt require any JavaScript framework, its based on Vanilla JavaScript.
## Installation
```bash

View File

@ -2,7 +2,7 @@
[![Version](https://img.shields.io/npm/v/@tiptap/extension-task-list.svg?label=version)](https://www.npmjs.com/package/@tiptap/extension-task-list)
[![Downloads](https://img.shields.io/npm/dm/@tiptap/extension-task-list.svg)](https://npmcharts.com/compare/@tiptap/extension-task-list?minimal=true)
This extension enables you to use task lists in the editor. They are rendered as `<ul data-type="task_list">`. This implementation doesnt require any framework, its using plain JavaScript only.
This extension enables you to use task lists in the editor. They are rendered as `<ul data-type="task_list">`. This implementation doesnt require any framework, its using Vanilla JavaScript only.
Type <code>[ ]&nbsp;</code> or <code>[x]&nbsp;</code> at the beginning of a new line and it will magically transform to a task list.

View File

@ -22,19 +22,17 @@ Be nice! The content of this editor is shared with other users from the Internet
In case youre wondering what kind of sorcery you need on the server to achieve this, here is the whole backend code for the demo:
:::warning Request early access
Our plug & play collaboration backend hocuspocus is still work in progress. If you want to give it a try, [request early access](https://hocuspocus.dev/).
Our plug & play collaboration backend hocuspocus is still work in progress. If you want to give it a try, [request early access](https://www.hocuspocus.dev).
:::
```js
import { Server } from '@hocuspocus/server'
import { RocksDB } from '@hocuspocus/rocksdb'
import { RocksDB } from '@hocuspocus/extension-rocksdb'
const server = Server.configure({
port: 80,
extensions: [
new RocksDB({
path: './database',
})
new RocksDB({ path: './database' }),
],
})

View File

@ -1,7 +1,7 @@
# Images
:::pro Fund the development ♥
We need your support to maintain, update, support and develop tiptap 2. If youre hoping for more features related to images, [become a sponsor and fund our work](/sponsor).
We need your support to maintain, update, support and develop tiptap. If youre hoping for more features related to images, [become a sponsor and fund our work](/sponsor).
:::
<demo name="Examples/Images" />

View File

@ -6,3 +6,8 @@ Thanks to [node views](/guide/node-views) you can add interactivity to your node
Vue: 'Guide/NodeViews/VueComponent',
React: 'Guide/NodeViews/ReactComponent',
}" />
<demos :items="{
Vue: 'Guide/NodeViews/VueComponentContent',
React: 'Guide/NodeViews/ReactComponentContent',
}" />

View File

@ -1,3 +1,3 @@
# Code block language
# Syntax highlighting
<demo name="Examples/CodeBlockLanguage" />

View File

@ -9,3 +9,4 @@ Congratulations! Youve found our playground with a list of experiments. Be aw
* [@tiptap/extension-iframe?](/experiments/embeds)
* [@tiptap/extension-toggle-list?](/experiments/details)
* [@tiptap/extension-collaboration-annotation](/experiments/collaboration-annotation)
* [@tiptap/extension-word-break](/experiments/word-break)

View File

@ -0,0 +1,5 @@
# Word break
⚠️ Experiment
<demo name="Experiments/WordBreak" />

View File

@ -1,7 +1,7 @@
# Accessibility
:::pro Fund the development ♥
We need your support to maintain, update, support and develop tiptap 2. If youre waiting for progress here, [become a sponsor and fund our work](/sponsor).
We need your support to maintain, update, support and develop tiptap. If youre waiting for progress here, [become a sponsor and fund our work](/sponsor).
:::
## toc

View File

@ -216,7 +216,7 @@ Yes, its magic. As already mentioned, that is all based on the fantastic Y.js
Our collaborative editing backend handles the syncing, authorization, persistence and scaling. Lets go through a few common use cases here!
:::warning Request early access
Our plug & play collaboration backend hocuspocus is still work in progress. If you want to give it a try, [request early access](https://hocuspocus.dev/).
Our plug & play collaboration backend hocuspocus is still work in progress. If you want to give it a try, [request early access](https://www.hocuspocus.dev).
:::
### The document name

View File

@ -127,7 +127,7 @@ import { Editor, defaultExtensions } from '@tiptap/starter-kit'
new Editor({
extensions: [
...defaultExtensions().filter(extension => extension.config.name !== 'history'),
...defaultExtensions().filter(extension => extension.name !== 'history'),
],
})
```

View File

@ -111,7 +111,7 @@ This section needs some work. Do you know what else needs to be taken into accou
:::
### Icons
Most editor menus use icons for their buttons. In some of our demos, we use the open-source icon set [Remix Icon](https://remixicon.com/), thats free to use. But its totally up to you what you use. Here are a few icon sets you can consider:
Most editor menus use icons for their buttons. In some of our demos, we use the open source icon set [Remix Icon](https://remixicon.com/), thats free to use. But its totally up to you what you use. Here are a few icon sets you can consider:
* [Remix Icon](https://remixicon.com/#editor)
* [Font Awesome](https://fontawesome.com/icons?c=editors)

View File

@ -3,7 +3,7 @@
## toc
## Introduction
Using frameworks like Vue or React can feel too complex, if youre used to work without those two. Good news: You can use plain JavaScript in your node views. There is just a little bit you need to know, but lets go through this one by one.
Using frameworks like Vue or React can feel too complex, if youre used to work without those two. Good news: You can use Vanilla JavaScript in your node views. There is just a little bit you need to know, but lets go through this one by one.
## Render a node view with JavaScript
Here is what you need to do to render a node view inside your editor:

View File

@ -3,7 +3,7 @@
## toc
## Introduction
Using plain JavaScript can feel complex if you are used to work in React. Good news: You can use regular React components in your node views, too. There is just a little bit you need to know, but lets go through this one by one.
Using Vanilla JavaScript can feel complex if you are used to work in React. Good news: You can use regular React components in your node views, too. There is just a little bit you need to know, but lets go through this one by one.
## Render a React component
Here is what you need to do to render React components inside your editor:

View File

@ -3,7 +3,7 @@
## toc
## Introduction
Using plain JavaScript can feel complex if you are used to work in Vue. Good news: You can use regular Vue components in your node views, too. There is just a little bit you need to know, but lets go through this one by one.
Using Vanilla JavaScript can feel complex if you are used to work in Vue. Good news: You can use regular Vue components in your node views, too. There is just a little bit you need to know, but lets go through this one by one.
## Render a Vue component
Here is what you need to do to render Vue components inside your editor:

View File

@ -67,7 +67,7 @@ Use this interactive example to fiddle around:
<demo name="Guide/Content/ExportHTML" hide-source/>
### Option 3: Y.js
Our editor has amazing support for Y.js, which is amazing to add [realtime collaboration, offline editing, or syncing between devices](/guide/collaborative-editing).
Our editor has top notch support for Y.js, which is amazing to add features like [realtime collaboration, offline editing, or syncing between devices](/guide/collaborative-editing).
Internally, Y.js stores a history of all changes. That can be in the browser, on a server, synced with other connected clients, or on a USB stick. But, its important to know that Y.js needs those stored changes. A simple JSON document is not enough to merge changes.
@ -79,13 +79,13 @@ That said, its amazing and were about to provide an amazing backend, that
Unfortunately, **tiptap doesnt support Markdown as an input or output format**. We considered to add support for it, but those are the reasons why we decided to not do it:
* Both, HTML and JSON, can have deeply nested structures, Markdown is flat.
* There are enough packages to convert HTML to Markdown and vice-versa.
* Markdown standards vary.
* tiptaps strength is cutomization, that doesnt work very well with Markdown.
* There are enough packages to convert HTML to Markdown and vice-versa.
You should really consider to work with HTML or JSON to store your content, they are perfectly fine for most use cases.
If you still think you need Markdown, ProseMirror has an [example on how to deal with Markdown](https://prosemirror.net/examples/markdown/), [Nextcloud Text](https://github.com/nextcloud/text) uses tiptap 1 to work with Markdown. Maybe you can learn from them. Or if youre looking for a really good Markdown editor, try [CodeMirror](https://codemirror.net/).
If you still think you need Markdown, ProseMirror has an [example on how to deal with Markdown](https://prosemirror.net/examples/markdown/), [Nextcloud Text](https://github.com/nextcloud/text) uses tiptap 1 to work with Markdown. Maybe you can learn from them. Or if you are looking for a really good Markdown editor, try [CodeMirror](https://codemirror.net/).
That said, tiptap does support [Markdown shortcuts](/examples/markdown-shortcuts) to format your content. Also youre free to let your content look like Markdown, for example add a `#` before an `<h1>` with CSS.
@ -127,4 +127,4 @@ Were about to go through a few cases to help with that, for example we provid
[Share your experiences with us!](mailto:humans@tiptap.dev) Wed like to add more information here.
## Security
Theres no reason to use on or the other because of security concerns. If someone wants to send malicious content to your server, it doesnt matter if its JSON or HTML. You should always validate user input.
There is no reason to use on or the other because of security concerns. If someone wants to send malicious content to your server, it doesnt matter if its JSON or HTML. It doesnt even matter if youre using tiptap or not. You should always validate user input.

View File

@ -5,7 +5,7 @@
## Introduction
The whole tiptap is code base is written in TypeScript. If you havent heard of it or never used it, no worries. You dont have to.
TypeScript extends JavaScript by adding types (hence the name). It adds new syntax, which doesnt exist in plain JavaScript. Its actually removed before running in the browser, but this step the compilation is important to find bugs early. It checks if you passe the right types of data to functions. For a big and complex project, thats very valuable. It means well get notified of lot of bugs, before shipping code to you.
TypeScript extends JavaScript by adding types (hence the name). It adds new syntax, which doesnt exist in Vanilla JavaScript. Its actually removed before running in the browser, but this step the compilation is important to find bugs early. It checks if you passe the right types of data to functions. For a big and complex project, thats very valuable. It means well get notified of lot of bugs, before shipping code to you.
**Anyway, if you dont use TypeScript in your project, thats fine.** You will still be able to use tiptap and nevertheless get a nice autocomplete for the tiptap API (if your editor supports it, but most do).

View File

@ -3,17 +3,17 @@
## toc
## Introduction
tiptap 2 is framework-agnostic and even works with plain JavaScript, if thats your thing. Were working on guides for all the different frameworks and workflows. The following steps should help you to integrate tiptap in your JavaScript project.
tiptap is framework-agnostic and even works with Vanilla JavaScript, if thats your thing. The following integration guides should help you to integrate tiptap in your JavaScript project.
## Integration guides
* [CDN](/installation/cdn)
* [CodeSandbox](/installation/codesandbox)
* [Vue 2](/installation/vue2)
<!-- * [CodeSandbox](/installation/codesandbox) -->
* [React](/installation/react)
* [Vue 3](/installation/vue3)
* [Vue 2](/installation/vue2)
* [Nuxt.js](/installation/nuxt)
* [React](/installation/react) (Draft)
* [Svelte](/installation/svelte) (Draft)
* [Alpine.js](/installation/alpine) (Draft)
* [Svelte](/installation/svelte)
* [Alpine.js](/installation/alpine)
* [Livewire](/installation/livewire) (Draft)
## Vanilla JavaScript
@ -22,7 +22,7 @@ tiptap 2 is framework-agnostic and even works with plain JavaScript, if thats
* [Node](https://nodejs.org/en/download/) installed on your machine
### 1. Install the dependencies
For the following example you will need `@tiptap/core` (the actual editor) and `@tiptap/starter-kit` which has everything to get started quickly, for example the most common extensions.
For the following example you will need `@tiptap/core` (the actual editor) and `@tiptap/starter-kit` which has the most common extensions to get started quickly.
```bash
# install with npm
@ -40,7 +40,7 @@ Add the following HTML where you want the editor to be mounted:
```
### 3. Initialize the editor
Now, lets initialize the editor in JavaScript:
Lets initialize the editor in JavaScript now:
```js
import { Editor } from '@tiptap/core'
@ -53,4 +53,4 @@ new Editor({
})
```
Open your project in the browser and you should see tiptap. Good work! Time to give yourself a pat on the back.
Open your project in the browser to see tiptap in action. Good work! Time to give yourself a pat on the back.

View File

@ -10,7 +10,7 @@ title: Headless WYSIWYG Text Editor
tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) a toolkit for building rich text WYSIWYG editors, which is already in use at many well-known companies such as *New York Times*, *The Guardian* or *Atlassian*.
Create exactly the rich text editor you want out of customizable building blocks. tiptap comes with sensible defaults, a lot of extensions and a friendly API to customize every aspect. Its backed by a welcoming community, open-source, and free.
Create exactly the rich text editor you want out of customizable building blocks. tiptap comes with sensible defaults, a lot of extensions and a friendly API to customize every aspect. Its backed by a welcoming community, open source, and free.
## Example
<demo name="Examples/CollaborativeEditing" hide-source inline />
@ -18,9 +18,9 @@ Create exactly the rich text editor you want out of customizable building blocks
## Features
**Headless.** We dont tell you what a menu should look like or where it should be rendered in the DOM. Thats why tiptap is headless and comes without any CSS. You are in full control over markup, styling and behaviour.
**Framework-agnostic.** No matter what framework you use, youll enjoy tiptap. Out of the box, it works with plain JavaScript and Vue.js, but its also possible to use it in [React](/installation/react), Svelte and others.
**Framework-agnostic.** No matter what framework you use, youll enjoy tiptap. Out of the box, it works with Vanilla JavaScript and Vue.js, but its also possible to use it in [React](/installation/react), Svelte and others.
**TypeScript.** tiptap 2 is written in TypeScript. That helps us to find bugs early and gives you a nice autocomplete for the API (if your IDE supports that) on top of the extensive human written documentation.
**TypeScript.** tiptap is written in TypeScript. That helps us to find bugs early and gives you a nice autocomplete for the API (if your IDE supports that) on top of the extensive human written documentation.
**Collaborative.** Real-time collaboration, syncing between different devices and working offline used to be hard. We provide everything you need to keep everything in sync, conflict-free with the power of [Y.js](https://github.com/yjs/yjs). Our production-grade setup requires less than 20 lines of code.

View File

@ -5,10 +5,6 @@
## Introduction
tiptap would be nothing without its lively community. Contributions have always been and will always be welcome. Here is a little bit you should know, before you send your contribution:
:::warning Private repository
Currently, the repository is private. That means PRs are disabled, too. Well release a public version of tiptap 2 soonish, please wait with your PRs until the repository is public.
:::
## Welcome examples
* Failing regression tests as bug reports
* Documentation improvements, e. g. fix a typo, add a section

View File

@ -172,6 +172,9 @@ The reference implementation for collaborative editing uses Y.js now. Thats a
Read more about [the new collaborative editing experience](/guide/collaborative-editing) in our guide.
### Marks dont support node view anymore
For marks, node views are [not well supported in ProseMirror](https://discuss.prosemirror.net/t/there-is-a-bug-in-marks-nodeview/2722/2). There is also [a related issue](https://github.com/ueberdosis/tiptap/issues/613) for tiptap 1. Thats why we removed it in tiptap 2.
### Become a sponsor
tiptap wouldnt exist without the funding of its community. If you fell in love with tiptap, dont forget to [become a sponsor](/sponsor) and make the maintenance, development and support sustainable.

View File

@ -1,12 +0,0 @@
# Monthly reports
| Month | Tasks | Sponsors | Funding | Worked |
| --------- | ---------------------------------------------------------------------- | -------- | ------- | ------------- |
| 01/2021 | Maintenance tiptap v1, … | … | … | … |
| 12/2020 | Invited all sponsors, gathered feedback | 60 | $1,035 | 60 h |
| 11/2020 | Wrote documentation, improved collaborative editing, advanced examples | 31 | $468 | 188 h |
| 10/2020 | Rewrote extensions for tiptap 2 | 25 | $423 | 102 h |
| 09/2020 | Developed the new API, wrote documentation | | | 125 h |
| 08/2020 | Set up tiptap 2 | | | 56 h |
| **Total** | | | | **531 h** |

View File

@ -3,21 +3,20 @@
## Introduction
To deliver a top-notch developer experience and user experience, we put ~~hundreds~~ thousands of hours of unpaid work into tiptap. Your funding helps us to make this work more and more financially sustainable. This enables us to provide helpful support, maintain all our packages, keep everything up to date, and develop new features and extensions for tiptap.
Give back to the open-source community and [sponsor us on GitHub](https://github.com/sponsors/ueberdosis)! ♥
Give back to the open source community and [sponsor us on GitHub](https://github.com/sponsors/ueberdosis)! ♥
## Your benefits as a sponsor
* Give back to the open-source community
* Get early access to private repositories
* Ensure the further maintenace and development of tiptap
* Your issues and pull requests get a `sponsor ♥` label
* Get a sponsor badge in all your comments on GitHub
* Invest in the future of tiptap
* Give back to the open source community
* Show support in your GitHub profile
* Receive monthly reports about our open-source work
Does that sound good? [Sponsor us on GitHub!](https://github.com/sponsors/ueberdosis)
Sounds good? [Sponsor us on GitHub!](https://github.com/sponsors/ueberdosis)
## The maintainers of tiptap
If youre thankful for tiptap, you should say thank you to all 12 lovely people of [überdosis](https://twitter.com/_ueberdosis). The amazing company were all building together and the amazing company that funded the initial development costs of tiptap 2.
If you are thankful for tiptap, you should say thank you to the lovely people of [überdosis](https://twitter.com/_ueberdosis). The amazing company we are all building together and the amazing company that funded the initial development costs of tiptap.
AND you should definitely hire us if you want us to design und build an amazing digital product for you. Bonus points if its somehow text editing related.

View File

@ -30,19 +30,17 @@
skip: true
- title: Svelte
link: /installation/svelte
type: new
skip: true
# - title: CodeSandbox
# link: /installation/codesandbox
# skip: true
- title: Alpine.js
link: /installation/alpine
type: new
skip: true
- title: Livewire
link: /installation/livewire
type: draft
skip: true
# - title: Livewire
# link: /installation/livewire
# type: draft
# skip: true
- title: Upgrade guide
link: /overview/upgrade-guide
- title: Become a sponsor
@ -59,15 +57,12 @@
link: /examples/default
- title: Collaborative editing
link: /examples/collaborative-editing
# type: pro
- title: Markdown shortcuts
link: /examples/markdown-shortcuts
- title: Menus
link: /examples/menus
type: new
- title: Tables
link: /examples/tables
# type: pro
- title: Images
link: /examples/images
- title: Formatting
@ -86,8 +81,8 @@
link: /examples/savvy
- title: Interactivity
link: /examples/interactivity
- title: Code block language
link: /examples/code-block-language
- title: Syntax highlighting
link: /examples/syntax-highlighting
type: new
- title: Guide
@ -96,7 +91,6 @@
link: /guide/configuration
- title: Menus
link: /guide/menus
type: new
- title: Styling
link: /guide/styling
- title: Output
@ -105,21 +99,17 @@
link: /guide/accessibility
- title: Collaborative editing
link: /guide/collaborative-editing
# type: pro
- title: Custom extensions
link: /guide/custom-extensions
- title: Overwrite & extend
link: /guide/extend-extensions
- title: Interactive node views
link: /guide/node-views
type: new
items:
- title: With JavaScript
link: /guide/node-views/js
type: new
- title: With React
link: /guide/node-views/react
type: new
- title: With Vue
link: /guide/node-views/vue
- title: A few examples
@ -129,8 +119,8 @@
- title: API
items:
- title: Concept
link: /api/concept
- title: Introduction
link: /api/introduction
- title: Editor
link: /api/editor
- title: Commands
@ -185,16 +175,12 @@
link: /api/nodes/paragraph
- title: Table
link: /api/nodes/table
# type: pro
- title: TableRow
link: /api/nodes/table-row
# type: pro
- title: TableCell
link: /api/nodes/table-cell
# type: pro
- title: TableHeader
link: /api/nodes/table-header
# type: pro
- title: TaskList
link: /api/nodes/task-list
- title: TaskItem
@ -223,9 +209,6 @@
- title: Extensions
link: /api/extensions
items:
# - title: Annotation
# link: /api/extensions/annotation
# type: draft
- title: BubbleMenu
link: /api/extensions/bubble-menu
type: new
@ -233,10 +216,8 @@
link: /api/extensions/character-count
- title: Collaboration
link: /api/extensions/collaboration
# type: pro
- title: CollaborationCursor
link: /api/extensions/collaboration-cursor
# type: pro
- title: Dropcursor
link: /api/extensions/dropcursor
- title: FloatingMenu
@ -265,9 +246,9 @@
link: /api/utilities/html
- title: Suggestion
link: /api/utilities/suggestion
- title: Events
link: /api/events
- title: Schema
link: /api/schema
- title: Keyboard shortcuts
link: /api/keyboard-shortcuts
- title: Schema
link: /api/schema
- title: Events
link: /api/events

View File

@ -6,7 +6,7 @@
The headless editor framework for web artisans.
</h1>
<p class="is-large">
tiptap gives you full control about every single aspect of your text editor experience. Its customizable, comes with a ton of extensions, is open-source, has an extensive documentation, and is simply a joy to use. Join our welcoming community and start building cool things! Its free.
tiptap gives you full control about every single aspect of your text editor experience. Its customizable, comes with a ton of extensions, is open source, has an extensive documentation, and is simply a joy to use. Join our welcoming community and start building cool things!
</p>
<btn-wrapper>
<btn type="primary" icon="arrow-right" to="/installation">
@ -49,7 +49,7 @@
Framework-agnostic
</h3>
<p>
Out of the box, tiptap works with plain JavaScript and Vue.js, but its also possible to use it in <g-link to="/installation/react">React</g-link>, <g-link to="/installation/svelte">Svelte</g-link> and others.
Out of the box, tiptap works with Vanilla JavaScript and Vue.js, but its also possible to use it in <g-link to="/installation/react">React</g-link>, <g-link to="/installation/svelte">Svelte</g-link> and others.
</p>
<div>
<btn type="tertiary" icon="arrow-right" to="/installation">

View File

@ -1,4 +1,4 @@
/overview /
/examples /examples/basic
/guide /guide/get-started
/examples /examples/default
/guide /guide/configuration
/api /api/concept

View File

@ -26,10 +26,10 @@
},
"devDependencies": {
"@atomico/rollup-plugin-sizes": "^1.1.4",
"@babel/core": "^7.13.14",
"@babel/core": "^7.13.15",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.0",
"@babel/plugin-proposal-optional-chaining": "^7.13.12",
"@babel/preset-env": "^7.13.12",
"@babel/preset-env": "^7.13.15",
"@babel/preset-react": "^7.13.13",
"@lerna/batch-packages": "^3.16.0",
"@lerna/filter-packages": "^3.18.0",
@ -54,7 +54,7 @@
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.30.0",
"rollup-plugin-vue": "5",
"typescript": "^4.2.2",
"typescript": "^4.2.4",
"vue": "^2.6.12"
}
}

View File

@ -3,6 +3,47 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.0.0-beta.31](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/core@2.0.0-beta.30...@tiptap/core@2.0.0-beta.31) (2021-04-15)
### Bug Fixes
* fix type for emitUpdate, fix [#276](https://github.com/ueberdosis/tiptap-next/issues/276) ([4137e00](https://github.com/ueberdosis/tiptap-next/commit/4137e00d987c152b883022525056df94ad033be7))
# [2.0.0-beta.30](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/core@2.0.0-beta.29...@tiptap/core@2.0.0-beta.30) (2021-04-15)
**Note:** Version bump only for package @tiptap/core
# [2.0.0-beta.29](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/core@2.0.0-beta.28...@tiptap/core@2.0.0-beta.29) (2021-04-12)
### Bug Fixes
* fix adding mark only with insertContent(), fix [#264](https://github.com/ueberdosis/tiptap-next/issues/264) ([0a63123](https://github.com/ueberdosis/tiptap-next/commit/0a6312382f38af5b8fdf7f94fc4b6c1de1a15e25))
# [2.0.0-beta.28](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/core@2.0.0-beta.27...@tiptap/core@2.0.0-beta.28) (2021-04-12)
### Features
* add parentConfig to extension context for more extendable extensions, fix [#259](https://github.com/ueberdosis/tiptap-next/issues/259) ([5e1ec5d](https://github.com/ueberdosis/tiptap-next/commit/5e1ec5d2a66be164f505d631f97861ab9344ba96))
# [2.0.0-beta.27](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/core@2.0.0-beta.26...@tiptap/core@2.0.0-beta.27) (2021-04-09)

View File

@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) a
Documentation can be found on the [tiptap website](https://tiptap.dev).
## License
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).

View File

@ -1,7 +1,7 @@
{
"name": "@tiptap/core",
"description": "headless rich text editor",
"version": "2.0.0-beta.27",
"version": "2.0.0-beta.31",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",

View File

@ -3,8 +3,9 @@ import { Command as ProseMirrorCommand } from 'prosemirror-commands'
import { InputRule } from 'prosemirror-inputrules'
import { Editor } from './Editor'
import { Node } from './Node'
import { Mark } from './Mark'
import mergeDeep from './utilities/mergeDeep'
import { GlobalAttributes, RawCommands } from './types'
import { GlobalAttributes, RawCommands, ParentConfig } from './types'
import { ExtensionConfig } from '.'
declare module '@tiptap/core' {
@ -31,6 +32,7 @@ declare module '@tiptap/core' {
*/
addGlobalAttributes?: (this: {
options: Options,
parent: ParentConfig<ExtensionConfig<Options>>['addGlobalAttributes'],
}) => GlobalAttributes | {},
/**
@ -39,6 +41,7 @@ declare module '@tiptap/core' {
addCommands?: (this: {
options: Options,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['addCommands'],
}) => Partial<RawCommands>,
/**
@ -47,6 +50,7 @@ declare module '@tiptap/core' {
addKeyboardShortcuts?: (this: {
options: Options,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['addKeyboardShortcuts'],
}) => {
[key: string]: ProseMirrorCommand,
},
@ -57,6 +61,7 @@ declare module '@tiptap/core' {
addInputRules?: (this: {
options: Options,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['addInputRules'],
}) => InputRule[],
/**
@ -65,6 +70,7 @@ declare module '@tiptap/core' {
addPasteRules?: (this: {
options: Options,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['addPasteRules'],
}) => Plugin[],
/**
@ -73,6 +79,7 @@ declare module '@tiptap/core' {
addProseMirrorPlugins?: (this: {
options: Options,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['addProseMirrorPlugins'],
}) => Plugin[],
/**
@ -81,6 +88,7 @@ declare module '@tiptap/core' {
extendNodeSchema?: ((
this: {
options: Options,
parent: ParentConfig<ExtensionConfig<Options>>['extendNodeSchema'],
},
extension: Node,
) => {
@ -93,8 +101,9 @@ declare module '@tiptap/core' {
extendMarkSchema?: ((
this: {
options: Options,
parent: ParentConfig<ExtensionConfig<Options>>['extendMarkSchema'],
},
extension: Node,
extension: Mark,
) => {
[key: string]: any,
}) | null,
@ -105,6 +114,7 @@ declare module '@tiptap/core' {
onBeforeCreate?: ((this: {
options: Options,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['onBeforeCreate'],
}) => void) | null,
/**
@ -113,6 +123,7 @@ declare module '@tiptap/core' {
onCreate?: ((this: {
options: Options,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['onCreate'],
}) => void) | null,
/**
@ -121,6 +132,7 @@ declare module '@tiptap/core' {
onUpdate?: ((this: {
options: Options,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['onUpdate'],
}) => void) | null,
/**
@ -129,6 +141,7 @@ declare module '@tiptap/core' {
onSelectionUpdate?: ((this: {
options: Options,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['onSelectionUpdate'],
}) => void) | null,
/**
@ -138,6 +151,7 @@ declare module '@tiptap/core' {
this: {
options: Options,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['onTransaction'],
},
props: {
transaction: Transaction,
@ -151,6 +165,7 @@ declare module '@tiptap/core' {
this: {
options: Options,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['onFocus'],
},
props: {
event: FocusEvent,
@ -164,6 +179,7 @@ declare module '@tiptap/core' {
this: {
options: Options,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['onBlur'],
},
props: {
event: FocusEvent,
@ -176,6 +192,7 @@ declare module '@tiptap/core' {
onDestroy?: ((this: {
options: Options,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['onDestroy'],
}) => void) | null,
}
}
@ -183,43 +200,56 @@ declare module '@tiptap/core' {
export class Extension<Options = any> {
type = 'extension'
name = 'extension'
parent: Extension | null = null
child: Extension | null = null
options: Options
config: ExtensionConfig = {
name: 'extension',
name: this.name,
priority: 100,
defaultOptions: {},
}
options!: Options
constructor(config: ExtensionConfig<Options>) {
constructor(config: Partial<ExtensionConfig<Options>> = {}) {
this.config = {
...this.config,
...config,
}
this.name = this.config.name
this.options = this.config.defaultOptions
}
static create<O>(config: ExtensionConfig<O>) {
static create<O>(config: Partial<ExtensionConfig<O>> = {}) {
return new Extension<O>(config)
}
configure(options: Partial<Options> = {}) {
return Extension
.create<Options>(this.config as ExtensionConfig<Options>)
.#configure(options)
}
#configure = (options: Partial<Options>) => {
this.options = mergeDeep(this.config.defaultOptions, options) as Options
this.options = mergeDeep(this.options, options) as Options
return this
}
extend<ExtendedOptions = Options>(extendedConfig: Partial<ExtensionConfig<ExtendedOptions>>) {
return new Extension<ExtendedOptions>({
...this.config,
...extendedConfig,
} as ExtensionConfig<ExtendedOptions>)
extend<ExtendedOptions = Options>(extendedConfig: Partial<ExtensionConfig<ExtendedOptions>> = {}) {
const extension = new Extension<ExtendedOptions>(extendedConfig)
extension.parent = this
this.child = extension
extension.name = extendedConfig.name
? extendedConfig.name
: this.name
extension.options = {
...extension.parent.options,
...extension.options,
}
return extension
}
}

View File

@ -4,7 +4,8 @@ import { inputRules as inputRulesPlugin } from 'prosemirror-inputrules'
import { EditorView, Decoration } from 'prosemirror-view'
import { Plugin } from 'prosemirror-state'
import { Editor } from './Editor'
import { Extensions, NodeViewRenderer, RawCommands } from './types'
import { Extensions, RawCommands, AnyConfig } from './types'
import getExtensionField from './helpers/getExtensionField'
import getSchema from './helpers/getSchema'
import getSchemaTypeByName from './helpers/getSchemaTypeByName'
import getNodeType from './helpers/getNodeType'
@ -12,6 +13,7 @@ import splitExtensions from './helpers/splitExtensions'
import getAttributesFromExtensions from './helpers/getAttributesFromExtensions'
import getRenderedAttributes from './helpers/getRenderedAttributes'
import callOrReturn from './utilities/callOrReturn'
import { NodeConfig } from '.'
export default class ExtensionManager {
@ -32,47 +34,95 @@ export default class ExtensionManager {
const context = {
options: extension.options,
editor: this.editor,
type: getSchemaTypeByName(extension.config.name, this.schema),
type: getSchemaTypeByName(extension.name, this.schema),
}
if (extension.type === 'mark') {
const keepOnSplit = callOrReturn(extension.config.keepOnSplit, context) ?? true
const keepOnSplit = callOrReturn(getExtensionField(extension, 'keepOnSplit', context)) ?? true
if (keepOnSplit) {
this.splittableMarks.push(extension.config.name)
this.splittableMarks.push(extension.name)
}
}
if (typeof extension.config.onBeforeCreate === 'function') {
this.editor.on('beforeCreate', extension.config.onBeforeCreate.bind(context))
const onBeforeCreate = getExtensionField<AnyConfig['onBeforeCreate']>(
extension,
'onBeforeCreate',
context,
)
if (onBeforeCreate) {
this.editor.on('beforeCreate', onBeforeCreate)
}
if (typeof extension.config.onCreate === 'function') {
this.editor.on('create', extension.config.onCreate.bind(context))
const onCreate = getExtensionField<AnyConfig['onCreate']>(
extension,
'onCreate',
context,
)
if (onCreate) {
this.editor.on('create', onCreate)
}
if (typeof extension.config.onUpdate === 'function') {
this.editor.on('update', extension.config.onUpdate.bind(context))
const onUpdate = getExtensionField<AnyConfig['onUpdate']>(
extension,
'onUpdate',
context,
)
if (onUpdate) {
this.editor.on('update', onUpdate)
}
if (typeof extension.config.onSelectionUpdate === 'function') {
this.editor.on('selectionUpdate', extension.config.onSelectionUpdate.bind(context))
const onSelectionUpdate = getExtensionField<AnyConfig['onSelectionUpdate']>(
extension,
'onSelectionUpdate',
context,
)
if (onSelectionUpdate) {
this.editor.on('selectionUpdate', onSelectionUpdate)
}
if (typeof extension.config.onTransaction === 'function') {
this.editor.on('transaction', extension.config.onTransaction.bind(context))
const onTransaction = getExtensionField<AnyConfig['onTransaction']>(
extension,
'onTransaction',
context,
)
if (onTransaction) {
this.editor.on('transaction', onTransaction)
}
if (typeof extension.config.onFocus === 'function') {
this.editor.on('focus', extension.config.onFocus.bind(context))
const onFocus = getExtensionField<AnyConfig['onFocus']>(
extension,
'onFocus',
context,
)
if (onFocus) {
this.editor.on('focus', onFocus)
}
if (typeof extension.config.onBlur === 'function') {
this.editor.on('blur', extension.config.onBlur.bind(context))
const onBlur = getExtensionField<AnyConfig['onBlur']>(
extension,
'onBlur',
context,
)
if (onBlur) {
this.editor.on('blur', onBlur)
}
if (typeof extension.config.onDestroy === 'function') {
this.editor.on('destroy', extension.config.onDestroy.bind(context))
const onDestroy = getExtensionField<AnyConfig['onDestroy']>(
extension,
'onDestroy',
context,
)
if (onDestroy) {
this.editor.on('destroy', onDestroy)
}
})
}
@ -81,11 +131,14 @@ export default class ExtensionManager {
const defaultPriority = 100
return extensions.sort((a, b) => {
if ((a.config.priority || defaultPriority) > (b.config.priority || defaultPriority)) {
const priorityA = getExtensionField<AnyConfig['priority']>(a, 'priority') || defaultPriority
const priorityB = getExtensionField<AnyConfig['priority']>(b, 'priority') || defaultPriority
if (priorityA > priorityB) {
return -1
}
if ((a.config.priority || defaultPriority) < (b.config.priority || defaultPriority)) {
if (priorityA < priorityB) {
return 1
}
@ -98,16 +151,22 @@ export default class ExtensionManager {
const context = {
options: extension.options,
editor: this.editor,
type: getSchemaTypeByName(extension.config.name, this.schema),
type: getSchemaTypeByName(extension.name, this.schema),
}
if (!extension.config.addCommands) {
const addCommands = getExtensionField<AnyConfig['addCommands']>(
extension,
'addCommands',
context,
)
if (!addCommands) {
return commands
}
return {
...commands,
...extension.config.addCommands.bind(context)(),
...addCommands(),
}
}, {} as RawCommands)
}
@ -119,19 +178,31 @@ export default class ExtensionManager {
const context = {
options: extension.options,
editor: this.editor,
type: getSchemaTypeByName(extension.config.name, this.schema),
type: getSchemaTypeByName(extension.name, this.schema),
}
const plugins: Plugin[] = []
if (extension.config.addKeyboardShortcuts) {
const keyMapPlugin = keymap(extension.config.addKeyboardShortcuts.bind(context)())
const addKeyboardShortcuts = getExtensionField<AnyConfig['addKeyboardShortcuts']>(
extension,
'addKeyboardShortcuts',
context,
)
if (addKeyboardShortcuts) {
const keyMapPlugin = keymap(addKeyboardShortcuts())
plugins.push(keyMapPlugin)
}
if (this.editor.options.enableInputRules && extension.config.addInputRules) {
const inputRules = extension.config.addInputRules.bind(context)()
const addInputRules = getExtensionField<AnyConfig['addInputRules']>(
extension,
'addInputRules',
context,
)
if (this.editor.options.enableInputRules && addInputRules) {
const inputRules = addInputRules()
const inputRulePlugins = inputRules.length
? [inputRulesPlugin({ rules: inputRules })]
: []
@ -139,14 +210,26 @@ export default class ExtensionManager {
plugins.push(...inputRulePlugins)
}
if (this.editor.options.enablePasteRules && extension.config.addPasteRules) {
const pasteRulePlugins = extension.config.addPasteRules.bind(context)()
const addPasteRules = getExtensionField<AnyConfig['addPasteRules']>(
extension,
'addPasteRules',
context,
)
if (this.editor.options.enablePasteRules && addPasteRules) {
const pasteRulePlugins = addPasteRules()
plugins.push(...pasteRulePlugins)
}
if (extension.config.addProseMirrorPlugins) {
const proseMirrorPlugins = extension.config.addProseMirrorPlugins.bind(context)()
const addProseMirrorPlugins = getExtensionField<AnyConfig['addProseMirrorPlugins']>(
extension,
'addProseMirrorPlugins',
context,
)
if (addProseMirrorPlugins) {
const proseMirrorPlugins = addProseMirrorPlugins()
plugins.push(...proseMirrorPlugins)
}
@ -165,15 +248,23 @@ export default class ExtensionManager {
const { nodeExtensions } = splitExtensions(this.extensions)
return Object.fromEntries(nodeExtensions
.filter(extension => !!extension.config.addNodeView)
.filter(extension => !!getExtensionField(extension, 'addNodeView'))
.map(extension => {
const extensionAttributes = this.attributes.filter(attribute => attribute.type === extension.config.name)
const extensionAttributes = this.attributes.filter(attribute => attribute.type === extension.name)
const context = {
options: extension.options,
editor,
type: getNodeType(extension.config.name, this.schema),
type: getNodeType(extension.name, this.schema),
}
const addNodeView = getExtensionField<NodeConfig['addNodeView']>(
extension,
'addNodeView',
context,
)
if (!addNodeView) {
return []
}
const renderer = extension.config.addNodeView?.call(context) as NodeViewRenderer
const nodeview = (
node: ProsemirrorNode,
@ -183,7 +274,7 @@ export default class ExtensionManager {
) => {
const HTMLAttributes = getRenderedAttributes(node, extensionAttributes)
return renderer({
return addNodeView()({
editor,
node,
getPos,
@ -193,7 +284,7 @@ export default class ExtensionManager {
})
}
return [extension.config.name, nodeview]
return [extension.name, nodeview]
}))
}
@ -202,17 +293,23 @@ export default class ExtensionManager {
const { nodeExtensions } = splitExtensions(this.extensions)
return Object.fromEntries(nodeExtensions
.filter(extension => !!extension.config.renderText)
.filter(extension => !!getExtensionField(extension, 'renderText'))
.map(extension => {
const context = {
options: extension.options,
editor,
type: getNodeType(extension.config.name, this.schema),
type: getNodeType(extension.name, this.schema),
}
const textSerializer = (props: { node: ProsemirrorNode }) => extension.config.renderText?.call(context, props)
const renderText = getExtensionField<NodeConfig['renderText']>(extension, 'renderText', context)
return [extension.config.name, textSerializer]
if (!renderText) {
return []
}
const textSerializer = (props: { node: ProsemirrorNode }) => renderText(props)
return [extension.name, textSerializer]
}))
}

View File

@ -8,7 +8,13 @@ import { Plugin, Transaction } from 'prosemirror-state'
import { Command as ProseMirrorCommand } from 'prosemirror-commands'
import { InputRule } from 'prosemirror-inputrules'
import mergeDeep from './utilities/mergeDeep'
import { Attributes, RawCommands, GlobalAttributes } from './types'
import {
Attributes,
RawCommands,
GlobalAttributes,
ParentConfig,
} from './types'
import { Node } from './Node'
import { MarkConfig } from '.'
import { Editor } from './Editor'
@ -36,6 +42,7 @@ declare module '@tiptap/core' {
*/
addGlobalAttributes?: (this: {
options: Options,
parent: ParentConfig<MarkConfig<Options>>['addGlobalAttributes'],
}) => GlobalAttributes | {},
/**
@ -45,6 +52,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['addCommands'],
}) => Partial<RawCommands>,
/**
@ -54,6 +62,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['addKeyboardShortcuts'],
}) => {
[key: string]: ProseMirrorCommand,
},
@ -65,6 +74,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['addInputRules'],
}) => InputRule[],
/**
@ -74,6 +84,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['addPasteRules'],
}) => Plugin[],
/**
@ -83,6 +94,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['addProseMirrorPlugins'],
}) => Plugin[],
/**
@ -91,6 +103,7 @@ declare module '@tiptap/core' {
extendNodeSchema?: ((
this: {
options: Options,
parent: ParentConfig<MarkConfig<Options>>['extendNodeSchema'],
},
extension: Node,
) => {
@ -103,8 +116,9 @@ declare module '@tiptap/core' {
extendMarkSchema?: ((
this: {
options: Options,
parent: ParentConfig<MarkConfig<Options>>['extendMarkSchema'],
},
extension: Node,
extension: Mark,
) => {
[key: string]: any,
}) | null,
@ -112,10 +126,11 @@ declare module '@tiptap/core' {
/**
* The editor is not ready yet.
*/
onBeforeCreate?: ((this: {
onBeforeCreate?: ((this: {
options: Options,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['onBeforeCreate'],
}) => void) | null,
/**
@ -125,6 +140,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['onCreate'],
}) => void) | null,
/**
@ -134,6 +150,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['onUpdate'],
}) => void) | null,
/**
@ -143,6 +160,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['onSelectionUpdate'],
}) => void) | null,
/**
@ -153,6 +171,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['onTransaction'],
},
props: {
transaction: Transaction,
@ -167,6 +186,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['onFocus'],
},
props: {
event: FocusEvent,
@ -181,6 +201,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['onBlur'],
},
props: {
event: FocusEvent,
@ -194,6 +215,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['onDestroy'],
}) => void) | null,
/**
@ -204,22 +226,34 @@ declare module '@tiptap/core' {
/**
* Inclusive
*/
inclusive?: MarkSpec['inclusive'] | ((this: { options: Options }) => MarkSpec['inclusive']),
inclusive?: MarkSpec['inclusive'] | ((this: {
options: Options,
parent: ParentConfig<MarkConfig<Options>>['inclusive'],
}) => MarkSpec['inclusive']),
/**
* Excludes
*/
excludes?: MarkSpec['excludes'] | ((this: { options: Options }) => MarkSpec['excludes']),
excludes?: MarkSpec['excludes'] | ((this: {
options: Options,
parent: ParentConfig<MarkConfig<Options>>['excludes'],
}) => MarkSpec['excludes']),
/**
* Group
*/
group?: MarkSpec['group'] | ((this: { options: Options }) => MarkSpec['group']),
group?: MarkSpec['group'] | ((this: {
options: Options,
parent: ParentConfig<MarkConfig<Options>>['group'],
}) => MarkSpec['group']),
/**
* Spanning
*/
spanning?: MarkSpec['spanning'] | ((this: { options: Options }) => MarkSpec['spanning']),
spanning?: MarkSpec['spanning'] | ((this: {
options: Options,
parent: ParentConfig<MarkConfig<Options>>['spanning'],
}) => MarkSpec['spanning']),
/**
* Parse HTML
@ -227,6 +261,7 @@ declare module '@tiptap/core' {
parseHTML?: (
this: {
options: Options,
parent: ParentConfig<MarkConfig<Options>>['parseHTML'],
},
) => MarkSpec['parseDOM'],
@ -236,11 +271,12 @@ declare module '@tiptap/core' {
renderHTML?: ((
this: {
options: Options,
parent: ParentConfig<MarkConfig<Options>>['renderHTML'],
},
props: {
mark: ProseMirrorMark,
HTMLAttributes: { [key: string]: any },
}
},
) => DOMOutputSpec) | null,
/**
@ -249,6 +285,7 @@ declare module '@tiptap/core' {
addAttributes?: (
this: {
options: Options,
parent: ParentConfig<MarkConfig<Options>>['addAttributes'],
},
) => Attributes | {},
}
@ -257,43 +294,56 @@ declare module '@tiptap/core' {
export class Mark<Options = any> {
type = 'mark'
name = 'mark'
parent: Mark | null = null
child: Mark | null = null
options: Options
config: MarkConfig = {
name: 'mark',
name: this.name,
priority: 100,
defaultOptions: {},
}
options!: Options
constructor(config: MarkConfig<Options>) {
constructor(config: Partial<MarkConfig<Options>> = {}) {
this.config = {
...this.config,
...config,
}
this.name = this.config.name
this.options = this.config.defaultOptions
}
static create<O>(config: MarkConfig<O>) {
static create<O>(config: Partial<MarkConfig<O>> = {}) {
return new Mark<O>(config)
}
configure(options: Partial<Options> = {}) {
return Mark
.create<Options>(this.config as MarkConfig<Options>)
.#configure(options)
}
#configure = (options: Partial<Options>) => {
this.options = mergeDeep(this.config.defaultOptions, options) as Options
this.options = mergeDeep(this.options, options) as Options
return this
}
extend<ExtendedOptions = Options>(extendedConfig: Partial<MarkConfig<ExtendedOptions>>) {
return new Mark<ExtendedOptions>({
...this.config,
...extendedConfig,
} as MarkConfig<ExtendedOptions>)
extend<ExtendedOptions = Options>(extendedConfig: Partial<MarkConfig<ExtendedOptions>> = {}) {
const extension = new Mark<ExtendedOptions>(extendedConfig)
extension.parent = this
this.child = extension
extension.name = extendedConfig.name
? extendedConfig.name
: this.name
extension.options = {
...extension.parent.options,
...extension.options,
}
return extension
}
}

View File

@ -13,6 +13,7 @@ import {
NodeViewRenderer,
GlobalAttributes,
RawCommands,
ParentConfig,
} from './types'
import { NodeConfig } from '.'
import { Editor } from './Editor'
@ -41,6 +42,7 @@ declare module '@tiptap/core' {
*/
addGlobalAttributes?: (this: {
options: Options,
parent: ParentConfig<NodeConfig<Options>>['addGlobalAttributes'],
}) => GlobalAttributes | {},
/**
@ -50,6 +52,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['addCommands'],
}) => Partial<RawCommands>,
/**
@ -59,6 +62,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['addKeyboardShortcuts'],
}) => {
[key: string]: ProseMirrorCommand,
},
@ -70,6 +74,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['addInputRules'],
}) => InputRule[],
/**
@ -79,6 +84,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['addPasteRules'],
}) => Plugin[],
/**
@ -88,6 +94,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['addProseMirrorPlugins'],
}) => Plugin[],
/**
@ -96,6 +103,7 @@ declare module '@tiptap/core' {
extendNodeSchema?: ((
this: {
options: Options,
parent: ParentConfig<NodeConfig<Options>>['extendNodeSchema'],
},
extension: Node,
) => {
@ -108,6 +116,7 @@ declare module '@tiptap/core' {
extendMarkSchema?: ((
this: {
options: Options,
parent: ParentConfig<NodeConfig<Options>>['extendMarkSchema'],
},
extension: Node,
) => {
@ -121,6 +130,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['onBeforeCreate'],
}) => void) | null,
/**
@ -130,6 +140,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['onCreate'],
}) => void) | null,
/**
@ -139,6 +150,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['onUpdate'],
}) => void) | null,
/**
@ -148,6 +160,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['onSelectionUpdate'],
}) => void) | null,
/**
@ -158,6 +171,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['onTransaction'],
},
props: {
transaction: Transaction,
@ -172,6 +186,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['onFocus'],
},
props: {
event: FocusEvent,
@ -186,6 +201,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['onBlur'],
},
props: {
event: FocusEvent,
@ -199,6 +215,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['onDestroy'],
}) => void) | null,
/**
@ -208,6 +225,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['addNodeView'],
}) => NodeViewRenderer) | null,
/**
@ -218,52 +236,82 @@ declare module '@tiptap/core' {
/**
* Content
*/
content?: NodeSpec['content'] | ((this: { options: Options }) => NodeSpec['content']),
content?: NodeSpec['content'] | ((this: {
options: Options,
parent: ParentConfig<NodeConfig<Options>>['content'],
}) => NodeSpec['content']),
/**
* Marks
*/
marks?: NodeSpec['marks'] | ((this: { options: Options }) => NodeSpec['marks']),
marks?: NodeSpec['marks'] | ((this: {
options: Options,
parent: ParentConfig<NodeConfig<Options>>['marks'],
}) => NodeSpec['marks']),
/**
* Group
*/
group?: NodeSpec['group'] | ((this: { options: Options }) => NodeSpec['group']),
group?: NodeSpec['group'] | ((this: {
options: Options,
parent: ParentConfig<NodeConfig<Options>>['group'],
}) => NodeSpec['group']),
/**
* Inline
*/
inline?: NodeSpec['inline'] | ((this: { options: Options }) => NodeSpec['inline']),
inline?: NodeSpec['inline'] | ((this: {
options: Options,
parent: ParentConfig<NodeConfig<Options>>['inline'],
}) => NodeSpec['inline']),
/**
* Atom
*/
atom?: NodeSpec['atom'] | ((this: { options: Options }) => NodeSpec['atom']),
atom?: NodeSpec['atom'] | ((this: {
options: Options,
parent: ParentConfig<NodeConfig<Options>>['atom'],
}) => NodeSpec['atom']),
/**
* Selectable
*/
selectable?: NodeSpec['selectable'] | ((this: { options: Options }) => NodeSpec['selectable']),
selectable?: NodeSpec['selectable'] | ((this: {
options: Options,
parent: ParentConfig<NodeConfig<Options>>['selectable'],
}) => NodeSpec['selectable']),
/**
* Draggable
*/
draggable?: NodeSpec['draggable'] | ((this: { options: Options }) => NodeSpec['draggable']),
draggable?: NodeSpec['draggable'] | ((this: {
options: Options,
parent: ParentConfig<NodeConfig<Options>>['draggable'],
}) => NodeSpec['draggable']),
/**
* Code
*/
code?: NodeSpec['code'] | ((this: { options: Options }) => NodeSpec['code']),
code?: NodeSpec['code'] | ((this: {
options: Options,
parent: ParentConfig<NodeConfig<Options>>['code'],
}) => NodeSpec['code']),
/**
* Defining
*/
defining?: NodeSpec['defining'] | ((this: { options: Options }) => NodeSpec['defining']),
defining?: NodeSpec['defining'] | ((this: {
options: Options,
parent: ParentConfig<NodeConfig<Options>>['defining'],
}) => NodeSpec['defining']),
/**
* Isolating
*/
isolating?: NodeSpec['isolating'] | ((this: { options: Options }) => NodeSpec['isolating']),
isolating?: NodeSpec['isolating'] | ((this: {
options: Options,
parent: ParentConfig<NodeConfig<Options>>['isolating'],
}) => NodeSpec['isolating']),
/**
* Parse HTML
@ -271,6 +319,7 @@ declare module '@tiptap/core' {
parseHTML?: (
this: {
options: Options,
parent: ParentConfig<NodeConfig<Options>>['parseHTML'],
},
) => NodeSpec['parseDOM'],
@ -280,6 +329,7 @@ declare module '@tiptap/core' {
renderHTML?: ((
this: {
options: Options,
parent: ParentConfig<NodeConfig<Options>>['renderHTML'],
},
props: {
node: ProseMirrorNode,
@ -295,6 +345,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['renderText'],
},
props: {
node: ProseMirrorNode,
@ -307,6 +358,7 @@ declare module '@tiptap/core' {
addAttributes?: (
this: {
options: Options,
parent: ParentConfig<NodeConfig<Options>>['addAttributes'],
},
) => Attributes | {},
}
@ -315,43 +367,56 @@ declare module '@tiptap/core' {
export class Node<Options = any> {
type = 'node'
name = 'node'
parent: Node | null = null
child: Node | null = null
options: Options
config: NodeConfig = {
name: 'node',
name: this.name,
priority: 100,
defaultOptions: {},
}
options!: Options
constructor(config: NodeConfig<Options>) {
constructor(config: Partial<NodeConfig<Options>> = {}) {
this.config = {
...this.config,
...config,
}
this.name = this.config.name
this.options = this.config.defaultOptions
}
static create<O>(config: NodeConfig<O>) {
static create<O>(config: Partial<NodeConfig<O>> = {}) {
return new Node<O>(config)
}
configure(options: Partial<Options> = {}) {
return Node
.create<Options>(this.config as NodeConfig<Options>)
.#configure(options)
}
#configure = (options: Partial<Options>) => {
this.options = mergeDeep(this.config.defaultOptions, options) as Options
this.options = mergeDeep(this.options, options) as Options
return this
}
extend<ExtendedOptions = Options>(extendedConfig: Partial<NodeConfig<ExtendedOptions>>) {
return new Node<ExtendedOptions>({
...this.config,
...extendedConfig,
} as NodeConfig<ExtendedOptions>)
extend<ExtendedOptions = Options>(extendedConfig: Partial<NodeConfig<ExtendedOptions>> = {}) {
const extension = new Node<ExtendedOptions>(extendedConfig)
extension.parent = this
this.child = extension
extension.name = extendedConfig.name
? extendedConfig.name
: this.name
extension.options = {
...extension.parent.options,
...extension.options,
}
return extension
}
}

View File

@ -6,7 +6,7 @@ declare module '@tiptap/core' {
/**
* Clear the whole document.
*/
clearContent: (emitUpdate: Boolean) => Command,
clearContent: (emitUpdate?: Boolean) => Command,
}
}
}

View File

@ -1,11 +1,14 @@
import splitExtensions from './splitExtensions'
import getExtensionField from './getExtensionField'
import {
Extensions,
GlobalAttributes,
Attributes,
Attribute,
ExtensionAttribute,
AnyConfig,
} from '../types'
import { NodeConfig, MarkConfig } from '..'
/**
* Get a list of all extension attributes defined in `addAttribute` and `addGlobalAttribute`.
@ -28,11 +31,18 @@ export default function getAttributesFromExtensions(extensions: Extensions): Ext
options: extension.options,
}
if (!extension.config.addGlobalAttributes) {
const addGlobalAttributes = getExtensionField<AnyConfig['addGlobalAttributes']>(
extension,
'addGlobalAttributes',
context,
)
if (!addGlobalAttributes) {
return
}
const globalAttributes = extension.config.addGlobalAttributes.bind(context)() as GlobalAttributes
// TODO: remove `as GlobalAttributes`
const globalAttributes = addGlobalAttributes() as GlobalAttributes
globalAttributes.forEach(globalAttribute => {
globalAttribute.types.forEach(type => {
@ -57,17 +67,24 @@ export default function getAttributesFromExtensions(extensions: Extensions): Ext
options: extension.options,
}
if (!extension.config.addAttributes) {
const addAttributes = getExtensionField<NodeConfig['addAttributes'] | MarkConfig['addAttributes']>(
extension,
'addAttributes',
context,
)
if (!addAttributes) {
return
}
const attributes = extension.config.addAttributes.bind(context)() as Attributes
// TODO: remove `as Attributes`
const attributes = addAttributes() as Attributes
Object
.entries(attributes)
.forEach(([name, attribute]) => {
extensionAttributes.push({
type: extension.config.name,
type: extension.name,
name,
attribute: {
...defaultAttribute,

View File

@ -0,0 +1,25 @@
import { AnyExtension, AnyObject, RemoveThis } from '../types'
export default function getExtensionField<T = any>(
extension: AnyExtension,
field: string,
context: AnyObject = {},
): RemoveThis<T> {
if (extension.config[field] === undefined && extension.parent) {
return getExtensionField(extension.parent, field, context)
}
if (typeof extension.config[field] === 'function') {
const value = extension.config[field].bind({
...context,
parent: extension.parent
? getExtensionField(extension.parent, field, context)
: null,
})
return value
}
return extension.config[field]
}

View File

@ -1,12 +1,13 @@
import { NodeSpec, MarkSpec, Schema } from 'prosemirror-model'
import { Extensions } from '../types'
import { ExtensionConfig, NodeConfig, MarkConfig } from '..'
import { AnyConfig, Extensions } from '../types'
import { NodeConfig, MarkConfig } from '..'
import splitExtensions from './splitExtensions'
import getAttributesFromExtensions from './getAttributesFromExtensions'
import getRenderedAttributes from './getRenderedAttributes'
import isEmptyObject from '../utilities/isEmptyObject'
import injectExtensionAttributesToParseRule from './injectExtensionAttributesToParseRule'
import callOrReturn from '../utilities/callOrReturn'
import getExtensionField from './getExtensionField'
function cleanUpSchemaItem<T>(data: T) {
return Object.fromEntries(Object.entries(data).filter(([key, value]) => {
@ -21,112 +22,110 @@ function cleanUpSchemaItem<T>(data: T) {
export default function getSchema(extensions: Extensions): Schema {
const allAttributes = getAttributesFromExtensions(extensions)
const { nodeExtensions, markExtensions } = splitExtensions(extensions)
const topNode = nodeExtensions.find(extension => extension.config.topNode)?.config.name
const nodeSchemaExtenders: (
| ExtensionConfig['extendNodeSchema']
| NodeConfig['extendNodeSchema']
| MarkConfig['extendNodeSchema']
)[] = []
const markSchemaExtenders: (
| ExtensionConfig['extendNodeSchema']
| NodeConfig['extendNodeSchema']
| MarkConfig['extendNodeSchema']
)[] = []
extensions.forEach(extension => {
if (typeof extension.config.extendNodeSchema === 'function') {
nodeSchemaExtenders.push(extension.config.extendNodeSchema)
}
if (typeof extension.config.extendMarkSchema === 'function') {
markSchemaExtenders.push(extension.config.extendMarkSchema)
}
})
const topNode = nodeExtensions.find(extension => getExtensionField(extension, 'topNode'))?.name
const nodes = Object.fromEntries(nodeExtensions.map(extension => {
const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.config.name)
const context = { options: extension.options }
const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.name)
const context = {
options: extension.options,
}
const extraNodeFields = nodeSchemaExtenders.reduce((fields, nodeSchemaExtender) => {
const extraFields = callOrReturn(nodeSchemaExtender, context, extension)
const extraNodeFields = extensions.reduce((fields, e) => {
const extendNodeSchema = getExtensionField<AnyConfig['extendNodeSchema']>(
e,
'extendNodeSchema',
context,
)
return {
...fields,
...extraFields,
...(extendNodeSchema ? extendNodeSchema(extension) : {}),
}
}, {})
const schema: NodeSpec = cleanUpSchemaItem({
...extraNodeFields,
content: callOrReturn(extension.config.content, context),
marks: callOrReturn(extension.config.marks, context),
group: callOrReturn(extension.config.group, context),
inline: callOrReturn(extension.config.inline, context),
atom: callOrReturn(extension.config.atom, context),
selectable: callOrReturn(extension.config.selectable, context),
draggable: callOrReturn(extension.config.draggable, context),
code: callOrReturn(extension.config.code, context),
defining: callOrReturn(extension.config.defining, context),
isolating: callOrReturn(extension.config.isolating, context),
content: callOrReturn(getExtensionField<NodeConfig['content']>(extension, 'content', context)),
marks: callOrReturn(getExtensionField<NodeConfig['marks']>(extension, 'marks', context)),
group: callOrReturn(getExtensionField<NodeConfig['group']>(extension, 'group', context)),
inline: callOrReturn(getExtensionField<NodeConfig['inline']>(extension, 'inline', context)),
atom: callOrReturn(getExtensionField<NodeConfig['atom']>(extension, 'atom', context)),
selectable: callOrReturn(getExtensionField<NodeConfig['selectable']>(extension, 'selectable', context)),
draggable: callOrReturn(getExtensionField<NodeConfig['draggable']>(extension, 'draggable', context)),
code: callOrReturn(getExtensionField<NodeConfig['code']>(extension, 'code', context)),
defining: callOrReturn(getExtensionField<NodeConfig['defining']>(extension, 'defining', context)),
isolating: callOrReturn(getExtensionField<NodeConfig['isolating']>(extension, 'isolating', context)),
attrs: Object.fromEntries(extensionAttributes.map(extensionAttribute => {
return [extensionAttribute.name, { default: extensionAttribute?.attribute?.default }]
})),
})
if (extension.config.parseHTML) {
schema.parseDOM = extension.config.parseHTML
.bind(context)()
?.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes))
const parseHTML = callOrReturn(getExtensionField<NodeConfig['parseHTML']>(extension, 'parseHTML', context))
if (parseHTML) {
schema.parseDOM = parseHTML
.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes))
}
if (extension.config.renderHTML) {
schema.toDOM = node => (extension.config.renderHTML as Function)?.bind(context)({
const renderHTML = getExtensionField<NodeConfig['renderHTML']>(extension, 'renderHTML', context)
if (renderHTML) {
schema.toDOM = node => renderHTML({
node,
HTMLAttributes: getRenderedAttributes(node, extensionAttributes),
})
}
return [extension.config.name, schema]
return [extension.name, schema]
}))
const marks = Object.fromEntries(markExtensions.map(extension => {
const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.config.name)
const context = { options: extension.options }
const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.name)
const context = {
options: extension.options,
}
const extraMarkFields = markSchemaExtenders.reduce((fields, markSchemaExtender) => {
const extraFields = callOrReturn(markSchemaExtender, context, extension)
const extraMarkFields = extensions.reduce((fields, e) => {
const extendMarkSchema = getExtensionField<AnyConfig['extendMarkSchema']>(
e,
'extendMarkSchema',
context,
)
return {
...fields,
...extraFields,
...(extendMarkSchema ? extendMarkSchema(extension) : {}),
}
}, {})
const schema: MarkSpec = cleanUpSchemaItem({
...extraMarkFields,
inclusive: callOrReturn(extension.config.inclusive, context),
excludes: callOrReturn(extension.config.excludes, context),
group: callOrReturn(extension.config.group, context),
spanning: callOrReturn(extension.config.spanning, context),
inclusive: callOrReturn(getExtensionField<NodeConfig['inclusive']>(extension, 'inclusive', context)),
excludes: callOrReturn(getExtensionField<NodeConfig['excludes']>(extension, 'excludes', context)),
group: callOrReturn(getExtensionField<NodeConfig['group']>(extension, 'group', context)),
spanning: callOrReturn(getExtensionField<NodeConfig['spanning']>(extension, 'spanning', context)),
attrs: Object.fromEntries(extensionAttributes.map(extensionAttribute => {
return [extensionAttribute.name, { default: extensionAttribute?.attribute?.default }]
})),
})
if (extension.config.parseHTML) {
schema.parseDOM = extension.config.parseHTML
.bind(context)()
?.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes))
const parseHTML = callOrReturn(getExtensionField<MarkConfig['parseHTML']>(extension, 'parseHTML', context))
if (parseHTML) {
schema.parseDOM = parseHTML
.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes))
}
if (extension.config.renderHTML) {
schema.toDOM = mark => (extension.config.renderHTML as Function)?.bind(context)({
const renderHTML = getExtensionField<MarkConfig['renderHTML']>(extension, 'renderHTML', context)
if (renderHTML) {
schema.toDOM = mark => renderHTML({
mark,
HTMLAttributes: getRenderedAttributes(mark, extensionAttributes),
})
}
return [extension.config.name, schema]
return [extension.name, schema]
}))
return new Schema({

View File

@ -1,20 +1,23 @@
import { Extensions } from '../types'
import { NodeConfig } from '..'
import splitExtensions from './splitExtensions'
import callOrReturn from '../utilities/callOrReturn'
import getExtensionField from '../helpers/getExtensionField'
export default function isList(name: string, extensions: Extensions): boolean {
const { nodeExtensions } = splitExtensions(extensions)
const extension = nodeExtensions.find(item => item.config.name === name)
const extension = nodeExtensions.find(item => item.name === name)
if (!extension) {
return false
}
const groups = callOrReturn(extension.config.group, { options: extension.options })
const context = { options: extension.options }
const group = callOrReturn(getExtensionField<NodeConfig['group']>(extension, 'group', context))
if (typeof groups !== 'string') {
if (typeof group !== 'string') {
return false
}
return groups.split(' ').includes('list')
return group.split(' ').includes('list')
}

View File

@ -12,6 +12,7 @@ export { default as markPasteRule } from './pasteRules/markPasteRule'
export { default as callOrReturn } from './utilities/callOrReturn'
export { default as mergeAttributes } from './utilities/mergeAttributes'
export { default as getExtensionField } from './helpers/getExtensionField'
export { default as findChildren } from './helpers/findChildren'
export { default as findParentNode } from './helpers/findParentNode'
export { default as findParentNodeClosestToPos } from './helpers/findParentNodeClosestToPos'

View File

@ -14,9 +14,30 @@ import { Extension } from './Extension'
import { Node } from './Node'
import { Mark } from './Mark'
import { Editor } from './Editor'
import { Commands } from '.'
import {
Commands,
ExtensionConfig,
NodeConfig,
MarkConfig,
} from '.'
export type Extensions = (Extension | Node | Mark)[]
export type AnyConfig = ExtensionConfig | NodeConfig | MarkConfig
export type AnyExtension = Extension | Node | Mark
export type Extensions = AnyExtension[]
export type ParentConfig<T> = Partial<{
[P in keyof T]: Required<T>[P] extends (...args: any) => any
? (...args: Parameters<Required<T>[P]>) => ReturnType<Required<T>[P]>
: T[P]
}>
export type RemoveThis<T> = T extends (...args: any) => any
? (...args: Parameters<T>) => ReturnType<T>
: T
export type MaybeReturnType<T> = T extends (...args: any) => any
? ReturnType<T>
: T
export interface EditorOptions {
element: Element,

View File

@ -1,3 +1,5 @@
import { MaybeReturnType } from '../types'
/**
* Optionally calls `value` as a function.
* Otherwise it is returned directly.
@ -5,7 +7,7 @@
* @param context Optional context to bind to function.
* @param props Optional props to pass to function.
*/
export default function callOrReturn(value: any, context: any = undefined, ...props: any[]): any {
export default function callOrReturn<T>(value: T, context: any = undefined, ...props: any[]): MaybeReturnType<T> {
if (typeof value === 'function') {
if (context) {
return value.bind(context)(...props)
@ -14,5 +16,5 @@ export default function callOrReturn(value: any, context: any = undefined, ...pr
return value(...props)
}
return value
return value as MaybeReturnType<T>
}

View File

@ -1,7 +1,3 @@
export default function elementFromString(value: string): HTMLElement {
const htmlString = `<div>${value}</div>`
const parser = new window.DOMParser()
const element = parser.parseFromString(htmlString, 'text/html').body
return element
return new window.DOMParser().parseFromString(value, 'text/html').body
}

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.0.0-beta.2](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-blockquote@2.0.0-beta.1...@tiptap/extension-blockquote@2.0.0-beta.2) (2021-04-15)
**Note:** Version bump only for package @tiptap/extension-blockquote
# [2.0.0-beta.1](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-blockquote@2.0.0-alpha.11...@tiptap/extension-blockquote@2.0.0-beta.1) (2021-03-05)
**Note:** Version bump only for package @tiptap/extension-blockquote

View File

@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) a
Documentation can be found on the [tiptap website](https://tiptap.dev).
## License
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).

View File

@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-blockquote",
"description": "blockquote extension for tiptap",
"version": "2.0.0-beta.1",
"version": "2.0.0-beta.2",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.0.0-beta.2](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-bold@2.0.0-beta.1...@tiptap/extension-bold@2.0.0-beta.2) (2021-04-15)
**Note:** Version bump only for package @tiptap/extension-bold
# [2.0.0-beta.1](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-bold@2.0.0-alpha.11...@tiptap/extension-bold@2.0.0-beta.1) (2021-03-05)
**Note:** Version bump only for package @tiptap/extension-bold

View File

@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) a
Documentation can be found on the [tiptap website](https://tiptap.dev).
## License
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).

View File

@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-bold",
"description": "bold extension for tiptap",
"version": "2.0.0-beta.1",
"version": "2.0.0-beta.2",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.0.0-beta.6](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-bubble-menu@2.0.0-beta.5...@tiptap/extension-bubble-menu@2.0.0-beta.6) (2021-04-15)
**Note:** Version bump only for package @tiptap/extension-bubble-menu
# [2.0.0-beta.5](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-bubble-menu@2.0.0-beta.4...@tiptap/extension-bubble-menu@2.0.0-beta.5) (2021-04-01)
**Note:** Version bump only for package @tiptap/extension-bubble-menu

View File

@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) a
Documentation can be found on the [tiptap website](https://tiptap.dev).
## License
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).

View File

@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-bubble-menu",
"description": "bubble-menu extension for tiptap",
"version": "2.0.0-beta.5",
"version": "2.0.0-beta.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.0.0-beta.2](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-bullet-list@2.0.0-beta.1...@tiptap/extension-bullet-list@2.0.0-beta.2) (2021-04-15)
**Note:** Version bump only for package @tiptap/extension-bullet-list
# [2.0.0-beta.1](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-bullet-list@2.0.0-alpha.11...@tiptap/extension-bullet-list@2.0.0-beta.1) (2021-03-05)
**Note:** Version bump only for package @tiptap/extension-bullet-list

View File

@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) a
Documentation can be found on the [tiptap website](https://tiptap.dev).
## License
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).

View File

@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-bullet-list",
"description": "bullet list extension for tiptap",
"version": "2.0.0-beta.1",
"version": "2.0.0-beta.2",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.0.0-beta.2](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-character-count@2.0.0-beta.1...@tiptap/extension-character-count@2.0.0-beta.2) (2021-04-15)
**Note:** Version bump only for package @tiptap/extension-character-count
# [2.0.0-beta.1](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-character-count@2.0.0-alpha.11...@tiptap/extension-character-count@2.0.0-beta.1) (2021-03-05)
**Note:** Version bump only for package @tiptap/extension-character-count

View File

@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) a
Documentation can be found on the [tiptap website](https://tiptap.dev).
## License
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).

View File

@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-character-count",
"description": "font family extension for tiptap",
"version": "2.0.0-beta.1",
"version": "2.0.0-beta.2",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",

View File

@ -3,6 +3,52 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.0.0-beta.7](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-code-block-lowlight@2.0.0-beta.6...@tiptap/extension-code-block-lowlight@2.0.0-beta.7) (2021-04-15)
**Note:** Version bump only for package @tiptap/extension-code-block-lowlight
# [2.0.0-beta.6](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-code-block-lowlight@2.0.0-beta.4...@tiptap/extension-code-block-lowlight@2.0.0-beta.6) (2021-04-14)
**Note:** Version bump only for package @tiptap/extension-code-block-lowlight
# [2.0.0-beta.4](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-code-block-lowlight@2.0.0-beta.3...@tiptap/extension-code-block-lowlight@2.0.0-beta.4) (2021-04-14)
### Bug Fixes
* fix lowlight decorations for vue 3 ([daa5dc0](https://github.com/ueberdosis/tiptap-next/commit/daa5dc0fb1ec2db6889565fc9c091f3dbdbbda6d))
# [2.0.0-beta.3](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-code-block-lowlight@2.0.0-beta.2...@tiptap/extension-code-block-lowlight@2.0.0-beta.3) (2021-04-12)
### Features
* add parentConfig to extension context for more extendable extensions, fix [#259](https://github.com/ueberdosis/tiptap-next/issues/259) ([5e1ec5d](https://github.com/ueberdosis/tiptap-next/commit/5e1ec5d2a66be164f505d631f97861ab9344ba96))
# [2.0.0-beta.2](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-code-block-lowlight@2.0.0-beta.1...@tiptap/extension-code-block-lowlight@2.0.0-beta.2) (2021-04-11)
**Note:** Version bump only for package @tiptap/extension-code-block-lowlight
# 2.0.0-beta.1 (2021-04-08)
**Note:** Version bump only for package @tiptap/extension-code-block-lowlight

View File

@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) a
Documentation can be found on the [tiptap website](https://tiptap.dev).
## License
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).

View File

@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-code-block-lowlight",
"description": "code block extension for tiptap",
"version": "2.0.0-beta.1",
"version": "2.0.0-beta.7",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
@ -25,7 +25,7 @@
"@tiptap/core": "^2.0.0-beta.1"
},
"dependencies": {
"@tiptap/extension-code-block": "^2.0.0-beta.1",
"@tiptap/extension-code-block": "^2.0.0-beta.4",
"@types/lowlight": "^0.0.1",
"lowlight": "^1.20.0",
"prosemirror-model": "^1.14.0",

View File

@ -1,10 +1,24 @@
import CodeBlock from '@tiptap/extension-code-block'
import lowlight from 'lowlight/lib/core'
import CodeBlock, { CodeBlockOptions } from '@tiptap/extension-code-block'
import { LowlightPlugin } from './lowlight-plugin'
export const CodeBlockLowlight = CodeBlock.extend({
export interface CodeBlockLowlightOptions extends CodeBlockOptions {
lowlight: any,
}
export const CodeBlockLowlight = CodeBlock.extend<CodeBlockLowlightOptions>({
defaultOptions: {
...CodeBlock.options,
lowlight,
},
addProseMirrorPlugins() {
return [
LowlightPlugin({ name: 'codeBlock' }),
...this.parent?.() || [],
LowlightPlugin({
name: 'codeBlock',
lowlight: this.options.lowlight,
}),
]
},
})

View File

@ -2,7 +2,6 @@ import { Plugin, PluginKey } from 'prosemirror-state'
import { Decoration, DecorationSet } from 'prosemirror-view'
import { Node as ProsemirrorNode } from 'prosemirror-model'
import { findChildren } from '@tiptap/core'
import lowlight from 'lowlight/lib/core'
function parseNodes(nodes: any[], className: string[] = []): { text: string, classes: string[] }[] {
return nodes
@ -26,7 +25,7 @@ function parseNodes(nodes: any[], className: string[] = []): { text: string, cla
.flat()
}
function getDecorations({ doc, name }: { doc: ProsemirrorNode, name: string}) {
function getDecorations({ doc, name, lowlight }: { doc: ProsemirrorNode, name: string, lowlight: any }) {
const decorations: Decoration[] = []
findChildren(doc, node => node.type.name === name)
@ -58,12 +57,12 @@ function getDecorations({ doc, name }: { doc: ProsemirrorNode, name: string}) {
return DecorationSet.create(doc, decorations)
}
export function LowlightPlugin({ name }: { name: string }) {
export function LowlightPlugin({ name, lowlight }: { name: string, lowlight: any }) {
return new Plugin({
key: new PluginKey('lowlight'),
state: {
init: (_, { doc }) => getDecorations({ doc, name }),
init: (_, { doc }) => getDecorations({ doc, name, lowlight }),
apply: (transaction, decorationSet, oldState, newState) => {
const oldNodeName = oldState.selection.$head.parent.type.name
const newNodeName = newState.selection.$head.parent.type.name
@ -95,7 +94,7 @@ export function LowlightPlugin({ name }: { name: string }) {
})
)
) {
return getDecorations({ doc: transaction.doc, name })
return getDecorations({ doc: transaction.doc, name, lowlight })
}
return decorationSet.map(transaction.mapping, transaction.doc)

View File

@ -3,6 +3,25 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.0.0-beta.4](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-code-block@2.0.0-beta.3...@tiptap/extension-code-block@2.0.0-beta.4) (2021-04-15)
**Note:** Version bump only for package @tiptap/extension-code-block
# [2.0.0-beta.3](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-code-block@2.0.0-beta.2...@tiptap/extension-code-block@2.0.0-beta.3) (2021-04-11)
### Bug Fixes
* remove codeblock when at start of document, fix [#262](https://github.com/ueberdosis/tiptap-next/issues/262) ([92f6ea2](https://github.com/ueberdosis/tiptap-next/commit/92f6ea25cc7623d0bd34f5a2342be6f5aae951aa))
# [2.0.0-beta.2](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-code-block@2.0.0-beta.1...@tiptap/extension-code-block@2.0.0-beta.2) (2021-04-02)
**Note:** Version bump only for package @tiptap/extension-code-block

View File

@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) a
Documentation can be found on the [tiptap website](https://tiptap.dev).
## License
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).

View File

@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-code-block",
"description": "code block extension for tiptap",
"version": "2.0.0-beta.2",
"version": "2.0.0-beta.4",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",

View File

@ -101,6 +101,22 @@ export const CodeBlock = Node.create<CodeBlockOptions>({
addKeyboardShortcuts() {
return {
'Mod-Alt-c': () => this.editor.commands.toggleCodeBlock(),
// remove code block when at start of document or code block is empty
Backspace: state => {
const { empty, $anchor } = state.selection
const isAtStart = $anchor.pos === 1
if (!empty || $anchor.parent.type.name !== 'codeBlock') {
return false
}
if (isAtStart || !$anchor.parent.textContent.length) {
return this.editor.commands.clearNodes()
}
return false
},
}
},

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.0.0-beta.2](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-code@2.0.0-beta.1...@tiptap/extension-code@2.0.0-beta.2) (2021-04-15)
**Note:** Version bump only for package @tiptap/extension-code
# [2.0.0-beta.1](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-code@2.0.0-alpha.11...@tiptap/extension-code@2.0.0-beta.1) (2021-03-05)
**Note:** Version bump only for package @tiptap/extension-code

View File

@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) a
Documentation can be found on the [tiptap website](https://tiptap.dev).
## License
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).

View File

@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-code",
"description": "code extension for tiptap",
"version": "2.0.0-beta.1",
"version": "2.0.0-beta.2",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.0.0-beta.6](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-collaboration-cursor@2.0.0-beta.5...@tiptap/extension-collaboration-cursor@2.0.0-beta.6) (2021-04-15)
**Note:** Version bump only for package @tiptap/extension-collaboration-cursor
# [2.0.0-beta.5](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-collaboration-cursor@2.0.0-beta.4...@tiptap/extension-collaboration-cursor@2.0.0-beta.5) (2021-04-07)

View File

@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) a
Documentation can be found on the [tiptap website](https://tiptap.dev).
## License
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).

View File

@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-collaboration-cursor",
"description": "collaboration cursor extension for tiptap",
"version": "2.0.0-beta.5",
"version": "2.0.0-beta.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.0.0-beta.5](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-collaboration@2.0.0-beta.4...@tiptap/extension-collaboration@2.0.0-beta.5) (2021-04-15)
**Note:** Version bump only for package @tiptap/extension-collaboration
# [2.0.0-beta.4](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-collaboration@2.0.0-beta.3...@tiptap/extension-collaboration@2.0.0-beta.4) (2021-03-16)
**Note:** Version bump only for package @tiptap/extension-collaboration

View File

@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) a
Documentation can be found on the [tiptap website](https://tiptap.dev).
## License
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).

View File

@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-collaboration",
"description": "collaboration extension for tiptap",
"version": "2.0.0-beta.4",
"version": "2.0.0-beta.5",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.0.0-beta.2](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-document@2.0.0-beta.1...@tiptap/extension-document@2.0.0-beta.2) (2021-04-15)
**Note:** Version bump only for package @tiptap/extension-document
# [2.0.0-beta.1](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-document@2.0.0-alpha.11...@tiptap/extension-document@2.0.0-beta.1) (2021-03-05)
**Note:** Version bump only for package @tiptap/extension-document

View File

@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) a
Documentation can be found on the [tiptap website](https://tiptap.dev).
## License
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).

View File

@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-document",
"description": "document extension for tiptap",
"version": "2.0.0-beta.1",
"version": "2.0.0-beta.2",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.0.0-beta.3](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-dropcursor@2.0.0-beta.2...@tiptap/extension-dropcursor@2.0.0-beta.3) (2021-04-15)
**Note:** Version bump only for package @tiptap/extension-dropcursor
# [2.0.0-beta.2](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/extension-dropcursor@2.0.0-beta.1...@tiptap/extension-dropcursor@2.0.0-beta.2) (2021-04-06)
**Note:** Version bump only for package @tiptap/extension-dropcursor

View File

@ -11,4 +11,4 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) a
Documentation can be found on the [tiptap website](https://tiptap.dev).
## License
tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).

View File

@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-dropcursor",
"description": "dropcursor extension for tiptap",
"version": "2.0.0-beta.2",
"version": "2.0.0-beta.3",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",

Some files were not shown because too many files have changed in this diff Show More