diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..0e8a7db7 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +static/images/* +!static/images/.gitkeep diff --git a/docs/fonts/Inter-Black.otf b/docs/fonts/Inter-Black.otf new file mode 100644 index 00000000..8394a9f9 Binary files /dev/null and b/docs/fonts/Inter-Black.otf differ diff --git a/docs/fonts/Inter-BlackItalic.otf b/docs/fonts/Inter-BlackItalic.otf new file mode 100644 index 00000000..b24c8cac Binary files /dev/null and b/docs/fonts/Inter-BlackItalic.otf differ diff --git a/docs/fonts/Inter-Bold.otf b/docs/fonts/Inter-Bold.otf new file mode 100644 index 00000000..ed9019a5 Binary files /dev/null and b/docs/fonts/Inter-Bold.otf differ diff --git a/docs/fonts/Inter-BoldItalic.otf b/docs/fonts/Inter-BoldItalic.otf new file mode 100644 index 00000000..be8a3d3c Binary files /dev/null and b/docs/fonts/Inter-BoldItalic.otf differ diff --git a/docs/fonts/Inter-ExtraBold.otf b/docs/fonts/Inter-ExtraBold.otf new file mode 100644 index 00000000..e96535fe Binary files /dev/null and b/docs/fonts/Inter-ExtraBold.otf differ diff --git a/docs/fonts/Inter-ExtraBoldItalic.otf b/docs/fonts/Inter-ExtraBoldItalic.otf new file mode 100644 index 00000000..3b196c8e Binary files /dev/null and b/docs/fonts/Inter-ExtraBoldItalic.otf differ diff --git a/docs/fonts/Inter-ExtraLight.otf b/docs/fonts/Inter-ExtraLight.otf new file mode 100644 index 00000000..677bce03 Binary files /dev/null and b/docs/fonts/Inter-ExtraLight.otf differ diff --git a/docs/fonts/Inter-ExtraLightItalic.otf b/docs/fonts/Inter-ExtraLightItalic.otf new file mode 100644 index 00000000..00512ba6 Binary files /dev/null and b/docs/fonts/Inter-ExtraLightItalic.otf differ diff --git a/docs/fonts/Inter-Italic.otf b/docs/fonts/Inter-Italic.otf new file mode 100644 index 00000000..12319b07 Binary files /dev/null and b/docs/fonts/Inter-Italic.otf differ diff --git a/docs/fonts/Inter-Light.otf b/docs/fonts/Inter-Light.otf new file mode 100644 index 00000000..95c69f84 Binary files /dev/null and b/docs/fonts/Inter-Light.otf differ diff --git a/docs/fonts/Inter-LightItalic.otf b/docs/fonts/Inter-LightItalic.otf new file mode 100644 index 00000000..8dee08c8 Binary files /dev/null and b/docs/fonts/Inter-LightItalic.otf differ diff --git a/docs/fonts/Inter-Medium.otf b/docs/fonts/Inter-Medium.otf new file mode 100644 index 00000000..01b0b4ae Binary files /dev/null and b/docs/fonts/Inter-Medium.otf differ diff --git a/docs/fonts/Inter-MediumItalic.otf b/docs/fonts/Inter-MediumItalic.otf new file mode 100644 index 00000000..abd98779 Binary files /dev/null and b/docs/fonts/Inter-MediumItalic.otf differ diff --git a/docs/fonts/Inter-Regular.otf b/docs/fonts/Inter-Regular.otf new file mode 100644 index 00000000..e94fa454 Binary files /dev/null and b/docs/fonts/Inter-Regular.otf differ diff --git a/docs/fonts/Inter-SemiBold.otf b/docs/fonts/Inter-SemiBold.otf new file mode 100644 index 00000000..23294024 Binary files /dev/null and b/docs/fonts/Inter-SemiBold.otf differ diff --git a/docs/fonts/Inter-SemiBoldItalic.otf b/docs/fonts/Inter-SemiBoldItalic.otf new file mode 100644 index 00000000..2137d653 Binary files /dev/null and b/docs/fonts/Inter-SemiBoldItalic.otf differ diff --git a/docs/fonts/Inter-Thin.otf b/docs/fonts/Inter-Thin.otf new file mode 100644 index 00000000..0aac1a9c Binary files /dev/null and b/docs/fonts/Inter-Thin.otf differ diff --git a/docs/fonts/Inter-ThinItalic.otf b/docs/fonts/Inter-ThinItalic.otf new file mode 100644 index 00000000..f6e240a6 Binary files /dev/null and b/docs/fonts/Inter-ThinItalic.otf differ diff --git a/docs/fonts/Inter-V.ttf b/docs/fonts/Inter-V.ttf new file mode 100644 index 00000000..e81fa4b1 Binary files /dev/null and b/docs/fonts/Inter-V.ttf differ diff --git a/docs/gridsome.server.js b/docs/gridsome.server.js index 42c89336..f86728d0 100644 --- a/docs/gridsome.server.js +++ b/docs/gridsome.server.js @@ -1,5 +1,45 @@ +const fs = require('fs') +const { createCanvas, registerFont } = require('canvas') const path = require('path') const globby = require('globby') + +registerFont('fonts/Inter-Regular.otf', { family: 'InterRegular' }) +registerFont('fonts/Inter-Medium.otf', { family: 'InterMedium' }) + +const wrapText = function (context, text, x, y, maxWidth, lineHeight) { + const words = text.split(' ') + let line = '' + + for (let n = 0; n < words.length; n += 1) { + const testLine = `${line + words[n]} ` + const metrics = context.measureText(testLine) + const testWidth = metrics.width + if (testWidth > maxWidth && n > 0) { + context.fillText(line, x, y) + line = `${words[n]} ` + y += lineHeight + } else { + line = testLine + } + } + context.fillText(line, x, y) +} + +const calculateReadingTime = function (text) { + const wordsPerMinute = 200 + const textLength = text.split(' ').length + + if (textLength > 0) { + const value = Math.ceil(textLength / wordsPerMinute) + + if (value === 1) { + return `${value} minute` + } + + return `${value} minutes` + } +} + // const TypeDoc = require('typedoc') // const packages = globby.sync('../packages/*', { onlyDirectories: true }) @@ -142,4 +182,67 @@ module.exports = function (api) { .set(`@tiptap/${name}`, path.resolve(`../packages/${name}/src/index.ts`)) }) }) + + // Generate OpenGraph images for all pages + api.onCreateNode(options => { + // if (process.env.NODE_ENV !== 'production') { + // return null + // } + + if (options.internal.typeName !== 'DocPage') { + return + } + + const imagePath = `static/images${options.path}` + const imageFile = `static/images${options.path}og-image.png` + + // console.log(`Found Post “${options.title}” in ${options.internal.origin} …`) + + const width = 1200 + const height = 630 + + const border = 40 + + const canvas = createCanvas(width, height) + const context = canvas.getContext('2d') + + // background + context.fillStyle = '#000000' + context.fillRect(0, 0, width, height) + + // project + const project = 'tiptap documentation' + context.textBaseline = 'top' + context.fillStyle = '#666666' + context.font = '32pt InterRegular' + context.fillText(project, border, border) + + // title + const { title } = options + const lineHeight = 96 + context.textBaseline = 'top' + context.fillStyle = '#ffffff' + context.font = '58pt InterMedium' + wrapText(context, title, border, border + 60 + border, width - border - border, lineHeight) + + // reading time + const readingTime = calculateReadingTime(options.content) + context.textBaseline = 'bottom' + context.fillStyle = '#666666' + context.font = '32pt InterRegular' + context.fillText(readingTime, border, height - border) + + // store + const buffer = canvas.toBuffer('image/png') + + fs.mkdir(imagePath, { recursive: true }, error => { + if (error) { + throw error + } + + fs.writeFileSync(imageFile, buffer) + + // console.log(`OpenGraph image generated (${imageFile}).`) + }) + }) } diff --git a/docs/package.json b/docs/package.json index 22e5d472..d7697b8b 100644 --- a/docs/package.json +++ b/docs/package.json @@ -12,6 +12,7 @@ "@gridsome/transformer-json": "^0.2.1", "@gridsome/vue-remark": "^0.2.6", "@mvasilkov/outdent": "^1.0.4", + "canvas": "^2.6.1", "collect.js": "^4.28.6", "d3": "^6.5.0", "globby": "^11.0.0", diff --git a/docs/src/components/LiveDemo/index.vue b/docs/src/components/LiveDemo/index.vue index cd032fb0..9ea74a24 100644 --- a/docs/src/components/LiveDemo/index.vue +++ b/docs/src/components/LiveDemo/index.vue @@ -23,6 +23,8 @@ + + + diff --git a/docs/src/demos/Experiments/Embeds/iframe.ts b/docs/src/demos/Experiments/Embeds/iframe.ts new file mode 100644 index 00000000..d2fdb636 --- /dev/null +++ b/docs/src/demos/Experiments/Embeds/iframe.ts @@ -0,0 +1,70 @@ +import { Node, Command } from '@tiptap/core' + +export interface IframeOptions { + allowFullscreen: boolean, + HTMLAttributes: { + [key: string]: any + }, +} + +export default Node.create({ + name: 'iframe', + + group: 'block', + + // selectable: false, + + defaultOptions: { + allowFullscreen: true, + HTMLAttributes: { + class: 'iframe-wrapper', + }, + }, + + addAttributes() { + return { + src: { + default: null, + }, + frameborder: { + default: 0, + }, + allowfullscreen: { + default: this.options.allowFullscreen, + parseHTML: () => { + return { + allowfullscreen: this.options.allowFullscreen, + } + }, + }, + } + }, + + parseHTML() { + return [{ + tag: 'iframe', + }] + }, + + renderHTML({ HTMLAttributes }) { + return ['div', this.options.HTMLAttributes, ['iframe', HTMLAttributes, 0]] + }, + + addCommands() { + return { + /** + * Add an iframe + */ + setIframe: (options: { src: string }): Command => ({ tr, dispatch }) => { + const { selection } = tr + const node = this.type.create(options) + + if (dispatch) { + tr.replaceRangeWith(selection.from, selection.to, node) + } + + return true + }, + } + }, +}) diff --git a/docs/src/demos/Experiments/Embeds/index.vue b/docs/src/demos/Experiments/Embeds/index.vue new file mode 100644 index 00000000..b88a5617 --- /dev/null +++ b/docs/src/demos/Experiments/Embeds/index.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/docs/src/demos/Experiments/Placeholder/extension/placeholder.ts b/docs/src/demos/Experiments/Placeholder/extension/placeholder.ts new file mode 100644 index 00000000..a26207e9 --- /dev/null +++ b/docs/src/demos/Experiments/Placeholder/extension/placeholder.ts @@ -0,0 +1,67 @@ +import { Extension } from '@tiptap/core' +import { Decoration, DecorationSet } from 'prosemirror-view' +import { Plugin } from 'prosemirror-state' + +export interface PlaceholderOptions { + emptyEditorClass: string, + emptyNodeClass: string, + placeholder: string | Function, + showOnlyWhenEditable: boolean, + showOnlyCurrent: boolean, +} + +export default Extension.create({ + name: 'placeholder', + + defaultOptions: { + emptyEditorClass: 'is-editor-empty', + emptyNodeClass: 'is-empty', + placeholder: 'Write something …', + showOnlyWhenEditable: true, + showOnlyCurrent: true, + }, + + addProseMirrorPlugins() { + return [ + new Plugin({ + props: { + decorations: ({ doc, selection }) => { + const active = this.editor.isEditable || !this.options.showOnlyWhenEditable + const { anchor } = selection + const decorations: Decoration[] = [] + const isEditorEmpty = doc.textContent.length === 0 + + if (!active) { + return + } + + doc.descendants((node, pos) => { + const hasAnchor = anchor >= pos && anchor <= (pos + node.nodeSize) + const isNodeEmpty = node.content.size === 0 + + if ((hasAnchor || !this.options.showOnlyCurrent) && isNodeEmpty) { + const classes = [this.options.emptyNodeClass] + + if (isEditorEmpty) { + classes.push(this.options.emptyEditorClass) + } + + const decoration = Decoration.node(pos, pos + node.nodeSize, { + class: classes.join(' '), + 'data-empty-text': typeof this.options.placeholder === 'function' + ? this.options.placeholder(node) + : this.options.placeholder, + }) + decorations.push(decoration) + } + + return false + }) + + return DecorationSet.create(doc, decorations) + }, + }, + }), + ] + }, +}) diff --git a/docs/src/demos/Experiments/Placeholder/index.vue b/docs/src/demos/Experiments/Placeholder/index.vue new file mode 100644 index 00000000..ee22bdd6 --- /dev/null +++ b/docs/src/demos/Experiments/Placeholder/index.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/docs/src/demos/Nodes/Image/index.vue b/docs/src/demos/Nodes/Image/index.vue index 8c59b3bb..6a458dfc 100644 --- a/docs/src/demos/Nodes/Image/index.vue +++ b/docs/src/demos/Nodes/Image/index.vue @@ -31,7 +31,9 @@ export default { addImage() { const url = window.prompt('URL') - this.editor.chain().focus().setImage({ src: url }).run() + if (url) { + this.editor.chain().focus().setImage({ src: url }).run() + } }, }, @@ -68,6 +70,10 @@ export default { img { max-width: 100%; height: auto; + + &.ProseMirror-selectednode { + outline: 3px solid #68CEF8; + } } } diff --git a/docs/src/docPages/examples/images.md b/docs/src/docPages/examples/images.md new file mode 100644 index 00000000..28b75739 --- /dev/null +++ b/docs/src/docPages/examples/images.md @@ -0,0 +1,3 @@ +# Images + + diff --git a/docs/src/docPages/experiments.md b/docs/src/docPages/experiments.md index 89d5f090..615e04f6 100644 --- a/docs/src/docPages/experiments.md +++ b/docs/src/docPages/experiments.md @@ -7,6 +7,7 @@ Congratulations! You’ve found our secret playground with a list of experiments * [Comments](/experiments/comments) * [Color](/experiments/color) * [Commands](/experiments/commands) +* [Embeds](/experiments/embeds) ## Waiting for approval -– +* [Placeholder](/experiments/placeholder) diff --git a/docs/src/docPages/experiments/embeds.md b/docs/src/docPages/experiments/embeds.md new file mode 100644 index 00000000..2b3cb3e5 --- /dev/null +++ b/docs/src/docPages/experiments/embeds.md @@ -0,0 +1,5 @@ +# Embeds + +⚠️ Experiment + + diff --git a/docs/src/docPages/experiments/placeholder.md b/docs/src/docPages/experiments/placeholder.md new file mode 100644 index 00000000..fe2e669f --- /dev/null +++ b/docs/src/docPages/experiments/placeholder.md @@ -0,0 +1,5 @@ +# Placeholder + +⚠️ Experiment + + diff --git a/docs/src/docPages/guide/collaborative-editing.md b/docs/src/docPages/guide/collaborative-editing.md index 6323579e..62442302 100644 --- a/docs/src/docPages/guide/collaborative-editing.md +++ b/docs/src/docPages/guide/collaborative-editing.md @@ -1,7 +1,7 @@ # Collaborative editing -:::pro Become a sponsor -Using collaborative editing in production? Do the right thing and [sponsor our work](/sponsor)! +:::pro Professionals +Using the collaborative editing commercially? [Become a sponsor](/sponsor) to fund its development! ::: ## toc @@ -9,7 +9,7 @@ Using collaborative editing in production? Do the right thing and [sponsor our w ## Introduction 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). The following guide explains all things to take into account when you consider to make tiptap collaborative. Don’t worry, a production-grade setup doesn’t require much code. -## Configure collaboration +## Configure the editor The underyling schema tiptap uses is an excellent foundation to sync documents. With the [`Collaboration`](/api/extensions/collaboration) you can tell tiptap to track changes to the document with [Y.js](https://github.com/yjs/yjs). Y.js is a conflict-free replicated data types implementation, or in other words: It’s reaaally good in merging changes. And to achieve that, changes don’t have to come in order. It’s totally fine to change a document while being offline and merge the it with other changes when the device is online again. @@ -209,17 +209,13 @@ All changes will be stored in the browser then, even if you close the tab, go of Yes, it’s magic. As already mentioned, that is all based on the fantastic Y.js framework. And if you’re using it, or our integration, you should definitely [sponsor Kevin Jahns on GitHub](https://github.com/dmonad), he is the brain behind Y.js. -## Store the content +## A plug & play backend Our collaborative editing backend is ready to handle advanced use cases, like authorization, persistence and scaling. Let’s go through a few common use cases here! +### Where is it? :::warning Work in progress -Our plug & play collaboration backend hocuspocus is still work in progress. We’re setting up a dedicated website and documentation, and need to add one or two features before publishing it. - -If you want to give it a try, send us an email to humans@tiptap.dev to receive early access. +Our plug & play collaboration backend hocuspocus is still work in progress. If you want to give it a try, send us an email to [humans@tiptap.dev](mailto:humans@tiptap.dev) to receive early access. ::: - ### The document name The document name is `'example-document'` in all examples here, but it could be any string. In a real-world app you’d probably add the name of your entity and the ID of the entity. Here is how that could look like: diff --git a/docs/src/index.html b/docs/src/index.html index 187c7f34..e175def8 100644 --- a/docs/src/index.html +++ b/docs/src/index.html @@ -7,6 +7,10 @@ ${app} ${scripts} + ${process.env.NODE_ENV === 'production' + ? '' + : '' + } ${process.env.NODE_ENV === 'production' ? '' : '' diff --git a/docs/src/links.yaml b/docs/src/links.yaml index 37fbafee..586a5b41 100644 --- a/docs/src/links.yaml +++ b/docs/src/links.yaml @@ -41,6 +41,9 @@ - title: Tables link: /examples/tables type: draft + - title: Images + link: /examples/images + type: draft - title: Guide items: diff --git a/docs/src/pages/404.vue b/docs/src/pages/404.vue index 5572250e..9784d8f9 100644 --- a/docs/src/pages/404.vue +++ b/docs/src/pages/404.vue @@ -1,17 +1,24 @@