icon und menu update

This commit is contained in:
beo3000 2026-03-02 20:59:57 +01:00
parent 3a4fc0e8e0
commit cf220dd3a0
14 changed files with 13225 additions and 89 deletions

View File

@ -1 +1 @@
1.2.14 1.2.17

View File

@ -25,6 +25,7 @@
"dexie": "^4.0.11", "dexie": "^4.0.11",
"dompurify": "^3.3.1", "dompurify": "^3.3.1",
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
"lucide-svelte": "^0.576.0",
"marked": "^17.0.3", "marked": "^17.0.3",
"tiptap-markdown": "^0.9.0" "tiptap-markdown": "^0.9.0"
}, },

View File

@ -2,6 +2,8 @@
import { page } from "$app/state"; import { page } from "$app/state";
import { goto } from "$app/navigation"; import { goto } from "$app/navigation";
import { commandBarOpen } from "$lib/stores/commandBar"; import { commandBarOpen } from "$lib/stores/commandBar";
import { LayoutList, CalendarRange, FolderOpen, BookOpen, Search, Menu } from "lucide-svelte";
import type { Component } from "svelte";
interface Props { interface Props {
onsidebaropen: () => void; onsidebaropen: () => void;
@ -13,7 +15,8 @@
interface Tab { interface Tab {
id: string; id: string;
label: string; label: string;
icon: string; icon: Component;
color: string;
action: () => void; action: () => void;
active: boolean; active: boolean;
} }
@ -22,42 +25,48 @@
{ {
id: "daily", id: "daily",
label: "Daily Log", label: "Daily Log",
icon: "📋", icon: LayoutList,
color: "text-slate-400",
action: () => goto("/context/daily-log"), action: () => goto("/context/daily-log"),
active: currentPath === "/context/daily-log" || currentPath === "/", active: currentPath === "/context/daily-log" || currentPath === "/",
}, },
{ {
id: "meetings", id: "meetings",
label: "Meetings", label: "Meetings",
icon: "👥", icon: CalendarRange,
color: "text-violet-400",
action: () => goto("/meetings"), action: () => goto("/meetings"),
active: currentPath === "/meetings", active: currentPath === "/meetings",
}, },
{ {
id: "projects", id: "projects",
label: "Projekte", label: "Projekte",
icon: "📁", icon: FolderOpen,
color: "text-amber-400",
action: () => goto("/projects"), action: () => goto("/projects"),
active: currentPath === "/projects", active: currentPath === "/projects",
}, },
{ {
id: "wiki", id: "wiki",
label: "Wiki", label: "Wiki",
icon: "📖", icon: BookOpen,
color: "text-orange-400",
action: () => goto("/wiki"), action: () => goto("/wiki"),
active: currentPath.startsWith("/wiki"), active: currentPath.startsWith("/wiki"),
}, },
{ {
id: "search", id: "search",
label: "Suche", label: "Suche",
icon: "🔍", icon: Search,
color: "text-muted",
action: () => ($commandBarOpen = true), action: () => ($commandBarOpen = true),
active: false, active: false,
}, },
{ {
id: "more", id: "more",
label: "Mehr", label: "Mehr",
icon: "☰", icon: Menu,
color: "text-muted",
action: () => onsidebaropen(), action: () => onsidebaropen(),
active: false, active: false,
}, },
@ -69,12 +78,13 @@
style="padding-bottom: env(safe-area-inset-bottom, 0px);" style="padding-bottom: env(safe-area-inset-bottom, 0px);"
> >
{#each tabs as tab} {#each tabs as tab}
{@const Icon = tab.icon}
<button <button
class="flex flex-1 flex-col items-center gap-0.5 py-2.5 text-xs transition-colors class="flex flex-1 flex-col items-center justify-center gap-1 py-2.5 min-h-[52px] text-xs transition-colors
{tab.active ? 'text-accent' : 'text-muted'}" {tab.active ? tab.color + ' font-medium' : 'text-muted'}"
onclick={tab.action} onclick={tab.action}
> >
<span class="text-lg leading-none">{tab.icon}</span> <Icon size={22} />
<span>{tab.label}</span> <span>{tab.label}</span>
</button> </button>
{/each} {/each}

View File

@ -8,6 +8,7 @@
import { updateEvent, updateEventNotes, softDeleteContext } from '$lib/db/repositories'; import { updateEvent, updateEventNotes, softDeleteContext } from '$lib/db/repositories';
import { notesTopicId } from '$lib/db/repositories'; import { notesTopicId } from '$lib/db/repositories';
import { mention } from '$lib/actions/mention'; import { mention } from '$lib/actions/mention';
import { Pencil, Trash2 } from 'lucide-svelte';
import { handlePersonClick } from '$lib/actions/refClick'; import { handlePersonClick } from '$lib/actions/refClick';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
@ -179,8 +180,8 @@
</div> </div>
</div> </div>
<div class="flex gap-1 opacity-0 transition-opacity group-hover:opacity-100"> <div class="flex gap-1 opacity-0 transition-opacity group-hover:opacity-100">
<button class="rounded p-1 text-muted hover:text-white" onclick={(e) => { e.stopPropagation(); startEditMeta(); }} title="Bearbeiten">✏️</button> <button class="rounded p-1 text-muted hover:text-white" onclick={(e) => { e.stopPropagation(); startEditMeta(); }} title="Bearbeiten"><Pencil size={14} /></button>
<button class="rounded p-1 text-muted hover:text-red-400" onclick={(e) => { e.stopPropagation(); handleDelete(); }} title="Löschen">🗑️</button> <button class="rounded p-1 text-muted hover:text-red-400" onclick={(e) => { e.stopPropagation(); handleDelete(); }} title="Löschen"><Trash2 size={14} /></button>
</div> </div>
</div> </div>
{/if} {/if}

View File

@ -11,6 +11,11 @@
import { deletedItemCount } from '$lib/stores/agenda'; import { deletedItemCount } from '$lib/stores/agenda';
import { favoriteNotebooks } from '$lib/stores/wiki'; import { favoriteNotebooks } from '$lib/stores/wiki';
import { scopeColor, scopeIcon } from '$lib/stores/scopeContext'; import { scopeColor, scopeIcon } from '$lib/stores/scopeContext';
import {
CalendarDays, CalendarRange, Users, FolderOpen, Building2, User, BookOpen,
LayoutList, Archive, Trash2, Settings, HardDrive, LogOut,
ChevronDown, Home, RefreshCw
} from 'lucide-svelte';
const isDev = import.meta.env.DEV; const isDev = import.meta.env.DEV;
const trashCount$ = deletedItemCount(); const trashCount$ = deletedItemCount();
@ -55,14 +60,14 @@
// Active nav item style: subtle left border indicator // Active nav item style: subtle left border indicator
function navItem(active: boolean) { function navItem(active: boolean) {
return active return active
? 'mb-1 w-full rounded-r px-2.5 py-2 text-left text-sm border-l-2 border-accent bg-white/8 font-semibold text-white' ? 'mb-1 w-full rounded-r px-3 py-2.5 text-left text-sm border-l-2 border-accent bg-white/10 font-semibold text-white'
: 'mb-1 w-full rounded px-2.5 py-2 text-left text-sm border-l-2 border-transparent text-[#aaa] hover:bg-white/5 hover:text-white transition-colors'; : 'mb-1 w-full rounded px-3 py-2.5 text-left text-sm border-l-2 border-transparent text-[#aaa] hover:bg-white/5 hover:text-white transition-colors';
} }
// Section header: ▼ toggle (small) + label (navigates to overview) // Section header: ▼ toggle (small) + label (navigates to overview)
const sectionHeader = 'flex items-center gap-2 mt-6 mb-1 pl-1 pr-1'; const sectionHeader = 'flex items-center gap-2 mt-6 mb-1 pl-1 pr-1';
const sectionToggle = 'text-[10px] text-muted hover:text-white transition-colors flex-shrink-0 px-1 py-1'; const sectionToggle = 'text-xs text-muted hover:text-white transition-colors flex-shrink-0 px-2 py-1.5';
const sectionLabel = 'text-xs uppercase text-muted hover:text-white flex items-center gap-1.5 transition-colors flex-1 text-left font-medium tracking-wide'; const sectionLabel = 'text-sm text-muted hover:text-white flex items-center gap-1.5 transition-colors flex-1 text-left font-semibold';
</script> </script>
{#if !hideLogo} {#if !hideLogo}
@ -74,21 +79,25 @@
{/if} {/if}
<!-- General --> <!-- General -->
<div class="mb-1 mt-6 pl-1 text-xs uppercase text-muted flex items-center gap-1.5 font-medium tracking-wide"><span>🏠</span> General</div> <div class="mb-1 mt-6 pl-1 text-sm text-muted flex items-center gap-2 font-semibold">
<Home size={15} class="flex-shrink-0 text-slate-400" /> General
</div>
<ul class="m-0 list-none p-0"> <ul class="m-0 list-none p-0">
{#if dailyLog} {#if dailyLog}
<li> <li>
<button class={navItem(currentId === 'daily-log')} onclick={() => navigate('daily-log')}> <button class={navItem(currentId === 'daily-log') + ' flex items-center gap-2'} onclick={() => navigate('daily-log')}>
{dailyLog.name} <LayoutList size={15} class="flex-shrink-0 text-slate-400" />
<span class="truncate">{dailyLog.name}</span>
</button> </button>
</li> </li>
{/if} {/if}
<li> <li>
<button <button
class={navItem(page.url.pathname.startsWith('/journal/archive'))} class={navItem(page.url.pathname.startsWith('/journal/archive')) + ' flex items-center gap-2'}
onclick={() => { goto('/journal/archive'); onnavigate?.(); }} onclick={() => { goto('/journal/archive'); onnavigate?.(); }}
> >
Archiv <Archive size={15} class="flex-shrink-0 text-slate-400" />
<span class="truncate">Archiv</span>
</button> </button>
</li> </li>
</ul> </ul>
@ -96,18 +105,19 @@
<!-- Jour Fixes --> <!-- Jour Fixes -->
<div class={sectionHeader}> <div class={sectionHeader}>
<button class={sectionToggle} onclick={() => toggleSection('meetings')} title="Auf-/Zuklappen"> <button class={sectionToggle} onclick={() => toggleSection('meetings')} title="Auf-/Zuklappen">
<span class="inline-block transition-transform {collapsed['meetings'] ? '-rotate-90' : ''}">{@html '&#9660;'}</span> <ChevronDown size={14} class="transition-transform {collapsed['meetings'] ? '-rotate-90' : ''}" />
</button> </button>
<button class={sectionLabel} onclick={() => { goto('/meetings'); onnavigate?.(); }}> <button class={sectionLabel} onclick={() => { goto('/meetings'); onnavigate?.(); }}>
<span>💬</span> Jour Fixes <CalendarRange size={15} class="flex-shrink-0 text-violet-400" /> Jour Fixes
</button> </button>
</div> </div>
{#if !collapsed['meetings']} {#if !collapsed['meetings']}
<ul class="m-0 list-none p-0"> <ul class="m-0 list-none p-0">
{#each meetings as ctx} {#each meetings as ctx}
<li> <li>
<button class={navItem(currentId === ctx.id)} onclick={() => navigate(ctx.id)}> <button class={navItem(currentId === ctx.id) + ' flex items-center gap-2'} onclick={() => navigate(ctx.id)}>
{ctx.name} <CalendarDays size={14} class="flex-shrink-0 text-violet-400/60" />
<span class="truncate">{ctx.name}</span>
</button> </button>
</li> </li>
{/each} {/each}
@ -117,18 +127,19 @@
<!-- Projects --> <!-- Projects -->
<div class={sectionHeader}> <div class={sectionHeader}>
<button class={sectionToggle} onclick={() => toggleSection('projects')} title="Auf-/Zuklappen"> <button class={sectionToggle} onclick={() => toggleSection('projects')} title="Auf-/Zuklappen">
<span class="inline-block transition-transform {collapsed['projects'] ? '-rotate-90' : ''}">{@html '&#9660;'}</span> <ChevronDown size={14} class="transition-transform {collapsed['projects'] ? '-rotate-90' : ''}" />
</button> </button>
<button class={sectionLabel} onclick={() => { goto('/projects'); onnavigate?.(); }}> <button class={sectionLabel} onclick={() => { goto('/projects'); onnavigate?.(); }}>
<span>📁</span> Projects <FolderOpen size={15} class="flex-shrink-0 text-amber-400" /> Projects
</button> </button>
</div> </div>
{#if !collapsed['projects']} {#if !collapsed['projects']}
<ul class="m-0 list-none p-0"> <ul class="m-0 list-none p-0">
{#each projects as ctx} {#each projects as ctx}
<li> <li>
<button class={navItem(currentId === ctx.id)} onclick={() => navigate(ctx.id)}> <button class={navItem(currentId === ctx.id) + ' flex items-center gap-2'} onclick={() => navigate(ctx.id)}>
{displayName(ctx)} <FolderOpen size={14} class="flex-shrink-0 text-amber-400/60" />
<span class="truncate">{displayName(ctx)}</span>
</button> </button>
</li> </li>
{/each} {/each}
@ -138,18 +149,19 @@
<!-- Companies --> <!-- Companies -->
<div class={sectionHeader}> <div class={sectionHeader}>
<button class={sectionToggle} onclick={() => toggleSection('companies')} title="Auf-/Zuklappen"> <button class={sectionToggle} onclick={() => toggleSection('companies')} title="Auf-/Zuklappen">
<span class="inline-block transition-transform {collapsed['companies'] ? '-rotate-90' : ''}">{@html '&#9660;'}</span> <ChevronDown size={14} class="transition-transform {collapsed['companies'] ? '-rotate-90' : ''}" />
</button> </button>
<button class={sectionLabel} onclick={() => { goto('/companies'); onnavigate?.(); }}> <button class={sectionLabel} onclick={() => { goto('/companies'); onnavigate?.(); }}>
<span>🏢</span> Firmen <Building2 size={15} class="flex-shrink-0 text-sky-400" /> Firmen
</button> </button>
</div> </div>
{#if !collapsed['companies']} {#if !collapsed['companies']}
<ul class="m-0 list-none p-0"> <ul class="m-0 list-none p-0">
{#each companies as ctx} {#each companies as ctx}
<li> <li>
<button class={navItem(currentId === ctx.id)} onclick={() => navigate(ctx.id)}> <button class={navItem(currentId === ctx.id) + ' flex items-center gap-2'} onclick={() => navigate(ctx.id)}>
{displayName(ctx)} <Building2 size={14} class="flex-shrink-0 text-sky-400/60" />
<span class="truncate">{displayName(ctx)}</span>
</button> </button>
</li> </li>
{/each} {/each}
@ -159,18 +171,19 @@
<!-- People --> <!-- People -->
<div class={sectionHeader}> <div class={sectionHeader}>
<button class={sectionToggle} onclick={() => toggleSection('people')} title="Auf-/Zuklappen"> <button class={sectionToggle} onclick={() => toggleSection('people')} title="Auf-/Zuklappen">
<span class="inline-block transition-transform {collapsed['people'] ? '-rotate-90' : ''}">{@html '&#9660;'}</span> <ChevronDown size={14} class="transition-transform {collapsed['people'] ? '-rotate-90' : ''}" />
</button> </button>
<button class={sectionLabel} onclick={() => { goto('/persons'); onnavigate?.(); }}> <button class={sectionLabel} onclick={() => { goto('/persons'); onnavigate?.(); }}>
<span>👤</span> People <Users size={15} class="flex-shrink-0 text-emerald-400" /> People
</button> </button>
</div> </div>
{#if !collapsed['people']} {#if !collapsed['people']}
<ul class="m-0 list-none p-0"> <ul class="m-0 list-none p-0">
{#each people as ctx} {#each people as ctx}
<li> <li>
<button class={navItem(currentId === ctx.id)} onclick={() => navigate(ctx.id)}> <button class={navItem(currentId === ctx.id) + ' flex items-center gap-2'} onclick={() => navigate(ctx.id)}>
{displayName(ctx)} <User size={14} class="flex-shrink-0 text-emerald-400/60" />
<span class="truncate">{displayName(ctx)}</span>
</button> </button>
</li> </li>
{/each} {/each}
@ -180,10 +193,10 @@
<!-- Wiki --> <!-- Wiki -->
<div class={sectionHeader}> <div class={sectionHeader}>
<button class={sectionToggle} onclick={() => toggleSection('wiki')} title="Auf-/Zuklappen"> <button class={sectionToggle} onclick={() => toggleSection('wiki')} title="Auf-/Zuklappen">
<span class="inline-block transition-transform {collapsed['wiki'] ? '-rotate-90' : ''}">{@html '&#9660;'}</span> <ChevronDown size={14} class="transition-transform {collapsed['wiki'] ? '-rotate-90' : ''}" />
</button> </button>
<button class={sectionLabel} onclick={() => { goto('/wiki'); onnavigate?.(); }}> <button class={sectionLabel} onclick={() => { goto('/wiki'); onnavigate?.(); }}>
<span>📖</span> Wiki <BookOpen size={15} class="flex-shrink-0 text-orange-400" /> Wiki
</button> </button>
</div> </div>
{#if !collapsed['wiki']} {#if !collapsed['wiki']}
@ -191,39 +204,35 @@
{#each ($wikiNotebooks$ ?? []) as nb} {#each ($wikiNotebooks$ ?? []) as nb}
<li> <li>
<button <button
class={navItem(page.url.pathname === `/wiki/notebook/${nb.id}`)} class={navItem(page.url.pathname === `/wiki/notebook/${nb.id}`) + ' flex items-center gap-2'}
onclick={() => { goto(`/wiki/notebook/${nb.id}`); onnavigate?.(); }} onclick={() => { goto(`/wiki/notebook/${nb.id}`); onnavigate?.(); }}
> >
{nb.name} <BookOpen size={14} class="flex-shrink-0 text-orange-400/60" />
<span class="truncate">{nb.name}</span>
</button> </button>
</li> </li>
{/each} {/each}
</ul> </ul>
{/if} {/if}
<div class="mt-4"> <div class="mt-auto border-t border-border pt-3">
<button <button
class={navItem(page.url.pathname === '/trash') + ' flex items-center gap-2'} class={navItem(page.url.pathname === '/trash') + ' flex items-center gap-2 mb-2'}
onclick={() => { goto('/trash'); onnavigate?.(); }} onclick={() => { goto('/trash'); onnavigate?.(); }}
> >
<span>🗑</span> <Trash2 size={15} class="flex-shrink-0 text-red-400/70" />
<span>Papierkorb</span> <span>Papierkorb</span>
{#if ($trashCount$ ?? 0) > 0} {#if ($trashCount$ ?? 0) > 0}
<span class="ml-auto rounded-full bg-white/15 px-1.5 py-0.5 text-xs text-[#888] leading-none">{$trashCount$}</span> <span class="ml-auto rounded-full bg-white/15 px-1.5 py-0.5 text-xs text-[#888] leading-none">{$trashCount$}</span>
{/if} {/if}
</button> </button>
</div>
<div class="mt-auto border-t border-border pt-3">
<div class="mb-2 flex items-center justify-between px-1"> <div class="mb-2 flex items-center justify-between px-1">
<button <button
class="flex items-center gap-1.5 text-xs text-muted hover:text-white transition-colors" class="flex items-center gap-1.5 text-xs text-muted hover:text-white transition-colors"
onclick={() => { goto('/sync'); onnavigate?.(); }} onclick={() => { goto('/sync'); onnavigate?.(); }}
> >
{#if $syncStatus === 'syncing'} {#if $syncStatus === 'syncing'}
<svg class="h-3 w-3 animate-spin" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <RefreshCw size={12} class="animate-spin" />
<path d="M21 12a9 9 0 1 1-6.219-8.56" stroke-linecap="round"/>
</svg>
<span>Syncing…</span> <span>Syncing…</span>
{:else if $syncStatus === 'auth-required'} {:else if $syncStatus === 'auth-required'}
<span class="text-yellow-400">⚠ Neu anmelden</span> <span class="text-yellow-400">⚠ Neu anmelden</span>
@ -240,13 +249,13 @@
onclick={() => fullSync()} onclick={() => fullSync()}
disabled={$syncStatus === 'syncing'} disabled={$syncStatus === 'syncing'}
title="Full sync" title="Full sync"
></button> ><RefreshCw size={13} /></button>
</div> </div>
<button <button
class="mb-1 w-full rounded px-2.5 py-2 text-left text-xs text-muted transition-colors hover:bg-white/5 hover:text-white flex items-center gap-1" class="mb-1 w-full rounded px-3 py-2.5 text-left text-sm text-muted transition-colors hover:bg-white/5 hover:text-white flex items-center gap-2"
onclick={() => toggleSection('user')} onclick={() => toggleSection('user')}
> >
<span class="inline-block text-[10px] transition-transform {collapsed['user'] ? '-rotate-90' : ''}">{@html '&#9660;'}</span> <ChevronDown size={14} class="flex-shrink-0 transition-transform {collapsed['user'] ? '-rotate-90' : ''}" />
{#if $account} {#if $account}
<span class="truncate" title={$account.username}>{$account.name ?? $account.username}</span> <span class="truncate" title={$account.username}>{$account.name ?? $account.username}</span>
{:else} {:else}
@ -255,22 +264,25 @@
</button> </button>
{#if !collapsed['user']} {#if !collapsed['user']}
<button <button
class="mb-1 w-full rounded px-2.5 py-2 text-left text-xs text-muted transition-colors hover:bg-white/5 hover:text-white pl-5" class="mb-1 w-full rounded px-3 py-2.5 text-left text-sm text-muted transition-colors hover:bg-white/5 hover:text-white flex items-center gap-2 pl-6"
onclick={() => { goto('/settings'); onnavigate?.(); }} onclick={() => { goto('/settings'); onnavigate?.(); }}
> >
Einstellungen <Settings size={14} class="flex-shrink-0 opacity-60" />
<span>Einstellungen</span>
</button> </button>
<button <button
class="mb-1 w-full rounded px-2.5 py-2 text-left text-xs text-muted transition-colors hover:bg-white/5 hover:text-white pl-5" class="mb-1 w-full rounded px-3 py-2.5 text-left text-sm text-muted transition-colors hover:bg-white/5 hover:text-white flex items-center gap-2 pl-6"
onclick={() => { goto('/backup'); onnavigate?.(); }} onclick={() => { goto('/backup'); onnavigate?.(); }}
> >
Backup <HardDrive size={14} class="flex-shrink-0 opacity-60" />
<span>Backup</span>
</button> </button>
<button <button
class="mb-1 w-full rounded px-2.5 py-2 text-left text-xs text-muted transition-colors hover:bg-white/5 hover:text-white pl-5" class="mb-1 w-full rounded px-3 py-2.5 text-left text-sm text-muted transition-colors hover:bg-white/5 hover:text-white flex items-center gap-2 pl-6"
onclick={() => logout()} onclick={() => logout()}
> >
Sign out <LogOut size={14} class="flex-shrink-0 opacity-60" />
<span>Sign out</span>
</button> </button>
{/if} {/if}
{#if isDev} {#if isDev}

View File

@ -5,6 +5,7 @@
import type { HistoryEntry } from '@ka-note/shared'; import type { HistoryEntry } from '@ka-note/shared';
import HistoryEntryText from './HistoryEntryText.svelte'; import HistoryEntryText from './HistoryEntryText.svelte';
import LinkTitle from './LinkTitle.svelte'; import LinkTitle from './LinkTitle.svelte';
import { Check, CalendarDays, ArrowUpRight, Trash2 } from 'lucide-svelte';
interface Props { interface Props {
entry: HistoryEntry; entry: HistoryEntry;
@ -100,25 +101,25 @@
{:else} {:else}
<div class="flex gap-1.5"> <div class="flex gap-1.5">
<button <button
class="rounded bg-green-700 px-2.5 py-1.5 text-base leading-none text-white hover:brightness-110 active:brightness-90" class="flex items-center justify-center rounded bg-green-700 px-2.5 py-1.5 text-white hover:brightness-110 active:brightness-90"
title="Erledigt" title="Erledigt"
onclick={handleOk} onclick={handleOk}
></button> ><Check size={15} /></button>
<button <button
class="rounded bg-amber-700 px-2.5 py-1.5 text-base leading-none text-white hover:brightness-110 active:brightness-90" class="flex items-center justify-center rounded bg-amber-700 px-2.5 py-1.5 text-white hover:brightness-110 active:brightness-90"
title="Verschieben" title="Verschieben"
onclick={() => showVerschieben = true} onclick={() => showVerschieben = true}
>📅</button> ><CalendarDays size={15} /></button>
<button <button
class="rounded bg-[#444] px-2.5 py-1.5 text-base leading-none text-white hover:bg-[#555] active:bg-[#555]" class="flex items-center justify-center rounded bg-[#444] px-2.5 py-1.5 text-white hover:bg-[#555] active:bg-[#555]"
title="In Thema wandeln" title="In Thema wandeln"
onclick={() => showConvert = true} onclick={() => showConvert = true}
></button> ><ArrowUpRight size={15} /></button>
<button <button
class="ml-auto rounded bg-red-900/60 px-2.5 py-1.5 text-base leading-none text-red-300 hover:bg-red-800 active:bg-red-800" class="ml-auto flex items-center justify-center rounded bg-red-900/60 px-2.5 py-1.5 text-red-300 hover:bg-red-800 active:bg-red-800"
title="Löschen" title="Löschen"
onclick={handleDelete} onclick={handleDelete}
>🗑</button> ><Trash2 size={15} /></button>
</div> </div>
{/if} {/if}
</div> </div>

View File

@ -370,7 +370,7 @@ export async function createHistoryEntry(topicId: string, date: string, text: st
updatedAt: now(), updatedAt: now(),
deletedAt: null, deletedAt: null,
version: 1, version: 1,
...(isPrivate ? { isPrivate: true } : {}) isPrivate: !!isPrivate
}; };
await db.historyEntries.put(entry); await db.historyEntries.put(entry);
return entry; return entry;

View File

@ -37,6 +37,7 @@
"dexie": "^4.0.11", "dexie": "^4.0.11",
"dompurify": "^3.3.1", "dompurify": "^3.3.1",
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
"lucide-svelte": "^0.576.0",
"marked": "^17.0.3", "marked": "^17.0.3",
"tiptap-markdown": "^0.9.0" "tiptap-markdown": "^0.9.0"
}, },
@ -2665,7 +2666,6 @@
"version": "0.3.13", "version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/sourcemap-codec": "^1.5.0",
@ -2676,7 +2676,6 @@
"version": "2.3.5", "version": "2.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/gen-mapping": "^0.3.5",
@ -2687,7 +2686,6 @@
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6.0.0" "node": ">=6.0.0"
@ -2708,14 +2706,12 @@
"version": "1.5.5", "version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@jridgewell/trace-mapping": { "node_modules/@jridgewell/trace-mapping": {
"version": "0.3.31", "version": "0.3.31",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/resolve-uri": "^3.1.0",
@ -3344,7 +3340,6 @@
"version": "1.0.9", "version": "1.0.9",
"resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.9.tgz", "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.9.tgz",
"integrity": "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==", "integrity": "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==",
"dev": true,
"license": "MIT", "license": "MIT",
"peerDependencies": { "peerDependencies": {
"acorn": "^8.9.0" "acorn": "^8.9.0"
@ -3969,7 +3964,6 @@
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/linkify-it": { "node_modules/@types/linkify-it": {
@ -4015,14 +4009,12 @@
"version": "2.0.7", "version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
"devOptional": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.16.0", "version": "8.16.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"dev": true,
"license": "MIT", "license": "MIT",
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
@ -4112,7 +4104,6 @@
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
"integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
"dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@ -4241,7 +4232,6 @@
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
"integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
"dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@ -4657,7 +4647,6 @@
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6" "node": ">=6"
@ -5004,7 +4993,6 @@
"version": "5.6.3", "version": "5.6.3",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.3.tgz", "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.3.tgz",
"integrity": "sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg==", "integrity": "sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/dexie": { "node_modules/dexie": {
@ -5959,14 +5947,12 @@
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz",
"integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/esrap": { "node_modules/esrap": {
"version": "2.2.3", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.3.tgz", "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.3.tgz",
"integrity": "sha512-8fOS+GIGCQZl/ZIlhl59htOlms6U8NvX6ZYgYHpRU/b6tVSh3uHkOHZikl3D4cMbYM0JlpBe+p/BkZEi8J9XIQ==", "integrity": "sha512-8fOS+GIGCQZl/ZIlhl59htOlms6U8NvX6ZYgYHpRU/b6tVSh3uHkOHZikl3D4cMbYM0JlpBe+p/BkZEi8J9XIQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15" "@jridgewell/sourcemap-codec": "^1.4.15"
@ -6960,7 +6946,6 @@
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
"integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/estree": "^1.0.6" "@types/estree": "^1.0.6"
@ -7330,7 +7315,6 @@
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/lodash": { "node_modules/lodash": {
@ -7364,11 +7348,19 @@
"yallist": "^3.0.2" "yallist": "^3.0.2"
} }
}, },
"node_modules/lucide-svelte": {
"version": "0.576.0",
"resolved": "https://registry.npmjs.org/lucide-svelte/-/lucide-svelte-0.576.0.tgz",
"integrity": "sha512-bm7RCoptI8unoEyo9H9sRHTHgnleuBW8npge05ZtxHkNsDNnO3p/BQEU79sshf4k+MSrjqlWvsCN5vVZtgV7ww==",
"license": "ISC",
"peerDependencies": {
"svelte": "^3 || ^4 || ^5.0.0-next.42"
}
},
"node_modules/magic-string": { "node_modules/magic-string": {
"version": "0.30.21", "version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5" "@jridgewell/sourcemap-codec": "^1.5.5"
@ -9247,7 +9239,6 @@
"version": "5.53.0", "version": "5.53.0",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.53.0.tgz", "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.53.0.tgz",
"integrity": "sha512-7dhHkSamGS2vtoBmIW2hRab+gl5Z60alEHZB4910ePqqJNxAWnDAxsofVmlZ2tREmWyHNE+A1nCKwICAquoD2A==", "integrity": "sha512-7dhHkSamGS2vtoBmIW2hRab+gl5Z60alEHZB4910ePqqJNxAWnDAxsofVmlZ2tREmWyHNE+A1nCKwICAquoD2A==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@jridgewell/remapping": "^2.3.4", "@jridgewell/remapping": "^2.3.4",
@ -11046,7 +11037,6 @@
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz",
"integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==", "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/zod": { "node_modules/zod": {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

3524
ka-note/repair/dump.sql Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,13 @@
import { readFileSync, writeFileSync } from 'fs';
let sql = readFileSync('dump-fixed.sql', 'utf8');
// unistr('\uXXXX') -> plain UTF-8 string
sql = sql.replace(/unistr\('((?:[^'\\]|\\.)*)'\)/g, (_, inner) => {
const decoded = inner.replace(/\\u([0-9a-fA-F]{4})/g, (_, hex) =>
String.fromCharCode(parseInt(hex, 16))
);
return `'${decoded}'`;
});
// ROLLBACK at end of corrupt dump -> COMMIT so import doesn't discard everything
sql = sql.replace(/^ROLLBACK;.*$/m, 'COMMIT;');
writeFileSync('dump-fixed2.sql', sql, 'utf8');
console.log('done');

Binary file not shown.

Binary file not shown.