Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
ccdb068
feat(Editor): new component
benjamincanac Nov 3, 2025
9e70cb1
Merge branch 'v4' into feat/editor
benjamincanac Nov 5, 2025
f82f6dc
improve
benjamincanac Nov 6, 2025
1032eea
rename `as` to `kind`
benjamincanac Nov 6, 2025
bb85b2f
handle undo/redo
benjamincanac Nov 6, 2025
17931fa
improve theme
benjamincanac Nov 6, 2025
d0dc989
improve theme
benjamincanac Nov 6, 2025
b24f5c6
improve playground content
benjamincanac Nov 6, 2025
7ca8c29
improve
benjamincanac Nov 7, 2025
04fa8de
improve
benjamincanac Nov 7, 2025
25a83b7
clean
benjamincanac Nov 7, 2025
fc65e79
feat(EditorToolbar): disable dropdown button if all commands are disa…
benjamincanac Nov 7, 2025
d1e1c60
fix: uniformize as props with Primitive
benjamincanac Nov 7, 2025
c2bb310
fix(EditorDragHandle): wrong type
benjamincanac Nov 7, 2025
3fa5d30
test: tmp ts fix
benjamincanac Nov 7, 2025
4a4410f
improve horizontal rule
benjamincanac Nov 7, 2025
fcddd03
fix(EditorDragHandle): center vertically when under 40px
benjamincanac Nov 7, 2025
5e98ac9
test: update vue snapshots
benjamincanac Nov 7, 2025
216506d
move text align extension to playground only
benjamincanac Nov 7, 2025
94d6ef5
fix(EditorToolbar): refactor
benjamincanac Nov 7, 2025
d8c9372
feat(EditorToolbar): handle custom handlers
benjamincanac Nov 7, 2025
1a8092b
playground: remove tasklist
benjamincanac Nov 7, 2025
700e7b1
fix(EditorToolbar): clean
benjamincanac Nov 7, 2025
c89a6b7
Merge branch 'v4' into feat/editor
benjamincanac Nov 8, 2025
adc2614
feat(Editor): add `placeholder` prop
benjamincanac Nov 8, 2025
4bcd6b3
feat(Editor): implement menus
benjamincanac Nov 8, 2025
9445f33
fix: improve menus
benjamincanac Nov 9, 2025
99a926f
test: update snapshots
benjamincanac Nov 9, 2025
509127b
fix: clean code
benjamincanac Nov 9, 2025
09bba95
fix(useEditorMenu): ignore label and separator items
benjamincanac Nov 9, 2025
4ddb3db
fix(useEditorMenu): handle animations
benjamincanac Nov 9, 2025
4296e00
fix(useEditorMenu): lint
benjamincanac Nov 9, 2025
2da3c50
feat(EditorMenu): configure dropcursor
benjamincanac Nov 9, 2025
1a282ed
fix(Editor): clean
benjamincanac Nov 9, 2025
4a4fcc4
playground: fix text align extension
benjamincanac Nov 9, 2025
106a065
chore: add missing files
benjamincanac Nov 9, 2025
dda7164
docs: prepare
benjamincanac Nov 9, 2025
105053e
Merge branch 'v4' into feat/editor
benjamincanac Nov 10, 2025
1ffa4b0
chore(deps): update
benjamincanac Nov 10, 2025
ed65435
fix: consistent options
benjamincanac Nov 11, 2025
bbdd268
fix(useEditorMenu): improve positioning
benjamincanac Nov 11, 2025
0d661a0
fix(Editor): only show placeholder on p and headings
benjamincanac Nov 11, 2025
cf36ab4
Merge branch 'v4' into feat/editor
benjamincanac Nov 11, 2025
3c84530
fix(editor): clean theme
benjamincanac Nov 11, 2025
61bae97
feat(Editor): handle links with prompt for now
benjamincanac Nov 11, 2025
47113ac
feat(Editor): export `Editor` type
benjamincanac Nov 12, 2025
61862da
fix(Editor): improve execute handler
benjamincanac Nov 12, 2025
89667e2
feat(EditorToolbar): improve slots
benjamincanac Nov 12, 2025
5f85f56
playground: add link example
benjamincanac Nov 12, 2025
666b800
feat(FileUpload): add image upload extension
benjamincanac Nov 12, 2025
4b4f590
docs: add images
benjamincanac Nov 12, 2025
a3599c1
playground: fix extension import
benjamincanac Nov 12, 2025
063cd21
fix(EditorDragHandle): improve padding for mobile
benjamincanac Nov 12, 2025
2987c59
playground: fix imports
benjamincanac Nov 12, 2025
164b0ff
chore: update theme
benjamincanac Nov 12, 2025
8db80b3
Merge branch 'v4' into feat/editor
benjamincanac Nov 12, 2025
051e8c0
chore(Editor): add text align extension as baseline
benjamincanac Nov 12, 2025
8b5084a
Merge branch 'v4' into feat/editor
benjamincanac Nov 13, 2025
a4210a2
feat: add `data-slot` attrs
benjamincanac Nov 13, 2025
f816eeb
fix: add missing attr
benjamincanac Nov 13, 2025
025c42e
playground(nuxt.config): add optimize deps
benjamincanac Nov 13, 2025
6fa004b
chore(theme): update placeholder
benjamincanac Nov 13, 2025
cc12ba2
fix(VersionMenu): clean
benjamincanac Nov 14, 2025
30129f7
chore(deps): update tiptap
benjamincanac Nov 14, 2025
ace46b9
playground: update optimizeDeps
benjamincanac Nov 14, 2025
88b5795
chore(theme): update
benjamincanac Nov 14, 2025
7a01956
feat(Editor): major changes with types + mapEditorItems fn
benjamincanac Nov 14, 2025
b877ef5
chore(theme): improve selection
benjamincanac Nov 15, 2025
836b11f
fix(content.config): wrong components category
benjamincanac Nov 15, 2025
25f3724
fix(Editor): prevent content rendering before editor init
benjamincanac Nov 15, 2025
9c57394
fix(EditorDragHandle): remove blur on click
benjamincanac Nov 15, 2025
074e4f4
chore(theme): prevent selected node display for node view
benjamincanac Nov 15, 2025
f50710a
feat(Editor): improve actions
benjamincanac Nov 16, 2025
4a0f866
fix(useEditorMenu): wrong type import
benjamincanac Nov 17, 2025
dd0fd0f
Merge branch 'v4' into feat/editor
benjamincanac Nov 17, 2025
f883211
Merge branch 'v4' into feat/editor
benjamincanac Nov 17, 2025
1e3e684
Merge branch 'v4' into feat/editor
benjamincanac Nov 19, 2025
4243839
up
benjamincanac Nov 19, 2025
5ea503a
chore(deps): update tiptap
benjamincanac Nov 19, 2025
14c970f
feat(Editor): add `suggestion` handler
benjamincanac Nov 19, 2025
fb8b0ba
chore: update theme
benjamincanac Nov 19, 2025
427558c
test: add nextTick
benjamincanac Nov 19, 2025
09df76d
feat(Editor): improve
benjamincanac Nov 19, 2025
2acbf0d
chore(editor): clean utils
benjamincanac Nov 19, 2025
b06c456
feat(EditorDragHandle): expose click method
benjamincanac Nov 20, 2025
5c02973
fix: remove id from mentions
benjamincanac Nov 20, 2025
24763ae
fix: prevent toolbar display
benjamincanac Nov 20, 2025
decda0e
chore(EditorDragHandle): add gap on items
benjamincanac Nov 20, 2025
0c93edf
fix(Editor): improve list handler
benjamincanac Nov 20, 2025
83e504a
chore: improve metas
benjamincanac Nov 20, 2025
7c81e24
chore: dont re-export Editor type
benjamincanac Nov 20, 2025
af197af
fix(Editor): handle model value refresh
benjamincanac Nov 20, 2025
0c5eb66
docs: add missing image
benjamincanac Nov 20, 2025
98820c7
Merge branch 'v4' into feat/editor
benjamincanac Nov 21, 2025
1288d95
fix(Editor): dont register TextAlign extension by default
benjamincanac Nov 21, 2025
da73a91
docs: update `nuxt-component-meta` to override Editor type
benjamincanac Nov 21, 2025
151286d
fix: provide defaults to inject
benjamincanac Nov 21, 2025
e5a772b
docs: update
benjamincanac Nov 21, 2025
cca3424
docs: update
benjamincanac Nov 21, 2025
575ba17
fix(EditorToolbar/useEditorMenu): default options
benjamincanac Nov 21, 2025
00b1d01
feat(Editor): add props to configure extensions
benjamincanac Nov 22, 2025
5941f3f
Merge branch 'v4' into feat/editor
benjamincanac Nov 24, 2025
4a1678b
Merge branch 'v4' into feat/editor
benjamincanac Nov 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/app/assets/icons/tiptap.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/app/components/VersionMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const items = computed(() => {
trailing-icon="i-lucide-chevron-down"
size="xs"
class="-mb-[6px] font-semibold rounded-full truncate"
:class="[open && 'bg-primary/15 ']"
:class="[open && 'bg-primary/15']"
:ui="{
trailingIcon: ['transition-transform duration-200', open ? 'rotate-180' : undefined].filter(Boolean).join(' ')
}"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<script setup lang="ts">
import type { EditorContent, EditorToolbarItem } from '@nuxt/ui'

const content = ref<EditorContent>(`Select some text to see the bubble menu appear with formatting options.

You can make text bold, italic, or apply other formatting options from the contextual menu.`)

const toolbarItems: EditorToolbarItem[][] = [[{
kind: 'mark',
mark: 'bold',
icon: 'i-lucide-bold'
}, {
kind: 'mark',
mark: 'italic',
icon: 'i-lucide-italic'
}, {
kind: 'mark',
mark: 'underline',
icon: 'i-lucide-underline'
}, {
kind: 'mark',
mark: 'strike',
icon: 'i-lucide-strikethrough'
}, {
kind: 'mark',
mark: 'code',
icon: 'i-lucide-code'
}]]
</script>

<template>
<UEditor v-slot="{ editor }" v-model="content" content-type="markdown" placeholder="Start typing...">
<UEditorToolbar
:editor="editor"
:items="toolbarItems"
layout="bubble"
/>
</UEditor>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<script setup lang="ts">
import type { EditorContent, EditorHandlers, EditorToolbarItem } from '@nuxt/ui'
import type { Editor } from '@tiptap/vue-3'
import { ImageUpload } from '~/utils/editor/image-upload'

const content = ref<EditorContent>(`This editor includes a custom ImageUpload extension with custom handlers.

Click the image button to upload a file. The extension uses a custom Vue component to handle the file upload process.`)

const customHandlers: Partial<EditorHandlers> = {
image: {
canExecute: (editor: Editor) => editor.can().insertContent({ type: 'imageUpload' }),
execute: (editor: Editor) => editor.chain().focus().insertImageUpload().run(),
isActive: (editor: Editor) => editor.isActive('imageUpload'),
isDisabled: undefined
}
}

const toolbarItems: EditorToolbarItem[][] = [[{
kind: 'mark',
mark: 'bold',
icon: 'i-lucide-bold'
}, {
kind: 'mark',
mark: 'italic',
icon: 'i-lucide-italic'
}], [{
kind: 'image',
icon: 'i-lucide-image'
}]]
</script>

<template>
<UEditor
v-slot="{ editor }"
v-model="content"
:extensions="[ImageUpload]"
:handlers="customHandlers"
content-type="markdown"
placeholder="Start typing..."
class="min-h-80"
>
<UEditorToolbar :editor="editor" :items="toolbarItems" class="mb-2" />
</UEditor>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<script setup lang="ts">
import type { EditorContent, EditorToolbarItem, EditorEmojiMenuItem } from '@nuxt/ui'
import { Emoji, gitHubEmojis } from '@tiptap/extension-emoji'
import TextAlign from '@tiptap/extension-text-align'

const content = ref<EditorContent>(`This editor includes custom extensions for emoji picker and text alignment.

Type : to add emojis or use the alignment buttons in the toolbar.`)

const toolbarItems: EditorToolbarItem[][] = [[{
kind: 'mark',
mark: 'bold',
icon: 'i-lucide-bold'
}, {
kind: 'mark',
mark: 'italic',
icon: 'i-lucide-italic'
}], [{
kind: 'textAlign',
align: 'left',
icon: 'i-lucide-align-left'
}, {
kind: 'textAlign',
align: 'center',
icon: 'i-lucide-align-center'
}, {
kind: 'textAlign',
align: 'right',
icon: 'i-lucide-align-right'
}]]

const emojiItems: EditorEmojiMenuItem[] = gitHubEmojis.filter(emoji => !emoji.name.startsWith('regional_indicator_')).slice(0, 50)
</script>

<template>
<UEditor
v-slot="{ editor }"
v-model="content"
:extensions="[
Emoji,
TextAlign.configure({ types: ['heading', 'paragraph'] })
]"
content-type="markdown"
placeholder="Start typing..."
>
<UEditorToolbar :editor="editor" :items="toolbarItems" class="mb-2" />
<UEditorEmojiMenu :editor="editor" :items="emojiItems" />
</UEditor>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<script setup lang="ts">
import type { EditorContent, EditorHandlers, EditorToolbarItem } from '@nuxt/ui'
import type { Editor } from '@tiptap/vue-3'

