This commit is contained in:
Philipp Kühn
2020-11-17 14:38:49 +01:00
12 changed files with 459 additions and 53 deletions

View File

@@ -27,6 +27,7 @@
"vue-live": "^1.15.1", "vue-live": "^1.15.1",
"y-indexeddb": "^9.0.5", "y-indexeddb": "^9.0.5",
"y-webrtc": "^10.1.6", "y-webrtc": "^10.1.6",
"y-websocket": "^1.3.6",
"yjs": "^13.4.4" "yjs": "^13.4.4"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -0,0 +1,5 @@
context('/examples/collaborative-editing-ws', () => {
before(() => {
cy.visit('/examples/collaborative-editing-ws')
})
})

View File

@@ -0,0 +1,331 @@
<template>
<div>
<div v-if="editor">
<button @click="editor.chain().focus().bold().run()" :class="{ 'is-active': editor.isActive('bold') }">
bold
</button>
<button @click="editor.chain().focus().italic().run()" :class="{ 'is-active': editor.isActive('italic') }">
italic
</button>
<button @click="editor.chain().focus().strike().run()" :class="{ 'is-active': editor.isActive('strike') }">
strike
</button>
<button @click="editor.chain().focus().code().run()" :class="{ 'is-active': editor.isActive('code') }">
code
</button>
<button @click="editor.chain().focus().removeMarks().run()">
clear marks
</button>
<button @click="editor.chain().focus().clearNodes().run()">
clear nodes
</button>
<button @click="editor.chain().focus().paragraph().run()" :class="{ 'is-active': editor.isActive('paragraph') }">
paragraph
</button>
<button @click="editor.chain().focus().heading({ level: 1 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 1 }) }">
h1
</button>
<button @click="editor.chain().focus().heading({ level: 2 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 2 }) }">
h2
</button>
<button @click="editor.chain().focus().heading({ level: 3 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 3 }) }">
h3
</button>
<button @click="editor.chain().focus().heading({ level: 4 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 4 }) }">
h4
</button>
<button @click="editor.chain().focus().heading({ level: 5 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 5 }) }">
h5
</button>
<button @click="editor.chain().focus().heading({ level: 6 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 6 }) }">
h6
</button>
<button @click="editor.chain().focus().bulletList().run()" :class="{ 'is-active': editor.isActive('bulletList') }">
bullet list
</button>
<button @click="editor.chain().focus().orderedList().run()" :class="{ 'is-active': editor.isActive('orderedList') }">
ordered list
</button>
<button @click="editor.chain().focus().codeBlock().run()" :class="{ 'is-active': editor.isActive('codeBlock') }">
code block
</button>
<button @click="editor.chain().focus().blockquote().run()" :class="{ 'is-active': editor.isActive('blockquote') }">
blockquote
</button>
<button @click="editor.chain().focus().horizontalRule().run()">
horizontal rule
</button>
<button @click="editor.chain().focus().hardBreak().run()">
hard break
</button>
<button @click="editor.chain().focus().undo().run()">
undo
</button>
<button @click="editor.chain().focus().redo().run()">
redo
</button>
<br>
<br>
<button @click="setName">
Set Name
</button>
<button @click="changeName">
Random Name
</button>
<button @click="changeColor">
Random Color
</button>
</div>
<div class="collaboration-status">
{{ users.length }} user{{ users.length === 1 ? '' : 's' }}
</div>
<div class="collaboration-users">
<div
class="collaboration-users__item"
:style="`background-color: ${user.color}`"
v-for="user in users"
:key="user.id"
>
{{ user.name }}
</div>
</div>
<editor-content :editor="editor" />
</div>
</template>
<script>
import { Editor, EditorContent, defaultExtensions } from '@tiptap/vue-starter-kit'
import Collaboration from '@tiptap/extension-collaboration'
import CollaborationCursor from '@tiptap/extension-collaboration-cursor'
import * as Y from 'yjs'
// import { WebrtcProvider } from 'y-webrtc'
import { WebsocketProvider } from 'y-websocket'
import { IndexeddbPersistence } from 'y-indexeddb'
export default {
components: {
EditorContent,
},
data() {
return {
documentName: 'tiptap-collaboration-example',
name: this.getRandomName(),
color: this.getRandomColor(),
ydoc: null,
provider: null,
type: null,
indexdb: null,
editor: null,
users: [],
}
},
mounted() {
this.ydoc = new Y.Doc()
this.type = this.ydoc.getXmlFragment('prosemirror')
this.indexdb = new IndexeddbPersistence(this.documentName, this.ydoc)
// this.provider = new WebrtcProvider(this.documentName, this.ydoc)
this.provider = new WebsocketProvider('ws://127.0.0.1:1234', 'tiptap', this.ydoc)
// this.provider = new WebsocketProvider('wss://demos.yjs.dev', 'tiptap', this.ydoc)
this.provider.awareness.on('change', this.updateState)
this.editor = new Editor({
extensions: [
...defaultExtensions(),
Collaboration.configure({
type: this.type,
}),
CollaborationCursor.configure({
provider: this.provider,
name: this.name,
color: this.color,
}),
],
})
this.updateState()
},
methods: {
setName() {
const name = window.prompt('Name')
if (name) {
this.name = name
return this.updateUser()
}
},
changeName() {
this.name = this.getRandomName()
this.updateUser()
},
changeColor() {
this.color = this.getRandomColor()
this.updateUser()
},
updateUser() {
this.editor.chain().focus().user({
name: this.name,
color: this.color,
}).run()
this.updateState()
},
getRandomColor() {
return this.getRandomElement([
'#616161',
'#A975FF',
'#FB5151',
'#fd9170',
'#FFCB6B',
'#68CEF8',
'#80cbc4',
'#9DEF8F',
])
},
getRandomName() {
return this.getRandomElement([
'Lea Thompson', 'Cyndi Lauper', 'Tom Cruise', 'Madonna', 'Jerry Hall', 'Joan Collins', 'Winona Ryder', 'Christina Applegate', 'Alyssa Milano', 'Molly Ringwald', 'Ally Sheedy', 'Debbie Harry', 'Olivia Newton-John', 'Elton John', 'Michael J. Fox', 'Axl Rose', 'Emilio Estevez', 'Ralph Macchio', 'Rob Lowe', 'Jennifer Grey', 'Mickey Rourke', 'John Cusack', 'Matthew Broderick', 'Justine Bateman', 'Lisa Bonet',
])
},
getRandomElement(list) {
return list[Math.floor(Math.random() * list.length)]
},
updateState() {
const { states } = this.provider.awareness
this.users = Array.from(states.entries()).map(state => {
return {
id: state[0],
...state[1].user,
}
})
},
},
beforeDestroy() {
this.editor.destroy()
this.provider.destroy()
},
}
</script>
<style lang="scss">
/* A list of all available users */
.collaboration-users {
margin-top: 0.5rem;
&__item {
display: inline-block;
border-radius: 5px;
padding: 0.25rem 0.5rem;
color: white;
margin-right: 0.5rem;
margin-bottom: 0.5rem;
}
}
/* Some information about the status */
.collaboration-status {
background: #eee;
color: #666;
border-radius: 5px;
padding: 0.5rem 1rem;
margin-top: 1rem;
&::before {
content: ' ';
display: inline-block;
width: 0.5rem;
height: 0.5rem;
background: green;
border-radius: 50%;
margin-right: 0.5rem;
}
}
/* Give a remote user a caret */
.collaboration-cursor__caret {
position: relative;
margin-left: -1px;
margin-right: -1px;
border-left: 1px solid black;
border-right: 1px solid black;
word-break: normal;
pointer-events: none;
}
/* Render the username above the caret */
.collaboration-cursor__label {
position: absolute;
top: -1.4em;
left: -1px;
font-size: 13px;
font-style: normal;
font-weight: normal;
line-height: normal;
user-select: none;
color: white;
padding: 0.1rem 0.3rem;
border-radius: 3px;
white-space: nowrap;
}
/* Basic editor styles */
.ProseMirror {
> * + * {
margin-top: 0.75em;
}
ul,
ol {
padding: 0 1rem;
}
code {
background-color: rgba(#616161, 0.1);
color: #616161;
}
pre {
background: #0D0D0D;
color: #FFF;
font-family: 'JetBrainsMono', monospace;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
code {
color: inherit;
background: none;
font-size: 0.8rem;
}
}
img {
max-width: 100%;
height: auto;
}
hr {
margin: 1rem 0;
}
blockquote {
padding-left: 1rem;
border-left: 2px solid rgba(#0D0D0D, 0.1);
}
}
</style>

View File

@@ -1,6 +1,6 @@
# Blockquote # Blockquote
The Blockquote extension enables you to use the `<blockquote>` HTML tag in the editor. This is great you might have guessed to use quotes in the editor. The Blockquote extension enables you to use the `<blockquote>` HTML tag in the editor. This is great to use quotes in the editor.
Type <code>>&nbsp;</code> at the beginning of a new line and it will magically transform to a blockquote. Type <code>>&nbsp;</code> at the beginning of a new line and it will magically transform to a blockquote.

View File

@@ -1,5 +1,5 @@
# Overview # Overview
tiptap is a friendly wrapper around [ProseMirror](https://ProseMirror.net). 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 ### Structure
ProseMirror works with a strict [Schema](/api/schema), which defines the allowed structure of a document. A document is a tree of headings, paragraphs and others elements, so called nodes. Marks can be attached to a node, e. g. to emphasize part of it. [Commands](/api/commands) change that document programmatically. ProseMirror works with a strict [Schema](/api/schema), which defines the allowed structure of a document. A document is a tree of headings, paragraphs and others elements, so called nodes. Marks can be attached to a node, e. g. to emphasize part of it. [Commands](/api/commands) change that document programmatically.
@@ -9,5 +9,3 @@ The document is stored in a state. All changes are applied as transactions to th
### Extensions ### Extensions
Extensions add [nodes](/api/nodes), [marks](/api/marks) and/or [functionalities](/api/extensions) to the editor. A lot of those extensions bound their commands to common [keyboard shortcuts](/api/keyboard-shortcuts). Extensions add [nodes](/api/nodes), [marks](/api/marks) and/or [functionalities](/api/extensions) to the editor. A lot of those extensions bound their commands to common [keyboard shortcuts](/api/keyboard-shortcuts).
All those concepts are explained in detail on the following pages.

View File

@@ -0,0 +1,5 @@
# Collaborative editing
Websockets
<demo name="Examples/CollaborativeEditingWs" />

View File

@@ -0,0 +1,81 @@
# Get started
## toc
## Introduction
tiptap 2 is framework-agnostic and even works with plain JavaScript, if thats your thing. As the previous major version required Vue.js, we decided to focus on Vue.js in the first version of this guide. That said, its probably also helpful for developers who work with different technologies.
Lets take a few basic building blocks for a test drive.
## Requirements
The following guide assumes youre working with Vue.js. Hopefully, that helps to get you going with other frameworks (or without a framework at all), while were working on more guides. We also assume youve [set up Node.js](https://nodejs.org/en/download/) on your machine already.
## 1. Create a new project
### Install Vue CLI (optional)
```bash
# with npm
npm install -g @vue/cli
# with Yarn
yarn global add @vue/cli
```
Lets start with a fresh Vue.js project. If you already have an existing Vue.js project, thats fine too. Just skip this first step and proceed with the next step.
### Create a project (optional)
Pick *Default ([Vue 2] babel, eslint)*
```bash
# create a project
vue create tiptap-example
# change directory
cd tiptap-example
```
### Install the dependencies
You can install tiptap for Vue.js as a dependency in your project:
```bash
# install the Vue.js adapter with npm
npm install @tiptap/core @tiptap/vue-starter-kit
# or: install the Vue.js adapter with Yarn
yarn add @tiptap/core @tiptap/vue-starter-kit
```
The `@tiptap/vue-starter-kit` includes a few basics you would probably need anyway. Cool, you have got everything in place to start fiddling around with tiptap! 🙌
Start your project with `$ yarn serve` or `$ npm run serve`. Open [http://localhost:8080/](http://localhost:8080/) in your favorite browser.
## 2. Create a new component
Create a new Vue component (you can call it `<tiptap />`) and add the following content. This is the fastest way to get tiptap up and running with Vue.js. It will give you a very basic version of tiptap, without any buttons. No worries, you will be able to add more functionality soon.
<demo name="Guide/GettingStarted" />
## 3. Add it to your app
```js
<template>
<div id="app">
<tiptap />
</div>
</template>
<script>
import Tiptap from './components/Tiptap.vue'
export default {
name: 'App',
components: {
Tiptap
}
}
</script>
```
::: warning Nuxt.js
If you use Nuxt.js, note that tiptap needs to run in the client, not on the server. Its required to wrap the editor in a `<client-only>` tag.
:::
Congrats! Youve got it! 🎉 Lets start to configure your editor in the next step.

View File

@@ -1,30 +0,0 @@
# Getting started
## toc
## Introduction
tiptap is framework-agnostic and works with Vue.js and React. It even works with plain JavaScript, if thats your thing. To keep everything as small as possible, we put the code to use tiptap with those frameworks in different packages.
## 1. Install the dependencies
We assume you already have a [Vue.js](https://cli.vuejs.org/) (or [Nuxt.js](https://nuxtjs.org/)) project. To connect tiptap with Vue.js you are going to need an adapter. You can install tiptap for Vue.js as a dependency in your project:
```bash
# Install the Vue.js adapter with npm
npm install @tiptap/vue @tiptap/vue-starter-kit
# Or: Install the Vue.js adapter with Yarn
yarn add @tiptap/vue @tiptap/vue-starter-kit
```
The `@tiptap/vue-starter-kit` includes a few basics you would probably need anyway. Cool, you have got everything in place to set up tiptap! 🙌
## 2. Create a new component
Create a new Vue component (you can call it `<tiptap />`) and add the following content. This is the fastest way to get tiptap up and running with Vue.js. It will give you a very basic version of tiptap, without any buttons. No worries, you will be able to add more functionality soon.
<demo name="Guide/GettingStarted" />
::: warning Nuxt.js
If you use Nuxt.js, note that tiptap needs to run in the client, not on the server. Its required to wrap the editor in a `<client-only>` tag.
:::
Congrats! Youve got it! 🎉 Lets start to configure your editor in the next step.

View File

@@ -7,20 +7,18 @@ Nothing here is production-ready, dont use it anywhere.
::: :::
# Introduction # Introduction
[![Version](https://img.shields.io/npm/v/@tiptap/core.svg?label=version)](https://www.npmjs.com/package/@tiptap/core)
<!-- [![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)
<!-- [![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)
<!-- [![License](https://img.shields.io/npm/l/@tiptap/core.svg)](https://www.npmjs.com/package/@tiptap/core) -->
<!-- [![Filesize](https://img.badgesize.io/https://unpkg.com/tiptap/dist/tiptap.min.js?compression=gzip&label=size&colorB=000000)](https://www.npmjs.com/package/tiptap) --> <!-- [![Filesize](https://img.badgesize.io/https://unpkg.com/tiptap/dist/tiptap.min.js?compression=gzip&label=size&colorB=000000)](https://www.npmjs.com/package/tiptap) -->
<!-- [![Build Status](https://github.com/ueberdosis/tiptap-next/workflows/build/badge.svg)](https://github.com/ueberdosis/tiptap-next/actions) --> <!-- [![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) [![Sponsor](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub)](https://github.com/sponsors/ueberdosis)
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*. 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*.
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. Youll have a better understanding of how everything works under the hood and get more familiar with many terms and jargon used by tiptap. Create exactly the rich text editor you want out of modular, and customizable building blocks. Tiptap comes with sensible defaults, many default extensions and a friendly API. Its open source, free, and backed by a welcoming community.
## Features ## 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 and styling. **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 and styling.
**Framework-agnostic.** We dont care what framework you use. tiptap is ready to be used with plain JavaScript or Vue.js. That makes it even possible to write a renderer for React, Svelte and others. **Framework-agnostic.** We dont care what framework you use. tiptap is ready to be used with plain JavaScript or Vue.js. That makes it even possible to write a renderer for React, Svelte and others.
@@ -34,6 +32,8 @@ Although tiptap tries to hide most of the complexity of ProseMirror, its buil
- [ApostropheCMS](https://apostrophecms.com) - [ApostropheCMS](https://apostrophecms.com)
- [Directus CMS](https://directus.io) - [Directus CMS](https://directus.io)
- [Nextcloud](https://apps.nextcloud.com/apps/text) - [Nextcloud](https://apps.nextcloud.com/apps/text)
- [DocIQ](https://www.dociq.io)
- [Scrumpy](https://www.scrumpy.io)
- [and many more →](https://github.com/ueberdosis/tiptap/network/dependents?package_id=UGFja2FnZS0xMzE5OTg0ODc%3D) - [and many more →](https://github.com/ueberdosis/tiptap/network/dependents?package_id=UGFja2FnZS0xMzE5OTg0ODc%3D)
## License ## License

View File

@@ -11,14 +11,21 @@ We dont provide email support for tiptap, but if you have some legal issues,
## List of external services ## List of external services
### GitHub ### Community (GitHub)
We use [GitHub](http://github.com/) to store our code, collaborate, give support, and offer sponsorships. Check their [privacy statement](https://docs.github.com/en/free-pro-team@latest/github/site-policy/github-privacy-statement) to learn more about what data they process. We use [GitHub](http://github.com/) to store our code, collaborate, give support, and offer sponsorships. Check their [privacy statement](https://docs.github.com/en/free-pro-team@latest/github/site-policy/github-privacy-statement) to learn more about what data they process.
### Netlify ### Hosting (Netlify)
We use Netlify to host the documentation. It features continuous deployment from Git across a global application delivery network and full integration with Lets Encrypt. If you want to know more, [read Netlifys privacy policy](https://www.netlify.com/privacy/). We use Netlify to host the documentation. It features continuous deployment from Git across a global application delivery network and full integration with Lets Encrypt. If you want to know more, [read Netlifys privacy policy](https://www.netlify.com/privacy/).
### Simple Analytics ### Tracking (Simple Analytics)
We use [Simple Analytics](https://simpleanalytics.com/) to gain insight about our visitors in general. It doesnt track individual users per se and does not store any personal identifiable information. Go to their documentation to find out what Simple Analytics collects (and more importantly what they dont). We use [Simple Analytics](https://simpleanalytics.com/) to gain insight about our visitors in general. It doesnt track individual users per se and does not store any personal identifiable information. Go to their documentation to find out what Simple Analytics collects (and more importantly what they dont).
Or have a look at the [public analytics dashboard](https://simpleanalytics.com/tiptap.dev) they provide. Its not a stripped down version, its the exact same dashboard we use to check the traffic. Or have a look at the [public analytics dashboard](https://simpleanalytics.com/tiptap.dev) they provide. Its not a stripped down version, its the exact same dashboard we use to check the traffic.
### Search (Algolia)
We use [Algolia DocSearch](https://docsearch.algolia.com/) to offer search functionality for the documentation. They crawl the same pages as you see once every day. If you click on the search field on top of this page, their search interface pops up.
If you want to know more about what data they collect and process, [read their privacy policy](https://www.algolia.com/policies/privacy/).
## Suggesting alternatives
If you know a good self-hosted version of any of the mentioned services, create an issue on GitHub or send us an email to [humans@tiptap.dev](mailto:humans@tiptap.dev).

View File

@@ -16,6 +16,10 @@
link: /examples/basic link: /examples/basic
- title: Collaborative editing - title: Collaborative editing
link: /examples/collaborative-editing link: /examples/collaborative-editing
pro: true
- title: Collaborative editing (WS)
link: /examples/collaborative-editing-ws
draft: true
- title: Markdown shortcuts - title: Markdown shortcuts
link: /examples/markdown-shortcuts link: /examples/markdown-shortcuts
- title: Formatting - title: Formatting
@@ -37,30 +41,30 @@
- title: Guide - title: Guide
items: items:
- title: Getting started - title: Get started
link: /guide/getting-started link: /guide/get-started
draft: true draft: true
- title: Configuration - title: Configure the editor
link: /guide/configuration link: /guide/configuration
draft: true draft: true
- title: Create your editor - title: Create a new toolbar
link: /guide/create-your-editor link: /guide/create-your-editor
draft: true draft: true
- title: Custom styling - title: Add custom styling
link: /guide/custom-styling link: /guide/custom-styling
- title: Store content - title: Store content
link: /guide/store-content link: /guide/store-content
- title: Build custom extensions - title: Build custom extensions
link: /guide/build-custom-extensions link: /guide/build-custom-extensions
- title: Collaborative editing
link: /guide/collaborative-editing
draft: true
pro: true
- title: Advanced node views - title: Advanced node views
link: /guide/advanced-node-views link: /guide/advanced-node-views
draft: true draft: true
- title: Working with TypeScript - title: Working with TypeScript
link: /guide/working-with-typescript link: /guide/working-with-typescript
- title: Collaborative editing
link: /guide/collaborative-editing
draft: true
pro: true
- title: API - title: API
items: items:

View File

@@ -0,0 +1,4 @@
/overview /
/examples /examples/basic
/guide /guide/get-started
/api /api/overview