ui optimierungen

This commit is contained in:
beo3000 2026-02-24 18:10:53 +01:00
parent b24915783d
commit 53b20cf492
13 changed files with 104 additions and 98 deletions

View File

@ -1 +1 @@
1.1.49 1.1.52

View File

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { page } from "$app/stores"; import { page } from "$app/state";
import { goto } from "$app/navigation"; import { goto } from "$app/navigation";
interface Props { interface Props {
@ -7,7 +7,7 @@
} }
let { onsidebaropen }: Props = $props(); let { onsidebaropen }: Props = $props();
const currentPath = $derived($page.url.pathname); const currentPath = $derived(page.url.pathname);
interface Tab { interface Tab {
id: string; id: string;

View File

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { page } from '$app/stores'; import { page } from '$app/state';
import { liveQuery } from 'dexie'; import { liveQuery } from 'dexie';
import { db } from '$lib/db/schema'; import { db } from '$lib/db/schema';
import { collapsedTopicIds } from '$lib/stores/agenda'; import { collapsedTopicIds } from '$lib/stores/agenda';
@ -34,7 +34,7 @@
journalScope = s; journalScope = s;
localStorage.setItem(SCOPE_KEY, s); localStorage.setItem(SCOPE_KEY, s);
} }
const initialDate = $derived($page.url.searchParams.get('date') ?? undefined); const initialDate = $derived(page.url.searchParams.get('date') ?? undefined);
let compact = $state(false); let compact = $state(false);
// Rating modal state // Rating modal state

View File