const content = ref<EditorContent>(`This editor has a custom handler for the bold command that also adds a notification.

Try clicking the bold button!`)

const toast = useToast()

const customHandlers: Partial<EditorHandlers> = {
mark: {
canExecute: (editor: Editor, item) => editor.can().toggleMark(item.mark),
execute: (editor: Editor, item) => {
if (item.mark === 'bold') {
toast.add({
title: 'Bold toggled!',
description: 'Custom handler executed'
})
}
return editor.chain().focus().toggleMark(item.mark)
},
isActive: (editor: Editor, item) => editor.isActive(item.mark),
isDisabled: undefined
}
}

const toolbarItems: EditorToolbarItem[][] = [[{
kind: 'mark',
mark: 'bold',
icon: 'i-lucide-bold'
}, {
kind: 'mark',
mark: 'italic',
icon: 'i-lucide-italic'
}]]
</script>

<template>
<UEditor
v-slot="{ editor }"
v-model="content"
:handlers="customHandlers"
content-type="markdown"
placeholder="Start typing..."
>
<UEditorToolbar :editor="editor" :items="toolbarItems" class="mb-2" />
</UEditor>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<script setup lang="ts">
import type { EditorContent, EditorToolbarItem } from '@nuxt/ui'
import EditorLinkPopover from './EditorLinkPopover.vue'

