fix(manacore/web): make notes and finance workbench panels directly usable

Notes: always-visible compose field at top (title + content), expands
on focus, Cmd+Enter to save. Note list below.

Finance: always-visible quick-add with type toggle, amount + description
inline, category chips. No hidden button — ready to use immediately.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-03 14:02:00 +02:00
parent c01eccb852
commit 4ec4694f8f
2 changed files with 346 additions and 409 deletions

View file

@ -1,6 +1,6 @@
<!--
Finance — Workbench ListView
Monthly overview with quick-add transaction and category breakdown.
Always-visible quick-add at top, monthly summary, recent transactions.
-->
<script lang="ts">
import {
@ -47,16 +47,15 @@
let catMap = $derived(new Map(categories.map((c) => [c.id, c])));
// Recent transactions (last 10)
let recentTxs = $derived(monthTxs.slice(0, 10));
let grouped = $derived(groupByDate(recentTxs));
// Quick add
let showAdd = $state(false);
// Always-visible quick add
let addType = $state<TransactionType>('expense');
let addAmount = $state('');
let addDesc = $state('');
let addCatId = $state<string | null>(null);
let showCats = $state(false);
let filteredCats = $derived(categories.filter((c) => c.type === addType));
@ -75,86 +74,91 @@
addAmount = '';
addDesc = '';
addCatId = null;
showAdd = false;
showCats = false;
}
function handleKeydown(e: KeyboardEvent) {
if (e.key === 'Enter') {
e.preventDefault();
handleAdd(e);
}
}
</script>
<div class="finance-list-view">
<!-- Always-visible Quick Add -->
<form class="quick-add" onsubmit={handleAdd}>
<div class="type-toggle">
<button
type="button"
class="type-btn"
class:active={addType === 'expense'}
onclick={() => {
addType = 'expense';
addCatId = null;
}}>Ausgabe</button
>
<button
type="button"
class="type-btn inc"
class:active={addType === 'income'}
onclick={() => {
addType = 'income';
addCatId = null;
}}>Einnahme</button
>
</div>
<div class="input-row">
<input
class="amount-input"
type="text"
inputmode="decimal"
placeholder="0,00"
bind:value={addAmount}
onkeydown={handleKeydown}
/>
<span class="currency">&euro;</span>
<input
class="desc-input"
type="text"
placeholder="Beschreibung..."
bind:value={addDesc}
onkeydown={handleKeydown}
/>
<button type="submit" class="submit-btn" disabled={!addAmount || !addDesc.trim()}>+</button>
</div>
<!-- Category chips -->
<div class="cat-row">
{#each filteredCats as cat (cat.id)}
<button
type="button"
class="cat-chip"
class:selected={addCatId === cat.id}
onclick={() => (addCatId = addCatId === cat.id ? null : cat.id)}
>{cat.emoji} {cat.name}</button
>
{/each}
</div>
</form>
<!-- Monthly Summary -->
<div class="month-summary">
<div class="summary-row">
<span class="summary-label">Einnahmen</span>
<div class="summary-item">
<span class="summary-value income">+{formatCurrency(income)}</span>
<span class="summary-label">Einnahmen</span>
</div>
<div class="summary-row">
<span class="summary-label">Ausgaben</span>
<div class="summary-item">
<span class="summary-value expense">-{formatCurrency(expenses)}</span>
<span class="summary-label">Ausgaben</span>
</div>
<div class="summary-row balance">
<span class="summary-label">Bilanz</span>
<div class="summary-item">
<span class="summary-value" class:income={balance >= 0} class:expense={balance < 0}>
{balance >= 0 ? '+' : ''}{formatCurrency(balance)}
</span>
<span class="summary-label">Bilanz</span>
</div>
</div>
<!-- Add Button -->
{#if !showAdd}
<button class="add-btn" onclick={() => (showAdd = true)}>+ Transaktion</button>
{/if}
<!-- Quick Add Form -->
{#if showAdd}
<form class="add-form" onsubmit={handleAdd}>
<div class="type-toggle">
<button
type="button"
class="type-btn"
class:active={addType === 'expense'}
onclick={() => (addType = 'expense')}>Ausgabe</button
>
<button
type="button"
class="type-btn income"
class:active={addType === 'income'}
onclick={() => (addType = 'income')}>Einnahme</button
>
</div>
<div class="add-row">
<input
class="amount-input"
type="text"
inputmode="decimal"
placeholder="0,00"
bind:value={addAmount}
autofocus
/>
<span class="currency">\u20ac</span>
</div>
<input class="desc-input" type="text" placeholder="Beschreibung..." bind:value={addDesc} />
<div class="cat-row">
{#each filteredCats as cat (cat.id)}
<button
type="button"
class="cat-chip"
class:selected={addCatId === cat.id}
onclick={() => (addCatId = addCatId === cat.id ? null : cat.id)}
>
<span>{cat.emoji}</span>
<span>{cat.name}</span>
</button>
{/each}
</div>
<div class="add-actions">
<button type="button" class="btn-cancel" onclick={() => (showAdd = false)}>Abbrechen</button
>
<button type="submit" class="btn-save" disabled={!addAmount || !addDesc.trim()}
>Hinzufügen</button
>
</div>
</form>
{/if}
<!-- Recent Transactions -->
{#if recentTxs.length > 0}
<div class="tx-list">
@ -163,10 +167,10 @@
{#each dayTxs as tx (tx.id)}
{@const cat = tx.categoryId ? catMap.get(tx.categoryId) : null}
<div class="tx-row">
<span class="tx-cat-emoji">{cat?.emoji ?? '\ud83d\udcb3'}</span>
<span class="tx-emoji">{cat?.emoji ?? '\ud83d\udcb3'}</span>
<div class="tx-info">
<span class="tx-desc">{tx.description}</span>
{#if cat}<span class="tx-cat-name">{cat.name}</span>{/if}
{#if cat}<span class="tx-cat">{cat.name}</span>{/if}
</div>
<span
class="tx-amount"
@ -179,87 +183,27 @@
{/each}
{/each}
</div>
{/if}
{#if txs.length === 0 && !showAdd}
<div class="empty">
<p>Noch keine Transaktionen.</p>
<button class="add-btn" onclick={() => (showAdd = true)}>Erste Transaktion</button>
</div>
{:else}
<div class="empty">Noch keine Transaktionen diesen Monat.</div>
{/if}
</div>
<style>
.finance-list-view {
display: flex;
flex-direction: column;
gap: 0.625rem;
padding: 0.5rem;
}
/* ── Summary ─────────────────────────────────── */
.month-summary {
display: flex;
flex-direction: column;
gap: 0.25rem;
padding: 0.625rem;
border-radius: 0.75rem;
background: var(--color-surface, rgba(255, 255, 255, 0.04));
border: 1px solid var(--color-border, rgba(255, 255, 255, 0.08));
}
.summary-row {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.8125rem;
}
.summary-row.balance {
padding-top: 0.375rem;
margin-top: 0.25rem;
border-top: 1px solid var(--color-border, rgba(255, 255, 255, 0.08));
font-weight: 600;
}
.summary-label {
color: var(--color-muted-foreground);
}
.summary-value {
font-variant-numeric: tabular-nums;
}
.summary-value.income {
color: #22c55e;
}
.summary-value.expense {
color: #ef4444;
}
/* ── Add Form ────────────────────────────────── */
.add-btn {
width: 100%;
padding: 0.5rem;
border-radius: 0.5rem;
background: var(--color-primary, #6366f1);
color: white;
border: none;
font-size: 0.8125rem;
font-weight: 500;
cursor: pointer;
transition: filter 0.15s;
}
.add-btn:hover {
filter: brightness(1.1);
}
.add-form {
display: flex;
flex-direction: column;
gap: 0.5rem;
padding: 0.625rem;
border-radius: 0.75rem;
background: var(--color-surface, rgba(255, 255, 255, 0.06));
padding: 0.5rem;
}
/* ── Quick Add (always visible) ─────────────── */
.quick-add {
display: flex;
flex-direction: column;
gap: 0.375rem;
padding: 0.5rem;
border-radius: 0.625rem;
background: var(--color-surface, rgba(255, 255, 255, 0.04));
border: 1px solid var(--color-border, rgba(255, 255, 255, 0.1));
}
@ -267,46 +211,49 @@
display: flex;
gap: 0.25rem;
}
.type-btn {
flex: 1;
padding: 0.375rem;
padding: 0.3rem;
border-radius: 0.375rem;
font-size: 0.75rem;
font-weight: 500;
font-size: 0.6875rem;
font-weight: 600;
background: transparent;
color: var(--color-muted-foreground);
border: 1px solid var(--color-border, rgba(255, 255, 255, 0.1));
border: 1px solid var(--color-border, rgba(255, 255, 255, 0.08));
cursor: pointer;
transition: all 0.15s;
}
.type-btn.active {
background: rgba(239, 68, 68, 0.15);
background: rgba(239, 68, 68, 0.12);
color: #ef4444;
border-color: rgba(239, 68, 68, 0.3);
border-color: rgba(239, 68, 68, 0.25);
}
.type-btn.income.active {
background: rgba(34, 197, 94, 0.15);
.type-btn.inc.active {
background: rgba(34, 197, 94, 0.12);
color: #22c55e;
border-color: rgba(34, 197, 94, 0.3);
border-color: rgba(34, 197, 94, 0.25);
}
.add-row {
.input-row {
display: flex;
align-items: center;
gap: 0.25rem;
}
.amount-input {
flex: 1;
width: 4.5rem;
background: transparent;
border: none;
border-bottom: 2px solid var(--color-border, rgba(255, 255, 255, 0.15));
color: var(--color-foreground);
font-size: 1.25rem;
font-size: 1rem;
font-weight: 700;
padding: 0.25rem 0;
outline: none;
text-align: right;
font-variant-numeric: tabular-nums;
flex-shrink: 0;
}
.amount-input:focus {
border-color: var(--color-primary, #6366f1);
@ -318,80 +265,110 @@
.currency {
color: var(--color-muted-foreground);
font-size: 1rem;
font-size: 0.8125rem;
flex-shrink: 0;
}
.desc-input {
flex: 1;
background: transparent;
border: none;
border-bottom: 1px solid var(--color-border, rgba(255, 255, 255, 0.1));
border-bottom: 2px solid var(--color-border, rgba(255, 255, 255, 0.15));
color: var(--color-foreground);
font-size: 0.8125rem;
padding: 0.375rem 0;
padding: 0.25rem 0;
outline: none;
min-width: 0;
}
.desc-input:focus {
border-color: var(--color-primary, #6366f1);
}
.desc-input::placeholder {
color: var(--color-muted-foreground);
}
.submit-btn {
width: 1.75rem;
height: 1.75rem;
border-radius: 0.375rem;
background: var(--color-primary, #6366f1);
color: white;
border: none;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: filter 0.15s;
}
.submit-btn:hover:not(:disabled) {
filter: brightness(1.1);
}
.submit-btn:disabled {
opacity: 0.3;
cursor: not-allowed;
}
.cat-row {
display: flex;
flex-wrap: wrap;
gap: 0.25rem;
gap: 0.2rem;
}
.cat-chip {
display: flex;
align-items: center;
gap: 0.25rem;
padding: 0.25rem 0.5rem;
padding: 0.2rem 0.4rem;
border-radius: 9999px;
font-size: 0.6875rem;
background: rgba(255, 255, 255, 0.06);
border: 1px solid var(--color-border, rgba(255, 255, 255, 0.1));
font-size: 0.625rem;
background: rgba(255, 255, 255, 0.04);
border: 1px solid var(--color-border, rgba(255, 255, 255, 0.08));
color: var(--color-foreground);
cursor: pointer;
transition: all 0.15s;
}
.cat-chip:hover {
background: rgba(255, 255, 255, 0.1);
background: rgba(255, 255, 255, 0.08);
}
.cat-chip.selected {
border-color: var(--color-primary, #6366f1);
background: rgba(99, 102, 241, 0.15);
background: rgba(99, 102, 241, 0.12);
}
.add-actions {
/* ── Summary ─────────────────────────────────── */
.month-summary {
display: flex;
justify-content: flex-end;
gap: 0.375rem;
gap: 0.25rem;
}
.btn-cancel,
.btn-save {
padding: 0.3rem 0.625rem;
border-radius: 0.375rem;
.summary-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 0.375rem 0.25rem;
border-radius: 0.5rem;
background: var(--color-surface, rgba(255, 255, 255, 0.04));
border: 1px solid var(--color-border, rgba(255, 255, 255, 0.06));
}
.summary-value {
font-size: 0.75rem;
font-weight: 500;
cursor: pointer;
border: none;
font-weight: 700;
font-variant-numeric: tabular-nums;
}
.btn-cancel {
background: transparent;
.summary-value.income {
color: #22c55e;
}
.summary-value.expense {
color: #ef4444;
}
.summary-label {
font-size: 0.5625rem;
color: var(--color-muted-foreground);
}
.btn-cancel:hover {
background: var(--color-muted, rgba(255, 255, 255, 0.08));
}
.btn-save {
background: var(--color-primary, #6366f1);
color: white;
}
.btn-save:hover:not(:disabled) {
filter: brightness(1.1);
}
.btn-save:disabled {
opacity: 0.4;
cursor: not-allowed;
text-transform: uppercase;
letter-spacing: 0.03em;
}
/* ── Transaction List ────────────────────────── */
@ -402,24 +379,23 @@
}
.day-label {
font-size: 0.6875rem;
font-size: 0.625rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--color-muted-foreground);
padding: 0.375rem 0 0.125rem;
padding: 0.25rem 0 0.125rem;
}
.tx-row {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.375rem 0.25rem;
border-radius: 0.375rem;
gap: 0.375rem;
padding: 0.25rem 0.125rem;
}
.tx-cat-emoji {
font-size: 0.875rem;
.tx-emoji {
font-size: 0.8125rem;
flex-shrink: 0;
}
@ -431,20 +407,20 @@
}
.tx-desc {
font-size: 0.8125rem;
font-size: 0.75rem;
color: var(--color-foreground);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tx-cat-name {
font-size: 0.6875rem;
.tx-cat {
font-size: 0.625rem;
color: var(--color-muted-foreground);
}
.tx-amount {
font-size: 0.8125rem;
font-size: 0.75rem;
font-weight: 600;
font-variant-numeric: tabular-nums;
flex-shrink: 0;
@ -456,15 +432,10 @@
color: #ef4444;
}
/* ── Empty ──────────────────────────────────── */
.empty {
text-align: center;
color: var(--color-muted-foreground);
font-size: 0.875rem;
padding: 2rem 0;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.75rem;
font-size: 0.8125rem;
padding: 1.5rem 0;
}
</style>

View file

@ -1,12 +1,11 @@
<!--
Notes — Workbench ListView
Compact note list with search, inline create, and click to edit.
Always-visible compose field at top, note list below.
-->
<script lang="ts">
import { useAllNotes, searchNotes, getPreview, formatRelativeTime } from './queries';
import { notesStore } from './stores/notes.svelte';
import type { Note } from './types';
import { NOTE_COLORS } from './types';
import type { ViewProps } from '$lib/app-registry';
let { navigate, goBack, params }: ViewProps = $props();
@ -22,28 +21,36 @@
});
let searchQuery = $state('');
let showCreate = $state(false);
let editingId = $state<string | null>(null);
let editTitle = $state('');
let editContent = $state('');
let newTitle = $state('');
let newContent = $state('');
let newColor = $state<string | null>(null);
// Always-visible compose field
let composeTitle = $state('');
let composeContent = $state('');
let composeFocused = $state(false);
let filtered = $derived(searchNotes(notes, searchQuery));
async function handleCreate(e: Event) {
e.preventDefault();
if (!newTitle.trim() && !newContent.trim()) return;
async function handleCompose() {
if (!composeTitle.trim() && !composeContent.trim()) return;
await notesStore.createNote({
title: newTitle.trim() || 'Unbenannt',
content: newContent,
color: newColor,
title: composeTitle.trim() || 'Unbenannt',
content: composeContent,
});
newTitle = '';
newContent = '';
newColor = null;
showCreate = false;
composeTitle = '';
composeContent = '';
composeFocused = false;
}
function handleComposeKeydown(e: KeyboardEvent) {
if (e.key === 'Enter' && e.metaKey) {
e.preventDefault();
handleCompose();
}
if (e.key === 'Escape') {
composeFocused = false;
}
}
function startEdit(note: Note) {
@ -64,66 +71,63 @@
function cancelEdit() {
editingId = null;
}
function handleEditKeydown(e: KeyboardEvent) {
if (e.key === 'Escape') cancelEdit();
}
</script>
<div class="notes-list-view">
<!-- Search -->
<div class="search-row">
<input class="search-input" type="text" placeholder="Suchen..." bind:value={searchQuery} />
<button class="add-btn" onclick={() => (showCreate = !showCreate)} title="Neue Notiz">
+
</button>
</div>
<!-- Inline Create -->
{#if showCreate}
<form class="create-form" onsubmit={handleCreate}>
<input
class="create-title"
type="text"
placeholder="Titel..."
bind:value={newTitle}
autofocus
/>
<!-- Always-visible compose area -->
<div class="compose-area" class:expanded={composeFocused || composeTitle || composeContent}>
<input
class="compose-title"
type="text"
placeholder="Neue Notiz..."
bind:value={composeTitle}
onfocus={() => (composeFocused = true)}
onkeydown={handleComposeKeydown}
/>
{#if composeFocused || composeTitle || composeContent}
<textarea
class="create-content"
placeholder="Notiz schreiben..."
bind:value={newContent}
class="compose-content"
placeholder="Schreibe etwas..."
bind:value={composeContent}
rows="3"
onkeydown={handleComposeKeydown}
></textarea>
<div class="create-footer">
<div class="color-row">
{#each NOTE_COLORS as c}
<button
type="button"
class="color-dot"
class:selected={newColor === c}
style:background={c ?? 'var(--color-muted-foreground)'}
style:opacity={c ? 1 : 0.4}
onclick={() => (newColor = c)}
></button>
{/each}
</div>
<div class="create-actions">
<button type="button" class="btn-cancel" onclick={() => (showCreate = false)}
>Abbrechen</button
<div class="compose-footer">
<span class="compose-hint">&#x2318;+Enter zum Speichern</span>
<div class="compose-actions">
<button
class="btn-cancel"
onclick={() => {
composeTitle = '';
composeContent = '';
composeFocused = false;
}}>Abbrechen</button
>
<button
class="btn-save"
onclick={handleCompose}
disabled={!composeTitle.trim() && !composeContent.trim()}>Speichern</button
>
<button type="submit" class="btn-save">Erstellen</button>
</div>
</div>
</form>
{/if}
</div>
<!-- Search (only when notes exist) -->
{#if notes.length > 3}
<input class="search-input" type="text" placeholder="Suchen..." bind:value={searchQuery} />
{/if}
<!-- Note List -->
<div class="note-list">
{#each filtered as note (note.id)}
{#if editingId === note.id}
<!-- Inline Edit -->
<div class="note-card editing" onkeydown={handleEditKeydown}>
<div
class="note-card editing"
onkeydown={(e) => {
if (e.key === 'Escape') cancelEdit();
}}
>
<input class="edit-title" type="text" bind:value={editTitle} autofocus />
<textarea class="edit-content" bind:value={editContent} rows="4"></textarea>
<div class="edit-actions">
@ -132,7 +136,6 @@
</div>
</div>
{:else}
<!-- Note Card -->
<button
class="note-card"
class:pinned={note.isPinned}
@ -142,7 +145,7 @@
<div class="note-header">
<span class="note-title">{note.title || 'Unbenannt'}</span>
{#if note.isPinned}
<span class="pin-icon" title="Angepinnt">&#x1f4cc;</span>
<span class="pin-icon">&#x1f4cc;</span>
{/if}
</div>
<div class="note-preview">{getPreview(note.content)}</div>
@ -152,13 +155,8 @@
{/each}
</div>
{#if notes.length === 0 && !showCreate}
<div class="empty">
<p>Noch keine Notizen.</p>
<button class="empty-add-btn" onclick={() => (showCreate = true)}
>Erste Notiz erstellen</button
>
</div>
{#if notes.length === 0 && !composeFocused}
<div class="empty">Tippe oben, um deine erste Notiz zu schreiben.</div>
{/if}
</div>
@ -170,13 +168,73 @@
padding: 0.5rem;
}
.search-row {
/* ── Compose Area (always visible) ──────────── */
.compose-area {
display: flex;
flex-direction: column;
gap: 0.25rem;
padding: 0.5rem 0.625rem;
border-radius: 0.625rem;
background: var(--color-surface, rgba(255, 255, 255, 0.04));
border: 1px solid var(--color-border, rgba(255, 255, 255, 0.1));
transition:
border-color 0.15s,
background 0.15s;
}
.compose-area.expanded {
border-color: var(--color-primary, #6366f1);
background: var(--color-surface, rgba(255, 255, 255, 0.06));
}
.compose-title {
background: transparent;
border: none;
color: var(--color-foreground);
font-size: 0.875rem;
font-weight: 600;
padding: 0.25rem 0;
outline: none;
}
.compose-title::placeholder {
color: var(--color-muted-foreground);
font-weight: 400;
}
.compose-content {
background: transparent;
border: none;
color: var(--color-foreground);
font-size: 0.8125rem;
padding: 0.25rem 0;
outline: none;
resize: vertical;
min-height: 2.5rem;
font-family: inherit;
}
.compose-content::placeholder {
color: var(--color-muted-foreground);
}
.compose-footer {
display: flex;
justify-content: space-between;
align-items: center;
}
.compose-hint {
font-size: 0.625rem;
color: var(--color-muted-foreground);
opacity: 0.6;
}
.compose-actions {
display: flex;
gap: 0.375rem;
}
/* ── Search ─────────────────────────────────── */
.search-input {
flex: 1;
background: var(--color-surface, rgba(255, 255, 255, 0.04));
border: 1px solid var(--color-border, rgba(255, 255, 255, 0.1));
border-radius: 0.5rem;
@ -192,102 +250,7 @@
color: var(--color-muted-foreground);
}
.add-btn {
width: 2rem;
height: 2rem;
border-radius: 0.5rem;
background: var(--color-primary, #6366f1);
color: white;
border: none;
font-size: 1.125rem;
font-weight: 300;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: filter 0.15s;
}
.add-btn:hover {
filter: brightness(1.1);
}
/* ── Create / Edit Form ─────────────────────── */
.create-form {
display: flex;
flex-direction: column;
gap: 0.375rem;
padding: 0.625rem;
border-radius: 0.625rem;
background: var(--color-surface, rgba(255, 255, 255, 0.06));
border: 1px solid var(--color-border, rgba(255, 255, 255, 0.1));
}
.create-title,
.edit-title {
background: transparent;
border: none;
color: var(--color-foreground);
font-size: 0.875rem;
font-weight: 600;
padding: 0.25rem 0;
outline: none;
}
.create-title::placeholder,
.edit-title::placeholder {
color: var(--color-muted-foreground);
}
.create-content,
.edit-content {
background: transparent;
border: none;
color: var(--color-foreground);
font-size: 0.8125rem;
padding: 0.25rem 0;
outline: none;
resize: vertical;
min-height: 3rem;
font-family: inherit;
}
.create-content::placeholder {
color: var(--color-muted-foreground);
}
.create-footer {
display: flex;
justify-content: space-between;
align-items: center;
gap: 0.5rem;
}
.color-row {
display: flex;
gap: 0.25rem;
}
.color-dot {
width: 1rem;
height: 1rem;
border-radius: 50%;
border: 2px solid transparent;
cursor: pointer;
transition: transform 0.15s;
}
.color-dot:hover {
transform: scale(1.25);
}
.color-dot.selected {
border-color: white;
box-shadow: 0 0 0 1px var(--color-primary, #6366f1);
}
.create-actions,
.edit-actions {
display: flex;
gap: 0.375rem;
}
/* ── Buttons ────────────────────────────────── */
.btn-cancel,
.btn-save {
padding: 0.3rem 0.625rem;
@ -297,7 +260,6 @@
cursor: pointer;
border: none;
}
.btn-cancel {
background: transparent;
color: var(--color-muted-foreground);
@ -305,14 +267,17 @@
.btn-cancel:hover {
background: var(--color-muted, rgba(255, 255, 255, 0.08));
}
.btn-save {
background: var(--color-primary, #6366f1);
color: white;
}
.btn-save:hover {
.btn-save:hover:not(:disabled) {
filter: brightness(1.1);
}
.btn-save:disabled {
opacity: 0.4;
cursor: not-allowed;
}
/* ── Note Cards ─────────────────────────────── */
.note-list {
@ -338,12 +303,10 @@
.note-card:hover {
background: var(--color-muted, rgba(255, 255, 255, 0.08));
}
.note-card.editing {
cursor: default;
border-left-color: var(--color-primary, #6366f1) !important;
}
.note-card.pinned {
background: rgba(99, 102, 241, 0.04);
}
@ -353,7 +316,6 @@
align-items: center;
gap: 0.375rem;
}
.note-title {
font-size: 0.8125rem;
font-weight: 600;
@ -363,12 +325,10 @@
text-overflow: ellipsis;
white-space: nowrap;
}
.pin-icon {
font-size: 0.6875rem;
flex-shrink: 0;
}
.note-preview {
font-size: 0.75rem;
color: var(--color-muted-foreground);
@ -376,36 +336,42 @@
text-overflow: ellipsis;
white-space: nowrap;
}
.note-meta {
font-size: 0.6875rem;
color: var(--color-muted-foreground);
opacity: 0.7;
}
/* ── Empty ──────────────────────────────────── */
.edit-title {
background: transparent;
border: none;
color: var(--color-foreground);
font-size: 0.875rem;
font-weight: 600;
padding: 0.25rem 0;
outline: none;
}
.edit-content {
background: transparent;
border: none;
color: var(--color-foreground);
font-size: 0.8125rem;
padding: 0.25rem 0;
outline: none;
resize: vertical;
min-height: 3rem;
font-family: inherit;
}
.edit-actions {
display: flex;
gap: 0.375rem;
justify-content: flex-end;
}
.empty {
text-align: center;
color: var(--color-muted-foreground);
font-size: 0.875rem;
padding: 2rem 0;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.75rem;
}
.empty-add-btn {
padding: 0.5rem 1rem;
border-radius: 0.5rem;
background: var(--color-primary, #6366f1);
color: white;
border: none;
font-size: 0.8125rem;
font-weight: 500;
cursor: pointer;
}
.empty-add-btn:hover {
filter: brightness(1.1);
padding: 1.5rem 0;
}
</style>