@ -12,7 +12,7 @@
import LinkTitle from './LinkTitle.svelte'; import LinkTitle from './LinkTitle.svelte';
import { get } from 'svelte/store'; import { get } from 'svelte/store';
import { currentScope, scopeSettings } from '$lib/stores/scopeContext'; import { currentScope, scopeSettings } from '$lib/stores/scopeContext';
import { normalizeTitleAndBody } from '$lib/utils/titleUtils'; import { normalizeTitleAndBody, extractTitleAndBody } from '$lib/utils/titleUtils';
import { useUnsavedGuard } from '$lib/utils/unsavedGuard.svelte'; import { useUnsavedGuard } from '$lib/utils/unsavedGuard.svelte';
interface Props { interface Props {
@ -27,7 +27,6 @@
const isDailyLog = $derived(contextId === 'daily-log'); const isDailyLog = $derived(contextId === 'daily-log');
// --- Daily-log journal mode --- // --- Daily-log journal mode ---
let entryTitle = $state('');
let entryText = $state(''); let entryText = $state('');
let entryEditor: MarkdownEditor; let entryEditor: MarkdownEditor;
let selectedDate = $state(initialDate ?? today()); let selectedDate = $state(initialDate ?? today());
@ -35,7 +34,7 @@
let wiedervorlageChecked = $state(true); let wiedervorlageChecked = $state(true);
let meetingPickerOpen = $state(false); let meetingPickerOpen = $state(false);
useUnsavedGuard(() => entryTitle.trim() !== '' || entryText.trim() !== '' || editingId !== null); useUnsavedGuard(() => entryText.trim() !== '' || editingId !== null);
// All meeting contexts for the link dropdown // All meeting contexts for the link dropdown
const meetingContexts = liveQuery(() => const meetingContexts = liveQuery(() =>
@ -83,9 +82,12 @@
async function handleAddEntry() { async function handleAddEntry() {
let title = entryTitle.trim(); const raw = entryText.trim();
let body = entryText.trim(); if (!raw) return;
if (!title) return; const { maxTitleLength } = get(scopeSettings);
const extracted = extractTitleAndBody(raw, maxTitleLength);
let title = extracted.title;
let body = extracted.body;
// If title is a URL, try to fetch its page title // If title is a URL, try to fetch its page title
if (/^https?:\/\//i.test(title)) { if (/^https?:\/\//i.test(title)) {
@ -103,14 +105,11 @@
await createHistoryEntry(topic.id, selectedDate, body, null, false, isPrivate); await createHistoryEntry(topic.id, selectedDate, body, null, false, isPrivate);
} }
} else { } else {
const { maxTitleLength } = get(scopeSettings); const text = body ? `${title}\n\n${body}` : title;
const { title: shortTitle, body: fullBody } = normalizeTitleAndBody(title, body, maxTitleLength);
const text = fullBody ? `${shortTitle}\n\n${fullBody}` : shortTitle;
await getOrCreateJournalTopic(); await getOrCreateJournalTopic();
await createHistoryEntry(JOURNAL_TOPIC_ID, selectedDate, text, null, wiedervorlageChecked, isPrivate); await createHistoryEntry(JOURNAL_TOPIC_ID, selectedDate, text, null, wiedervorlageChecked, isPrivate);
} }
entryTitle = '';
entryText = ''; entryText = '';
entryEditor?.clear(); entryEditor?.clear();
selectedLinkedContextId = ''; selectedLinkedContextId = '';
@ -118,10 +117,6 @@
wiedervorlageChecked = true; wiedervorlageChecked = true;
} }
function handleTitleKeypress(e: KeyboardEvent) {
if (e.key === 'Enter') handleAddEntry();
}
let confirmDeleteId = $state<string | null>(null); let confirmDeleteId = $state<string | null>(null);
// --- Edit mode --- // --- Edit mode ---
@ -160,9 +155,13 @@
confirmDeleteId = null; confirmDeleteId = null;
} }
function formatTime(isoString: string): string { function formatTime(entry: { sortOrder: number; updatedAt: string }): string {
try { try {
return new Date(isoString).toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' }); // sortOrder >= 1e12 means it was set as Date.now() (ms timestamp) → use as creation time
const ts = entry.sortOrder >= 1_000_000_000_000
? new Date(entry.sortOrder)
: new Date(entry.updatedAt);
return ts.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' });
} catch { } catch {
return ''; return '';
} }
@ -238,52 +237,15 @@
{/if} {/if}
<div class="mb-8 flex flex-col gap-2.5 rounded-lg border border-border bg-sidebar p-4"> <div class="mb-8 flex flex-col gap-2.5 rounded-lg border border-border bg-sidebar p-4">
<div class="relative flex items-center gap-2">
<input
type="text"
class="flex-1 rounded border border-[#444] bg-bg px-2.5 py-2 font-mono text-white"
placeholder="Titel / Was ist passiert?"
bind:value={entryTitle}
onkeypress={handleTitleKeypress}
use:mention
/>
<!-- Meeting picker icon button -->
<button
type="button"
class="flex items-center gap-1 rounded border px-2 py-2 text-sm transition-colors {journalScope === 'private' ? 'hidden' : selectedLinkedContextId ? 'border-accent bg-accent/20 text-accent' : 'border-[#444] bg-bg text-muted hover:text-white'}"
onclick={() => meetingPickerOpen = !meetingPickerOpen}
title="Jour-fix verknüpfen"
>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"/>
<line x1="16" y1="2" x2="16" y2="6"/>
<line x1="8" y1="2" x2="8" y2="6"/>
<line x1="3" y1="10" x2="21" y2="10"/>
</svg>
</button>
{#if meetingPickerOpen}
<div class="absolute right-0 top-full z-50 mt-1 min-w-48 rounded border border-[#444] bg-[#1e1e1e] py-1 shadow-lg">
<button
class="w-full px-3 py-1.5 text-left text-sm text-muted hover:bg-[#2a2a2a] {!selectedLinkedContextId ? 'text-white' : ''}"
onclick={() => { selectedLinkedContextId = ''; meetingPickerOpen = false; }}
>— kein Jour-fix —</button>
{#each $meetingContexts ?? [] as ctx}
<button
class="w-full px-3 py-1.5 text-left text-sm hover:bg-[#2a2a2a] {selectedLinkedContextId === ctx.id ? 'text-accent' : 'text-white'}"
onclick={() => { selectedLinkedContextId = ctx.id; meetingPickerOpen = false; }}
>{ctx.name}</button>
{/each}
</div>
{/if}
</div>
<MarkdownEditor <MarkdownEditor
bind:this={entryEditor} bind:this={entryEditor}
placeholder="Details (optional)" placeholder="Was ist passiert? (1. Zeile = Titel)"
minHeight="60px" minHeight="60px"
wikiScope={journalScope === 'private'} wikiScope={journalScope === 'private'}
onchange={(md) => entryText = md} onchange={(md) => entryText = md}
onsave={handleAddEntry}
/> />
<div class="flex items-center gap-4"> <div class="flex items-center gap-2">
<button <button
class="rounded px-4 py-2 font-bold text-white hover:brightness-110" class="rounded px-4 py-2 font-bold text-white hover:brightness-110"
style="background-color: {journalScope === 'private' ? $scopeSettings.privateColor : $scopeSettings.businessColor}" style="background-color: {journalScope === 'private' ? $scopeSettings.privateColor : $scopeSettings.businessColor}"
@ -294,13 +256,41 @@
{#if !selectedLinkedContextId} {#if !selectedLinkedContextId}
<button <button
type="button" type="button"
class="flex items-center gap-1.5 rounded-full border px-3 py-1 text-xs font-medium transition-colors {wiedervorlageChecked ? 'border-amber-500 bg-amber-500/20 text-amber-400' : 'border-[#444] text-muted hover:border-[#666] hover:text-white'}" class="rounded border p-1.5 text-base leading-none transition-colors {wiedervorlageChecked ? 'border-amber-500 bg-amber-500/20 text-amber-400' : 'border-[#444] text-muted hover:border-[#666] hover:text-white'}"
onclick={() => wiedervorlageChecked = !wiedervorlageChecked} onclick={() => wiedervorlageChecked = !wiedervorlageChecked}
> title="Wiedervorlage"
<span class="text-base leading-none">{wiedervorlageChecked ? '⏰' : '○'}</span> >{wiedervorlageChecked ? '⏰' : '○'}</button>
Wiedervorlage
</button>
{/if} {/if}
<!-- Meeting picker button -->
<div class="relative {journalScope === 'private' ? 'hidden' : ''}">
<button
type="button"
class="flex items-center gap-1 rounded border px-2 py-1.5 text-sm transition-colors {selectedLinkedContextId ? 'border-accent bg-accent/20 text-accent' : 'border-[#444] bg-bg text-muted hover:text-white'}"
onclick={() => meetingPickerOpen = !meetingPickerOpen}
title="Jour-fix verknüpfen"
>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"/>
<line x1="16" y1="2" x2="16" y2="6"/>
<line x1="8" y1="2" x2="8" y2="6"/>
<line x1="3" y1="10" x2="21" y2="10"/>
</svg>
</button>
{#if meetingPickerOpen}
<div class="absolute left-0 top-full z-50 mt-1 min-w-48 rounded border border-[#444] bg-[#1e1e1e] py-1 shadow-lg">
<button
class="w-full px-3 py-1.5 text-left text-sm text-muted hover:bg-[#2a2a2a] {!selectedLinkedContextId ? 'text-white' : ''}"
onclick={() => { selectedLinkedContextId = ''; meetingPickerOpen = false; }}
>— kein Jour-fix —</button>
{#each $meetingContexts ?? [] as ctx}
<button
class="w-full px-3 py-1.5 text-left text-sm hover:bg-[#2a2a2a] {selectedLinkedContextId === ctx.id ? 'text-accent' : 'text-white'}"
onclick={() => { selectedLinkedContextId = ctx.id; meetingPickerOpen = false; }}
>{ctx.name}</button>
{/each}
</div>
{/if}
</div>
</div> </div>
</div> </div>
@ -358,7 +348,7 @@
{@const title = lines[0].replace(/^#{1,6}\s+/, '')} {@const title = lines[0].replace(/^#{1,6}\s+/, '')}
{@const body = lines.slice(1).join('\n').trim()} {@const body = lines.slice(1).join('\n').trim()}
<div class="group mb-3 flex items-start gap-2 rounded bg-card-bg p-2.5"> <div class="group mb-3 flex items-start gap-2 rounded bg-card-bg p-2.5">
<span class="mt-0.5 text-xs text-muted whitespace-nowrap">{formatTime(entry.updatedAt)}</span> <span class="mt-0.5 text-xs text-muted whitespace-nowrap">{formatTime(entry)}</span>
<div class="flex-1"> <div class="flex-1">
<div class="flex flex-wrap items-center gap-1.5 font-bold"> <div class="flex flex-wrap items-center gap-1.5 font-bold">
<LinkTitle text={title} /> <LinkTitle text={title} />

View File

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { page } from '$app/stores'; import { page } from '$app/state';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { liveQuery } from 'dexie'; import { liveQuery } from 'dexie';
@ -26,7 +26,7 @@
} }
let { onnavigate, hideLogo = false }: Props = $props(); let { onnavigate, hideLogo = false }: Props = $props();
const currentId = $derived($page.params.id ?? ''); const currentId = $derived(page.params.id ?? '');
const contexts = liveQuery(() => db.contexts.filter(c => !c.deletedAt && !c.archivedAt).sortBy('sortOrder')); const contexts = liveQuery(() => db.contexts.filter(c => !c.deletedAt && !c.archivedAt).sortBy('sortOrder'));
@ -108,12 +108,12 @@
{/if} {/if}
<!-- General --> <!-- General -->
<div class="mb-1 mt-4 pl-1 text-xs uppercase text-muted">General</div> <div class="mb-1 mt-6 pl-1 text-xs uppercase text-muted">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 <button
class="mb-1 w-full rounded px-2.5 py-2 text-left text-sm transition-colors {currentId === 'daily-log' ? 'bg-accent font-bold text-white' : 'text-[#ccc] hover:bg-white/5 hover:text-white'}" class="mb-1 w-full rounded px-2.5 py-2.5 text-left text-sm transition-colors {currentId === 'daily-log' ? 'bg-accent font-bold text-white' : 'text-[#ccc] hover:bg-white/5 hover:text-white'}"
onclick={() => navigate('daily-log')} onclick={() => navigate('daily-log')}
> >
{dailyLog.name} {dailyLog.name}
@ -126,7 +126,7 @@
<ul class="m-0 list-none p-0"> <ul class="m-0 list-none p-0">
<li> <li>
<button <button
class="mb-1 w-full rounded px-2.5 py-2 text-left text-sm transition-colors {$page.url.pathname.startsWith('/journal/archive') ? 'bg-accent font-bold text-white' : 'text-[#ccc] hover:bg-white/5 hover:text-white'}" class="mb-1 w-full rounded px-2.5 py-2.5 text-left text-sm transition-colors {page.url.pathname.startsWith('/journal/archive') ? 'bg-accent font-bold text-white' : 'text-[#ccc] hover:bg-white/5 hover:text-white'}"
onclick={() => { goto('/journal/archive'); onnavigate?.(); }} onclick={() => { goto('/journal/archive'); onnavigate?.(); }}
> >
📅 Archiv 📅 Archiv
@ -135,7 +135,7 @@
</ul> </ul>
<!-- Jour Fixes --> <!-- Jour Fixes -->
<div class="mb-1 mt-4 flex items-center justify-between pl-1 pr-1"> <div class="mb-1 mt-6 flex items-center justify-between pl-1 pr-1">
<button class="text-xs uppercase text-muted hover:text-white flex items-center gap-1" onclick={() => toggleSection('meetings')}> <button class="text-xs uppercase text-muted hover:text-white flex items-center gap-1" onclick={() => toggleSection('meetings')}>
<span class="inline-block text-[10px] transition-transform {collapsed['meetings'] ? '-rotate-90' : ''}">{@html '&#9660;'}</span> <span class="inline-block text-[10px] transition-transform {collapsed['meetings'] ? '-rotate-90' : ''}">{@html '&#9660;'}</span>
Jour Fixes Jour Fixes
@ -147,7 +147,7 @@
{#each meetings as ctx} {#each meetings as ctx}
<li> <li>
<button <button
class="mb-1 w-full rounded px-2.5 py-2 text-left text-sm transition-colors {currentId === ctx.id ? 'bg-accent font-bold text-white' : 'text-[#ccc] hover:bg-white/5 hover:text-white'}" class="mb-1 w-full rounded px-2.5 py-2.5 text-left text-sm transition-colors {currentId === ctx.id ? 'bg-accent font-bold text-white' : 'text-[#ccc] hover:bg-white/5 hover:text-white'}"
onclick={() => navigate(ctx.id)} onclick={() => navigate(ctx.id)}
> >
{ctx.name} {ctx.name}
@ -168,7 +168,7 @@
{/if} {/if}
<li> <li>
<button <button
class="mb-1 w-full rounded px-2.5 py-2 text-left text-xs text-[#666] transition-colors hover:bg-white/5 hover:text-[#aaa]" class="mb-1 w-full rounded px-2.5 py-2.5 text-left text-xs text-[#666] transition-colors hover:bg-white/5 hover:text-[#aaa]"
onclick={() => { goto('/meetings'); onnavigate?.(); }} onclick={() => { goto('/meetings'); onnavigate?.(); }}
> >
Alle Jour Fixes &rarr; Alle Jour Fixes &rarr;
@ -178,7 +178,7 @@
{/if} {/if}
<!-- Projects --> <!-- Projects -->
<div class="mb-1 mt-4 flex items-center justify-between pl-1 pr-1"> <div class="mb-1 mt-6 flex items-center justify-between pl-1 pr-1">
<button class="text-xs uppercase text-muted hover:text-white flex items-center gap-1" onclick={() => toggleSection('projects')}> <button class="text-xs uppercase text-muted hover:text-white flex items-center gap-1" onclick={() => toggleSection('projects')}>
<span class="inline-block text-[10px] transition-transform {collapsed['projects'] ? '-rotate-90' : ''}">{@html '&#9660;'}</span> <span class="inline-block text-[10px] transition-transform {collapsed['projects'] ? '-rotate-90' : ''}">{@html '&#9660;'}</span>
Projects Projects
@ -190,7 +190,7 @@
{#each projects as ctx} {#each projects as ctx}
<li> <li>
<button <button
class="mb-1 w-full rounded px-2.5 py-2 text-left text-sm transition-colors {currentId === ctx.id ? 'bg-accent font-bold text-white' : 'text-[#ccc] hover:bg-white/5 hover:text-white'}" class="mb-1 w-full rounded px-2.5 py-2.5 text-left text-sm transition-colors {currentId === ctx.id ? 'bg-accent font-bold text-white' : 'text-[#ccc] hover:bg-white/5 hover:text-white'}"
onclick={() => navigate(ctx.id)} onclick={() => navigate(ctx.id)}
> >
{displayName(ctx)} {displayName(ctx)}
@ -211,7 +211,7 @@
{/if} {/if}
<li> <li>
<button <button
class="mb-1 w-full rounded px-2.5 py-2 text-left text-xs text-[#666] transition-colors hover:bg-white/5 hover:text-[#aaa]" class="mb-1 w-full rounded px-2.5 py-2.5 text-left text-xs text-[#666] transition-colors hover:bg-white/5 hover:text-[#aaa]"
onclick={() => { goto('/projects'); onnavigate?.(); }} onclick={() => { goto('/projects'); onnavigate?.(); }}
> >
Alle Projekte &rarr; Alle Projekte &rarr;
@ -221,7 +221,7 @@
{/if} {/if}
<!-- Companies --> <!-- Companies -->
<div class="mb-1 mt-4 flex items-center justify-between pl-1 pr-1"> <div class="mb-1 mt-6 flex items-center justify-between pl-1 pr-1">
<button class="text-xs uppercase text-muted hover:text-white flex items-center gap-1" onclick={() => toggleSection('companies')}> <button class="text-xs uppercase text-muted hover:text-white flex items-center gap-1" onclick={() => toggleSection('companies')}>
<span class="inline-block text-[10px] transition-transform {collapsed['companies'] ? '-rotate-90' : ''}">{@html '&#9660;'}</span> <span class="inline-block text-[10px] transition-transform {collapsed['companies'] ? '-rotate-90' : ''}">{@html '&#9660;'}</span>
Firmen Firmen
@ -233,7 +233,7 @@
{#each companies as ctx} {#each companies as ctx}
<li> <li>
<button <button
class="mb-1 w-full rounded px-2.5 py-2 text-left text-sm transition-colors {currentId === ctx.id ? 'bg-accent font-bold text-white' : 'text-[#ccc] hover:bg-white/5 hover:text-white'}" class="mb-1 w-full rounded px-2.5 py-2.5 text-left text-sm transition-colors {currentId === ctx.id ? 'bg-accent font-bold text-white' : 'text-[#ccc] hover:bg-white/5 hover:text-white'}"
onclick={() => navigate(ctx.id)} onclick={() => navigate(ctx.id)}
> >
{displayName(ctx)} {displayName(ctx)}
@ -254,7 +254,7 @@
{/if} {/if}
<li> <li>
<button <button
class="mb-1 w-full rounded px-2.5 py-2 text-left text-xs text-[#666] transition-colors hover:bg-white/5 hover:text-[#aaa]" class="mb-1 w-full rounded px-2.5 py-2.5 text-left text-xs text-[#666] transition-colors hover:bg-white/5 hover:text-[#aaa]"
onclick={() => { goto('/companies'); onnavigate?.(); }} onclick={() => { goto('/companies'); onnavigate?.(); }}
> >
Alle Firmen &rarr; Alle Firmen &rarr;
@ -264,7 +264,7 @@
{/if} {/if}
<!-- People --> <!-- People -->
<div class="mb-1 mt-4 flex items-center justify-between pl-1 pr-1"> <div class="mb-1 mt-6 flex items-center justify-between pl-1 pr-1">
<button class="text-xs uppercase text-muted hover:text-white flex items-center gap-1" onclick={() => toggleSection('people')}> <button class="text-xs uppercase text-muted hover:text-white flex items-center gap-1" onclick={() => toggleSection('people')}>
<span class="inline-block text-[10px] transition-transform {collapsed['people'] ? '-rotate-90' : ''}">{@html '&#9660;'}</span> <span class="inline-block text-[10px] transition-transform {collapsed['people'] ? '-rotate-90' : ''}">{@html '&#9660;'}</span>
People People
@ -276,7 +276,7 @@
{#each people as ctx} {#each people as ctx}
<li> <li>
<button <button
class="mb-1 w-full rounded px-2.5 py-2 text-left text-sm transition-colors {currentId === ctx.id ? 'bg-accent font-bold text-white' : 'text-[#ccc] hover:bg-white/5 hover:text-white'}" class="mb-1 w-full rounded px-2.5 py-2.5 text-left text-sm transition-colors {currentId === ctx.id ? 'bg-accent font-bold text-white' : 'text-[#ccc] hover:bg-white/5 hover:text-white'}"
onclick={() => navigate(ctx.id)} onclick={() => navigate(ctx.id)}
> >
{displayName(ctx)} {displayName(ctx)}
@ -297,7 +297,7 @@
{/if} {/if}
<li> <li>
<button <button
class="mb-1 w-full rounded px-2.5 py-2 text-left text-xs text-[#666] transition-colors hover:bg-white/5 hover:text-[#aaa]" class="mb-1 w-full rounded px-2.5 py-2.5 text-left text-xs text-[#666] transition-colors hover:bg-white/5 hover:text-[#aaa]"
onclick={() => { goto('/persons'); onnavigate?.(); }} onclick={() => { goto('/persons'); onnavigate?.(); }}
> >
Alle Personen &rarr; Alle Personen &rarr;
@ -307,7 +307,7 @@
{/if} {/if}
<!-- Wiki --> <!-- Wiki -->
<div class="mb-1 mt-4 flex items-center justify-between pl-1 pr-1"> <div class="mb-1 mt-6 flex items-center justify-between pl-1 pr-1">
<button class="text-xs uppercase text-muted hover:text-white flex items-center gap-1" onclick={() => toggleSection('wiki')}> <button class="text-xs uppercase text-muted hover:text-white flex items-center gap-1" onclick={() => toggleSection('wiki')}>
<span class="inline-block text-[10px] transition-transform {collapsed['wiki'] ? '-rotate-90' : ''}">{@html '&#9660;'}</span> <span class="inline-block text-[10px] transition-transform {collapsed['wiki'] ? '-rotate-90' : ''}">{@html '&#9660;'}</span>
Wiki Wiki
@ -319,7 +319,7 @@
{#each ($wikiNotebooks$ ?? []) as nb} {#each ($wikiNotebooks$ ?? []) as nb}
<li> <li>
<button <button
class="mb-1 w-full rounded px-2.5 py-2 text-left text-sm transition-colors text-[#ccc] hover:bg-white/5 hover:text-white" class="mb-1 w-full rounded px-2.5 py-2.5 text-left text-sm transition-colors text-[#ccc] hover:bg-white/5 hover:text-white"
onclick={() => { goto(`/wiki/notebook/${nb.id}`); onnavigate?.(); }} onclick={() => { goto(`/wiki/notebook/${nb.id}`); onnavigate?.(); }}
> >
{nb.name} {nb.name}
@ -328,7 +328,7 @@
{/each} {/each}
<li> <li>
<button <button
class="mb-1 w-full rounded px-2.5 py-2 text-left text-xs text-[#666] transition-colors hover:bg-white/5 hover:text-[#aaa]" class="mb-1 w-full rounded px-2.5 py-2.5 text-left text-xs text-[#666] transition-colors hover:bg-white/5 hover:text-[#aaa]"
onclick={() => { goto('/wiki'); onnavigate?.(); }} onclick={() => { goto('/wiki'); onnavigate?.(); }}
> >
Alle Notizbücher &rarr; Alle Notizbücher &rarr;
@ -339,7 +339,7 @@
<div class="mt-4"> <div class="mt-4">
<button <button
class="mb-1 w-full rounded px-2.5 py-2 text-left text-sm transition-colors flex items-center gap-2 text-[#ccc] hover:bg-white/5 hover:text-white" class="mb-1 w-full rounded px-2.5 py-2.5 text-left text-sm transition-colors flex items-center gap-2 text-[#ccc] hover:bg-white/5 hover:text-white"
onclick={() => { goto('/trash'); onnavigate?.(); }} onclick={() => { goto('/trash'); onnavigate?.(); }}
> >
<span>🗑</span> <span>🗑</span>

View File

@ -245,7 +245,7 @@ export async function createHistoryEntry(topicId: string, date: string, text: st
topicId, topicId,
date, date,
text, text,
sortOrder: existing.length, sortOrder: Date.now(),
linkedContextId, linkedContextId,
doneAt: null, doneAt: null,
wiedervorlageDate: autoWiedervorlage ? date : null, wiedervorlageDate: autoWiedervorlage ? date : null,

View File

@ -13,3 +13,19 @@ export function normalizeTitleAndBody(
const newBody = body.trim() ? `${title}\n\n${body.trim()}` : title; const newBody = body.trim() ? `${title}\n\n${body.trim()}` : title;
return { title: short, body: newBody }; return { title: short, body: newBody };
} }
/**
* Splits raw combined input into title + body.
* First line (stripped of leading markdown heading markers) becomes the title.
* Remaining lines become the body.
* Then normalizeTitleAndBody is applied to enforce maxLen.
*/
export function extractTitleAndBody(
raw: string,
maxLen = DEFAULT_MAX_TITLE_LENGTH
): { title: string; body: string } {
const lines = raw.split('\n');
const firstLine = lines[0].replace(/^#{1,6}\s*/, '').trim();
const rest = lines.slice(1).join('\n').trim();
return normalizeTitleAndBody(firstLine, rest, maxLen);
}

View File

@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import { page } from '$app/stores'; import { page } from '$app/state';
import ContextPage from '$lib/components/ContextPage.svelte'; import ContextPage from '$lib/components/ContextPage.svelte';
const contextId = $derived($page.params.id); const contextId = $derived(page.params.id);
</script> </script>
{#key contextId} {#key contextId}

View File

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { page } from '$app/stores'; import { page } from '$app/state';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { db } from '$lib/db/schema'; import { db } from '$lib/db/schema';
import RenderedMarkdown from '$lib/components/RenderedMarkdown.svelte'; import RenderedMarkdown from '$lib/components/RenderedMarkdown.svelte';
@ -18,8 +18,8 @@
}); });
// URL params // URL params
const month = $derived($page.url.searchParams.get('month') ?? ''); // YYYY-MM const month = $derived(page.url.searchParams.get('month') ?? ''); // YYYY-MM
const day = $derived($page.url.searchParams.get('day') ?? ''); // DD const day = $derived(page.url.searchParams.get('day') ?? ''); // DD
// View: 'index' | 'month' | 'day' // View: 'index' | 'month' | 'day'
const view = $derived(day ? 'day' : month ? 'month' : 'index'); const view = $derived(day ? 'day' : month ? 'month' : 'index');

View File

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { page } from '$app/stores'; import { page } from '$app/state';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { db } from '$lib/db/schema'; import { db } from '$lib/db/schema';
import { upsertPage, softDeletePage, togglePageFavorite, getNotebooksForPage, getBacklinksForPage, assignPageToNotebook, removePageFromNotebook, getAllNotebooks } from '$lib/db/repositories'; import { upsertPage, softDeletePage, togglePageFavorite, getNotebooksForPage, getBacklinksForPage, assignPageToNotebook, removePageFromNotebook, getAllNotebooks } from '$lib/db/repositories';
@ -9,7 +9,7 @@
import type { Page, Notebook } from '@ka-note/shared'; import type { Page, Notebook } from '@ka-note/shared';
import { currentScope, scopeSettings } from '$lib/stores/scopeContext'; import { currentScope, scopeSettings } from '$lib/stores/scopeContext';
import { useUnsavedGuard } from '$lib/utils/unsavedGuard.svelte'; import { useUnsavedGuard } from '$lib/utils/unsavedGuard.svelte';
const pageId = $derived($page.params.id); const pageId = $derived(page.params.id);
let currentPage = $state<Page | undefined>(undefined); let currentPage = $state<Page | undefined>(undefined);
const SCOPE_KEY = 'wiki-scope'; const SCOPE_KEY = 'wiki-scope';

View File

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { page } from '$app/stores'; import { page } from '$app/state';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { liveQuery } from 'dexie'; import { liveQuery } from 'dexie';
import { db } from '$lib/db/schema'; import { db } from '$lib/db/schema';
@ -12,7 +12,7 @@
if ($notebook$) currentScope.set($notebook$.isPrivate ? 'private' : 'business'); if ($notebook$) currentScope.set($notebook$.isPrivate ? 'private' : 'business');
}); });
const notebookId = $derived($page.params.id); const notebookId = $derived(page.params.id);
const notebook$ = liveQuery(() => db.notebooks.get(notebookId)); const notebook$ = liveQuery(() => db.notebooks.get(notebookId));
const pages$ = pagesForNotebook(notebookId); const pages$ = pagesForNotebook(notebookId);

Binary file not shown.

Binary file not shown.