const content = ref<EditorContent>(`Select text and click the link button to add a link with the custom popover.

You can also edit existing links like [this one](https://ui.nuxt.com).`)

const toolbarItems: EditorToolbarItem[][] = [[{
kind: 'mark',
mark: 'bold',
icon: 'i-lucide-bold'
}, {
kind: 'mark',
mark: 'italic',
icon: 'i-lucide-italic'
}, {
slot: 'link'
}]]
</script>

<template>
<UEditor v-slot="{ editor }" v-model="content" content-type="markdown" placeholder="Start typing...">
<UEditorToolbar :editor="editor" :items="toolbarItems" class="mb-2">
<template #link>
<EditorLinkPopover :editor="editor" />
</template>
</UEditorToolbar>
</UEditor>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<script setup lang="ts">
import type { EditorContent } from '@nuxt/ui'

const content = ref<EditorContent>({
type: 'doc',
content: [{
type: 'paragraph',
content: [{ type: 'text', text: 'Click the plus button to add a new paragraph below the current block.' }]
}, {
type: 'paragraph',
content: [{ type: 'text', text: 'The button appears when hovering over blocks.' }]
}]
})
</script>

<template>
<UEditor v-slot="{ editor }" v-model="content" content-type="markdown" placeholder="Start typing...">
<UEditorDragHandle
v-slot="{ ui, onClick }"
:editor="editor"
>
<UButton
icon="i-lucide-plus"
color="primary"
variant="ghost"
size="sm"
:class="ui.handle()"
@click="(e) => {
const node = onClick(e)
if (node) {
editor.chain().focus().insertContentAt(node.pos + 1, { type: 'paragraph' }).run()
}
}"
/>

