fix: edit table and migrations

This commit is contained in:
2026-04-17 13:56:26 +02:00
59 changed files with 8918 additions and 88 deletions

View File

@@ -0,0 +1,99 @@
<template>
<div class="rich-editor rounded-lg border border-(--border-light) dark:border-(--border-dark) overflow-hidden focus-within:ring-1 focus-within:ring-sky-500 focus-within:border-sky-500 transition-colors">
<!-- Toolbar -->
<div class="flex items-center gap-0.5 px-2 py-1.5 border-b border-(--border-light) dark:border-(--border-dark) bg-gray-50 dark:bg-neutral-800 flex-wrap">
<button type="button" @click="editor?.chain().focus().toggleBold().run()"
:class="{ 'bg-sky-100 dark:bg-sky-900 text-sky-600 dark:text-sky-300': editor?.isActive('bold') }"
class="toolbar-btn font-bold">B</button>
<button type="button" @click="editor?.chain().focus().toggleItalic().run()"
:class="{ 'bg-sky-100 dark:bg-sky-900 text-sky-600 dark:text-sky-300': editor?.isActive('italic') }"
class="toolbar-btn italic">I</button>
<button type="button" @click="editor?.chain().focus().toggleStrike().run()"
:class="{ 'bg-sky-100 dark:bg-sky-900 text-sky-600 dark:text-sky-300': editor?.isActive('strike') }"
class="toolbar-btn line-through">S</button>
<div class="w-px h-4 bg-gray-300 dark:bg-neutral-600 mx-1" />
<button type="button" @click="editor?.chain().focus().toggleHeading({ level: 2 }).run()"
:class="{ 'bg-sky-100 dark:bg-sky-900 text-sky-600 dark:text-sky-300': editor?.isActive('heading', { level: 2 }) }"
class="toolbar-btn text-xs font-semibold">H2</button>
<button type="button" @click="editor?.chain().focus().toggleHeading({ level: 3 }).run()"
:class="{ 'bg-sky-100 dark:bg-sky-900 text-sky-600 dark:text-sky-300': editor?.isActive('heading', { level: 3 }) }"
class="toolbar-btn text-xs font-semibold">H3</button>
<div class="w-px h-4 bg-gray-300 dark:bg-neutral-600 mx-1" />
<button type="button" @click="editor?.chain().focus().toggleBulletList().run()"
:class="{ 'bg-sky-100 dark:bg-sky-900 text-sky-600 dark:text-sky-300': editor?.isActive('bulletList') }"
class="toolbar-btn">
<UIcon name="i-lucide-list" class="text-sm" />
</button>
<button type="button" @click="editor?.chain().focus().toggleOrderedList().run()"
:class="{ 'bg-sky-100 dark:bg-sky-900 text-sky-600 dark:text-sky-300': editor?.isActive('orderedList') }"
class="toolbar-btn">
<UIcon name="i-lucide-list-ordered" class="text-sm" />
</button>
<div class="w-px h-4 bg-gray-300 dark:bg-neutral-600 mx-1" />
<button type="button" @click="editor?.chain().focus().undo().run()" class="toolbar-btn">
<UIcon name="i-lucide-undo-2" class="text-sm" />
</button>
<button type="button" @click="editor?.chain().focus().redo().run()" class="toolbar-btn">
<UIcon name="i-lucide-redo-2" class="text-sm" />
</button>
</div>
<!-- Editor area -->
<EditorContent :editor="editor"
class="min-h-32 px-3 py-2.5 text-sm text-black dark:text-white bg-white dark:bg-neutral-900 prose prose-sm dark:prose-invert max-w-none focus:outline-none" />
</div>
</template>
<script setup lang="ts">
import { watch, onBeforeUnmount } from 'vue'
import { useEditor, EditorContent } from '@tiptap/vue-3'
import StarterKit from '@tiptap/starter-kit'
import Placeholder from '@tiptap/extension-placeholder'
const props = defineProps<{
modelValue: string
placeholder?: string
}>()
const emit = defineEmits<{
'update:modelValue': [value: string]
}>()
const editor = useEditor({
content: props.modelValue,
extensions: [
StarterKit,
Placeholder.configure({ placeholder: props.placeholder ?? '' }),
],
editorProps: {
attributes: { class: 'focus:outline-none' },
},
onUpdate({ editor }) {
emit('update:modelValue', editor.getHTML())
},
})
// Sync external changes (e.g. store reset)
watch(() => props.modelValue, (val) => {
if (editor.value && editor.value.getHTML() !== val) {
editor.value.commands.setContent(val, false)
}
})
onBeforeUnmount(() => editor.value?.destroy())
</script>
<style>
/* .toolbar-btn {
@apply flex items-center justify-center w-7 h-7 rounded text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-neutral-700 transition-colors text-sm;
} */
/* Tiptap placeholder */
.tiptap p.is-editor-empty:first-child::before {
content: attr(data-placeholder);
float: left;
/* color: theme('colors.gray.400'); */
pointer-events: none;
height: 0;
}
</style>