From ca2020b9c684e5b10a9ebbfe67554fb01b90b7dc Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 26 Jan 2026 21:11:16 +0100 Subject: [PATCH] Added more text functions --- frontend/components/MainNav.vue | 5 + frontend/components/wiki/TreeItem.vue | 84 +++----- frontend/components/wiki/WikiEditor.vue | 248 +++++++++++------------- frontend/composables/useWikiTree.ts | 42 ++-- frontend/nuxt.config.ts | 6 +- frontend/package.json | 5 + frontend/pages/wiki/[[id]].vue | 231 ++++++++++++++-------- 7 files changed, 335 insertions(+), 286 deletions(-) diff --git a/frontend/components/MainNav.vue b/frontend/components/MainNav.vue index 1556c7b..4ccffdf 100644 --- a/frontend/components/MainNav.vue +++ b/frontend/components/MainNav.vue @@ -50,6 +50,11 @@ const links = computed(() => { to: "/standardEntity/tasks", icon: "i-heroicons-rectangle-stack" }] : [], + ...true ? [{ + label: "Wiki", + to: "/wiki", + icon: "i-heroicons-book-open" + }] : [], ] }, { diff --git a/frontend/components/wiki/TreeItem.vue b/frontend/components/wiki/TreeItem.vue index 110d742..a47e453 100644 --- a/frontend/components/wiki/TreeItem.vue +++ b/frontend/components/wiki/TreeItem.vue @@ -1,79 +1,74 @@ \ No newline at end of file diff --git a/frontend/components/wiki/WikiEditor.vue b/frontend/components/wiki/WikiEditor.vue index edbfd4c..2f2f342 100644 --- a/frontend/components/wiki/WikiEditor.vue +++ b/frontend/components/wiki/WikiEditor.vue @@ -11,25 +11,29 @@
- - + + +
- - + +
- + +
- - - - - - + + + + + +
@@ -38,34 +42,41 @@ v-if="editor" :editor="editor" :tippy-options="{ duration: 200, placement: 'top-start', animation: 'scale' }" - class="bg-white border border-gray-200 text-gray-700 rounded-lg shadow-xl flex items-center p-1 gap-1 z-50 overflow-hidden" + class="bg-white border border-gray-200 text-gray-700 rounded-lg shadow-xl flex items-center p-1 gap-1 z-50" > -
- - - - -
- + + + +
- -
- - -
- +
- -
- - -
+ + + + + + + + + + + +
+ {{ editor.storage.characterCount.words() }} Wörter • {{ editor.storage.characterCount.characters() }} Zeichen +
+ @@ -75,20 +86,27 @@ import { watch } from 'vue' // Tiptap Core import { useEditor, EditorContent } from '@tiptap/vue-3' // @ts-ignore -import { BubbleMenu } from '@tiptap/vue-3/menus' // Fix für Vite Import +import { BubbleMenu, FloatingMenu } from '@tiptap/vue-3/menus' import StarterKit from '@tiptap/starter-kit' import Placeholder from '@tiptap/extension-placeholder' // Extensions import BubbleMenuExtension from '@tiptap/extension-bubble-menu' +import FloatingMenuExtension from '@tiptap/extension-floating-menu' import Link from '@tiptap/extension-link' import TaskList from '@tiptap/extension-task-list' import TaskItem from '@tiptap/extension-task-item' import Image from '@tiptap/extension-image' import Highlight from '@tiptap/extension-highlight' +import Youtube from '@tiptap/extension-youtube' +import Typography from '@tiptap/extension-typography' +import CharacterCount from '@tiptap/extension-character-count' -// Table Imports (Named Imports) +// --- FIX: Standard CodeBlock statt Lowlight nutzen (Vermeidet Vite Fehler) --- +import CodeBlock from '@tiptap/extension-code-block' + +// Table Imports import { Table } from '@tiptap/extension-table' import { TableRow } from '@tiptap/extension-table-row' import { TableCell } from '@tiptap/extension-table-cell' @@ -100,24 +118,31 @@ const emit = defineEmits(['update:modelValue']) const editor = useEditor({ content: props.modelValue, extensions: [ - StarterKit, - Placeholder.configure({ placeholder: 'Tippe "/" für Befehle oder schreibe los...' }), - - // Bubble Menu Logik: Zeigt sich nur bei Text-Auswahl - BubbleMenuExtension.configure({ - shouldShow: ({ editor, from, to }) => { - // Nur anzeigen, wenn Text markiert ist und Dokument nicht leer - return !editor.state.selection.empty && (to - from > 0) - }, + StarterKit.configure({ + codeBlock: false, // Standard StarterKit Block aus, wir laden unseren eigenen }), + Placeholder.configure({ placeholder: 'Tippe "/" oder schreibe los...' }), + // UI + BubbleMenuExtension.configure({ + shouldShow: ({ editor, from, to }) => !editor.state.selection.empty && (to - from > 0), + }), + FloatingMenuExtension, + + // Basics Link.configure({ openOnClick: false, autolink: true }), - Image, // Für Copy-Paste Bilder aktiv lassen - Highlight, // Gelber Marker + Image, + Highlight, + Typography, + CharacterCount, - // Tasks + // Struktur TaskList, TaskItem.configure({ nested: true }), + Youtube.configure({ width: 640, height: 480 }), + + // Code (Standard Block ohne Bunte Farben, dafür stabil) + CodeBlock, // Tables Table.configure({ resizable: true }), @@ -141,6 +166,8 @@ watch(() => props.modelValue, (val) => { } }) +// --- ACTIONS --- + const setLink = () => { if (!editor.value) return const previousUrl = editor.value.getAttributes('link').href @@ -152,10 +179,17 @@ const setLink = () => { } editor.value.chain().focus().extendMarkRange('link').setLink({ href: url }).run() } + +const addVideo = () => { + const url = prompt('YouTube URL eingeben:') + if (url && editor.value) { + editor.value.commands.setYoutubeVideo({ src: url }) + } +} \ No newline at end of file diff --git a/frontend/composables/useWikiTree.ts b/frontend/composables/useWikiTree.ts index 203e29d..23f3d19 100644 --- a/frontend/composables/useWikiTree.ts +++ b/frontend/composables/useWikiTree.ts @@ -1,4 +1,3 @@ -// composables/useWikiTree.ts export interface WikiPageItem { id: string parentId: string | null @@ -12,12 +11,12 @@ export interface WikiPageItem { export const useWikiTree = () => { const { $api } = useNuxtApp() - // State + // STATE const items = useState('wiki-items', () => []) const isLoading = useState('wiki-loading', () => false) const isSidebarOpen = useState('wiki-sidebar-open', () => true) - // --- Computed: Tree Logic --- + // COMPUTED TREE const tree = computed(() => { const rawItems = items.value || [] if (!rawItems.length) return [] @@ -25,12 +24,12 @@ export const useWikiTree = () => { const roots: WikiPageItem[] = [] const lookup: Record = {} - // Clone & Init + // Init Lookup rawItems.forEach(item => { lookup[item.id] = { ...item, children: [] } }) - // Build Tree + // Build Hierarchy rawItems.forEach(item => { const node = lookup[item.id] if (item.parentId && lookup[item.parentId]) { @@ -40,7 +39,7 @@ export const useWikiTree = () => { } }) - // Sort: Folders first, then Alphabet + // Sort: Folders first, then Alphabetical const sortNodes = (nodes: WikiPageItem[]) => { nodes.sort((a, b) => { if (a.isFolder !== b.isFolder) return a.isFolder ? -1 : 1 @@ -55,47 +54,52 @@ export const useWikiTree = () => { return roots }) - // --- Actions --- + // ACTIONS const loadTree = async () => { isLoading.value = true try { const data = await $api('/api/wiki/tree', { method: 'GET' }) items.value = data - } catch (e) { console.error(e) } - finally { isLoading.value = false } + } catch (e) { + console.error('Wiki Tree Load Error', e) + } finally { + isLoading.value = false + } } - // Zentralisierte Erstellen-Funktion + // Rein API-basierte Funktion ohne UI-Logik const createItem = async (title: string, parentId: string | null, isFolder: boolean) => { try { const newItem = await $api('/api/wiki', { method: 'POST', body: { title, parentId, isFolder } }) - await loadTree() // Neu laden + await loadTree() return newItem } catch (e) { - alert('Fehler beim Erstellen') - return null + throw e // Fehler weiterwerfen, damit UI reagieren kann } } - // Löschen Funktion + // Rein API-basierte Funktion ohne UI-Logik const deleteItem = async (id: string) => { - if (!confirm('Wirklich löschen? Alle Unterseiten werden ebenfalls gelöscht.')) return try { await $api(`/api/wiki/${id}`, { method: 'DELETE' }) await loadTree() return true } catch (e) { - alert('Fehler beim Löschen') - return false + throw e } } return { - tree, items, isLoading, isSidebarOpen, - loadTree, createItem, deleteItem + tree, + items, + isLoading, + isSidebarOpen, + loadTree, + createItem, + deleteItem } } \ No newline at end of file diff --git a/frontend/nuxt.config.ts b/frontend/nuxt.config.ts index 9bc778d..a0cb84d 100644 --- a/frontend/nuxt.config.ts +++ b/frontend/nuxt.config.ts @@ -24,7 +24,8 @@ export default defineNuxtConfig({ }], build: { - transpile: ['@vuepic/vue-datepicker','@tiptap/vue-3'] + transpile: ['@vuepic/vue-datepicker','@tiptap/vue-3','@tiptap/extension-code-block-lowlight', + 'lowlight',] }, @@ -41,7 +42,8 @@ export default defineNuxtConfig({ vite: { optimizeDeps: { - include: ["@editorjs/editorjs", "dayjs",'@tiptap/vue-3'], + include: ["@editorjs/editorjs", "dayjs",'@tiptap/vue-3','@tiptap/extension-code-block-lowlight', + 'lowlight',], }, }, diff --git a/frontend/package.json b/frontend/package.json index 80303ea..169ecd9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -47,6 +47,9 @@ "@sentry/browser": "^9.11.0", "@sentry/integrations": "^7.114.0", "@tiptap/extension-bubble-menu": "^3.17.1", + "@tiptap/extension-character-count": "^3.17.1", + "@tiptap/extension-code-block": "^3.17.1", + "@tiptap/extension-floating-menu": "^3.17.1", "@tiptap/extension-highlight": "^3.17.1", "@tiptap/extension-image": "^3.17.1", "@tiptap/extension-link": "^3.17.1", @@ -57,6 +60,8 @@ "@tiptap/extension-table-row": "^3.17.1", "@tiptap/extension-task-item": "^3.17.1", "@tiptap/extension-task-list": "^3.17.1", + "@tiptap/extension-typography": "^3.17.1", + "@tiptap/extension-youtube": "^3.17.1", "@tiptap/pm": "^3.17.1", "@tiptap/starter-kit": "^3.17.1", "@tiptap/vue-3": "^3.17.1", diff --git a/frontend/pages/wiki/[[id]].vue b/frontend/pages/wiki/[[id]].vue index d3950a7..4493db2 100644 --- a/frontend/pages/wiki/[[id]].vue +++ b/frontend/pages/wiki/[[id]].vue @@ -1,86 +1,60 @@