<UButton
icon="i-lucide-grip-vertical"
color="neutral"
variant="ghost"
size="sm"
:class="ui.handle()"
/>
</UEditorDragHandle>
</UEditor>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<script setup lang="ts">
import type { EditorContent } from '@nuxt/ui'

const content = ref<EditorContent>({
type: 'doc',
content: [{
type: 'heading',
attrs: { level: 2 },
content: [{ type: 'text', text: 'Drag and drop blocks' }]
}, {
type: 'paragraph',
content: [{ type: 'text', text: 'Hover over the left side of any block to see the drag handle.' }]
}, {
type: 'paragraph',
content: [{ type: 'text', text: 'Click to select the block or drag to reorder.' }]
}, {
type: 'paragraph',
content: [{ type: 'text', text: 'Try dragging this paragraph above the previous one.' }]
}]
})
</script>

<template>
<UEditor v-slot="{ editor }" v-model="content" content-type="markdown" placeholder="Start typing...">
<UEditorDragHandle :editor="editor" />
</UEditor>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script setup lang="ts">
import type { EditorContent } from '@nuxt/ui'

const content = ref<EditorContent>({
type: 'doc',
content: [{
type: 'paragraph',
content: [{ type: 'text', text: 'The drag handle has custom color and variant styling.' }]
}, {
type: 'paragraph',
content: [{ type: 'text', text: 'Hover to see the primary colored handle.' }]
}]
})
</script>

<template>
<UEditor v-slot="{ editor }" v-model="content" content-type="markdown" placeholder="Start typing...">
<UEditorDragHandle :editor="editor" color="primary" variant="soft" />
</UEditor>
</template>
Loading
Loading