Closed hanspagel closed 2 weeks ago
![]()
I've made an open-source project called to think, which relies on tiptap to develop a lot of extensions, maybe it can help you.
https://github.com/fantasticit/think/tree/main/packages/client/src/tiptap/core/extensions
Man, you have very interesting tools, but your documentation is in Chinese :').
![]()
I've made an open-source project called to think, which relies on tiptap to develop a lot of extensions, maybe it can help you. https://github.com/fantasticit/think/tree/main/packages/client/src/tiptap/core/extensions
Man, you have very interesting tools, but your documentation is in Chinese :').
google translate may help. ^_^
![]()
I've made an open-source project called to think, which relies on tiptap to develop a lot of extensions, maybe it can help you. https://github.com/fantasticit/think/tree/main/packages/client/src/tiptap/core/extensions
Man, you have very interesting tools, but your documentation is in Chinese :').
google translate may help. ^_^
I think it is not enough hahahahah
Hey folks, I've been meaning to post this there, but I always end up forgetting about it 😅
This is not an extension per se, but we open-source our Typist editor built on top of Tiptap. It includes a few custom/extended extensions with new and improved features, and it also comes with support for Markdown input/output.
Track Changes like Microsoft Office Word. I have implemented this feature, but the code is just in my project. I will publish one day. mark it
@chenyuncai I'm looking for this functionality these days. Please do share:)
code here
Is there a way to limit html size? I read the other thread as well, but I think the custom extensions were for tiptap 1. It's easy to abuse char limit otherwise. What's stoping a user from bolding and italicizing every other char in a 500 long text?
Hello everyone, I created two extensions for tiptap, here they are. Hope that it will help out someone
https://www.npmjs.com/package/@rcode-link/tiptap-drawio https://www.npmjs.com/package/@rcode-link/tiptap-comments
@chenyuncai Your idea looks amazing, can you share? Case with, the link seems to be broken
@chenyuncai Your idea looks amazing, can you share? Case with, the link seems to be broken
yes, take a look here https://github.com/chenyuncai/tiptap-track-change-extension
I've released a package called mui-tiptap https://github.com/sjdemartini/mui-tiptap, which adds built-in styling using Material UI, and includes a suite of additional components and extensions. I've been using this code in a production app successfully for months and have incorporated several things that I think add value beyond vanilla Tiptap. For instance:
ResizableImage
extension for interactively resizing images within the editor with a drag handleTableImproved
extension (which resolves some reported Tiptap Table
extension issues related to column-resizing, when used in conjunction with the mui-tiptap styles)HeadingWithAnchor
extension for dynamic GitHub-like clickable anchor links for every heading that's added (allowing users to share links and jump to specific headings within your rendered editor content)LinkBubbleMenu
component so adding and editing links is easy (with a Slack-like link-editing UI)TableBubbleMenu
for interactively editing rich text tables (add or delete columns or rows, merge cells, etc.)ControlledBubbleMenu
for building your own custom menus, solving some shortcomings of the Tiptap BubbleMenu
Here's a quick demo of some of the UI/functionality (check out the README for a CodeSandbox link and more details):
The package is still fairly new—I plan to add more functionality soon—but I figured folks here may be interested. I welcome feedback and/or contributions!
Hello, I want to share an extension I created for uploading images with a loading placeholder. I have based it on the ProseMirror example at "https://prosemirror.net/examples/upload/". I hope you can add it and find it useful.
I'm a backend developer, and this is my first npm package. I hope everything is alright configured.
https://github.com/carlosvaldesweb/tiptap-extension-upload-image
https://github.com/ueberdosis/tiptap/assets/32969705/ba6d89ec-be9f-4942-809f-866bf7c19951
Hello TipTappers!
We're excited to introduce two new extensions to enhance your Tiptap editing experience: @docs.plus/extension-hyperlink
and @docs.plus/extension-hypermultimedia
.
Inspired by Tiptap's extension-link
, our hyperlink extension adds a touch of Google Docs magic, streamlining hyperlinking with customizable protocols, auto-linking, and interactive dialog boxes for a user-friendly touch.
Enhance Tiptap with our HyperMultimedia extension, facilitating the embedding of Image
, YouTube
, Vimeo
, SoundCloud
, and Twitter
posts directly within the editor. Each media type comes with a snazzy modal—use ours or craft your own!
We value your feedback. Share your thoughts to help us refine these extensions! 💫💬
Hyperlink Demo | HyperMultimedia Demo
Hi, has anyone ever written a hashtag extension (like on Facebook) or a similar extension? I need such an extension but don't know how to customize it
Hi, has anyone ever written a hashtag extension (like on Facebook) or a similar extension? I need such an extension but don't know how to customize it
You can likely extend the mention plugin. The idea is pretty similar right? You trigger with the #
, display a set of suggestions, each hashtag is colored in some way with a class.
Hi, I needed to support div tags in the html editor, and I kind of get a solution. It is not perfect, but at least it allows you to add div to your code using editor.chain().insertContent().
I hope it will help to other people, and if you can help me to improve this extensiion would be great.
DivExtension.ts:
import { getNodeContent } from "./extensionUtils";
export interface DivOptions {
HTMLAttributes: Record<string, any>;
}
export interface DivStyleAttributes {
class?: string;
style?: string;
}
// declare module "@tiptap/core" {
// interface Commands<ReturnType> {
// div: {
// /**
// * Set the div
// */
// setDiv: (options?: DivStyleAttributes) => ReturnType;
// };
// }
// }
export const Div = Node.create<DivOptions>({
name: "div",
group: "block",
atom: true,
draggable: true,
content: "block*",
selectable: true,
isolating: false,
allowGapCursor: true,
defining: true,
addAttributes() {
return {
class: {
default: this.options.HTMLAttributes.class,
},
style: {
default: this.options.HTMLAttributes.style,
},
};
},
parseHTML: () => {
return [
{
tag: "div",
},
];
},
renderHTML({ node, HTMLAttributes }) {
const content = getNodeContent(node);
return ["div", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), ...content];
},
parseDOM: [{ tag: "div" }],
toDOM: () => ["div", 0],
// addCommands: () => {
// return {
// setDiv:
// (options) =>
// ({ tr, dispatch, editor }) => {
// const divNode = editor.schema.nodes.div.createChecked(options, null);
// if (dispatch) {
// const offset = tr.selection.anchor + 1;
// tr.replaceSelectionWith(divNode)
// .scrollIntoView()
// .setSelection(TextSelection.near(tr.doc.resolve(offset)));
// }
// return true;
// },
// };
// },
addOptions: () => {
return {
HTMLAttributes: {},
};
},
});
extensionUtils.ts:
import { DOMOutputSpec, Fragment, Node } from "@tiptap/pm/model";
export const getNodeContent = (node: Node | Fragment) => {
const childNodes: DOMOutputSpec[] = [];
for (let i = 0; i < node.childCount; i++) {
const currentChild = node.child(i);
if (currentChild.type.spec.toDOM) {
const nodeDOMOutputSpec = currentChild.type.spec.toDOM(currentChild);
const htmlTag = (nodeDOMOutputSpec as any)[0] as string;
const content = getNodeContent(currentChild.content);
childNodes.push([htmlTag, currentChild.attrs, ...content]);
} else {
if (currentChild.text) {
childNodes.push(currentChild.text);
}
}
}
return childNodes;
};
I am also trying to support icons in the editor, and it seems easy, but for any reason it is not rendering the svg even when it is inserted in the html. Somebody could help me to guess why?
SvgExtension.ts:
import { mergeAttributes, Node } from "@tiptap/core";
import { getNodeContent } from "./extensionUtils";
export interface SvgOptions {
HTMLAttributes: Record<string, any>;
}
export interface SvgAttributes {
class?: string;
style?: string;
fill?: string;
height?: string;
stroke?: string;
"stroke-width"?: string;
version?: string;
viewBox?: string;
width?: string;
xmlns?: string;
"aria-hidden"?: string;
}
export const Svg = Node.create<SvgOptions>({
name: "svg",
group: "block",
// atom: false,
draggable: true,
content: "path*",
selectable: true,
// isolating: true,
// allowGapCursor: true,
// defining: true,
addAttributes() {
return {
class: {
default: null,
renderHTML: (attributes) => {
return attributes.class
? {
style: attributes.class,
}
: undefined;
},
},
style: {
default: this.options.HTMLAttributes.style,
},
fill: {
default: this.options.HTMLAttributes.fill,
},
height: {
default: this.options.HTMLAttributes.height,
},
stroke: {
default: this.options.HTMLAttributes.stroke,
},
"stroke-width": {
default: this.options.HTMLAttributes["stroke-width"],
},
"aria-hidden": {
default: this.options.HTMLAttributes["aria-hidden"],
},
version: {
default: this.options.HTMLAttributes.version,
},
viewBox: {
default: this.options.HTMLAttributes.viewBox,
},
width: {
default: this.options.HTMLAttributes.width,
},
xmlns: {
default: this.options.HTMLAttributes.xmlns,
},
};
},
parseHTML: () => {
return [
{
tag: "svg",
},
];
},
renderHTML({ node, HTMLAttributes }) {
const content = getNodeContent(node);
return ["svg", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), ...content];
},
parseDOM: [{ tag: "svg" }],
toDOM: () => ["svg", 0],
addOptions: () => {
return {
HTMLAttributes: {},
};
},
});
PathExtension.ts:
import { mergeAttributes, Node } from "@tiptap/core";
export interface PathOptions {
HTMLAttributes: Record<string, any>;
}
export interface PathAttributes {
d?: string;
"stroke-linecap"?: string;
"stroke-linejoin"?: string;
}
export const Path = Node.create<PathOptions>({
name: "path",
group: "path",
draggable: false,
selectable: false,
addAttributes() {
return {
d: {
default: this.options.HTMLAttributes.d,
},
"stroke-linecap": {
default: this.options.HTMLAttributes["stroke-linecap"],
},
"stroke-linejoin": {
default: this.options.HTMLAttributes["stroke-linejoin"],
},
};
},
parseHTML: () => {
return [
{
tag: "path",
},
];
},
renderHTML({ HTMLAttributes }) {
return ["path", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)];
},
parseDOM: [{ tag: "path" }],
toDOM: () => ["path", 0],
addOptions: () => {
return {
HTMLAttributes: {},
};
},
});
I insert this icon in the editor, but for any reason it is not being displayed. If I add this in a plane html file, it works... Can somebody help me? Thank you!
<svg class="h-6 w-6 m-0.5 rounded-sm text-red-700 dark:text-red-700 dark:bg-gray-200" style="color:rgb(185,28,28);height:1.5rem;width:1.5rem;margin:0.125rem;border-radius:0.125rem" fill="currentColor" height="1em" stroke="currentColor" stroke-width="0" version="1.1" viewbox="0 0 16 16" width="1em" xmlns="http://www.w3.org/2000/svg">
<path d="M13.156 9.211c-0.213-0.21-0.686-0.321-1.406-0.331-0.487-0.005-1.073 0.038-1.69 0.124-0.276-0.159-0.561-0.333-0.784-0.542-0.601-0.561-1.103-1.34-1.415-2.197 0.020-0.080 0.038-0.15 0.054-0.222 0 0 0.339-1.923 0.249-2.573-0.012-0.089-0.020-0.115-0.044-0.184l-0.029-0.076c-0.092-0.212-0.273-0.437-0.556-0.425l-0.171-0.005c-0.316 0-0.573 0.161-0.64 0.403-0.205 0.757 0.007 1.889 0.39 3.355l-0.098 0.239c-0.275 0.67-0.619 1.345-0.923 1.94l-0.040 0.077c-0.32 0.626-0.61 1.157-0.873 1.607l-0.271 0.144c-0.020 0.010-0.485 0.257-0.594 0.323-0.926 0.553-1.539 1.18-1.641 1.678-0.032 0.159-0.008 0.362 0.156 0.456l0.263 0.132c0.114 0.057 0.234 0.086 0.357 0.086 0.659 0 1.425-0.821 2.48-2.662 1.218-0.396 2.604-0.726 3.819-0.908 0.926 0.521 2.065 0.883 2.783 0.883 0.128 0 0.238-0.012 0.327-0.036 0.138-0.037 0.254-0.115 0.325-0.222 0.139-0.21 0.168-0.499 0.13-0.795-0.011-0.088-0.081-0.196-0.157-0.271zM3.307 12.72c0.12-0.329 0.596-0.979 1.3-1.556 0.044-0.036 0.153-0.138 0.253-0.233-0.736 1.174-1.229 1.642-1.553 1.788zM7.476 3.12c0.212 0 0.333 0.534 0.343 1.035s-0.107 0.853-0.252 1.113c-0.12-0.385-0.179-0.992-0.179-1.389 0 0-0.009-0.759 0.088-0.759v0zM6.232 9.961c0.148-0.264 0.301-0.543 0.458-0.839 0.383-0.724 0.624-1.29 0.804-1.755 0.358 0.651 0.804 1.205 1.328 1.649 0.065 0.055 0.135 0.111 0.207 0.166-1.066 0.211-1.987 0.467-2.798 0.779v0zM12.952 9.901c-0.065 0.041-0.251 0.064-0.37 0.064-0.386 0-0.864-0.176-1.533-0.464 0.257-0.019 0.493-0.029 0.705-0.029 0.387 0 0.502-0.002 0.88 0.095s0.383 0.293 0.318 0.333v0z"></path><path d="M14.341 3.579c-0.347-0.473-0.831-1.027-1.362-1.558s-1.085-1.015-1.558-1.362c-0.806-0.591-1.197-0.659-1.421-0.659h-7.75c-0.689 0-1.25 0.561-1.25 1.25v13.5c0 0.689 0.561 1.25 1.25 1.25h11.5c0.689 0 1.25-0.561 1.25-1.25v-9.75c0-0.224-0.068-0.615-0.659-1.421v0zM12.271 2.729c0.48 0.48 0.856 0.912 1.134 1.271h-2.406v-2.405c0.359 0.278 0.792 0.654 1.271 1.134v0zM14 14.75c0 0.136-0.114 0.25-0.25 0.25h-11.5c-0.135 0-0.25-0.114-0.25-0.25v-13.5c0-0.135 0.115-0.25 0.25-0.25 0 0 7.749-0 7.75 0v3.5c0 0.276 0.224 0.5 0.5 0.5h3.5v9.75z">
</path>
</svg>
Even though I am still quite new to tiptap/prosemirror, I have managed to develope a working drag handle based on the drag handle from https://github.com/steven-tey/novel. However, unlike with the drag handle in novel, it is possible to drag single list items or whole lists through the editor as expected. Furthermore, it is also possible to select several nodes of different types and drag them as well. I look forward to your feedback and any suggestions for improvement. I have uploaded the drag handle as an extension to npm and of course opensourced it on github.
I threw together a little extension to add Shiki syntax highlighting to Tiptap. I only did it for myself, but thought it might be useful for someone else.
https://github.com/timomeh/tiptap-extension-code-block-shiki
Hey everyone,
I am using TipTap as a document service for sending bulk emails to clients using prefilled variables. I need to import the documents as HTML and maintain the original styling.
From what I have read it is considered a feature of TipTap that the styles are stripped away and I need to make an extension. I am really struggling with this.
Has anyone had any luck?
Hey everyone! I have been trying forever to get a space between functionality in my Tiptap editor. Basically, I would love to be able to have text aligned left and right on the same line - I want to be able to add dates to my header text and have the dates aligned to the right of the page. My current custom extension achieves this, but the text becomes uneditable after I use it:
import { Node, mergeAttributes } from '@tiptap/core';
export const LeftRightJustifyExtension = Node.create({
name: 'leftRightJustify',
group: 'block',
content: 'inline*',
parseHTML() {
return [
{
tag: 'div[left-right-justify]',
},
];
},
renderHTML({ node }) {
// Extract text content from the node's children
const text = node.textContent;
// Find the middle point or some split logic to divide the text
const middleIndex = text.indexOf(' | '); // Assuming the split point is ' | '
const leftText = text.slice(0, middleIndex);
const rightText = text.slice(middleIndex + 3); // Skipping ' | '
return ['div', mergeAttributes(node.attrs, { 'left-right-justify': 'true' }),
['p', { style: 'display: flex' }, leftText],
['p', { style: 'display: flex;' }, rightText],
];
},
addCommands() {
return {
setLeftRightJustify: () => ({ commands }) => {
return commands.setNode(this.name);
},
splitLeftRight: () => ({ state, dispatch }) => {
const { selection, schema } = state;
const { from, to } = selection;
if (from !== to) {
return false; // Do nothing if there's a text selection
}
const node = selection.$from.node();
const pos = selection.$from.parentOffset;
const textContent = node.textContent;
const leftText = textContent.slice(0, pos);
const rightText = textContent.slice(pos);
const newNode = schema.nodes.leftRightJustify.create({}, [
schema.text(leftText),
schema.text(' | '),
schema.text(rightText),
]);
const tr = state.tr.replaceWith(from - leftText.length, to + rightText.length, newNode);
dispatch(tr);
return true;
},
};
},
});
I'm closing this issue for now as this is super legacy.
You can submit your community extensions here: https://github.com/ueberdosis/tiptap/discussions/categories/community-extensions
I'd ask everyone else who had questions in this thread to move it over to Discord or Github Discussions as we can actually mark things as "answered" there :)
Hi everyone!
I’ve seen a ton of helpful Gists with custom extensions for Tiptap. It’s amazing what you all come up with! 🥰
Unfortunately, we don’t have the capabilities to maintain all of them in the core packages, but it’s sad to see those gems hidden in some Gists. What if you - the community - could easily provide those as separate npm packages?
Advantages of packages
Image
node including the Upload to S3 mechanic)Proof of Concept
I built a tiny proof of concept for superscript and subscript, see here: https://github.com/hanspagel/tiptap-extension-superscript https://github.com/hanspagel/tiptap-extension-subscript
Usage:
Examples of community Gists, code snippets, PRs and ideas
Tiptap v2
Tiptap v1
Not needed with Tiptap v2
Roadmap
I think we’d need to do a few things to make that easier for everyone:
Your feedback
What do you all think? Would you be up to contribute a community extension?
Feel free to post links to Gists of others you’d love to see published as a package.